xref: /dragonfly/contrib/cryptsetup/lib/utils_debug.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <errno.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <dirent.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include "libcryptsetup.h"
18 #include "internal.h"
19 
20 #define MAX_PATHNAME 1024
21 #define MAX_SHORTNAME 64
22 
numeric_name(const char * name)23 static int numeric_name(const char *name)
24 {
25           return (name[0] < '0' || name[0] > '9') ? 0 : 1;
26 }
27 
check_pid(const pid_t pid,const char * dev_name,const char * short_dev_name)28 static int check_pid(const pid_t pid, const char *dev_name, const char *short_dev_name)
29 {
30           char dirpath[MAX_SHORTNAME], fdpath[MAX_SHORTNAME], linkpath[MAX_PATHNAME];
31           DIR *dirp;
32           struct dirent *direntry;
33           size_t len;
34           int r = 0;
35 
36           snprintf(dirpath, sizeof(dirpath), "/proc/%d/fd", pid);
37 
38           if (!(dirp = opendir(dirpath)))
39                     return r;
40 
41           while ((direntry = readdir(dirp))) {
42                     if (!numeric_name(direntry->d_name))
43                               continue;
44 
45                     snprintf(fdpath, sizeof(fdpath), "/proc/%d/fd/%s", pid, direntry->d_name);
46 
47                     if ((len = readlink(fdpath, linkpath, MAX_PATHNAME-1)) < 0)
48                               break;
49                     linkpath[len] = '\0';
50 
51                     if (!strcmp(dev_name, linkpath)) {
52                               r = 1;
53                               break;
54                      }
55 
56                     if (!strcmp(short_dev_name, linkpath)) {
57                               r = 2;
58                               break;
59                      }
60           }
61           closedir(dirp);
62           return r;
63 }
64 
read_proc_info(const pid_t pid,pid_t * ppid,char * name,int max_size)65 static int read_proc_info(const pid_t pid, pid_t *ppid, char *name, int max_size)
66 {
67           char path[MAX_SHORTNAME], info[max_size], c;
68           int fd, xpid, r = 0;
69 
70           snprintf(path, sizeof(path), "/proc/%u/stat", pid);
71           if ((fd = open(path, O_RDONLY)) < 0)
72                     return 0;
73 
74           if (read(fd, info, max_size) > 0 &&
75               sscanf(info, "%d %s %c %d", &xpid, name, &c, ppid) == 4)
76                     r = 1;
77 
78           if (!r) {
79                     *ppid = 0;
80                     name[0] = '\0';
81           }
82           close(fd);
83           return r;
84 }
85 
report_proc(const pid_t pid,const char * dev_name)86 static void report_proc(const pid_t pid, const char *dev_name)
87 {
88           char name[MAX_PATHNAME], name2[MAX_PATHNAME];
89           pid_t ppid, ppid2;
90 
91           if (read_proc_info(pid, &ppid, name, MAX_PATHNAME) &&
92               read_proc_info(ppid, &ppid2, name2, MAX_PATHNAME))
93                     log_dbg("WARNING: Process PID %u %s [PPID %u %s] spying on internal device %s.",
94                               pid, name, ppid, name2, dev_name);
95 }
96 
debug_processes_using_device(const char * dm_name)97 void debug_processes_using_device(const char *dm_name)
98 {
99           char short_dev_name[MAX_SHORTNAME], dev_name[MAX_PATHNAME];
100           DIR *proc_dir;
101           struct dirent *proc_dentry;
102           struct stat st;
103           pid_t pid;
104 
105           if (crypt_get_debug_level() != CRYPT_LOG_DEBUG)
106                     return;
107 
108           snprintf(dev_name, sizeof(dev_name), "/dev/mapper/%s", dm_name);
109           if (stat(dev_name, &st) || !S_ISBLK(st.st_mode))
110                     return;
111           snprintf(short_dev_name, sizeof(short_dev_name), "/dev/dm-%u", minor(st.st_rdev));
112 
113           if (!(proc_dir = opendir("/proc")))
114                     return;
115 
116           while ((proc_dentry = readdir(proc_dir))) {
117                     if (!numeric_name(proc_dentry->d_name))
118                               continue;
119 
120                     pid = atoi(proc_dentry->d_name);
121                     switch(check_pid(pid, dev_name, short_dev_name)) {
122                     case 1: report_proc(pid, dev_name);
123                               break;
124                     case 2: report_proc(pid, short_dev_name);
125                     default:
126                               break;
127                     }
128           }
129           closedir(proc_dir);
130 }
131