2 * lsfd-unkn.c - handle associations opening unknown objects
4 * Copyright (C) 2021 Red Hat, Inc. All rights reserved.
5 * Written by Masatake YAMATO <yamato@redhat.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it would be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <linux/bpf.h>
23 #include <sys/syscall.h>
24 #include <sys/timerfd.h>
28 #include "timeutils.h"
32 #define offsetofend(TYPE, MEMBER) \
33 (offsetof(TYPE, MEMBER) + sizeof_member(TYPE, MEMBER))
37 const struct anon_ops
*anon_ops
;
43 bool (*probe
)(const char *);
44 char * (*get_name
)(struct unkn
*);
45 /* Return true is handled the column. */
46 bool (*fill_column
)(struct proc
*,
48 struct libscols_line
*,
52 void (*init
)(struct unkn
*);
53 void (*free
)(struct unkn
*);
54 int (*handle_fdinfo
)(struct unkn
*, const char *, const char *);
55 void (*attach_xinfo
)(struct unkn
*);
56 const struct ipc_class
*ipc_class
;
59 static const struct anon_ops
*anon_probe(const char *);
61 static char * anon_get_class(struct unkn
*unkn
)
65 if (unkn
->anon_ops
->class)
66 return xstrdup(unkn
->anon_ops
->class);
68 /* See unkn_init_content() */
69 name
= ((struct file
*)unkn
)->name
+ 11;
70 /* Does it have the form anon_inode:[class]? */
72 size_t len
= strlen(name
+ 1);
73 if (*(name
+ 1 + len
- 1) == ']')
74 return strndup(name
+ 1, len
- 1);
80 static bool unkn_fill_column(struct proc
*proc
,
82 struct libscols_line
*ln
,
87 struct unkn
*unkn
= (struct unkn
*)file
;
91 if (unkn
->anon_ops
&& unkn
->anon_ops
->get_name
) {
92 str
= unkn
->anon_ops
->get_name(unkn
);
101 case COL_AINODECLASS
:
102 if (unkn
->anon_ops
) {
103 str
= anon_get_class(unkn
);
108 if (unkn
->anon_ops
) {
109 str
= xstrdup("anon_inodefs");
114 if (unkn
->anon_ops
&& unkn
->anon_ops
->fill_column
) {
115 if (unkn
->anon_ops
->fill_column(proc
, unkn
, ln
,
116 column_id
, column_index
, &str
))
123 err(EXIT_FAILURE
, _("failed to add output data"));
124 if (scols_line_refer_data(ln
, column_index
, str
))
125 err(EXIT_FAILURE
, _("failed to add output data"));
129 static void unkn_attach_xinfo(struct file
*file
)
131 struct unkn
*unkn
= (struct unkn
*)file
;
132 if (unkn
->anon_ops
&& unkn
->anon_ops
->attach_xinfo
)
133 unkn
->anon_ops
->attach_xinfo(unkn
);
136 static const struct ipc_class
*unkn_get_ipc_class(struct file
*file
)
138 struct unkn
*unkn
= (struct unkn
*)file
;
140 if (unkn
->anon_ops
&& unkn
->anon_ops
->ipc_class
)
141 return unkn
->anon_ops
->ipc_class
;
145 static void unkn_init_content(struct file
*file
)
147 struct unkn
*unkn
= (struct unkn
*)file
;
150 unkn
->anon_ops
= NULL
;
151 unkn
->anon_data
= NULL
;
153 if (major(file
->stat
.st_dev
) == 0
154 && strncmp(file
->name
, "anon_inode:", 11) == 0) {
155 const char *rest
= file
->name
+ 11;
157 unkn
->anon_ops
= anon_probe(rest
);
159 if (unkn
->anon_ops
->init
)
160 unkn
->anon_ops
->init(unkn
);
164 static void unkn_content_free(struct file
*file
)
166 struct unkn
*unkn
= (struct unkn
*)file
;
169 if (unkn
->anon_ops
&& unkn
->anon_ops
->free
)
170 unkn
->anon_ops
->free((struct unkn
*)file
);
173 static int unkn_handle_fdinfo(struct file
*file
, const char *key
, const char *value
)
175 struct unkn
*unkn
= (struct unkn
*)file
;
178 if (unkn
->anon_ops
&& unkn
->anon_ops
->handle_fdinfo
)
179 return unkn
->anon_ops
->handle_fdinfo(unkn
, key
, value
);
180 return 0; /* Should be handled in parents */
186 struct anon_pidfd_data
{
191 static bool anon_pidfd_probe(const char *str
)
193 return strncmp(str
, "[pidfd]", 7) == 0;
196 static char *anon_pidfd_get_name(struct unkn
*unkn
)
199 struct anon_pidfd_data
*data
= (struct anon_pidfd_data
*)unkn
->anon_data
;
202 struct proc
*proc
= get_proc(data
->pid
);
204 comm
= proc
->command
;
206 xasprintf(&str
, "pid=%d comm=%s nspid=%s",
209 data
->nspid
? data
->nspid
: "");
213 static void anon_pidfd_init(struct unkn
*unkn
)
215 unkn
->anon_data
= xcalloc(1, sizeof(struct anon_pidfd_data
));
218 static void anon_pidfd_free(struct unkn
*unkn
)
220 struct anon_pidfd_data
*data
= (struct anon_pidfd_data
*)unkn
->anon_data
;
227 static int anon_pidfd_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
229 if (strcmp(key
, "Pid") == 0) {
232 int rc
= ul_strtou64(value
, &pid
, 10);
234 return 0; /* ignore -- parse failed */
235 ((struct anon_pidfd_data
*)unkn
->anon_data
)->pid
= (pid_t
)pid
;
237 } else if (strcmp(key
, "NSpid") == 0) {
238 ((struct anon_pidfd_data
*)unkn
->anon_data
)->nspid
= xstrdup(value
);
245 static bool anon_pidfd_fill_column(struct proc
*proc
__attribute__((__unused__
)),
247 struct libscols_line
*ln
__attribute__((__unused__
)),
249 size_t column_index
__attribute__((__unused__
)),
252 struct anon_pidfd_data
*data
= (struct anon_pidfd_data
*)unkn
->anon_data
;
255 case COL_PIDFD_COMM
: {
256 struct proc
*pidfd_proc
= get_proc(data
->pid
);
257 char *pidfd_comm
= NULL
;
259 pidfd_comm
= pidfd_proc
->command
;
261 *str
= xstrdup(pidfd_comm
);
266 case COL_PIDFD_NSPID
:
268 *str
= xstrdup(data
->nspid
);
273 xasprintf(str
, "%d", (int)data
->pid
);
280 static const struct anon_ops anon_pidfd_ops
= {
282 .probe
= anon_pidfd_probe
,
283 .get_name
= anon_pidfd_get_name
,
284 .fill_column
= anon_pidfd_fill_column
,
285 .init
= anon_pidfd_init
,
286 .free
= anon_pidfd_free
,
287 .handle_fdinfo
= anon_pidfd_handle_fdinfo
,
293 struct anon_eventfd_data
{
295 struct unkn
*backptr
;
296 struct ipc_endpoint endpoint
;
304 static unsigned int anon_eventfd_get_hash(struct file
*file
)
306 struct unkn
*unkn
= (struct unkn
*)file
;
307 struct anon_eventfd_data
*data
= (struct anon_eventfd_data
*)unkn
->anon_data
;
309 return (unsigned int)data
->id
;
312 static bool anon_eventfd_is_suitable_ipc(struct ipc
*ipc
, struct file
*file
)
314 struct unkn
*unkn
= (struct unkn
*)file
;
315 struct anon_eventfd_data
*data
= (struct anon_eventfd_data
*)unkn
->anon_data
;
317 return ((struct eventfd_ipc
*)ipc
)->id
== data
->id
;
320 static const struct ipc_class anon_eventfd_ipc_class
= {
321 .size
= sizeof(struct eventfd_ipc
),
322 .get_hash
= anon_eventfd_get_hash
,
323 .is_suitable_ipc
= anon_eventfd_is_suitable_ipc
,
327 static bool anon_eventfd_probe(const char *str
)
329 return strncmp(str
, "[eventfd]", 9) == 0;
332 static char *anon_eventfd_get_name(struct unkn
*unkn
)
335 struct anon_eventfd_data
*data
= (struct anon_eventfd_data
*)unkn
->anon_data
;
337 xasprintf(&str
, "id=%d", data
->id
);
341 static void anon_eventfd_init(struct unkn
*unkn
)
343 struct anon_eventfd_data
*data
= xcalloc(1, sizeof(struct anon_eventfd_data
));
344 init_endpoint(&data
->endpoint
);
345 data
->backptr
= unkn
;
346 unkn
->anon_data
= data
;
349 static void anon_eventfd_free(struct unkn
*unkn
)
351 free(unkn
->anon_data
);
354 static void anon_eventfd_attach_xinfo(struct unkn
*unkn
)
356 struct anon_eventfd_data
*data
= (struct anon_eventfd_data
*)unkn
->anon_data
;
358 struct ipc
*ipc
= get_ipc(&unkn
->file
);
362 ipc
= new_ipc(&anon_eventfd_ipc_class
);
363 ((struct eventfd_ipc
*)ipc
)->id
= data
->id
;
365 hash
= anon_eventfd_get_hash(&unkn
->file
);
369 add_endpoint(&data
->endpoint
, ipc
);
372 static int anon_eventfd_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
374 if (strcmp(key
, "eventfd-id") == 0) {
377 int rc
= ul_strtos64(value
, &id
, 10);
380 ((struct anon_eventfd_data
*)unkn
->anon_data
)->id
= (int)id
;
386 static inline char *anon_eventfd_data_xstrendpoint(struct file
*file
)
389 xasprintf(&str
, "%d,%s,%d",
390 file
->proc
->pid
, file
->proc
->command
, file
->association
);
394 static bool anon_eventfd_fill_column(struct proc
*proc
__attribute__((__unused__
)),
396 struct libscols_line
*ln
__attribute__((__unused__
)),
398 size_t column_index
__attribute__((__unused__
)),
401 struct anon_eventfd_data
*data
= (struct anon_eventfd_data
*)unkn
->anon_data
;
405 xasprintf(str
, "%d", data
->id
);
407 case COL_ENDPOINTS
: {
410 foreach_endpoint(e
, data
->endpoint
) {
411 struct anon_eventfd_data
*other
= list_entry(e
,
412 struct anon_eventfd_data
,
418 estr
= anon_eventfd_data_xstrendpoint(&other
->backptr
->file
);
419 xstrappend(str
, estr
);
431 static const struct anon_ops anon_eventfd_ops
= {
433 .probe
= anon_eventfd_probe
,
434 .get_name
= anon_eventfd_get_name
,
435 .fill_column
= anon_eventfd_fill_column
,
436 .init
= anon_eventfd_init
,
437 .free
= anon_eventfd_free
,
438 .handle_fdinfo
= anon_eventfd_handle_fdinfo
,
439 .attach_xinfo
= anon_eventfd_attach_xinfo
,
440 .ipc_class
= &anon_eventfd_ipc_class
,
446 struct anon_eventpoll_data
{
449 struct list_head siblings
;
452 static bool anon_eventpoll_probe(const char *str
)
454 return strncmp(str
, "[eventpoll]", 11) == 0;
457 static void anon_eventpoll_init(struct unkn
*unkn
)
459 struct anon_eventpoll_data
*data
= xcalloc(1, sizeof(struct anon_eventpoll_data
));
460 INIT_LIST_HEAD(&data
->siblings
);
461 unkn
->anon_data
= data
;
464 static void anon_eventpoll_free(struct unkn
*unkn
)
466 struct anon_eventpoll_data
*data
= unkn
->anon_data
;
471 static int anon_eventpoll_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
473 struct anon_eventpoll_data
*data
;
474 if (strcmp(key
, "tfd") == 0) {
479 tfd
= strtoul(value
, &end
, 0);
481 return 0; /* ignore -- parse failed */
483 data
= (struct anon_eventpoll_data
*)unkn
->anon_data
;
484 data
->tfds
= xreallocarray(data
->tfds
, ++data
->count
, sizeof(int));
485 data
->tfds
[data
->count
- 1] = (int)tfd
;
491 static int intcmp(const void *a
, const void *b
)
499 static void anon_eventpoll_attach_xinfo(struct unkn
*unkn
)
501 struct anon_eventpoll_data
*data
= (struct anon_eventpoll_data
*)unkn
->anon_data
;
502 if (data
->count
> 0) {
503 qsort(data
->tfds
, data
->count
, sizeof(data
->tfds
[0]),
505 list_add_tail(&data
->siblings
,
506 &unkn
->file
.proc
->eventpolls
);
510 static char *anon_eventpoll_make_tfds_string(struct anon_eventpoll_data
*data
,
514 char *str
= prefix
? xstrdup(prefix
): NULL
;
517 for (size_t i
= 0; i
< data
->count
; i
++) {
524 snprintf(buf
+ offset
, sizeof(buf
) - offset
, "%d", data
->tfds
[i
]);
525 xstrappend(&str
, buf
);
530 static char *anon_eventpoll_get_name(struct unkn
*unkn
)
532 return anon_eventpoll_make_tfds_string((struct anon_eventpoll_data
*)unkn
->anon_data
,
536 static bool anon_eventpoll_fill_column(struct proc
*proc
__attribute__((__unused__
)),
538 struct libscols_line
*ln
__attribute__((__unused__
)),
540 size_t column_index
__attribute__((__unused__
)),
543 struct anon_eventpoll_data
*data
= (struct anon_eventpoll_data
*)unkn
->anon_data
;
546 case COL_EVENTPOLL_TFDS
:
547 *str
=anon_eventpoll_make_tfds_string(data
, NULL
, '\n');
556 static const struct anon_ops anon_eventpoll_ops
= {
557 .class = "eventpoll",
558 .probe
= anon_eventpoll_probe
,
559 .get_name
= anon_eventpoll_get_name
,
560 .fill_column
= anon_eventpoll_fill_column
,
561 .init
= anon_eventpoll_init
,
562 .free
= anon_eventpoll_free
,
563 .handle_fdinfo
= anon_eventpoll_handle_fdinfo
,
564 .attach_xinfo
= anon_eventpoll_attach_xinfo
,
567 static int numcomp(const void *a
, const void *b
)
569 return *(int *)a
- *(int *)b
;
572 bool is_multiplexed_by_eventpoll(int fd
, struct list_head
*eventpolls
)
575 list_for_each (t
, eventpolls
) {
576 struct anon_eventpoll_data
*data
= list_entry(t
, struct anon_eventpoll_data
, siblings
);
578 if (bsearch(&fd
, data
->tfds
,
579 data
->count
, sizeof (data
->tfds
[0]),
590 struct anon_timerfd_data
{
592 struct itimerspec itimerspec
;
595 static bool anon_timerfd_probe(const char *str
)
597 return strncmp(str
, "[timerfd]", 9) == 0;
600 static void anon_timerfd_init(struct unkn
*unkn
)
602 unkn
->anon_data
= xcalloc(1, sizeof(struct anon_timerfd_data
));
605 static void anon_timerfd_free(struct unkn
*unkn
)
607 struct anon_timerfd_data
*data
= unkn
->anon_data
;
611 static int anon_timerfd_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
613 struct anon_timerfd_data
*data
= (struct anon_timerfd_data
*)unkn
->anon_data
;
615 if (strcmp(key
, "clockid") == 0) {
616 unsigned long clockid
;
620 clockid
= strtoul(value
, &end
, 0);
622 return 0; /* ignore -- parse failed */
624 return 0; /* ignore -- garbage remains. */
626 data
->clockid
= clockid
;
633 if (strcmp(key
, "it_value") == 0)
634 t
= &data
->itimerspec
.it_value
;
635 else if (strcmp(key
, "it_interval") == 0)
636 t
= &data
->itimerspec
.it_interval
;
640 if (sscanf(value
, "(%"SCNu64
", %"SCNu64
")",
641 &tv_sec
, &tv_nsec
) == 2) {
642 t
->tv_sec
= (time_t)tv_sec
;
643 t
->tv_nsec
= (long)tv_nsec
;
651 static const char *anon_timerfd_decode_clockid(int clockid
)
656 case CLOCK_MONOTONIC
:
660 case CLOCK_REALTIME_ALARM
:
661 return "realtime-alarm";
662 case CLOCK_BOOTTIME_ALARM
:
663 return "boottime-alarm";
669 static void anon_timerfd_render_timespec_string(char *buf
, size_t size
,
671 const struct timespec
*t
)
673 snprintf(buf
, size
, "%s%llu.%09ld",
675 (unsigned long long)t
->tv_sec
, t
->tv_nsec
);
678 static char *anon_timerfd_get_name(struct unkn
*unkn
)
682 struct anon_timerfd_data
*data
= (struct anon_timerfd_data
*)unkn
->anon_data
;
683 const struct timespec
*exp
;
684 const struct timespec
*ival
;
686 const char *clockid_name
;
687 char exp_buf
[BUFSIZ
] = {'\0'};
688 char ival_buf
[BUFSIZ
] = {'\0'};
690 clockid_name
= anon_timerfd_decode_clockid(data
->clockid
);
692 exp
= &data
->itimerspec
.it_value
;
693 if (is_timespecset(exp
))
694 anon_timerfd_render_timespec_string(exp_buf
, sizeof(exp_buf
),
697 ival
= &data
->itimerspec
.it_interval
;
698 if (is_timespecset(ival
))
699 anon_timerfd_render_timespec_string(ival_buf
, sizeof(ival_buf
),
702 xasprintf(&str
, "clockid=%s%s%s", clockid_name
, exp_buf
, ival_buf
);
706 static bool anon_timerfd_fill_column(struct proc
*proc
__attribute__((__unused__
)),
708 struct libscols_line
*ln
__attribute__((__unused__
)),
710 size_t column_index
__attribute__((__unused__
)),
713 struct anon_timerfd_data
*data
= (struct anon_timerfd_data
*)unkn
->anon_data
;
714 char buf
[BUFSIZ
] = {'\0'};
717 case COL_TIMERFD_CLOCKID
:
718 *str
= xstrdup(anon_timerfd_decode_clockid(data
->clockid
));
720 case COL_TIMERFD_INTERVAL
:
721 anon_timerfd_render_timespec_string(buf
, sizeof(buf
), NULL
,
722 &data
->itimerspec
.it_interval
);
725 case COL_TIMERFD_REMAINING
:
726 anon_timerfd_render_timespec_string(buf
, sizeof(buf
), NULL
,
727 &data
->itimerspec
.it_value
);
735 static const struct anon_ops anon_timerfd_ops
= {
737 .probe
= anon_timerfd_probe
,
738 .get_name
= anon_timerfd_get_name
,
739 .fill_column
= anon_timerfd_fill_column
,
740 .init
= anon_timerfd_init
,
741 .free
= anon_timerfd_free
,
742 .handle_fdinfo
= anon_timerfd_handle_fdinfo
,
748 struct anon_signalfd_data
{
752 static bool anon_signalfd_probe(const char *str
)
754 return strncmp(str
, "[signalfd]", 10) == 0;
757 static void anon_signalfd_init(struct unkn
*unkn
)
759 unkn
->anon_data
= xcalloc(1, sizeof(struct anon_signalfd_data
));
762 static void anon_signalfd_free(struct unkn
*unkn
)
764 struct anon_signalfd_data
*data
= unkn
->anon_data
;
768 static int anon_signalfd_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
770 struct anon_signalfd_data
*data
= (struct anon_signalfd_data
*)unkn
->anon_data
;
772 if (strcmp(key
, "sigmask") == 0) {
773 if (ul_strtou64(value
, &data
->sigmask
, 16) < 0) {
781 static char *anon_signalfd_make_mask_string(const char* prefix
, uint64_t sigmask
)
785 for (size_t i
= 0; i
< sizeof(sigmask
) * 8; i
++) {
786 if ((((uint64_t)0x1) << i
) & sigmask
) {
787 const int signum
= i
+ 1;
788 const char *signame
= signum_to_signame(signum
);
791 xstrappend(&str
, ",");
793 xstrappend(&str
, prefix
);
796 xstrappend(&str
, signame
);
799 snprintf(buf
, sizeof(buf
), "%d", signum
);
800 xstrappend(&str
, buf
);
808 static char *anon_signalfd_get_name(struct unkn
*unkn
)
810 struct anon_signalfd_data
*data
= (struct anon_signalfd_data
*)unkn
->anon_data
;
811 return anon_signalfd_make_mask_string("mask=", data
->sigmask
);
814 static bool anon_signalfd_fill_column(struct proc
*proc
__attribute__((__unused__
)),
816 struct libscols_line
*ln
__attribute__((__unused__
)),
818 size_t column_index
__attribute__((__unused__
)),
821 struct anon_signalfd_data
*data
= (struct anon_signalfd_data
*)unkn
->anon_data
;
824 case COL_SIGNALFD_MASK
:
825 *str
= anon_signalfd_make_mask_string(NULL
, data
->sigmask
);
832 static const struct anon_ops anon_signalfd_ops
= {
834 .probe
= anon_signalfd_probe
,
835 .get_name
= anon_signalfd_get_name
,
836 .fill_column
= anon_signalfd_fill_column
,
837 .init
= anon_signalfd_init
,
838 .free
= anon_signalfd_free
,
839 .handle_fdinfo
= anon_signalfd_handle_fdinfo
,
845 struct anon_inotify_data
{
846 struct list_head inodes
;
849 struct anon_inotify_inode
{
852 struct list_head inodes
;
855 static bool anon_inotify_probe(const char *str
)
857 return strncmp(str
, "inotify", 7) == 0;
860 /* A device number appeared in fdinfo of an inotify file uses the kernel
861 * internal representation. It is different from what we are familiar with;
862 * major(3) and minor(3) don't work with the representation.
863 * See linux/include/linux/kdev_t.h. */
864 #define ANON_INOTIFY_MINORBITS 20
865 #define ANON_INOTIFY_MINORMASK ((1U << ANON_INOTIFY_MINORBITS) - 1)
867 #define ANON_INOTIFY_MAJOR(dev) ((unsigned int) ((dev) >> ANON_INOTIFY_MINORBITS))
868 #define ANON_INOTIFY_MINOR(dev) ((unsigned int) ((dev) & ANON_INOTIFY_MINORMASK))
870 static char *anon_inotify_make_inodes_string(const char *prefix
,
872 enum decode_source_level decode_level
,
873 struct anon_inotify_data
*data
)
876 char buf
[BUFSIZ
] = {'\0'};
877 bool first_element
= true;
880 list_for_each(i
, &data
->inodes
) {
881 char source
[BUFSIZ
/2] = {'\0'};
882 struct anon_inotify_inode
*inode
= list_entry(i
,
883 struct anon_inotify_inode
,
886 decode_source(source
, sizeof(source
),
887 ANON_INOTIFY_MAJOR(inode
->sdev
), ANON_INOTIFY_MINOR(inode
->sdev
),
889 snprintf(buf
, sizeof(buf
), "%s%llu@%s", first_element
? prefix
: sep
,
890 (unsigned long long)inode
->ino
, source
);
891 first_element
= false;
893 xstrappend(&str
, buf
);
899 static char *anon_inotify_get_name(struct unkn
*unkn
)
901 return anon_inotify_make_inodes_string("inodes=", ",", DECODE_SOURCE_FULL
,
902 (struct anon_inotify_data
*)unkn
->anon_data
);
905 static void anon_inotify_init(struct unkn
*unkn
)
907 struct anon_inotify_data
*data
= xcalloc(1, sizeof(struct anon_inotify_data
));
908 INIT_LIST_HEAD (&data
->inodes
);
909 unkn
->anon_data
= data
;
912 static void anon_inotify_free(struct unkn
*unkn
)
914 struct anon_inotify_data
*data
= unkn
->anon_data
;
916 list_free(&data
->inodes
, struct anon_inotify_inode
, inodes
,
921 static void add_inode(struct anon_inotify_data
*data
, ino_t ino
, dev_t sdev
)
923 struct anon_inotify_inode
*inode
= xmalloc(sizeof(*inode
));
925 INIT_LIST_HEAD (&inode
->inodes
);
929 list_add_tail(&inode
->inodes
, &data
->inodes
);
932 static int anon_inotify_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
934 struct anon_inotify_data
*data
= (struct anon_inotify_data
*)unkn
->anon_data
;
936 if (strcmp(key
, "inotify wd") == 0) {
937 unsigned long long ino
;
938 unsigned long long sdev
;
940 if (sscanf(value
, "%*d ino:%llx sdev:%llx %*s", &ino
, &sdev
) == 2) {
941 add_inode(data
, (ino_t
)ino
, (dev_t
)sdev
);
948 static bool anon_inotify_fill_column(struct proc
*proc
__attribute__((__unused__
)),
950 struct libscols_line
*ln
__attribute__((__unused__
)),
952 size_t column_index
__attribute__((__unused__
)),
955 struct anon_inotify_data
*data
= (struct anon_inotify_data
*)unkn
->anon_data
;
958 case COL_INOTIFY_INODES
:
959 *str
= anon_inotify_make_inodes_string("", "\n", DECODE_SOURCE_FULL
,
964 case COL_INOTIFY_INODES_RAW
:
965 *str
= anon_inotify_make_inodes_string("", "\n", DECODE_SOURCE_MAJMIN
,
975 static const struct anon_ops anon_inotify_ops
= {
977 .probe
= anon_inotify_probe
,
978 .get_name
= anon_inotify_get_name
,
979 .fill_column
= anon_inotify_fill_column
,
980 .init
= anon_inotify_init
,
981 .free
= anon_inotify_free
,
982 .handle_fdinfo
= anon_inotify_handle_fdinfo
,
988 * Generally, we use "-" as the word separators in lsfd's output.
989 * However, about bpf*, we use "_" because bpftool uses "_".
991 static const char *bpf_prog_type_table
[] = {
992 [0] = "unspec", /* BPF_PROG_TYPE_UNSPEC*/
993 [1] = "socket_filter", /* BPF_PROG_TYPE_SOCKET_FILTER*/
994 [2] = "kprobe", /* BPF_PROG_TYPE_KPROBE*/
995 [3] = "sched_cls", /* BPF_PROG_TYPE_SCHED_CLS*/
996 [4] = "sched_act", /* BPF_PROG_TYPE_SCHED_ACT*/
997 [5] = "tracepoint", /* BPF_PROG_TYPE_TRACEPOINT*/
998 [6] = "xdp", /* BPF_PROG_TYPE_XDP*/
999 [7] = "perf_event", /* BPF_PROG_TYPE_PERF_EVENT*/
1000 [8] = "cgroup_skb", /* BPF_PROG_TYPE_CGROUP_SKB*/
1001 [9] = "cgroup_sock", /* BPF_PROG_TYPE_CGROUP_SOCK*/
1002 [10] = "lwt_in", /* BPF_PROG_TYPE_LWT_IN*/
1003 [11] = "lwt_out", /* BPF_PROG_TYPE_LWT_OUT*/
1004 [12] = "lwt_xmit", /* BPF_PROG_TYPE_LWT_XMIT*/
1005 [13] = "sock_ops", /* BPF_PROG_TYPE_SOCK_OPS*/
1006 [14] = "sk_skb", /* BPF_PROG_TYPE_SK_SKB*/
1007 [15] = "cgroup_device", /* BPF_PROG_TYPE_CGROUP_DEVICE*/
1008 [16] = "sk_msg", /* BPF_PROG_TYPE_SK_MSG*/
1009 [17] = "raw_tracepoint", /* BPF_PROG_TYPE_RAW_TRACEPOINT*/
1010 [18] = "cgroup_sock_addr", /* BPF_PROG_TYPE_CGROUP_SOCK_ADDR*/
1011 [19] = "lwt_seg6local", /* BPF_PROG_TYPE_LWT_SEG6LOCAL*/
1012 [20] = "lirc_mode2", /* BPF_PROG_TYPE_LIRC_MODE2*/
1013 [21] = "sk_reuseport", /* BPF_PROG_TYPE_SK_REUSEPORT*/
1014 [22] = "flow_dissector", /* BPF_PROG_TYPE_FLOW_DISSECTOR*/
1015 [23] = "cgroup_sysctl", /* BPF_PROG_TYPE_CGROUP_SYSCTL*/
1016 [24] = "raw_tracepoint_writable", /* BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE*/
1017 [25] = "cgroup_sockopt", /* BPF_PROG_TYPE_CGROUP_SOCKOPT*/
1018 [26] = "tracing", /* BPF_PROG_TYPE_TRACING*/
1019 [27] = "struct_ops", /* BPF_PROG_TYPE_STRUCT_OPS*/
1020 [28] = "ext", /* BPF_PROG_TYPE_EXT*/
1021 [29] = "lsm", /* BPF_PROG_TYPE_LSM*/
1022 [30] = "sk_lookup", /* BPF_PROG_TYPE_SK_LOOKUP*/
1023 [31] = "syscall", /* BPF_PROG_TYPE_SYSCALL*/
1026 struct anon_bpf_prog_data
{
1029 char name
[BPF_OBJ_NAME_LEN
+ 1];
1032 static bool anon_bpf_prog_probe(const char *str
)
1034 return strncmp(str
, "bpf-prog", 8) == 0;
1037 static const char *anon_bpf_prog_get_prog_type_name(int type
)
1039 if (0 <= type
&& type
< (int)ARRAY_SIZE(bpf_prog_type_table
))
1040 return bpf_prog_type_table
[type
];
1044 static bool anon_bpf_prog_fill_column(struct proc
*proc
__attribute__((__unused__
)),
1046 struct libscols_line
*ln
__attribute__((__unused__
)),
1048 size_t column_index
__attribute__((__unused__
)),
1051 struct anon_bpf_prog_data
*data
= (struct anon_bpf_prog_data
*)unkn
->anon_data
;
1055 case COL_BPF_PROG_ID
:
1056 xasprintf(str
, "%d", data
->id
);
1058 case COL_BPF_PROG_TYPE_RAW
:
1059 xasprintf(str
, "%d", data
->type
);
1061 case COL_BPF_PROG_TYPE
:
1062 t
= anon_bpf_prog_get_prog_type_name(data
->type
);
1066 xasprintf(str
, "UNKNOWN(%d)", data
->type
);
1069 *str
= xstrdup(data
->name
);
1076 static char *anon_bpf_prog_get_name(struct unkn
*unkn
)
1080 struct anon_bpf_prog_data
*data
= (struct anon_bpf_prog_data
*)unkn
->anon_data
;
1082 t
= anon_bpf_prog_get_prog_type_name(data
->type
);
1084 xasprintf(&str
, "id=%d type=%s", data
->id
, t
);
1086 xasprintf(&str
, "id=%d type=UNKNOWN(%d)", data
->id
, data
->type
);
1089 xstrfappend(&str
, " name=%s", data
->name
);
1095 static void anon_bpf_prog_init(struct unkn
*unkn
)
1097 struct anon_bpf_prog_data
*data
= xmalloc(sizeof(*data
));
1100 data
->name
[0] = '\0';
1101 unkn
->anon_data
= data
;
1104 static void anon_bpf_prog_free(struct unkn
*unkn
)
1106 struct anon_bpf_prog_data
*data
= (struct anon_bpf_prog_data
*)unkn
->anon_data
;
1110 static void anon_bpf_prog_get_more_info(struct anon_bpf_prog_data
*prog_data
)
1112 union bpf_attr attr
= {
1113 .prog_id
= (int32_t)prog_data
->id
,
1117 struct bpf_prog_info info
= { 0 };
1118 union bpf_attr info_attr
= {
1119 .info
.info_len
= sizeof(info
),
1120 .info
.info
= (uint64_t)(uintptr_t)&info
,
1123 int bpf_fd
= syscall(SYS_bpf
, BPF_PROG_GET_FD_BY_ID
, &attr
, sizeof(attr
));
1127 info_attr
.info
.bpf_fd
= bpf_fd
;
1128 if (syscall(SYS_bpf
, BPF_OBJ_GET_INFO_BY_FD
, &info_attr
, offsetofend(union bpf_attr
, info
)) == 0) {
1129 memcpy(prog_data
->name
,
1132 prog_data
->name
[BPF_OBJ_NAME_LEN
] = '\0';
1137 static int anon_bpf_prog_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
1139 if (strcmp(key
, "prog_id") == 0) {
1141 int rc
= ul_strtos32(value
, &t
, 10);
1143 return 0; /* ignore -- parse failed */
1144 ((struct anon_bpf_prog_data
*)unkn
->anon_data
)->id
= (int)t
;
1145 anon_bpf_prog_get_more_info((struct anon_bpf_prog_data
*)unkn
->anon_data
);
1149 if (strcmp(key
, "prog_type") == 0) {
1151 int rc
= ul_strtos32(value
, &t
, 10);
1153 return 0; /* ignore -- parse failed */
1154 ((struct anon_bpf_prog_data
*)unkn
->anon_data
)->type
= (int)t
;
1161 static const struct anon_ops anon_bpf_prog_ops
= {
1162 .class = "bpf-prog",
1163 .probe
= anon_bpf_prog_probe
,
1164 .get_name
= anon_bpf_prog_get_name
,
1165 .fill_column
= anon_bpf_prog_fill_column
,
1166 .init
= anon_bpf_prog_init
,
1167 .free
= anon_bpf_prog_free
,
1168 .handle_fdinfo
= anon_bpf_prog_handle_fdinfo
,
1174 static const char *bpf_map_type_table
[] = {
1175 [0] = "unspec", /* BPF_MAP_TYPE_UNSPEC */
1176 [1] = "hash", /* BPF_MAP_TYPE_HASH */
1177 [2] = "array", /* BPF_MAP_TYPE_ARRAY */
1178 [3] = "prog-array", /* BPF_MAP_TYPE_PROG_ARRAY */
1179 [4] = "perf-event-array", /* BPF_MAP_TYPE_PERF_EVENT_ARRAY */
1180 [5] = "percpu-hash", /* BPF_MAP_TYPE_PERCPU_HASH */
1181 [6] = "percpu-array", /* BPF_MAP_TYPE_PERCPU_ARRAY */
1182 [7] = "stack-trace", /* BPF_MAP_TYPE_STACK_TRACE */
1183 [8] = "cgroup-array", /* BPF_MAP_TYPE_CGROUP_ARRAY */
1184 [9] = "lru-hash", /* BPF_MAP_TYPE_LRU_HASH */
1185 [10] = "lru-percpu-hash", /* BPF_MAP_TYPE_LRU_PERCPU_HASH */
1186 [11] = "lpm-trie", /* BPF_MAP_TYPE_LPM_TRIE */
1187 [12] = "array-of-maps", /* BPF_MAP_TYPE_ARRAY_OF_MAPS */
1188 [13] = "hash-of-maps", /* BPF_MAP_TYPE_HASH_OF_MAPS */
1189 [14] = "devmap", /* BPF_MAP_TYPE_DEVMAP */
1190 [15] = "sockmap", /* BPF_MAP_TYPE_SOCKMAP */
1191 [16] = "cpumap", /* BPF_MAP_TYPE_CPUMAP */
1192 [17] = "xskmap", /* BPF_MAP_TYPE_XSKMAP */
1193 [18] = "sockhash", /* BPF_MAP_TYPE_SOCKHASH */
1194 [19] = "cgroup-storage", /* BPF_MAP_TYPE_CGROUP_STORAGE */
1195 [20] = "reuseport-sockarray", /* BPF_MAP_TYPE_REUSEPORT_SOCKARRAY */
1196 [21] = "percpu-cgroup-storage", /* BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE */
1197 [22] = "queue", /* BPF_MAP_TYPE_QUEUE */
1198 [23] = "stack", /* BPF_MAP_TYPE_STACK */
1199 [24] = "sk-storage", /* BPF_MAP_TYPE_SK_STORAGE */
1200 [25] = "devmap-hash", /* BPF_MAP_TYPE_DEVMAP_HASH */
1201 [26] = "struct-ops", /* BPF_MAP_TYPE_STRUCT_OPS */
1202 [27] = "ringbuf", /* BPF_MAP_TYPE_RINGBUF */
1203 [28] = "inode-storage", /* BPF_MAP_TYPE_INODE_STORAGE */
1204 [29] = "task-storage", /* BPF_MAP_TYPE_TASK_STORAGE */
1205 [30] = "bloom-filter", /* BPF_MAP_TYPE_BLOOM_FILTER */
1206 [31] = "user-ringbuf", /* BPF_MAP_TYPE_USER_RINGBUF */
1207 [32] = "cgrp-storage", /* BPF_MAP_TYPE_CGRP_STORAGE */
1210 struct anon_bpf_map_data
{
1213 char name
[BPF_OBJ_NAME_LEN
+ 1];
1216 static bool anon_bpf_map_probe(const char *str
)
1218 return strncmp(str
, "bpf-map", 8) == 0;
1221 static const char *anon_bpf_map_get_map_type_name(int type
)
1223 if (0 <= type
&& type
< (int)ARRAY_SIZE(bpf_map_type_table
))
1224 return bpf_map_type_table
[type
];
1228 static bool anon_bpf_map_fill_column(struct proc
*proc
__attribute__((__unused__
)),
1230 struct libscols_line
*ln
__attribute__((__unused__
)),
1232 size_t column_index
__attribute__((__unused__
)),
1235 struct anon_bpf_prog_data
*data
= (struct anon_bpf_prog_data
*)unkn
->anon_data
;
1239 case COL_BPF_MAP_ID
:
1240 xasprintf(str
, "%d", data
->id
);
1242 case COL_BPF_MAP_TYPE_RAW
:
1243 xasprintf(str
, "%d", data
->type
);
1245 case COL_BPF_MAP_TYPE
:
1246 t
= anon_bpf_map_get_map_type_name(data
->type
);
1250 xasprintf(str
, "UNKNOWN(%d)", data
->type
);
1253 *str
= xstrdup(data
->name
);
1260 static char *anon_bpf_map_get_name(struct unkn
*unkn
)
1264 struct anon_bpf_map_data
*data
= (struct anon_bpf_map_data
*)unkn
->anon_data
;
1266 t
= anon_bpf_map_get_map_type_name(data
->type
);
1268 xasprintf(&str
, "id=%d type=%s", data
->id
, t
);
1270 xasprintf(&str
, "id=%d type=UNKNOWN(%d)", data
->id
, data
->type
);
1273 xstrfappend(&str
, " name=%s", data
->name
);
1278 static void anon_bpf_map_init(struct unkn
*unkn
)
1280 struct anon_bpf_map_data
*data
= xmalloc(sizeof(*data
));
1283 data
->name
[0] = '\0';
1284 unkn
->anon_data
= data
;
1287 static void anon_bpf_map_free(struct unkn
*unkn
)
1289 struct anon_bpf_map_data
*data
= (struct anon_bpf_map_data
*)unkn
->anon_data
;
1293 static void anon_bpf_map_get_more_info(struct anon_bpf_map_data
*map_data
)
1295 union bpf_attr attr
= {
1296 .map_id
= (int32_t)map_data
->id
,
1300 struct bpf_map_info info
= { 0 };
1301 union bpf_attr info_attr
= {
1302 .info
.info_len
= sizeof(info
),
1303 .info
.info
= (uint64_t)(uintptr_t)&info
,
1306 int bpf_fd
= syscall(SYS_bpf
, BPF_MAP_GET_FD_BY_ID
, &attr
, sizeof(attr
));
1310 info_attr
.info
.bpf_fd
= bpf_fd
;
1311 if (syscall(SYS_bpf
, BPF_OBJ_GET_INFO_BY_FD
, &info_attr
, offsetofend(union bpf_attr
, info
)) == 0) {
1312 memcpy(map_data
->name
,
1315 map_data
->name
[BPF_OBJ_NAME_LEN
] = '\0';
1320 static int anon_bpf_map_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
1322 if (strcmp(key
, "map_id") == 0) {
1324 int rc
= ul_strtos32(value
, &t
, 10);
1326 return 0; /* ignore -- parse failed */
1327 ((struct anon_bpf_map_data
*)unkn
->anon_data
)->id
= (int)t
;
1328 anon_bpf_map_get_more_info((struct anon_bpf_map_data
*)unkn
->anon_data
);
1332 if (strcmp(key
, "map_type") == 0) {
1334 int rc
= ul_strtos32(value
, &t
, 10);
1336 return 0; /* ignore -- parse failed */
1337 ((struct anon_bpf_map_data
*)unkn
->anon_data
)->type
= (int)t
;
1344 static const struct anon_ops anon_bpf_map_ops
= {
1346 .probe
= anon_bpf_map_probe
,
1347 .get_name
= anon_bpf_map_get_name
,
1348 .fill_column
= anon_bpf_map_fill_column
,
1349 .init
= anon_bpf_map_init
,
1350 .free
= anon_bpf_map_free
,
1351 .handle_fdinfo
= anon_bpf_map_handle_fdinfo
,
1355 * generic (fallback implementation)
1357 static const struct anon_ops anon_generic_ops
= {
1360 .fill_column
= NULL
,
1363 .handle_fdinfo
= NULL
,
1366 static const struct anon_ops
*anon_ops
[] = {
1369 &anon_eventpoll_ops
,
1377 static const struct anon_ops
*anon_probe(const char *str
)
1379 for (size_t i
= 0; i
< ARRAY_SIZE(anon_ops
); i
++)
1380 if (anon_ops
[i
]->probe(str
))
1382 return &anon_generic_ops
;
1385 const struct file_class unkn_class
= {
1386 .super
= &file_class
,
1387 .size
= sizeof(struct unkn
),
1388 .fill_column
= unkn_fill_column
,
1389 .initialize_content
= unkn_init_content
,
1390 .free_content
= unkn_content_free
,
1391 .handle_fdinfo
= unkn_handle_fdinfo
,
1392 .attach_xinfo
= unkn_attach_xinfo
,
1393 .get_ipc_class
= unkn_get_ipc_class
,