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"
33 #define offsetofend(TYPE, MEMBER) \
34 (offsetof(TYPE, MEMBER) + sizeof_member(TYPE, MEMBER))
38 const struct anon_ops
*anon_ops
;
44 bool (*probe
)(const char *);
45 char * (*get_name
)(struct unkn
*);
46 /* Return true is handled the column. */
47 bool (*fill_column
)(struct proc
*,
49 struct libscols_line
*,
53 void (*init
)(struct unkn
*);
54 void (*free
)(struct unkn
*);
55 int (*handle_fdinfo
)(struct unkn
*, const char *, const char *);
56 void (*attach_xinfo
)(struct unkn
*);
57 const struct ipc_class
*ipc_class
;
60 static const struct anon_ops
*anon_probe(const char *);
62 static char * anon_get_class(struct unkn
*unkn
)
66 if (unkn
->anon_ops
->class)
67 return xstrdup(unkn
->anon_ops
->class);
69 /* See unkn_init_content() */
70 name
= ((struct file
*)unkn
)->name
+ 11;
71 /* Does it have the form anon_inode:[class]? */
73 size_t len
= strlen(name
+ 1);
74 if (*(name
+ 1 + len
- 1) == ']')
75 return strndup(name
+ 1, len
- 1);
81 static bool unkn_fill_column(struct proc
*proc
,
83 struct libscols_line
*ln
,
88 struct unkn
*unkn
= (struct unkn
*)file
;
92 if (unkn
->anon_ops
&& unkn
->anon_ops
->get_name
) {
93 str
= unkn
->anon_ops
->get_name(unkn
);
102 case COL_AINODECLASS
:
103 if (unkn
->anon_ops
) {
104 str
= anon_get_class(unkn
);
109 if (unkn
->anon_ops
) {
110 str
= xstrdup("anon_inodefs");
115 if (unkn
->anon_ops
&& unkn
->anon_ops
->fill_column
) {
116 if (unkn
->anon_ops
->fill_column(proc
, unkn
, ln
,
117 column_id
, column_index
, &str
))
124 err(EXIT_FAILURE
, _("failed to add output data"));
125 if (scols_line_refer_data(ln
, column_index
, str
))
126 err(EXIT_FAILURE
, _("failed to add output data"));
130 static void unkn_attach_xinfo(struct file
*file
)
132 struct unkn
*unkn
= (struct unkn
*)file
;
133 if (unkn
->anon_ops
&& unkn
->anon_ops
->attach_xinfo
)
134 unkn
->anon_ops
->attach_xinfo(unkn
);
137 static const struct ipc_class
*unkn_get_ipc_class(struct file
*file
)
139 struct unkn
*unkn
= (struct unkn
*)file
;
141 if (unkn
->anon_ops
&& unkn
->anon_ops
->ipc_class
)
142 return unkn
->anon_ops
->ipc_class
;
146 static void unkn_init_content(struct file
*file
)
148 struct unkn
*unkn
= (struct unkn
*)file
;
151 unkn
->anon_ops
= NULL
;
152 unkn
->anon_data
= NULL
;
154 if (major(file
->stat
.st_dev
) == 0
155 && strncmp(file
->name
, "anon_inode:", 11) == 0) {
156 const char *rest
= file
->name
+ 11;
158 unkn
->anon_ops
= anon_probe(rest
);
160 if (unkn
->anon_ops
->init
)
161 unkn
->anon_ops
->init(unkn
);
165 static void unkn_content_free(struct file
*file
)
167 struct unkn
*unkn
= (struct unkn
*)file
;
170 if (unkn
->anon_ops
&& unkn
->anon_ops
->free
)
171 unkn
->anon_ops
->free((struct unkn
*)file
);
174 static int unkn_handle_fdinfo(struct file
*file
, const char *key
, const char *value
)
176 struct unkn
*unkn
= (struct unkn
*)file
;
179 if (unkn
->anon_ops
&& unkn
->anon_ops
->handle_fdinfo
)
180 return unkn
->anon_ops
->handle_fdinfo(unkn
, key
, value
);
181 return 0; /* Should be handled in parents */
188 static bool anon_pidfd_probe(const char *str
)
190 return strncmp(str
, "[pidfd]", 7) == 0;
193 static char *anon_pidfd_get_name(struct unkn
*unkn
)
195 struct pidfd_data
*data
= (struct pidfd_data
*)unkn
->anon_data
;
197 return pidfd_get_name(data
);
200 static void anon_pidfd_init(struct unkn
*unkn
)
202 unkn
->anon_data
= xcalloc(1, sizeof(struct pidfd_data
));
205 static void anon_pidfd_free(struct unkn
*unkn
)
207 struct pidfd_data
*data
= (struct pidfd_data
*)unkn
->anon_data
;
213 static int anon_pidfd_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
215 return pidfd_handle_fdinfo((struct pidfd_data
*)unkn
->anon_data
,
219 static bool anon_pidfd_fill_column(struct proc
*proc
__attribute__((__unused__
)),
221 struct libscols_line
*ln
__attribute__((__unused__
)),
223 size_t column_index
__attribute__((__unused__
)),
226 return pidfd_fill_column((struct pidfd_data
*)unkn
->anon_data
,
231 static const struct anon_ops anon_pidfd_ops
= {
233 .probe
= anon_pidfd_probe
,
234 .get_name
= anon_pidfd_get_name
,
235 .fill_column
= anon_pidfd_fill_column
,
236 .init
= anon_pidfd_init
,
237 .free
= anon_pidfd_free
,
238 .handle_fdinfo
= anon_pidfd_handle_fdinfo
,
244 struct anon_eventfd_data
{
246 struct unkn
*backptr
;
247 struct ipc_endpoint endpoint
;
255 static unsigned int anon_eventfd_get_hash(struct file
*file
)
257 struct unkn
*unkn
= (struct unkn
*)file
;
258 struct anon_eventfd_data
*data
= (struct anon_eventfd_data
*)unkn
->anon_data
;
260 return (unsigned int)data
->id
;
263 static bool anon_eventfd_is_suitable_ipc(struct ipc
*ipc
, struct file
*file
)
265 struct unkn
*unkn
= (struct unkn
*)file
;
266 struct anon_eventfd_data
*data
= (struct anon_eventfd_data
*)unkn
->anon_data
;
268 return ((struct eventfd_ipc
*)ipc
)->id
== data
->id
;
271 static const struct ipc_class anon_eventfd_ipc_class
= {
272 .size
= sizeof(struct eventfd_ipc
),
273 .get_hash
= anon_eventfd_get_hash
,
274 .is_suitable_ipc
= anon_eventfd_is_suitable_ipc
,
278 static bool anon_eventfd_probe(const char *str
)
280 return strncmp(str
, "[eventfd]", 9) == 0;
283 static char *anon_eventfd_get_name(struct unkn
*unkn
)
286 struct anon_eventfd_data
*data
= (struct anon_eventfd_data
*)unkn
->anon_data
;
288 xasprintf(&str
, "id=%d", data
->id
);
292 static void anon_eventfd_init(struct unkn
*unkn
)
294 struct anon_eventfd_data
*data
= xcalloc(1, sizeof(struct anon_eventfd_data
));
295 init_endpoint(&data
->endpoint
);
296 data
->backptr
= unkn
;
297 unkn
->anon_data
= data
;
300 static void anon_eventfd_free(struct unkn
*unkn
)
302 free(unkn
->anon_data
);
305 static void anon_eventfd_attach_xinfo(struct unkn
*unkn
)
307 struct anon_eventfd_data
*data
= (struct anon_eventfd_data
*)unkn
->anon_data
;
309 struct ipc
*ipc
= get_ipc(&unkn
->file
);
313 ipc
= new_ipc(&anon_eventfd_ipc_class
);
314 ((struct eventfd_ipc
*)ipc
)->id
= data
->id
;
316 hash
= anon_eventfd_get_hash(&unkn
->file
);
320 add_endpoint(&data
->endpoint
, ipc
);
323 static int anon_eventfd_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
325 if (strcmp(key
, "eventfd-id") == 0) {
328 int rc
= ul_strtos64(value
, &id
, 10);
331 ((struct anon_eventfd_data
*)unkn
->anon_data
)->id
= (int)id
;
337 static inline char *anon_eventfd_data_xstrendpoint(struct file
*file
)
340 xasprintf(&str
, "%d,%s,%d",
341 file
->proc
->pid
, file
->proc
->command
, file
->association
);
345 static bool anon_eventfd_fill_column(struct proc
*proc
__attribute__((__unused__
)),
347 struct libscols_line
*ln
__attribute__((__unused__
)),
349 size_t column_index
__attribute__((__unused__
)),
352 struct anon_eventfd_data
*data
= (struct anon_eventfd_data
*)unkn
->anon_data
;
356 xasprintf(str
, "%d", data
->id
);
358 case COL_ENDPOINTS
: {
361 foreach_endpoint(e
, data
->endpoint
) {
362 struct anon_eventfd_data
*other
= list_entry(e
,
363 struct anon_eventfd_data
,
369 estr
= anon_eventfd_data_xstrendpoint(&other
->backptr
->file
);
370 xstrappend(str
, estr
);
382 static const struct anon_ops anon_eventfd_ops
= {
384 .probe
= anon_eventfd_probe
,
385 .get_name
= anon_eventfd_get_name
,
386 .fill_column
= anon_eventfd_fill_column
,
387 .init
= anon_eventfd_init
,
388 .free
= anon_eventfd_free
,
389 .handle_fdinfo
= anon_eventfd_handle_fdinfo
,
390 .attach_xinfo
= anon_eventfd_attach_xinfo
,
391 .ipc_class
= &anon_eventfd_ipc_class
,
397 struct anon_eventpoll_data
{
400 struct list_head siblings
;
403 static bool anon_eventpoll_probe(const char *str
)
405 return strncmp(str
, "[eventpoll]", 11) == 0;
408 static void anon_eventpoll_init(struct unkn
*unkn
)
410 struct anon_eventpoll_data
*data
= xcalloc(1, sizeof(struct anon_eventpoll_data
));
411 INIT_LIST_HEAD(&data
->siblings
);
412 unkn
->anon_data
= data
;
415 static void anon_eventpoll_free(struct unkn
*unkn
)
417 struct anon_eventpoll_data
*data
= unkn
->anon_data
;
422 static int anon_eventpoll_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
424 struct anon_eventpoll_data
*data
;
425 if (strcmp(key
, "tfd") == 0) {
430 tfd
= strtoul(value
, &end
, 0);
432 return 0; /* ignore -- parse failed */
434 data
= (struct anon_eventpoll_data
*)unkn
->anon_data
;
435 data
->tfds
= xreallocarray(data
->tfds
, ++data
->count
, sizeof(int));
436 data
->tfds
[data
->count
- 1] = (int)tfd
;
442 static int intcmp(const void *a
, const void *b
)
450 static void anon_eventpoll_attach_xinfo(struct unkn
*unkn
)
452 struct anon_eventpoll_data
*data
= (struct anon_eventpoll_data
*)unkn
->anon_data
;
453 if (data
->count
> 0) {
454 qsort(data
->tfds
, data
->count
, sizeof(data
->tfds
[0]),
456 list_add_tail(&data
->siblings
,
457 &unkn
->file
.proc
->eventpolls
);
461 static char *anon_eventpoll_make_tfds_string(struct anon_eventpoll_data
*data
,
465 char *str
= prefix
? xstrdup(prefix
): NULL
;
468 for (size_t i
= 0; i
< data
->count
; i
++) {
475 snprintf(buf
+ offset
, sizeof(buf
) - offset
, "%d", data
->tfds
[i
]);
476 xstrappend(&str
, buf
);
481 static char *anon_eventpoll_get_name(struct unkn
*unkn
)
483 return anon_eventpoll_make_tfds_string((struct anon_eventpoll_data
*)unkn
->anon_data
,
487 static bool anon_eventpoll_fill_column(struct proc
*proc
__attribute__((__unused__
)),
489 struct libscols_line
*ln
__attribute__((__unused__
)),
491 size_t column_index
__attribute__((__unused__
)),
494 struct anon_eventpoll_data
*data
= (struct anon_eventpoll_data
*)unkn
->anon_data
;
497 case COL_EVENTPOLL_TFDS
:
498 *str
=anon_eventpoll_make_tfds_string(data
, NULL
, '\n');
507 static const struct anon_ops anon_eventpoll_ops
= {
508 .class = "eventpoll",
509 .probe
= anon_eventpoll_probe
,
510 .get_name
= anon_eventpoll_get_name
,
511 .fill_column
= anon_eventpoll_fill_column
,
512 .init
= anon_eventpoll_init
,
513 .free
= anon_eventpoll_free
,
514 .handle_fdinfo
= anon_eventpoll_handle_fdinfo
,
515 .attach_xinfo
= anon_eventpoll_attach_xinfo
,
518 static int numcomp(const void *a
, const void *b
)
520 return *(int *)a
- *(int *)b
;
523 bool is_multiplexed_by_eventpoll(int fd
, struct list_head
*eventpolls
)
526 list_for_each (t
, eventpolls
) {
527 struct anon_eventpoll_data
*data
= list_entry(t
, struct anon_eventpoll_data
, siblings
);
529 if (bsearch(&fd
, data
->tfds
,
530 data
->count
, sizeof(data
->tfds
[0]),
541 struct anon_timerfd_data
{
543 struct itimerspec itimerspec
;
546 static bool anon_timerfd_probe(const char *str
)
548 return strncmp(str
, "[timerfd]", 9) == 0;
551 static void anon_timerfd_init(struct unkn
*unkn
)
553 unkn
->anon_data
= xcalloc(1, sizeof(struct anon_timerfd_data
));
556 static void anon_timerfd_free(struct unkn
*unkn
)
558 struct anon_timerfd_data
*data
= unkn
->anon_data
;
562 static int anon_timerfd_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
564 struct anon_timerfd_data
*data
= (struct anon_timerfd_data
*)unkn
->anon_data
;
566 if (strcmp(key
, "clockid") == 0) {
567 unsigned long clockid
;
571 clockid
= strtoul(value
, &end
, 0);
573 return 0; /* ignore -- parse failed */
575 return 0; /* ignore -- garbage remains. */
577 data
->clockid
= clockid
;
584 if (strcmp(key
, "it_value") == 0)
585 t
= &data
->itimerspec
.it_value
;
586 else if (strcmp(key
, "it_interval") == 0)
587 t
= &data
->itimerspec
.it_interval
;
591 if (sscanf(value
, "(%"SCNu64
", %"SCNu64
")",
592 &tv_sec
, &tv_nsec
) == 2) {
593 t
->tv_sec
= (time_t)tv_sec
;
594 t
->tv_nsec
= (long)tv_nsec
;
602 static const char *anon_timerfd_decode_clockid(int clockid
)
607 case CLOCK_MONOTONIC
:
611 case CLOCK_REALTIME_ALARM
:
612 return "realtime-alarm";
613 case CLOCK_BOOTTIME_ALARM
:
614 return "boottime-alarm";
620 static void anon_timerfd_render_timespec_string(char *buf
, size_t size
,
622 const struct timespec
*t
)
624 snprintf(buf
, size
, "%s%llu.%09ld",
626 (unsigned long long)t
->tv_sec
, t
->tv_nsec
);
629 static char *anon_timerfd_get_name(struct unkn
*unkn
)
633 struct anon_timerfd_data
*data
= (struct anon_timerfd_data
*)unkn
->anon_data
;
634 const struct timespec
*exp
;
635 const struct timespec
*ival
;
637 const char *clockid_name
;
638 char exp_buf
[BUFSIZ
] = {'\0'};
639 char ival_buf
[BUFSIZ
] = {'\0'};
641 clockid_name
= anon_timerfd_decode_clockid(data
->clockid
);
643 exp
= &data
->itimerspec
.it_value
;
644 if (is_timespecset(exp
))
645 anon_timerfd_render_timespec_string(exp_buf
, sizeof(exp_buf
),
648 ival
= &data
->itimerspec
.it_interval
;
649 if (is_timespecset(ival
))
650 anon_timerfd_render_timespec_string(ival_buf
, sizeof(ival_buf
),
653 xasprintf(&str
, "clockid=%s%s%s", clockid_name
, exp_buf
, ival_buf
);
657 static bool anon_timerfd_fill_column(struct proc
*proc
__attribute__((__unused__
)),
659 struct libscols_line
*ln
__attribute__((__unused__
)),
661 size_t column_index
__attribute__((__unused__
)),
664 struct anon_timerfd_data
*data
= (struct anon_timerfd_data
*)unkn
->anon_data
;
665 char buf
[BUFSIZ
] = {'\0'};
668 case COL_TIMERFD_CLOCKID
:
669 *str
= xstrdup(anon_timerfd_decode_clockid(data
->clockid
));
671 case COL_TIMERFD_INTERVAL
:
672 anon_timerfd_render_timespec_string(buf
, sizeof(buf
), NULL
,
673 &data
->itimerspec
.it_interval
);
676 case COL_TIMERFD_REMAINING
:
677 anon_timerfd_render_timespec_string(buf
, sizeof(buf
), NULL
,
678 &data
->itimerspec
.it_value
);
686 static const struct anon_ops anon_timerfd_ops
= {
688 .probe
= anon_timerfd_probe
,
689 .get_name
= anon_timerfd_get_name
,
690 .fill_column
= anon_timerfd_fill_column
,
691 .init
= anon_timerfd_init
,
692 .free
= anon_timerfd_free
,
693 .handle_fdinfo
= anon_timerfd_handle_fdinfo
,
699 struct anon_signalfd_data
{
703 static bool anon_signalfd_probe(const char *str
)
705 return strncmp(str
, "[signalfd]", 10) == 0;
708 static void anon_signalfd_init(struct unkn
*unkn
)
710 unkn
->anon_data
= xcalloc(1, sizeof(struct anon_signalfd_data
));
713 static void anon_signalfd_free(struct unkn
*unkn
)
715 struct anon_signalfd_data
*data
= unkn
->anon_data
;
719 static int anon_signalfd_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
721 struct anon_signalfd_data
*data
= (struct anon_signalfd_data
*)unkn
->anon_data
;
723 if (strcmp(key
, "sigmask") == 0) {
724 if (ul_strtou64(value
, &data
->sigmask
, 16) < 0) {
732 static char *anon_signalfd_make_mask_string(const char* prefix
, uint64_t sigmask
)
736 for (size_t i
= 0; i
< sizeof(sigmask
) * 8; i
++) {
737 if ((((uint64_t)0x1) << i
) & sigmask
) {
738 const int signum
= i
+ 1;
739 const char *signame
= signum_to_signame(signum
);
742 xstrappend(&str
, ",");
744 xstrappend(&str
, prefix
);
747 xstrappend(&str
, signame
);
750 snprintf(buf
, sizeof(buf
), "%d", signum
);
751 xstrappend(&str
, buf
);
759 static char *anon_signalfd_get_name(struct unkn
*unkn
)
761 struct anon_signalfd_data
*data
= (struct anon_signalfd_data
*)unkn
->anon_data
;
762 return anon_signalfd_make_mask_string("mask=", data
->sigmask
);
765 static bool anon_signalfd_fill_column(struct proc
*proc
__attribute__((__unused__
)),
767 struct libscols_line
*ln
__attribute__((__unused__
)),
769 size_t column_index
__attribute__((__unused__
)),
772 struct anon_signalfd_data
*data
= (struct anon_signalfd_data
*)unkn
->anon_data
;
775 case COL_SIGNALFD_MASK
:
776 *str
= anon_signalfd_make_mask_string(NULL
, data
->sigmask
);
783 static const struct anon_ops anon_signalfd_ops
= {
785 .probe
= anon_signalfd_probe
,
786 .get_name
= anon_signalfd_get_name
,
787 .fill_column
= anon_signalfd_fill_column
,
788 .init
= anon_signalfd_init
,
789 .free
= anon_signalfd_free
,
790 .handle_fdinfo
= anon_signalfd_handle_fdinfo
,
796 struct anon_inotify_data
{
797 struct list_head inodes
;
800 struct anon_inotify_inode
{
803 struct list_head inodes
;
806 static bool anon_inotify_probe(const char *str
)
808 return strncmp(str
, "inotify", 7) == 0;
811 /* A device number appeared in fdinfo of an inotify file uses the kernel
812 * internal representation. It is different from what we are familiar with;
813 * major(3) and minor(3) don't work with the representation.
814 * See linux/include/linux/kdev_t.h. */
815 #define ANON_INOTIFY_MINORBITS 20
816 #define ANON_INOTIFY_MINORMASK ((1U << ANON_INOTIFY_MINORBITS) - 1)
818 #define ANON_INOTIFY_MAJOR(dev) ((unsigned int) ((dev) >> ANON_INOTIFY_MINORBITS))
819 #define ANON_INOTIFY_MINOR(dev) ((unsigned int) ((dev) & ANON_INOTIFY_MINORMASK))
821 static char *anon_inotify_make_inodes_string(const char *prefix
,
823 enum decode_source_level decode_level
,
824 struct anon_inotify_data
*data
)
827 char buf
[BUFSIZ
] = {'\0'};
828 bool first_element
= true;
831 list_for_each(i
, &data
->inodes
) {
832 char source
[BUFSIZ
/2] = {'\0'};
833 struct anon_inotify_inode
*inode
= list_entry(i
,
834 struct anon_inotify_inode
,
837 decode_source(source
, sizeof(source
),
838 ANON_INOTIFY_MAJOR(inode
->sdev
), ANON_INOTIFY_MINOR(inode
->sdev
),
840 snprintf(buf
, sizeof(buf
), "%s%llu@%s", first_element
? prefix
: sep
,
841 (unsigned long long)inode
->ino
, source
);
842 first_element
= false;
844 xstrappend(&str
, buf
);
850 static char *anon_inotify_get_name(struct unkn
*unkn
)
852 return anon_inotify_make_inodes_string("inodes=", ",", DECODE_SOURCE_FULL
,
853 (struct anon_inotify_data
*)unkn
->anon_data
);
856 static void anon_inotify_init(struct unkn
*unkn
)
858 struct anon_inotify_data
*data
= xcalloc(1, sizeof(struct anon_inotify_data
));
859 INIT_LIST_HEAD (&data
->inodes
);
860 unkn
->anon_data
= data
;
863 static void anon_inotify_free(struct unkn
*unkn
)
865 struct anon_inotify_data
*data
= unkn
->anon_data
;
867 list_free(&data
->inodes
, struct anon_inotify_inode
, inodes
,
872 static void add_inode(struct anon_inotify_data
*data
, ino_t ino
, dev_t sdev
)
874 struct anon_inotify_inode
*inode
= xmalloc(sizeof(*inode
));
876 INIT_LIST_HEAD (&inode
->inodes
);
880 list_add_tail(&inode
->inodes
, &data
->inodes
);
883 static int anon_inotify_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
885 struct anon_inotify_data
*data
= (struct anon_inotify_data
*)unkn
->anon_data
;
887 if (strcmp(key
, "inotify wd") == 0) {
888 unsigned long long ino
;
889 unsigned long long sdev
;
891 if (sscanf(value
, "%*d ino:%llx sdev:%llx %*s", &ino
, &sdev
) == 2) {
892 add_inode(data
, (ino_t
)ino
, (dev_t
)sdev
);
899 static bool anon_inotify_fill_column(struct proc
*proc
__attribute__((__unused__
)),
901 struct libscols_line
*ln
__attribute__((__unused__
)),
903 size_t column_index
__attribute__((__unused__
)),
906 struct anon_inotify_data
*data
= (struct anon_inotify_data
*)unkn
->anon_data
;
909 case COL_INOTIFY_INODES
:
910 *str
= anon_inotify_make_inodes_string("", "\n", DECODE_SOURCE_FULL
,
915 case COL_INOTIFY_INODES_RAW
:
916 *str
= anon_inotify_make_inodes_string("", "\n", DECODE_SOURCE_MAJMIN
,
926 static const struct anon_ops anon_inotify_ops
= {
928 .probe
= anon_inotify_probe
,
929 .get_name
= anon_inotify_get_name
,
930 .fill_column
= anon_inotify_fill_column
,
931 .init
= anon_inotify_init
,
932 .free
= anon_inotify_free
,
933 .handle_fdinfo
= anon_inotify_handle_fdinfo
,
939 * Generally, we use "-" as the word separators in lsfd's output.
940 * However, about bpf*, we use "_" because bpftool uses "_".
942 static const char *bpf_prog_type_table
[] = {
943 [0] = "unspec", /* BPF_PROG_TYPE_UNSPEC*/
944 [1] = "socket_filter", /* BPF_PROG_TYPE_SOCKET_FILTER*/
945 [2] = "kprobe", /* BPF_PROG_TYPE_KPROBE*/
946 [3] = "sched_cls", /* BPF_PROG_TYPE_SCHED_CLS*/
947 [4] = "sched_act", /* BPF_PROG_TYPE_SCHED_ACT*/
948 [5] = "tracepoint", /* BPF_PROG_TYPE_TRACEPOINT*/
949 [6] = "xdp", /* BPF_PROG_TYPE_XDP*/
950 [7] = "perf_event", /* BPF_PROG_TYPE_PERF_EVENT*/
951 [8] = "cgroup_skb", /* BPF_PROG_TYPE_CGROUP_SKB*/
952 [9] = "cgroup_sock", /* BPF_PROG_TYPE_CGROUP_SOCK*/
953 [10] = "lwt_in", /* BPF_PROG_TYPE_LWT_IN*/
954 [11] = "lwt_out", /* BPF_PROG_TYPE_LWT_OUT*/
955 [12] = "lwt_xmit", /* BPF_PROG_TYPE_LWT_XMIT*/
956 [13] = "sock_ops", /* BPF_PROG_TYPE_SOCK_OPS*/
957 [14] = "sk_skb", /* BPF_PROG_TYPE_SK_SKB*/
958 [15] = "cgroup_device", /* BPF_PROG_TYPE_CGROUP_DEVICE*/
959 [16] = "sk_msg", /* BPF_PROG_TYPE_SK_MSG*/
960 [17] = "raw_tracepoint", /* BPF_PROG_TYPE_RAW_TRACEPOINT*/
961 [18] = "cgroup_sock_addr", /* BPF_PROG_TYPE_CGROUP_SOCK_ADDR*/
962 [19] = "lwt_seg6local", /* BPF_PROG_TYPE_LWT_SEG6LOCAL*/
963 [20] = "lirc_mode2", /* BPF_PROG_TYPE_LIRC_MODE2*/
964 [21] = "sk_reuseport", /* BPF_PROG_TYPE_SK_REUSEPORT*/
965 [22] = "flow_dissector", /* BPF_PROG_TYPE_FLOW_DISSECTOR*/
966 [23] = "cgroup_sysctl", /* BPF_PROG_TYPE_CGROUP_SYSCTL*/
967 [24] = "raw_tracepoint_writable", /* BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE*/
968 [25] = "cgroup_sockopt", /* BPF_PROG_TYPE_CGROUP_SOCKOPT*/
969 [26] = "tracing", /* BPF_PROG_TYPE_TRACING*/
970 [27] = "struct_ops", /* BPF_PROG_TYPE_STRUCT_OPS*/
971 [28] = "ext", /* BPF_PROG_TYPE_EXT*/
972 [29] = "lsm", /* BPF_PROG_TYPE_LSM*/
973 [30] = "sk_lookup", /* BPF_PROG_TYPE_SK_LOOKUP*/
974 [31] = "syscall", /* BPF_PROG_TYPE_SYSCALL*/
977 struct anon_bpf_prog_data
{
980 char name
[BPF_OBJ_NAME_LEN
+ 1];
983 static bool anon_bpf_prog_probe(const char *str
)
985 return strncmp(str
, "bpf-prog", 8) == 0;
988 static const char *anon_bpf_prog_get_prog_type_name(int type
)
990 if (0 <= type
&& type
< (int)ARRAY_SIZE(bpf_prog_type_table
))
991 return bpf_prog_type_table
[type
];
995 static bool anon_bpf_prog_fill_column(struct proc
*proc
__attribute__((__unused__
)),
997 struct libscols_line
*ln
__attribute__((__unused__
)),
999 size_t column_index
__attribute__((__unused__
)),
1002 struct anon_bpf_prog_data
*data
= (struct anon_bpf_prog_data
*)unkn
->anon_data
;
1006 case COL_BPF_PROG_ID
:
1007 xasprintf(str
, "%d", data
->id
);
1009 case COL_BPF_PROG_TYPE_RAW
:
1010 xasprintf(str
, "%d", data
->type
);
1012 case COL_BPF_PROG_TYPE
:
1013 t
= anon_bpf_prog_get_prog_type_name(data
->type
);
1017 xasprintf(str
, "UNKNOWN(%d)", data
->type
);
1020 *str
= xstrdup(data
->name
);
1027 static char *anon_bpf_prog_get_name(struct unkn
*unkn
)
1031 struct anon_bpf_prog_data
*data
= (struct anon_bpf_prog_data
*)unkn
->anon_data
;
1033 t
= anon_bpf_prog_get_prog_type_name(data
->type
);
1035 xasprintf(&str
, "id=%d type=%s", data
->id
, t
);
1037 xasprintf(&str
, "id=%d type=UNKNOWN(%d)", data
->id
, data
->type
);
1040 xstrfappend(&str
, " name=%s", data
->name
);
1046 static void anon_bpf_prog_init(struct unkn
*unkn
)
1048 struct anon_bpf_prog_data
*data
= xmalloc(sizeof(*data
));
1051 data
->name
[0] = '\0';
1052 unkn
->anon_data
= data
;
1055 static void anon_bpf_prog_free(struct unkn
*unkn
)
1057 struct anon_bpf_prog_data
*data
= (struct anon_bpf_prog_data
*)unkn
->anon_data
;
1061 static void anon_bpf_prog_get_more_info(struct anon_bpf_prog_data
*prog_data
)
1063 union bpf_attr attr
= {
1064 .prog_id
= (int32_t)prog_data
->id
,
1068 struct bpf_prog_info info
= { 0 };
1069 union bpf_attr info_attr
= {
1070 .info
.info_len
= sizeof(info
),
1071 .info
.info
= (uint64_t)(uintptr_t)&info
,
1074 int bpf_fd
= syscall(SYS_bpf
, BPF_PROG_GET_FD_BY_ID
, &attr
, sizeof(attr
));
1078 info_attr
.info
.bpf_fd
= bpf_fd
;
1079 if (syscall(SYS_bpf
, BPF_OBJ_GET_INFO_BY_FD
, &info_attr
, offsetofend(union bpf_attr
, info
)) == 0) {
1080 memcpy(prog_data
->name
,
1083 prog_data
->name
[BPF_OBJ_NAME_LEN
] = '\0';
1088 static int anon_bpf_prog_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
1090 if (strcmp(key
, "prog_id") == 0) {
1092 int rc
= ul_strtos32(value
, &t
, 10);
1094 return 0; /* ignore -- parse failed */
1095 ((struct anon_bpf_prog_data
*)unkn
->anon_data
)->id
= (int)t
;
1096 anon_bpf_prog_get_more_info((struct anon_bpf_prog_data
*)unkn
->anon_data
);
1100 if (strcmp(key
, "prog_type") == 0) {
1102 int rc
= ul_strtos32(value
, &t
, 10);
1104 return 0; /* ignore -- parse failed */
1105 ((struct anon_bpf_prog_data
*)unkn
->anon_data
)->type
= (int)t
;
1112 static const struct anon_ops anon_bpf_prog_ops
= {
1113 .class = "bpf-prog",
1114 .probe
= anon_bpf_prog_probe
,
1115 .get_name
= anon_bpf_prog_get_name
,
1116 .fill_column
= anon_bpf_prog_fill_column
,
1117 .init
= anon_bpf_prog_init
,
1118 .free
= anon_bpf_prog_free
,
1119 .handle_fdinfo
= anon_bpf_prog_handle_fdinfo
,
1125 static const char *bpf_map_type_table
[] = {
1126 [0] = "unspec", /* BPF_MAP_TYPE_UNSPEC */
1127 [1] = "hash", /* BPF_MAP_TYPE_HASH */
1128 [2] = "array", /* BPF_MAP_TYPE_ARRAY */
1129 [3] = "prog-array", /* BPF_MAP_TYPE_PROG_ARRAY */
1130 [4] = "perf-event-array", /* BPF_MAP_TYPE_PERF_EVENT_ARRAY */
1131 [5] = "percpu-hash", /* BPF_MAP_TYPE_PERCPU_HASH */
1132 [6] = "percpu-array", /* BPF_MAP_TYPE_PERCPU_ARRAY */
1133 [7] = "stack-trace", /* BPF_MAP_TYPE_STACK_TRACE */
1134 [8] = "cgroup-array", /* BPF_MAP_TYPE_CGROUP_ARRAY */
1135 [9] = "lru-hash", /* BPF_MAP_TYPE_LRU_HASH */
1136 [10] = "lru-percpu-hash", /* BPF_MAP_TYPE_LRU_PERCPU_HASH */
1137 [11] = "lpm-trie", /* BPF_MAP_TYPE_LPM_TRIE */
1138 [12] = "array-of-maps", /* BPF_MAP_TYPE_ARRAY_OF_MAPS */
1139 [13] = "hash-of-maps", /* BPF_MAP_TYPE_HASH_OF_MAPS */
1140 [14] = "devmap", /* BPF_MAP_TYPE_DEVMAP */
1141 [15] = "sockmap", /* BPF_MAP_TYPE_SOCKMAP */
1142 [16] = "cpumap", /* BPF_MAP_TYPE_CPUMAP */
1143 [17] = "xskmap", /* BPF_MAP_TYPE_XSKMAP */
1144 [18] = "sockhash", /* BPF_MAP_TYPE_SOCKHASH */
1145 [19] = "cgroup-storage", /* BPF_MAP_TYPE_CGROUP_STORAGE */
1146 [20] = "reuseport-sockarray", /* BPF_MAP_TYPE_REUSEPORT_SOCKARRAY */
1147 [21] = "percpu-cgroup-storage", /* BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE */
1148 [22] = "queue", /* BPF_MAP_TYPE_QUEUE */
1149 [23] = "stack", /* BPF_MAP_TYPE_STACK */
1150 [24] = "sk-storage", /* BPF_MAP_TYPE_SK_STORAGE */
1151 [25] = "devmap-hash", /* BPF_MAP_TYPE_DEVMAP_HASH */
1152 [26] = "struct-ops", /* BPF_MAP_TYPE_STRUCT_OPS */
1153 [27] = "ringbuf", /* BPF_MAP_TYPE_RINGBUF */
1154 [28] = "inode-storage", /* BPF_MAP_TYPE_INODE_STORAGE */
1155 [29] = "task-storage", /* BPF_MAP_TYPE_TASK_STORAGE */
1156 [30] = "bloom-filter", /* BPF_MAP_TYPE_BLOOM_FILTER */
1157 [31] = "user-ringbuf", /* BPF_MAP_TYPE_USER_RINGBUF */
1158 [32] = "cgrp-storage", /* BPF_MAP_TYPE_CGRP_STORAGE */
1161 struct anon_bpf_map_data
{
1164 char name
[BPF_OBJ_NAME_LEN
+ 1];
1167 static bool anon_bpf_map_probe(const char *str
)
1169 return strncmp(str
, "bpf-map", 8) == 0;
1172 static const char *anon_bpf_map_get_map_type_name(int type
)
1174 if (0 <= type
&& type
< (int)ARRAY_SIZE(bpf_map_type_table
))
1175 return bpf_map_type_table
[type
];
1179 static bool anon_bpf_map_fill_column(struct proc
*proc
__attribute__((__unused__
)),
1181 struct libscols_line
*ln
__attribute__((__unused__
)),
1183 size_t column_index
__attribute__((__unused__
)),
1186 struct anon_bpf_prog_data
*data
= (struct anon_bpf_prog_data
*)unkn
->anon_data
;
1190 case COL_BPF_MAP_ID
:
1191 xasprintf(str
, "%d", data
->id
);
1193 case COL_BPF_MAP_TYPE_RAW
:
1194 xasprintf(str
, "%d", data
->type
);
1196 case COL_BPF_MAP_TYPE
:
1197 t
= anon_bpf_map_get_map_type_name(data
->type
);
1201 xasprintf(str
, "UNKNOWN(%d)", data
->type
);
1204 *str
= xstrdup(data
->name
);
1211 static char *anon_bpf_map_get_name(struct unkn
*unkn
)
1215 struct anon_bpf_map_data
*data
= (struct anon_bpf_map_data
*)unkn
->anon_data
;
1217 t
= anon_bpf_map_get_map_type_name(data
->type
);
1219 xasprintf(&str
, "id=%d type=%s", data
->id
, t
);
1221 xasprintf(&str
, "id=%d type=UNKNOWN(%d)", data
->id
, data
->type
);
1224 xstrfappend(&str
, " name=%s", data
->name
);
1229 static void anon_bpf_map_init(struct unkn
*unkn
)
1231 struct anon_bpf_map_data
*data
= xmalloc(sizeof(*data
));
1234 data
->name
[0] = '\0';
1235 unkn
->anon_data
= data
;
1238 static void anon_bpf_map_free(struct unkn
*unkn
)
1240 struct anon_bpf_map_data
*data
= (struct anon_bpf_map_data
*)unkn
->anon_data
;
1244 static void anon_bpf_map_get_more_info(struct anon_bpf_map_data
*map_data
)
1246 union bpf_attr attr
= {
1247 .map_id
= (int32_t)map_data
->id
,
1251 struct bpf_map_info info
= { 0 };
1252 union bpf_attr info_attr
= {
1253 .info
.info_len
= sizeof(info
),
1254 .info
.info
= (uint64_t)(uintptr_t)&info
,
1257 int bpf_fd
= syscall(SYS_bpf
, BPF_MAP_GET_FD_BY_ID
, &attr
, sizeof(attr
));
1261 info_attr
.info
.bpf_fd
= bpf_fd
;
1262 if (syscall(SYS_bpf
, BPF_OBJ_GET_INFO_BY_FD
, &info_attr
, offsetofend(union bpf_attr
, info
)) == 0) {
1263 memcpy(map_data
->name
,
1266 map_data
->name
[BPF_OBJ_NAME_LEN
] = '\0';
1271 static int anon_bpf_map_handle_fdinfo(struct unkn
*unkn
, const char *key
, const char *value
)
1273 if (strcmp(key
, "map_id") == 0) {
1275 int rc
= ul_strtos32(value
, &t
, 10);
1277 return 0; /* ignore -- parse failed */
1278 ((struct anon_bpf_map_data
*)unkn
->anon_data
)->id
= (int)t
;
1279 anon_bpf_map_get_more_info((struct anon_bpf_map_data
*)unkn
->anon_data
);
1283 if (strcmp(key
, "map_type") == 0) {
1285 int rc
= ul_strtos32(value
, &t
, 10);
1287 return 0; /* ignore -- parse failed */
1288 ((struct anon_bpf_map_data
*)unkn
->anon_data
)->type
= (int)t
;
1295 static const struct anon_ops anon_bpf_map_ops
= {
1297 .probe
= anon_bpf_map_probe
,
1298 .get_name
= anon_bpf_map_get_name
,
1299 .fill_column
= anon_bpf_map_fill_column
,
1300 .init
= anon_bpf_map_init
,
1301 .free
= anon_bpf_map_free
,
1302 .handle_fdinfo
= anon_bpf_map_handle_fdinfo
,
1306 * generic (fallback implementation)
1308 static const struct anon_ops anon_generic_ops
= {
1311 .fill_column
= NULL
,
1314 .handle_fdinfo
= NULL
,
1317 static const struct anon_ops
*anon_ops
[] = {
1320 &anon_eventpoll_ops
,
1328 static const struct anon_ops
*anon_probe(const char *str
)
1330 for (size_t i
= 0; i
< ARRAY_SIZE(anon_ops
); i
++)
1331 if (anon_ops
[i
]->probe(str
))
1333 return &anon_generic_ops
;
1336 const struct file_class unkn_class
= {
1337 .super
= &file_class
,
1338 .size
= sizeof(struct unkn
),
1339 .fill_column
= unkn_fill_column
,
1340 .initialize_content
= unkn_init_content
,
1341 .free_content
= unkn_content_free
,
1342 .handle_fdinfo
= unkn_handle_fdinfo
,
1343 .attach_xinfo
= unkn_attach_xinfo
,
1344 .get_ipc_class
= unkn_get_ipc_class
,