]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lsfd-file.c
autotools: add missing dist_noinst_DATA
[thirdparty/util-linux.git] / misc-utils / lsfd-file.c
CommitLineData
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
49static struct idcache *username_cache;
50
c1cb2735
MY
51static size_t pagesize;
52
a4778cb5
MY
53static 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
73static 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 95extern void lsfd_decode_file_flags(struct ul_buffer *buf, int flags);
22b7cf51
MY
96static 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 106static 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
116void 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
150static 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
164static 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
353enum lock_mode {
354 LOCK_NONE,
355 READ_LOCK,
356 WRITE_LOCK,
357};
358
359static 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
387static 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
419static void file_free_content(struct file *file)
420{
421 free(file->name);
422}
423
de9ec7ee
MY
424static 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
467static 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
495static 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
515static void file_class_finalize(void)
516{
517 free_idcache(username_cache);
518}
519
600e6e52
MY
520const 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
534struct nsfs_file {
535 struct file file;
536 int clone_type;
537};
538
539static 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
567static 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
624static 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
657const 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
671struct mqueue_file {
672 struct file file;
673 struct ipc_endpoint endpoint;
674};
675
676struct mqueue_file_ipc {
677 struct ipc ipc;
678 ino_t ino;
679};
680
ad056988
MY
681bool 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
691static 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
701static 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
739static unsigned int mqueue_file_get_hash(struct file *file)
740{
741 return (unsigned int)(file->stat.st_ino % UINT_MAX);
742}
743
744static 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
749static 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
759static 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
779const 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};