]>
Commit | Line | Data |
---|---|---|
600e6e52 MY |
1 | /* |
2 | * lsfd(1) - list file descriptors | |
3 | * | |
4 | * Copyright (C) 2021 Red Hat, Inc. All rights reserved. | |
5 | * Written by Masatake YAMATO <yamato@redhat.com> | |
6 | * | |
7 | * Very generally based on lsof(8) by Victor A. Abell <abe@purdue.edu> | |
8 | * It supports multiple OSes. lsfd specializes to Linux. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it would be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; if not, write to the Free Software Foundation, | |
22 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 | */ | |
24 | ||
63f39385 MY |
25 | #include <unistd.h> |
26 | ||
c5eb81b3 MY |
27 | #ifdef HAVE_LINUX_NSFS_H |
28 | # include <linux/nsfs.h> | |
29 | # if defined(NS_GET_NSTYPE) | |
30 | # define USE_NS_GET_API 1 | |
31 | # include <sys/ioctl.h> | |
32 | # endif | |
33 | #endif | |
34 | #include <linux/sched.h> | |
de9ec7ee | 35 | #include <sys/shm.h> |
c5eb81b3 | 36 | |
e3dedb01 MY |
37 | #include <fcntl.h> |
38 | #include <sys/stat.h> | |
39 | #include <mqueue.h> /* mq_open */ | |
40 | ||
22b7cf51 | 41 | #include "buffer.h" |
602dbcd9 | 42 | #include "idcache.h" |
5adbe6cf | 43 | #include "strutils.h" |
600e6e52 | 44 | |
de9ec7ee | 45 | #include "procfs.h" |
600e6e52 MY |
46 | |
47 | #include "lsfd.h" | |
48 | ||
602dbcd9 MY |
49 | static struct idcache *username_cache; |
50 | ||
c1cb2735 MY |
51 | static size_t pagesize; |
52 | ||
a4778cb5 MY |
53 | static const char *assocstr[N_ASSOCS] = { |
54 | [ASSOC_CWD] = "cwd", | |
55 | [ASSOC_EXE] = "exe", | |
56 | /* "root" appears as user names, too. | |
57 | * So we use "rtd" here instead of "root". */ | |
58 | [ASSOC_ROOT] = "rtd", | |
f1d0b0bb MY |
59 | [ASSOC_NS_CGROUP] = "cgroup", |
60 | [ASSOC_NS_IPC] = "ipc", | |
61 | [ASSOC_NS_MNT] = "mnt", | |
62 | [ASSOC_NS_NET] = "net", | |
63 | [ASSOC_NS_PID] = "pid", | |
64 | [ASSOC_NS_PID4C] = "pid4c", | |
65 | [ASSOC_NS_TIME] = "time", | |
66 | [ASSOC_NS_TIME4C] = "time4c", | |
67 | [ASSOC_NS_USER] = "user", | |
68 | [ASSOC_NS_UTS] = "uts", | |
7388e0b3 | 69 | [ASSOC_MEM] = "mem", |
202a50e6 | 70 | [ASSOC_SHM] = "shm", |
a4778cb5 MY |
71 | }; |
72 | ||
600e6e52 MY |
73 | static const char *strftype(mode_t ftype) |
74 | { | |
75 | switch (ftype) { | |
76 | case S_IFBLK: | |
77 | return "BLK"; | |
78 | case S_IFCHR: | |
79 | return "CHR"; | |
80 | case S_IFDIR: | |
81 | return "DIR"; | |
82 | case S_IFIFO: | |
83 | return "FIFO"; | |
84 | case S_IFLNK: | |
85 | return "LINK"; | |
86 | case S_IFREG: | |
87 | return "REG"; | |
88 | case S_IFSOCK: | |
89 | return "SOCK"; | |
90 | default: | |
91 | return "UNKN"; | |
92 | } | |
93 | } | |
94 | ||
908ac36f | 95 | extern void lsfd_decode_file_flags(struct ul_buffer *buf, int flags); |
22b7cf51 MY |
96 | static void file_fill_flags_buf(struct ul_buffer *buf, int flags) |
97 | { | |
908ac36f | 98 | lsfd_decode_file_flags(buf, flags); |
22b7cf51 MY |
99 | } |
100 | ||
5701c643 MY |
101 | #define does_file_has_fdinfo_alike(file) \ |
102 | ((file)->association >= 0 \ | |
103 | || (file)->association == -ASSOC_SHM \ | |
104 | || (file)->association == -ASSOC_MEM) | |
105 | ||
174ab64a | 106 | static uint64_t get_map_length(struct file *file) |
99ce4250 | 107 | { |
174ab64a | 108 | uint64_t res = 0; |
99ce4250 | 109 | |
c1cb2735 | 110 | if (is_association(file, SHM) || is_association(file, MEM)) |
99ce4250 | 111 | res = (file->map_end - file->map_start) / pagesize; |
99ce4250 KZ |
112 | |
113 | return res; | |
114 | } | |
115 | ||
c032850e MY |
116 | void decode_source(char *buf, size_t bufsize, |
117 | unsigned int dev_major, unsigned int dev_minor, | |
118 | enum decode_source_level level) | |
119 | { | |
120 | if (bufsize == 0) | |
121 | return; | |
122 | ||
123 | buf[0] = '\0'; | |
124 | ||
125 | if (level & DECODE_SOURCE_FILESYS_BIT) { | |
126 | if (dev_major == 0) { | |
127 | const char *filesystem = get_nodev_filesystem(dev_minor); | |
128 | if (filesystem) { | |
129 | xstrncpy(buf, filesystem, bufsize); | |
130 | return; | |
131 | } | |
132 | } | |
133 | } | |
134 | ||
135 | if (level & DECODE_SOURCE_PARTITION_BIT) { | |
136 | dev_t dev = makedev(dev_major, dev_minor); | |
137 | const char *partition = get_partition(dev); | |
138 | if (partition) { | |
139 | xstrncpy(buf, partition, bufsize); | |
140 | return; | |
141 | } | |
142 | } | |
143 | ||
144 | if (level & DECODE_SOURCE_MAJMIN_BIT) | |
145 | snprintf(buf, bufsize, "%u:%u", | |
146 | dev_major, | |
147 | dev_minor); | |
148 | } | |
149 | ||
30ac13fa MY |
150 | static char *strnrstr(const char *haystack, const char *needle, size_t needle_len) |
151 | { | |
152 | char *last = strstr(haystack, needle); | |
153 | if (last == NULL) | |
154 | return NULL; | |
155 | ||
156 | do { | |
157 | char *current = strstr(last + needle_len, needle); | |
158 | if (current == NULL) | |
159 | return last; | |
160 | last = current; | |
161 | } while (1); | |
162 | } | |
163 | ||
600e6e52 MY |
164 | static bool file_fill_column(struct proc *proc, |
165 | struct file *file, | |
166 | struct libscols_line *ln, | |
167 | int column_id, | |
168 | size_t column_index) | |
169 | { | |
170 | char *str = NULL; | |
171 | mode_t ftype; | |
c032850e | 172 | char buf[BUFSIZ]; |
600e6e52 MY |
173 | |
174 | switch(column_id) { | |
175 | case COL_COMMAND: | |
64fddd62 MY |
176 | if (proc->command |
177 | && scols_line_set_data(ln, column_index, proc->command)) | |
600e6e52 MY |
178 | err(EXIT_FAILURE, _("failed to add output data")); |
179 | return true; | |
180 | case COL_NAME: | |
30ac13fa MY |
181 | if (file->name && file->stat.st_nlink == 0) { |
182 | char *d = strnrstr(file->name, "(deleted)", | |
183 | sizeof("(deleted)") - 1); | |
184 | if (d) { | |
185 | int r; | |
186 | *d = '\0'; | |
187 | r = scols_line_set_data(ln, column_index, file->name); | |
188 | *d = '('; | |
189 | if (r) | |
190 | err(EXIT_FAILURE, _("failed to add output data")); | |
191 | return true; | |
192 | } | |
193 | } | |
194 | /* FALL THROUGH */ | |
195 | case COL_KNAME: | |
64fddd62 MY |
196 | if (file->name |
197 | && scols_line_set_data(ln, column_index, file->name)) | |
600e6e52 MY |
198 | err(EXIT_FAILURE, _("failed to add output data")); |
199 | return true; | |
be019f66 | 200 | case COL_STTYPE: |
600e6e52 MY |
201 | case COL_TYPE: |
202 | ftype = file->stat.st_mode & S_IFMT; | |
203 | if (scols_line_set_data(ln, column_index, strftype(ftype))) | |
204 | err(EXIT_FAILURE, _("failed to add output data")); | |
205 | return true; | |
1deac7db | 206 | case COL_USER: |
d754885a | 207 | add_uid(username_cache, (int)proc->uid); |
1deac7db MY |
208 | if (scols_line_set_data(ln, column_index, |
209 | get_id(username_cache, | |
d754885a | 210 | (int)proc->uid)->name)) |
1deac7db MY |
211 | err(EXIT_FAILURE, _("failed to add output data")); |
212 | return true; | |
d40b7b4f MY |
213 | case COL_OWNER: |
214 | add_uid(username_cache, (int)file->stat.st_uid); | |
215 | if (scols_line_set_data(ln, column_index, | |
216 | get_id(username_cache, | |
217 | (int)file->stat.st_uid)->name)) | |
218 | err(EXIT_FAILURE, _("failed to add output data")); | |
219 | return true; | |
7591516d MY |
220 | case COL_DEVTYPE: |
221 | if (scols_line_set_data(ln, column_index, | |
222 | "nodev")) | |
223 | err(EXIT_FAILURE, _("failed to add output data")); | |
224 | return true; | |
2ac344fb | 225 | case COL_FD: |
c0f84bd1 | 226 | if (!is_opened_file(file)) |
2ac344fb MY |
227 | return false; |
228 | /* FALL THROUGH */ | |
229 | case COL_ASSOC: | |
c0f84bd1 | 230 | if (is_opened_file(file)) |
a4778cb5 MY |
231 | xasprintf(&str, "%d", file->association); |
232 | else { | |
233 | int assoc = file->association * -1; | |
234 | if (assoc >= N_ASSOCS) | |
235 | return false; /* INTERNAL ERROR */ | |
157a4696 | 236 | str = xstrdup(assocstr[assoc]); |
a4778cb5 | 237 | } |
2ac344fb | 238 | break; |
fb0cd93c MY |
239 | case COL_INODE: |
240 | xasprintf(&str, "%llu", (unsigned long long)file->stat.st_ino); | |
241 | break; | |
618db964 | 242 | case COL_SOURCE: |
c032850e MY |
243 | decode_source(buf, sizeof(buf), major(file->stat.st_dev), minor(file->stat.st_dev), |
244 | DECODE_SOURCE_FILESYS); | |
245 | str = xstrdup(buf); | |
246 | break; | |
b3a23f4d | 247 | case COL_PARTITION: |
c032850e MY |
248 | decode_source(buf, sizeof(buf), major(file->stat.st_dev), minor(file->stat.st_dev), |
249 | DECODE_SOURCE_PARTITION); | |
250 | str = xstrdup(buf); | |
251 | break; | |
15b47157 | 252 | case COL_DEV: |
6cdc9906 | 253 | case COL_MAJMIN: |
c032850e MY |
254 | decode_source(buf, sizeof(buf), major(file->stat.st_dev), minor(file->stat.st_dev), |
255 | DECODE_SOURCE_MAJMIN); | |
256 | str = xstrdup(buf); | |
6ada3523 | 257 | break; |
15b47157 MY |
258 | case COL_RDEV: |
259 | xasprintf(&str, "%u:%u", | |
260 | major(file->stat.st_rdev), | |
261 | minor(file->stat.st_rdev)); | |
262 | break; | |
600e6e52 | 263 | case COL_PID: |
6008b460 MY |
264 | xasprintf(&str, "%d", (int)proc->leader->pid); |
265 | break; | |
266 | case COL_TID: | |
600e6e52 | 267 | xasprintf(&str, "%d", (int)proc->pid); |
64fddd62 | 268 | break; |
04729461 | 269 | case COL_UID: |
d754885a | 270 | xasprintf(&str, "%d", (int)proc->uid); |
64fddd62 | 271 | break; |
d40b7b4f MY |
272 | case COL_FUID: |
273 | xasprintf(&str, "%d", (int)file->stat.st_uid); | |
274 | break; | |
bec8c271 | 275 | case COL_SIZE: |
e438aee7 | 276 | xasprintf(&str, "%jd", (intmax_t)file->stat.st_size); |
bec8c271 | 277 | break; |
6fab1215 | 278 | case COL_NLINK: |
1801c172 | 279 | xasprintf(&str, "%ju", (uintmax_t)file->stat.st_nlink); |
6fab1215 MY |
280 | break; |
281 | case COL_DELETED: | |
282 | xasprintf(&str, "%d", file->stat.st_nlink == 0); | |
283 | break; | |
f26ddd08 MY |
284 | case COL_KTHREAD: |
285 | xasprintf(&str, "%u", proc->kthread); | |
286 | break; | |
22b7cf51 | 287 | case COL_MNT_ID: |
c0f84bd1 | 288 | xasprintf(&str, "%d", is_opened_file(file)? file->mnt_id: 0); |
22b7cf51 | 289 | break; |
17adf20f | 290 | case COL_MODE: |
5701c643 MY |
291 | if (does_file_has_fdinfo_alike(file)) |
292 | xasprintf(&str, "%c%c%c", | |
17adf20f | 293 | file->mode & S_IRUSR? 'r': '-', |
5701c643 | 294 | file->mode & S_IWUSR? 'w': '-', |
eb313075 | 295 | (is_mapped_file(file) |
5701c643 MY |
296 | && file->mode & S_IXUSR)? 'x': '-'); |
297 | else | |
298 | xasprintf(&str, "---"); | |
17adf20f | 299 | break; |
92c00d72 MY |
300 | case COL_XMODE: { |
301 | char r, w, x; | |
30ac13fa | 302 | char D = file->stat.st_nlink == 0? 'D': '-'; |
9e381503 MY |
303 | char L = file->locked.write? 'L' |
304 | :file->locked.read? 'l' | |
305 | : '-'; | |
da901314 | 306 | char m = file->multiplexed? 'm': '-'; |
9e381503 | 307 | |
92c00d72 MY |
308 | if (does_file_has_fdinfo_alike(file)) { |
309 | r = file->mode & S_IRUSR? 'r': '-'; | |
310 | w = file->mode & S_IWUSR? 'w': '-'; | |
311 | x = (is_mapped_file(file) | |
312 | && file->mode & S_IXUSR)? 'x': '-'; | |
313 | } else | |
314 | r = w = x = '-'; | |
da901314 | 315 | xasprintf(&str, "%c%c%c%c%c%c", r, w, x, D, L, m); |
92c00d72 MY |
316 | break; |
317 | } | |
22b7cf51 | 318 | case COL_POS: |
5adbe6cf | 319 | xasprintf(&str, "%" PRIu64, |
5701c643 | 320 | (does_file_has_fdinfo_alike(file))? file->pos: 0); |
22b7cf51 MY |
321 | break; |
322 | case COL_FLAGS: { | |
323 | struct ul_buffer buf = UL_INIT_BUFFER; | |
324 | ||
c0f84bd1 | 325 | if (!is_opened_file(file)) |
22b7cf51 MY |
326 | return true; |
327 | ||
a9634212 | 328 | if (file->sys_flags == 0) |
22b7cf51 MY |
329 | return true; |
330 | ||
a9634212 | 331 | file_fill_flags_buf(&buf, file->sys_flags); |
22b7cf51 MY |
332 | if (ul_buffer_is_empty(&buf)) |
333 | return true; | |
334 | str = ul_buffer_get_data(&buf, NULL, NULL); | |
335 | break; | |
336 | } | |
63f39385 | 337 | case COL_MAPLEN: |
eb313075 | 338 | if (!is_mapped_file(file)) |
63f39385 | 339 | return true; |
05f51387 | 340 | xasprintf(&str, "%ju", (uintmax_t)get_map_length(file)); |
63f39385 | 341 | break; |
64fddd62 MY |
342 | default: |
343 | return false; | |
1e83b1fc | 344 | } |
600e6e52 | 345 | |
64fddd62 MY |
346 | if (!str) |
347 | err(EXIT_FAILURE, _("failed to add output data")); | |
348 | if (scols_line_refer_data(ln, column_index, str)) | |
349 | err(EXIT_FAILURE, _("failed to add output data")); | |
350 | return true; | |
600e6e52 MY |
351 | } |
352 | ||
9e381503 MY |
353 | enum lock_mode { |
354 | LOCK_NONE, | |
355 | READ_LOCK, | |
356 | WRITE_LOCK, | |
357 | }; | |
358 | ||
359 | static unsigned int parse_lock_line(const char *line) | |
360 | { | |
361 | char mode[6] = {0}; | |
362 | ||
363 | /* Exapmles of lines: | |
364 | ---------------------------------------------------- | |
365 | 1: FLOCK ADVISORY READ 2283292 fd:03:26219728 0 EOF | |
366 | 1: FLOCK ADVISORY WRITE 2283321 fd:03:26219728 0 EOF | |
367 | 1: POSIX ADVISORY READ 2283190 fd:03:26219728 0 0 | |
368 | 1: POSIX ADVISORY WRITE 2283225 fd:03:26219728 0 0 | |
369 | 1: OFDLCK ADVISORY READ -1 fd:03:26219728 0 0 | |
370 | 1: OFDLCK ADVISORY WRITE -1 fd:03:26219728 0 0 | |
371 | 1: LEASE ACTIVE WRITE 2328907 fd:03:26219472 0 EOF | |
372 | 1: LEASE ACTIVE READ 2326777 fd:03:26219472 0 EOF | |
373 | ---------------------------------------------------- */ | |
374 | ||
375 | if (sscanf(line, "%*d: %*s %*s %5s %*s", mode) != 1) | |
376 | return LOCK_NONE; | |
377 | ||
378 | if (strcmp(mode, "READ") == 0) | |
379 | return READ_LOCK; | |
380 | ||
381 | if (strcmp(mode, "WRITE") == 0) | |
382 | return WRITE_LOCK; | |
383 | ||
384 | return LOCK_NONE; | |
385 | } | |
386 | ||
22b7cf51 MY |
387 | static int file_handle_fdinfo(struct file *file, const char *key, const char* value) |
388 | { | |
399b78ea KZ |
389 | int rc; |
390 | ||
22b7cf51 | 391 | if (strcmp(key, "pos") == 0) { |
399b78ea KZ |
392 | rc = ul_strtou64(value, &file->pos, 10); |
393 | ||
22b7cf51 | 394 | } else if (strcmp(key, "flags") == 0) { |
399b78ea | 395 | rc = ul_strtou32(value, &file->sys_flags, 8); |
5adbe6cf | 396 | |
22b7cf51 | 397 | } else if (strcmp(key, "mnt_id") == 0) { |
399b78ea KZ |
398 | rc = ul_strtou32(value, &file->mnt_id, 10); |
399 | ||
9e381503 MY |
400 | } else if (strcmp(key, "lock") == 0) { |
401 | switch (parse_lock_line(value)) { | |
402 | case READ_LOCK: | |
403 | file->locked.read = 1; | |
404 | break; | |
405 | case WRITE_LOCK: | |
406 | file->locked.write = 1; | |
407 | break; | |
408 | } | |
409 | rc = 1; | |
399b78ea KZ |
410 | } else |
411 | return 0; /* ignore -- unknown item */ | |
412 | ||
413 | if (rc < 0) | |
414 | return 0; /* ignore -- parse failed */ | |
415 | ||
416 | return 1; /* success */ | |
22b7cf51 MY |
417 | } |
418 | ||
600e6e52 MY |
419 | static void file_free_content(struct file *file) |
420 | { | |
421 | free(file->name); | |
422 | } | |
423 | ||
de9ec7ee MY |
424 | static unsigned long get_minor_for_sysvipc(void) |
425 | { | |
426 | int id; | |
427 | void *start; | |
428 | ||
429 | pid_t self = getpid(); | |
430 | struct path_cxt *pc = NULL; | |
431 | char map_file[sizeof("map_files/0000000000000000-ffffffffffffffff")]; | |
432 | ||
433 | struct stat sb; | |
434 | unsigned long m = 0; | |
435 | ||
436 | id = shmget(IPC_PRIVATE, pagesize, IPC_CREAT | 0600); | |
437 | if (id == -1) | |
438 | return 0; | |
439 | ||
440 | start = shmat(id, NULL, SHM_RDONLY); | |
441 | if (start == (void *) -1) { | |
442 | shmctl(id, IPC_RMID, NULL); | |
443 | return 0; | |
444 | } | |
445 | ||
446 | pc = ul_new_path(NULL); | |
447 | if (!pc) | |
448 | goto out; | |
449 | ||
450 | if (procfs_process_init_path(pc, self) != 0) | |
451 | goto out; | |
452 | ||
453 | snprintf(map_file, sizeof(map_file), | |
454 | "map_files/%lx-%lx", (long)start, (long)start + pagesize); | |
455 | if (ul_path_stat(pc, &sb, 0, map_file) < 0) | |
456 | goto out; | |
457 | ||
458 | m = minor(sb.st_dev); | |
459 | out: | |
460 | if (pc) | |
461 | ul_unref_path(pc); | |
462 | shmdt(start); | |
463 | shmctl(id, IPC_RMID, NULL); | |
464 | return m; | |
465 | } | |
466 | ||
e3dedb01 MY |
467 | static unsigned long get_minor_for_mqueue(void) |
468 | { | |
469 | mqd_t mq; | |
470 | char mq_name[BUFSIZ]; | |
471 | struct mq_attr attr = { | |
472 | .mq_maxmsg = 1, | |
473 | .mq_msgsize = 1, | |
474 | }; | |
475 | ||
476 | pid_t self = getpid(); | |
477 | struct stat sb; | |
478 | ||
479 | snprintf(mq_name, sizeof(mq_name), "/.lsfd-mqueue-nodev-test:%d", self); | |
480 | mq = mq_open(mq_name, O_CREAT|O_EXCL | O_RDONLY, S_IRUSR | S_IWUSR, &attr); | |
481 | if (mq < 0) | |
482 | return 0; | |
483 | ||
484 | if (fstat((int)mq, &sb) < 0) { | |
485 | mq_close(mq); | |
486 | mq_unlink(mq_name); | |
487 | return 0; | |
488 | } | |
489 | ||
490 | mq_close(mq); | |
491 | mq_unlink(mq_name); | |
492 | return minor(sb.st_dev); | |
493 | } | |
494 | ||
602dbcd9 MY |
495 | static void file_class_initialize(void) |
496 | { | |
de9ec7ee MY |
497 | unsigned long m; |
498 | ||
c1cb2735 MY |
499 | if (!pagesize) |
500 | pagesize = getpagesize(); | |
501 | ||
602dbcd9 MY |
502 | username_cache = new_idcache(); |
503 | if (!username_cache) | |
504 | err(EXIT_FAILURE, _("failed to allocate UID cache")); | |
de9ec7ee MY |
505 | |
506 | m = get_minor_for_sysvipc(); | |
507 | if (m) | |
508 | add_nodev(m, "tmpfs"); | |
e3dedb01 MY |
509 | |
510 | m = get_minor_for_mqueue(); | |
511 | if (m) | |
512 | add_nodev(m, "mqueue"); | |
602dbcd9 MY |
513 | } |
514 | ||
515 | static void file_class_finalize(void) | |
516 | { | |
517 | free_idcache(username_cache); | |
518 | } | |
519 | ||
600e6e52 MY |
520 | const struct file_class file_class = { |
521 | .super = NULL, | |
a4778cb5 | 522 | .size = sizeof(struct file), |
602dbcd9 MY |
523 | .initialize_class = file_class_initialize, |
524 | .finalize_class = file_class_finalize, | |
600e6e52 | 525 | .fill_column = file_fill_column, |
22b7cf51 | 526 | .handle_fdinfo = file_handle_fdinfo, |
600e6e52 MY |
527 | .free_content = file_free_content, |
528 | }; | |
c5eb81b3 MY |
529 | |
530 | /* | |
531 | * Regular files on NSFS | |
532 | */ | |
533 | ||
534 | struct nsfs_file { | |
535 | struct file file; | |
536 | int clone_type; | |
537 | }; | |
538 | ||
539 | static const char *get_ns_type_name(int clone_type) | |
540 | { | |
541 | switch (clone_type) { | |
542 | #ifdef USE_NS_GET_API | |
543 | case CLONE_NEWNS: | |
544 | return "mnt"; | |
545 | case CLONE_NEWCGROUP: | |
546 | return "cgroup"; | |
547 | case CLONE_NEWUTS: | |
548 | return "uts"; | |
549 | case CLONE_NEWIPC: | |
550 | return "ipc"; | |
551 | case CLONE_NEWUSER: | |
552 | return "user"; | |
553 | case CLONE_NEWPID: | |
554 | return "pid"; | |
555 | case CLONE_NEWNET: | |
556 | return "net"; | |
557 | #ifdef CLONE_NEWTIME | |
558 | case CLONE_NEWTIME: | |
559 | return "time"; | |
560 | #endif /* CLONE_NEWTIME */ | |
561 | #endif /* USE_NS_GET_API */ | |
562 | default: | |
563 | return "unknown"; | |
564 | } | |
565 | } | |
566 | ||
567 | static void init_nsfs_file_content(struct file *file) | |
568 | { | |
569 | struct nsfs_file *nsfs_file = (struct nsfs_file *)file; | |
570 | nsfs_file->clone_type = -1; | |
571 | ||
572 | #ifdef USE_NS_GET_API | |
573 | char *proc_fname = NULL; | |
574 | int ns_fd; | |
575 | int ns_type; | |
576 | ||
577 | if (is_association (file, NS_CGROUP)) | |
578 | nsfs_file->clone_type = CLONE_NEWCGROUP; | |
579 | else if (is_association (file, NS_IPC)) | |
580 | nsfs_file->clone_type = CLONE_NEWIPC; | |
581 | else if (is_association (file, NS_MNT)) | |
582 | nsfs_file->clone_type = CLONE_NEWNS; | |
583 | else if (is_association (file, NS_NET)) | |
584 | nsfs_file->clone_type = CLONE_NEWNET; | |
585 | else if (is_association (file, NS_PID) | |
586 | || is_association (file, NS_PID4C)) | |
587 | nsfs_file->clone_type = CLONE_NEWPID; | |
588 | #ifdef CLONE_NEWTIME | |
589 | else if (is_association (file, NS_TIME) | |
590 | || is_association (file, NS_TIME4C)) | |
591 | nsfs_file->clone_type = CLONE_NEWTIME; | |
592 | #endif | |
593 | else if (is_association (file, NS_USER)) | |
594 | nsfs_file->clone_type = CLONE_NEWUSER; | |
595 | else if (is_association (file, NS_UTS)) | |
596 | nsfs_file->clone_type = CLONE_NEWUTS; | |
597 | ||
598 | if (nsfs_file->clone_type != -1) | |
599 | return; | |
600 | ||
601 | if (!is_opened_file(file)) | |
602 | return; | |
603 | ||
604 | if (!file->name) | |
605 | return; | |
606 | ||
607 | xasprintf(&proc_fname, "/proc/%d/fd/%d", | |
608 | file->proc->pid, file->association); | |
609 | ns_fd = open(proc_fname, O_RDONLY); | |
610 | free(proc_fname); | |
611 | if (ns_fd < 0) | |
612 | return; | |
613 | ||
614 | ns_type = ioctl(ns_fd, NS_GET_NSTYPE); | |
615 | close(ns_fd); | |
616 | if (ns_type < 0) | |
617 | return; | |
618 | ||
619 | nsfs_file->clone_type = ns_type; | |
620 | #endif /* USE_NS_GET_API */ | |
621 | } | |
622 | ||
623 | ||
624 | static bool nsfs_file_fill_column(struct proc *proc __attribute__((__unused__)), | |
625 | struct file *file, | |
626 | struct libscols_line *ln, | |
627 | int column_id, | |
628 | size_t column_index) | |
629 | { | |
630 | struct nsfs_file *nsfs_file = (struct nsfs_file *)file; | |
631 | char *name = NULL; | |
632 | ||
633 | if (nsfs_file->clone_type == -1) | |
634 | return false; | |
635 | ||
636 | switch (column_id) { | |
637 | case COL_NS_NAME: | |
638 | xasprintf(&name, "%s:[%llu]", | |
639 | get_ns_type_name(nsfs_file->clone_type), | |
640 | (unsigned long long)file->stat.st_ino); | |
641 | break; | |
642 | case COL_NS_TYPE: | |
643 | if (scols_line_set_data(ln, column_index, | |
644 | get_ns_type_name(nsfs_file->clone_type))) | |
645 | err(EXIT_FAILURE, _("failed to add output data")); | |
646 | return true; | |
647 | default: | |
648 | return false; | |
649 | } | |
650 | ||
651 | if (name && scols_line_refer_data(ln, column_index, name)) | |
652 | err(EXIT_FAILURE, _("failed to add output data")); | |
653 | ||
654 | return true; | |
655 | } | |
656 | ||
657 | const struct file_class nsfs_file_class = { | |
658 | .super = &file_class, | |
659 | .size = sizeof(struct nsfs_file), | |
660 | .initialize_class = NULL, | |
661 | .finalize_class = NULL, | |
662 | .initialize_content = init_nsfs_file_content, | |
663 | .free_content = NULL, | |
664 | .fill_column = nsfs_file_fill_column, | |
665 | .handle_fdinfo = NULL, | |
666 | }; | |
ad056988 MY |
667 | |
668 | /* | |
669 | * POSIX Mqueue | |
670 | */ | |
29bcb60e MY |
671 | struct mqueue_file { |
672 | struct file file; | |
673 | struct ipc_endpoint endpoint; | |
674 | }; | |
675 | ||
676 | struct mqueue_file_ipc { | |
677 | struct ipc ipc; | |
678 | ino_t ino; | |
679 | }; | |
680 | ||
ad056988 MY |
681 | bool is_mqueue_dev(dev_t dev) |
682 | { | |
683 | const char *fs = get_nodev_filesystem(minor(dev)); | |
684 | ||
685 | if (fs && (strcmp (fs, "mqueue") == 0)) | |
686 | return true; | |
687 | ||
688 | return false; | |
689 | } | |
690 | ||
29bcb60e MY |
691 | static inline char *mqueue_file_xstrendpoint(struct file *file) |
692 | { | |
693 | char *str = NULL; | |
694 | xasprintf(&str, "%d,%s,%d%c%c", | |
695 | file->proc->pid, file->proc->command, file->association, | |
696 | (file->mode & S_IRUSR)? 'r': '-', | |
697 | (file->mode & S_IWUSR)? 'w': '-'); | |
698 | return str; | |
699 | } | |
700 | ||
ad056988 MY |
701 | static bool mqueue_file_fill_column(struct proc *proc __attribute__((__unused__)), |
702 | struct file *file __attribute__((__unused__)), | |
703 | struct libscols_line *ln, | |
704 | int column_id, | |
705 | size_t column_index) | |
706 | { | |
707 | switch (column_id) { | |
708 | case COL_TYPE: | |
709 | if (scols_line_set_data(ln, column_index, "mqueue")) | |
710 | err(EXIT_FAILURE, _("failed to add output data")); | |
711 | return true; | |
29bcb60e MY |
712 | case COL_ENDPOINTS: { |
713 | char *str = NULL; | |
714 | struct mqueue_file *this = (struct mqueue_file *)file; | |
715 | struct list_head *e; | |
716 | foreach_endpoint(e, this->endpoint) { | |
717 | char *estr; | |
718 | struct mqueue_file *other = list_entry(e, struct mqueue_file, | |
719 | endpoint.endpoints); | |
720 | if (this == other) | |
721 | continue; | |
722 | if (str) | |
723 | xstrputc(&str, '\n'); | |
724 | estr = mqueue_file_xstrendpoint(&other->file); | |
725 | xstrappend(&str, estr); | |
726 | free(estr); | |
727 | } | |
728 | if (!str) | |
729 | return false; | |
730 | if (scols_line_refer_data(ln, column_index, str)) | |
731 | err(EXIT_FAILURE, _("failed to add output data")); | |
732 | return true; | |
733 | } | |
ad056988 MY |
734 | default: |
735 | return false; | |
736 | } | |
737 | } | |
738 | ||
29bcb60e MY |
739 | static unsigned int mqueue_file_get_hash(struct file *file) |
740 | { | |
741 | return (unsigned int)(file->stat.st_ino % UINT_MAX); | |
742 | } | |
743 | ||
744 | static bool mqueue_file_is_suitable_ipc(struct ipc *ipc, struct file *file) | |
745 | { | |
746 | return ((struct mqueue_file_ipc *)ipc)->ino == file->stat.st_ino; | |
747 | } | |
748 | ||
749 | static const struct ipc_class *mqueue_file_get_ipc_class(struct file *file __attribute__((__unused__))) | |
750 | { | |
751 | static const struct ipc_class mqueue_file_ipc_class = { | |
752 | .size = sizeof(struct mqueue_file_ipc), | |
753 | .get_hash = mqueue_file_get_hash, | |
754 | .is_suitable_ipc = mqueue_file_is_suitable_ipc, | |
755 | }; | |
756 | return &mqueue_file_ipc_class; | |
757 | } | |
758 | ||
759 | static void init_mqueue_file_content(struct file *file) | |
760 | { | |
761 | struct mqueue_file *mqueue_file = (struct mqueue_file *)file; | |
762 | struct ipc *ipc; | |
763 | unsigned int hash; | |
764 | ||
765 | init_endpoint(&mqueue_file->endpoint); | |
766 | ipc = get_ipc(file); | |
767 | if (ipc) | |
768 | goto link; | |
769 | ||
770 | ipc = new_ipc(mqueue_file_get_ipc_class(file)); | |
771 | ((struct mqueue_file_ipc *)ipc)->ino = file->stat.st_ino; | |
772 | ||
773 | hash = mqueue_file_get_hash(file); | |
774 | add_ipc(ipc, hash); | |
775 | link: | |
776 | add_endpoint(&mqueue_file->endpoint, ipc); | |
777 | } | |
778 | ||
ad056988 MY |
779 | const struct file_class mqueue_file_class = { |
780 | .super = &file_class, | |
29bcb60e MY |
781 | .size = sizeof(struct mqueue_file), |
782 | .initialize_content = init_mqueue_file_content, | |
ad056988 | 783 | .fill_column = mqueue_file_fill_column, |
29bcb60e | 784 | .get_ipc_class = mqueue_file_get_ipc_class, |
ad056988 | 785 | }; |