text version synced
[platform/upstream/cryptsetup.git] / lib / utils_debug.c
1 /*
2  * Temporary debug code to find processes locking internal cryptsetup devices.
3  * This code is intended to run only in debug mode.
4  *
5  * inspired by psmisc/fuser proc scanning code
6  *
7  * Copyright (C) 2009-2011, Red Hat, Inc. All rights reserved.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * version 2 as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <dirent.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include "libcryptsetup.h"
34 #include "internal.h"
35
36 #define MAX_PATHNAME 1024
37 #define MAX_SHORTNAME 64
38
39 static int numeric_name(const char *name)
40 {
41         return (name[0] < '0' || name[0] > '9') ? 0 : 1;
42 }
43
44 static int check_pid(const pid_t pid, const char *dev_name, const char *short_dev_name)
45 {
46         char dirpath[MAX_SHORTNAME], fdpath[MAX_SHORTNAME], linkpath[MAX_PATHNAME];
47         DIR *dirp;
48         struct dirent *direntry;
49         ssize_t len;
50         int r = 0;
51
52         snprintf(dirpath, sizeof(dirpath), "/proc/%d/fd", pid);
53
54         if (!(dirp = opendir(dirpath)))
55                 return r;
56
57         while ((direntry = readdir(dirp))) {
58                 if (!numeric_name(direntry->d_name))
59                         continue;
60
61                 snprintf(fdpath, sizeof(fdpath), "/proc/%d/fd/%s", pid, direntry->d_name);
62
63                 if ((len = readlink(fdpath, linkpath, MAX_PATHNAME-1)) < 0)
64                         break;
65                 linkpath[len] = '\0';
66
67                 if (!strcmp(dev_name, linkpath)) {
68                         r = 1;
69                         break;
70                  }
71
72                 if (!strcmp(short_dev_name, linkpath)) {
73                         r = 2;
74                         break;
75                  }
76         }
77         closedir(dirp);
78         return r;
79 }
80
81 static int read_proc_info(const pid_t pid, pid_t *ppid, char *name, int max_size)
82 {
83         char path[MAX_SHORTNAME], info[max_size], c;
84         int fd, xpid, r = 0;
85
86         snprintf(path, sizeof(path), "/proc/%u/stat", pid);
87         if ((fd = open(path, O_RDONLY)) < 0)
88                 return 0;
89
90         if (read(fd, info, max_size) > 0 &&
91             sscanf(info, "%d %s %c %d", &xpid, name, &c, ppid) == 4)
92                 r = 1;
93
94         if (!r) {
95                 *ppid = 0;
96                 name[0] = '\0';
97         }
98         close(fd);
99         return r;
100 }
101
102 static void report_proc(const pid_t pid, const char *dev_name)
103 {
104         char name[MAX_PATHNAME], name2[MAX_PATHNAME];
105         pid_t ppid, ppid2;
106
107         if (read_proc_info(pid, &ppid, name, MAX_PATHNAME) &&
108             read_proc_info(ppid, &ppid2, name2, MAX_PATHNAME))
109                 log_dbg("WARNING: Process PID %u %s [PPID %u %s] spying on internal device %s.",
110                         pid, name, ppid, name2, dev_name);
111 }
112
113 void debug_processes_using_device(const char *dm_name)
114 {
115         char short_dev_name[MAX_SHORTNAME], dev_name[MAX_PATHNAME];
116         DIR *proc_dir;
117         struct dirent *proc_dentry;
118         struct stat st;
119         pid_t pid;
120
121         if (crypt_get_debug_level() != CRYPT_LOG_DEBUG)
122                 return;
123
124         snprintf(dev_name, sizeof(dev_name), "/dev/mapper/%s", dm_name);
125         if (stat(dev_name, &st) || !S_ISBLK(st.st_mode))
126                 return;
127         snprintf(short_dev_name, sizeof(short_dev_name), "/dev/dm-%u", minor(st.st_rdev));
128
129         if (!(proc_dir = opendir("/proc")))
130                 return;
131
132         while ((proc_dentry = readdir(proc_dir))) {
133                 if (!numeric_name(proc_dentry->d_name))
134                         continue;
135
136                 pid = atoi(proc_dentry->d_name);
137                 switch(check_pid(pid, dev_name, short_dev_name)) {
138                 case 1: report_proc(pid, dev_name);
139                         break;
140                 case 2: report_proc(pid, short_dev_name);
141                 default:
142                         break;
143                 }
144         }
145         closedir(proc_dir);
146 }