]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/lsfd-unkn.c
Merge branch 'PR/libsmartcols-cell-data' of github.com:karelzak/util-linux-work
[thirdparty/util-linux.git] / misc-utils / lsfd-unkn.c
1 /*
2 * lsfd-unkn.c - handle associations opening unknown objects
3 *
4 * Copyright (C) 2021 Red Hat, Inc. All rights reserved.
5 * Written by Masatake YAMATO <yamato@redhat.com>
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #include <linux/bpf.h>
23 #include <sys/syscall.h>
24 #include <sys/timerfd.h>
25 #include <time.h>
26
27 #include "signames.h"
28 #include "timeutils.h"
29
30 #include "lsfd.h"
31
32 #define offsetofend(TYPE, MEMBER) \
33 (offsetof(TYPE, MEMBER) + sizeof_member(TYPE, MEMBER))
34
35 struct unkn {
36 struct file file;
37 const struct anon_ops *anon_ops;
38 void *anon_data;
39 };
40
41 struct anon_ops {
42 const char *class;
43 bool (*probe)(const char *);
44 char * (*get_name)(struct unkn *);
45 /* Return true is handled the column. */
46 bool (*fill_column)(struct proc *,
47 struct unkn *,
48 struct libscols_line *,
49 int,
50 size_t,
51 char **str);
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;
57 };
58
59 static const struct anon_ops *anon_probe(const char *);
60
61 static char * anon_get_class(struct unkn *unkn)
62 {
63 char *name;
64
65 if (unkn->anon_ops->class)
66 return xstrdup(unkn->anon_ops->class);
67
68 /* See unkn_init_content() */
69 name = ((struct file *)unkn)->name + 11;
70 /* Does it have the form anon_inode:[class]? */
71 if (*name == '[') {
72 size_t len = strlen(name + 1);
73 if (*(name + 1 + len - 1) == ']')
74 return strndup(name + 1, len - 1);
75 }
76
77 return xstrdup(name);
78 }
79
80 static bool unkn_fill_column(struct proc *proc,
81 struct file *file,
82 struct libscols_line *ln,
83 int column_id,
84 size_t column_index)
85 {
86 char *str = NULL;
87 struct unkn *unkn = (struct unkn *)file;
88
89 switch(column_id) {
90 case COL_NAME:
91 if (unkn->anon_ops && unkn->anon_ops->get_name) {
92 str = unkn->anon_ops->get_name(unkn);
93 if (str)
94 break;
95 }
96 return false;
97 case COL_TYPE:
98 if (!unkn->anon_ops)
99 return false;
100 /* FALL THROUGH */
101 case COL_AINODECLASS:
102 if (unkn->anon_ops) {
103 str = anon_get_class(unkn);
104 break;
105 }
106 return false;
107 case COL_SOURCE:
108 if (unkn->anon_ops) {
109 str = xstrdup("anon_inodefs");
110 break;
111 }
112 return false;
113 default:
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))
117 break;
118 }
119 return false;
120 }
121
122 if (!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"));
126 return true;
127 }
128
129 static void unkn_attach_xinfo(struct file *file)
130 {
131 struct unkn *unkn = (struct unkn *)file;
132 if (unkn->anon_ops && unkn->anon_ops->attach_xinfo)
133 unkn->anon_ops->attach_xinfo(unkn);
134 }
135
136 static const struct ipc_class *unkn_get_ipc_class(struct file *file)
137 {
138 struct unkn *unkn = (struct unkn *)file;
139
140 if (unkn->anon_ops && unkn->anon_ops->ipc_class)
141 return unkn->anon_ops->ipc_class;
142 return NULL;
143 }
144
145 static void unkn_init_content(struct file *file)
146 {
147 struct unkn *unkn = (struct unkn *)file;
148
149 assert(file);
150 unkn->anon_ops = NULL;
151 unkn->anon_data = NULL;
152
153 if (major(file->stat.st_dev) == 0
154 && strncmp(file->name, "anon_inode:", 11) == 0) {
155 const char *rest = file->name + 11;
156
157 unkn->anon_ops = anon_probe(rest);
158
159 if (unkn->anon_ops->init)
160 unkn->anon_ops->init(unkn);
161 }
162 }
163
164 static void unkn_content_free(struct file *file)
165 {
166 struct unkn *unkn = (struct unkn *)file;
167
168 assert(file);
169 if (unkn->anon_ops && unkn->anon_ops->free)
170 unkn->anon_ops->free((struct unkn *)file);
171 }
172
173 static int unkn_handle_fdinfo(struct file *file, const char *key, const char *value)
174 {
175 struct unkn *unkn = (struct unkn *)file;
176
177 assert(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 */
181 }
182
183 /*
184 * pidfd
185 */
186 struct anon_pidfd_data {
187 pid_t pid;
188 char *nspid;
189 };
190
191 static bool anon_pidfd_probe(const char *str)
192 {
193 return strncmp(str, "[pidfd]", 7) == 0;
194 }
195
196 static char *anon_pidfd_get_name(struct unkn *unkn)
197 {
198 char *str = NULL;
199 struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
200
201 char *comm = NULL;
202 struct proc *proc = get_proc(data->pid);
203 if (proc)
204 comm = proc->command;
205
206 xasprintf(&str, "pid=%d comm=%s nspid=%s",
207 data->pid,
208 comm? comm: "",
209 data->nspid? data->nspid: "");
210 return str;
211 }
212
213 static void anon_pidfd_init(struct unkn *unkn)
214 {
215 unkn->anon_data = xcalloc(1, sizeof(struct anon_pidfd_data));
216 }
217
218 static void anon_pidfd_free(struct unkn *unkn)
219 {
220 struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
221
222 if (data->nspid)
223 free(data->nspid);
224 free(data);
225 }
226
227 static int anon_pidfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
228 {
229 if (strcmp(key, "Pid") == 0) {
230 uint64_t pid;
231
232 int rc = ul_strtou64(value, &pid, 10);
233 if (rc < 0)
234 return 0; /* ignore -- parse failed */
235 ((struct anon_pidfd_data *)unkn->anon_data)->pid = (pid_t)pid;
236 return 1;
237 } else if (strcmp(key, "NSpid") == 0) {
238 ((struct anon_pidfd_data *)unkn->anon_data)->nspid = xstrdup(value);
239 return 1;
240
241 }
242 return 0;
243 }
244
245 static bool anon_pidfd_fill_column(struct proc *proc __attribute__((__unused__)),
246 struct unkn *unkn,
247 struct libscols_line *ln __attribute__((__unused__)),
248 int column_id,
249 size_t column_index __attribute__((__unused__)),
250 char **str)
251 {
252 struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
253
254 switch(column_id) {
255 case COL_PIDFD_COMM: {
256 struct proc *pidfd_proc = get_proc(data->pid);
257 char *pidfd_comm = NULL;
258 if (pidfd_proc)
259 pidfd_comm = pidfd_proc->command;
260 if (pidfd_comm) {
261 *str = xstrdup(pidfd_comm);
262 return true;
263 }
264 break;
265 }
266 case COL_PIDFD_NSPID:
267 if (data->nspid) {
268 *str = xstrdup(data->nspid);
269 return true;
270 }
271 break;
272 case COL_PIDFD_PID:
273 xasprintf(str, "%d", (int)data->pid);
274 return true;
275 }
276
277 return false;
278 }
279
280 static const struct anon_ops anon_pidfd_ops = {
281 .class = "pidfd",
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,
288 };
289
290 /*
291 * eventfd
292 */
293 struct anon_eventfd_data {
294 int id;
295 struct unkn *backptr;
296 struct ipc_endpoint endpoint;
297 };
298
299 struct eventfd_ipc {
300 struct ipc ipc;
301 int id;
302 };
303
304 static unsigned int anon_eventfd_get_hash(struct file *file)
305 {
306 struct unkn *unkn = (struct unkn *)file;
307 struct anon_eventfd_data *data = (struct anon_eventfd_data *)unkn->anon_data;
308
309 return (unsigned int)data->id;
310 }
311
312 static bool anon_eventfd_is_suitable_ipc(struct ipc *ipc, struct file *file)
313 {
314 struct unkn *unkn = (struct unkn *)file;
315 struct anon_eventfd_data *data = (struct anon_eventfd_data *)unkn->anon_data;
316
317 return ((struct eventfd_ipc *)ipc)->id == data->id;
318 }
319
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,
324 .free = NULL,
325 };
326
327 static bool anon_eventfd_probe(const char *str)
328 {
329 return strncmp(str, "[eventfd]", 9) == 0;
330 }
331
332 static char *anon_eventfd_get_name(struct unkn *unkn)
333 {
334 char *str = NULL;
335 struct anon_eventfd_data *data = (struct anon_eventfd_data *)unkn->anon_data;
336
337 xasprintf(&str, "id=%d", data->id);
338 return str;
339 }
340
341 static void anon_eventfd_init(struct unkn *unkn)
342 {
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;
347 }
348
349 static void anon_eventfd_free(struct unkn *unkn)
350 {
351 free(unkn->anon_data);
352 }
353
354 static void anon_eventfd_attach_xinfo(struct unkn *unkn)
355 {
356 struct anon_eventfd_data *data = (struct anon_eventfd_data *)unkn->anon_data;
357 unsigned int hash;
358 struct ipc *ipc = get_ipc(&unkn->file);
359 if (ipc)
360 goto link;
361
362 ipc = new_ipc(&anon_eventfd_ipc_class);
363 ((struct eventfd_ipc *)ipc)->id = data->id;
364
365 hash = anon_eventfd_get_hash(&unkn->file);
366 add_ipc(ipc, hash);
367
368 link:
369 add_endpoint(&data->endpoint, ipc);
370 }
371
372 static int anon_eventfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
373 {
374 if (strcmp(key, "eventfd-id") == 0) {
375 int64_t id;
376
377 int rc = ul_strtos64(value, &id, 10);
378 if (rc < 0)
379 return 0;
380 ((struct anon_eventfd_data *)unkn->anon_data)->id = (int)id;
381 return 1;
382 }
383 return 0;
384 }
385
386 static inline char *anon_eventfd_data_xstrendpoint(struct file *file)
387 {
388 char *str = NULL;
389 xasprintf(&str, "%d,%s,%d",
390 file->proc->pid, file->proc->command, file->association);
391 return str;
392 }
393
394 static bool anon_eventfd_fill_column(struct proc *proc __attribute__((__unused__)),
395 struct unkn *unkn,
396 struct libscols_line *ln __attribute__((__unused__)),
397 int column_id,
398 size_t column_index __attribute__((__unused__)),
399 char **str)
400 {
401 struct anon_eventfd_data *data = (struct anon_eventfd_data *)unkn->anon_data;
402
403 switch(column_id) {
404 case COL_EVENTFD_ID:
405 xasprintf(str, "%d", data->id);
406 return true;
407 case COL_ENDPOINTS: {
408 struct list_head *e;
409 char *estr;
410 foreach_endpoint(e, data->endpoint) {
411 struct anon_eventfd_data *other = list_entry(e,
412 struct anon_eventfd_data,
413 endpoint.endpoints);
414 if (data == other)
415 continue;
416 if (*str)
417 xstrputc(str, '\n');
418 estr = anon_eventfd_data_xstrendpoint(&other->backptr->file);
419 xstrappend(str, estr);
420 free(estr);
421 }
422 if (!*str)
423 return false;
424 return true;
425 }
426 default:
427 return false;
428 }
429 }
430
431 static const struct anon_ops anon_eventfd_ops = {
432 .class = "eventfd",
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,
441 };
442
443 /*
444 * eventpoll
445 */
446 struct anon_eventpoll_data {
447 size_t count;
448 int *tfds;
449 struct list_head siblings;
450 };
451
452 static bool anon_eventpoll_probe(const char *str)
453 {
454 return strncmp(str, "[eventpoll]", 11) == 0;
455 }
456
457 static void anon_eventpoll_init(struct unkn *unkn)
458 {
459 struct anon_eventpoll_data *data = xcalloc(1, sizeof(struct anon_eventpoll_data));
460 INIT_LIST_HEAD(&data->siblings);
461 unkn->anon_data = data;
462 }
463
464 static void anon_eventpoll_free(struct unkn *unkn)
465 {
466 struct anon_eventpoll_data *data = unkn->anon_data;
467 free(data->tfds);
468 free(data);
469 }
470
471 static int anon_eventpoll_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
472 {
473 struct anon_eventpoll_data *data;
474 if (strcmp(key, "tfd") == 0) {
475 unsigned long tfd;
476 char *end = NULL;
477
478 errno = 0;
479 tfd = strtoul(value, &end, 0);
480 if (errno != 0)
481 return 0; /* ignore -- parse failed */
482
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;
486 return 1;
487 }
488 return 0;
489 }
490
491 static int intcmp(const void *a, const void *b)
492 {
493 int ai = *(int *)a;
494 int bi = *(int *)b;
495
496 return ai - bi;
497 }
498
499 static void anon_eventpoll_attach_xinfo(struct unkn *unkn)
500 {
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]),
504 intcmp);
505 list_add_tail(&data->siblings,
506 &unkn->file.proc->eventpolls);
507 }
508 }
509
510 static char *anon_eventpoll_make_tfds_string(struct anon_eventpoll_data *data,
511 const char *prefix,
512 const char sep)
513 {
514 char *str = prefix? xstrdup(prefix): NULL;
515
516 char buf[256];
517 for (size_t i = 0; i < data->count; i++) {
518 size_t offset = 0;
519
520 if (i > 0) {
521 buf[0] = sep;
522 offset = 1;
523 }
524 snprintf(buf + offset, sizeof(buf) - offset, "%d", data->tfds[i]);
525 xstrappend(&str, buf);
526 }
527 return str;
528 }
529
530 static char *anon_eventpoll_get_name(struct unkn *unkn)
531 {
532 return anon_eventpoll_make_tfds_string((struct anon_eventpoll_data *)unkn->anon_data,
533 "tfds=", ',');
534 }
535
536 static bool anon_eventpoll_fill_column(struct proc *proc __attribute__((__unused__)),
537 struct unkn *unkn,
538 struct libscols_line *ln __attribute__((__unused__)),
539 int column_id,
540 size_t column_index __attribute__((__unused__)),
541 char **str)
542 {
543 struct anon_eventpoll_data *data = (struct anon_eventpoll_data *)unkn->anon_data;
544
545 switch(column_id) {
546 case COL_EVENTPOLL_TFDS:
547 *str =anon_eventpoll_make_tfds_string(data, NULL, '\n');
548 if (*str)
549 return true;
550 break;
551 }
552
553 return false;
554 }
555
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,
565 };
566
567 static int numcomp(const void *a, const void *b)
568 {
569 return *(int *)a - *(int *)b;
570 }
571
572 bool is_multiplexed_by_eventpoll(int fd, struct list_head *eventpolls)
573 {
574 struct list_head *t;
575 list_for_each (t, eventpolls) {
576 struct anon_eventpoll_data *data = list_entry(t, struct anon_eventpoll_data, siblings);
577 if (data->count) {
578 if (bsearch(&fd, data->tfds,
579 data->count, sizeof (data->tfds[0]),
580 numcomp))
581 return true;
582 }
583 }
584 return false;
585 }
586
587 /*
588 * timerfd
589 */
590 struct anon_timerfd_data {
591 int clockid;
592 struct itimerspec itimerspec;
593 };
594
595 static bool anon_timerfd_probe(const char *str)
596 {
597 return strncmp(str, "[timerfd]", 9) == 0;
598 }
599
600 static void anon_timerfd_init(struct unkn *unkn)
601 {
602 unkn->anon_data = xcalloc(1, sizeof(struct anon_timerfd_data));
603 }
604
605 static void anon_timerfd_free(struct unkn *unkn)
606 {
607 struct anon_timerfd_data *data = unkn->anon_data;
608 free(data);
609 }
610
611 static int anon_timerfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
612 {
613 struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data;
614
615 if (strcmp(key, "clockid") == 0) {
616 unsigned long clockid;
617 char *end = NULL;
618
619 errno = 0;
620 clockid = strtoul(value, &end, 0);
621 if (errno != 0)
622 return 0; /* ignore -- parse failed */
623 if (*end != '\0')
624 return 0; /* ignore -- garbage remains. */
625
626 data->clockid = clockid;
627 return 1;
628 } else {
629 struct timespec *t;
630 uint64_t tv_sec;
631 uint64_t tv_nsec;
632
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;
637 else
638 return 0;
639
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;
644 return 1;
645 }
646
647 return 0;
648 }
649 }
650
651 static const char *anon_timerfd_decode_clockid(int clockid)
652 {
653 switch (clockid) {
654 case CLOCK_REALTIME:
655 return "realtime";
656 case CLOCK_MONOTONIC:
657 return "monotonic";
658 case CLOCK_BOOTTIME:
659 return "boottime";
660 case CLOCK_REALTIME_ALARM:
661 return "realtime-alarm";
662 case CLOCK_BOOTTIME_ALARM:
663 return "boottime-alarm";
664 default:
665 return "unknown";
666 }
667 }
668
669 static void anon_timerfd_render_timespec_string(char *buf, size_t size,
670 const char *prefix,
671 const struct timespec *t)
672 {
673 snprintf(buf, size, "%s%llu.%09ld",
674 prefix? prefix: "",
675 (unsigned long long)t->tv_sec, t->tv_nsec);
676 }
677
678 static char *anon_timerfd_get_name(struct unkn *unkn)
679 {
680 char *str = NULL;
681
682 struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data;
683 const struct timespec *exp;
684 const struct timespec *ival;
685
686 const char *clockid_name;
687 char exp_buf[BUFSIZ] = {'\0'};
688 char ival_buf[BUFSIZ] = {'\0'};
689
690 clockid_name = anon_timerfd_decode_clockid(data->clockid);
691
692 exp = &data->itimerspec.it_value;
693 if (is_timespecset(exp))
694 anon_timerfd_render_timespec_string(exp_buf, sizeof(exp_buf),
695 " remaining=", exp);
696
697 ival = &data->itimerspec.it_interval;
698 if (is_timespecset(ival))
699 anon_timerfd_render_timespec_string(ival_buf, sizeof(ival_buf),
700 " interval=", ival);
701
702 xasprintf(&str, "clockid=%s%s%s", clockid_name, exp_buf, ival_buf);
703 return str;
704 }
705
706 static bool anon_timerfd_fill_column(struct proc *proc __attribute__((__unused__)),
707 struct unkn *unkn,
708 struct libscols_line *ln __attribute__((__unused__)),
709 int column_id,
710 size_t column_index __attribute__((__unused__)),
711 char **str)
712 {
713 struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data;
714 char buf[BUFSIZ] = {'\0'};
715
716 switch(column_id) {
717 case COL_TIMERFD_CLOCKID:
718 *str = xstrdup(anon_timerfd_decode_clockid(data->clockid));
719 return true;
720 case COL_TIMERFD_INTERVAL:
721 anon_timerfd_render_timespec_string(buf, sizeof(buf), NULL,
722 &data->itimerspec.it_interval);
723 *str = xstrdup(buf);
724 return true;
725 case COL_TIMERFD_REMAINING:
726 anon_timerfd_render_timespec_string(buf, sizeof(buf), NULL,
727 &data->itimerspec.it_value);
728 *str = xstrdup(buf);
729 return true;
730 }
731
732 return false;
733 }
734
735 static const struct anon_ops anon_timerfd_ops = {
736 .class = "timerfd",
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,
743 };
744
745 /*
746 * signalfd
747 */
748 struct anon_signalfd_data {
749 uint64_t sigmask;
750 };
751
752 static bool anon_signalfd_probe(const char *str)
753 {
754 return strncmp(str, "[signalfd]", 10) == 0;
755 }
756
757 static void anon_signalfd_init(struct unkn *unkn)
758 {
759 unkn->anon_data = xcalloc(1, sizeof(struct anon_signalfd_data));
760 }
761
762 static void anon_signalfd_free(struct unkn *unkn)
763 {
764 struct anon_signalfd_data *data = unkn->anon_data;
765 free(data);
766 }
767
768 static int anon_signalfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
769 {
770 struct anon_signalfd_data *data = (struct anon_signalfd_data *)unkn->anon_data;
771
772 if (strcmp(key, "sigmask") == 0) {
773 if (ul_strtou64(value, &data->sigmask, 16) < 0) {
774 data->sigmask = 0;
775 return 0;
776 }
777 }
778 return 0;
779 }
780
781 static char *anon_signalfd_make_mask_string(const char* prefix, uint64_t sigmask)
782 {
783 char *str = NULL;
784
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);
789
790 if (str)
791 xstrappend(&str, ",");
792 else if (prefix)
793 xstrappend(&str, prefix);
794
795 if (signame) {
796 xstrappend(&str, signame);
797 } else {
798 char buf[BUFSIZ];
799 snprintf(buf, sizeof(buf), "%d", signum);
800 xstrappend(&str, buf);
801 }
802 }
803 }
804
805 return str;
806 }
807
808 static char *anon_signalfd_get_name(struct unkn *unkn)
809 {
810 struct anon_signalfd_data *data = (struct anon_signalfd_data *)unkn->anon_data;
811 return anon_signalfd_make_mask_string("mask=", data->sigmask);
812 }
813
814 static bool anon_signalfd_fill_column(struct proc *proc __attribute__((__unused__)),
815 struct unkn *unkn,
816 struct libscols_line *ln __attribute__((__unused__)),
817 int column_id,
818 size_t column_index __attribute__((__unused__)),
819 char **str)
820 {
821 struct anon_signalfd_data *data = (struct anon_signalfd_data *)unkn->anon_data;
822
823 switch(column_id) {
824 case COL_SIGNALFD_MASK:
825 *str = anon_signalfd_make_mask_string(NULL, data->sigmask);
826 return true;
827 default:
828 return false;
829 }
830 }
831
832 static const struct anon_ops anon_signalfd_ops = {
833 .class = "signalfd",
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,
840 };
841
842 /*
843 * inotify
844 */
845 struct anon_inotify_data {
846 struct list_head inodes;
847 };
848
849 struct anon_inotify_inode {
850 ino_t ino;
851 dev_t sdev;
852 struct list_head inodes;
853 };
854
855 static bool anon_inotify_probe(const char *str)
856 {
857 return strncmp(str, "inotify", 7) == 0;
858 }
859
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)
866
867 #define ANON_INOTIFY_MAJOR(dev) ((unsigned int) ((dev) >> ANON_INOTIFY_MINORBITS))
868 #define ANON_INOTIFY_MINOR(dev) ((unsigned int) ((dev) & ANON_INOTIFY_MINORMASK))
869
870 static char *anon_inotify_make_inodes_string(const char *prefix,
871 const char *sep,
872 enum decode_source_level decode_level,
873 struct anon_inotify_data *data)
874 {
875 char *str = NULL;
876 char buf[BUFSIZ] = {'\0'};
877 bool first_element = true;
878
879 struct list_head *i;
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,
884 inodes);
885
886 decode_source(source, sizeof(source),
887 ANON_INOTIFY_MAJOR(inode->sdev), ANON_INOTIFY_MINOR(inode->sdev),
888 decode_level);
889 snprintf(buf, sizeof(buf), "%s%llu@%s", first_element? prefix: sep,
890 (unsigned long long)inode->ino, source);
891 first_element = false;
892
893 xstrappend(&str, buf);
894 }
895
896 return str;
897 }
898
899 static char *anon_inotify_get_name(struct unkn *unkn)
900 {
901 return anon_inotify_make_inodes_string("inodes=", ",", DECODE_SOURCE_FULL,
902 (struct anon_inotify_data *)unkn->anon_data);
903 }
904
905 static void anon_inotify_init(struct unkn *unkn)
906 {
907 struct anon_inotify_data *data = xcalloc(1, sizeof(struct anon_inotify_data));
908 INIT_LIST_HEAD (&data->inodes);
909 unkn->anon_data = data;
910 }
911
912 static void anon_inotify_free(struct unkn *unkn)
913 {
914 struct anon_inotify_data *data = unkn->anon_data;
915
916 list_free(&data->inodes, struct anon_inotify_inode, inodes,
917 free);
918 free(data);
919 }
920
921 static void add_inode(struct anon_inotify_data *data, ino_t ino, dev_t sdev)
922 {
923 struct anon_inotify_inode *inode = xmalloc(sizeof(*inode));
924
925 INIT_LIST_HEAD (&inode->inodes);
926 inode->ino = ino;
927 inode->sdev = sdev;
928
929 list_add_tail(&inode->inodes, &data->inodes);
930 }
931
932 static int anon_inotify_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
933 {
934 struct anon_inotify_data *data = (struct anon_inotify_data *)unkn->anon_data;
935
936 if (strcmp(key, "inotify wd") == 0) {
937 unsigned long long ino;
938 unsigned long long sdev;
939
940 if (sscanf(value, "%*d ino:%llx sdev:%llx %*s", &ino, &sdev) == 2) {
941 add_inode(data, (ino_t)ino, (dev_t)sdev);
942 return 1;
943 }
944 }
945 return 0;
946 }
947
948 static bool anon_inotify_fill_column(struct proc *proc __attribute__((__unused__)),
949 struct unkn *unkn,
950 struct libscols_line *ln __attribute__((__unused__)),
951 int column_id,
952 size_t column_index __attribute__((__unused__)),
953 char **str)
954 {
955 struct anon_inotify_data *data = (struct anon_inotify_data *)unkn->anon_data;
956
957 switch(column_id) {
958 case COL_INOTIFY_INODES:
959 *str = anon_inotify_make_inodes_string("", "\n", DECODE_SOURCE_FULL,
960 data);
961 if (*str)
962 return true;
963 break;
964 case COL_INOTIFY_INODES_RAW:
965 *str = anon_inotify_make_inodes_string("", "\n", DECODE_SOURCE_MAJMIN,
966 data);
967 if (*str)
968 return true;
969 break;
970 }
971
972 return false;
973 }
974
975 static const struct anon_ops anon_inotify_ops = {
976 .class = "inotify",
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,
983 };
984
985 /*
986 * bpf-prog
987 *
988 * Generally, we use "-" as the word separators in lsfd's output.
989 * However, about bpf*, we use "_" because bpftool uses "_".
990 */
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*/
1024 };
1025
1026 struct anon_bpf_prog_data {
1027 int type;
1028 int id;
1029 char name[BPF_OBJ_NAME_LEN + 1];
1030 };
1031
1032 static bool anon_bpf_prog_probe(const char *str)
1033 {
1034 return strncmp(str, "bpf-prog", 8) == 0;
1035 }
1036
1037 static const char *anon_bpf_prog_get_prog_type_name(int type)
1038 {
1039 if (0 <= type && type < (int)ARRAY_SIZE(bpf_prog_type_table))
1040 return bpf_prog_type_table[type];
1041 return NULL;
1042 }
1043
1044 static bool anon_bpf_prog_fill_column(struct proc *proc __attribute__((__unused__)),
1045 struct unkn *unkn,
1046 struct libscols_line *ln __attribute__((__unused__)),
1047 int column_id,
1048 size_t column_index __attribute__((__unused__)),
1049 char **str)
1050 {
1051 struct anon_bpf_prog_data *data = (struct anon_bpf_prog_data *)unkn->anon_data;
1052 const char *t;
1053
1054 switch(column_id) {
1055 case COL_BPF_PROG_ID:
1056 xasprintf(str, "%d", data->id);
1057 return true;
1058 case COL_BPF_PROG_TYPE_RAW:
1059 xasprintf(str, "%d", data->type);
1060 return true;
1061 case COL_BPF_PROG_TYPE:
1062 t = anon_bpf_prog_get_prog_type_name(data->type);
1063 if (t)
1064 *str = xstrdup(t);
1065 else
1066 xasprintf(str, "UNKNOWN(%d)", data->type);
1067 return true;
1068 case COL_BPF_NAME:
1069 *str = xstrdup(data->name);
1070 return true;
1071 default:
1072 return false;
1073 }
1074 }
1075
1076 static char *anon_bpf_prog_get_name(struct unkn *unkn)
1077 {
1078 const char *t;
1079 char *str = NULL;
1080 struct anon_bpf_prog_data *data = (struct anon_bpf_prog_data *)unkn->anon_data;
1081
1082 t = anon_bpf_prog_get_prog_type_name(data->type);
1083 if (t)
1084 xasprintf(&str, "id=%d type=%s", data->id, t);
1085 else
1086 xasprintf(&str, "id=%d type=UNKNOWN(%d)", data->id, data->type);
1087
1088 if (*data->name)
1089 xstrfappend(&str, " name=%s", data->name);
1090
1091 return str;
1092 }
1093
1094
1095 static void anon_bpf_prog_init(struct unkn *unkn)
1096 {
1097 struct anon_bpf_prog_data *data = xmalloc(sizeof(*data));
1098 data->type = -1;
1099 data->id = -1;
1100 data->name[0] = '\0';
1101 unkn->anon_data = data;
1102 }
1103
1104 static void anon_bpf_prog_free(struct unkn *unkn)
1105 {
1106 struct anon_bpf_prog_data *data = (struct anon_bpf_prog_data *)unkn->anon_data;
1107 free(data);
1108 }
1109
1110 static void anon_bpf_prog_get_more_info(struct anon_bpf_prog_data *prog_data)
1111 {
1112 union bpf_attr attr = {
1113 .prog_id = (int32_t)prog_data->id,
1114 .next_id = 0,
1115 .open_flags = 0,
1116 };
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,
1121 };
1122
1123 int bpf_fd = syscall(SYS_bpf, BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
1124 if (bpf_fd < 0)
1125 return;
1126
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,
1130 info.name,
1131 BPF_OBJ_NAME_LEN);
1132 prog_data->name[BPF_OBJ_NAME_LEN] = '\0';
1133 }
1134 close (bpf_fd);
1135 }
1136
1137 static int anon_bpf_prog_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
1138 {
1139 if (strcmp(key, "prog_id") == 0) {
1140 int32_t t = -1;
1141 int rc = ul_strtos32(value, &t, 10);
1142 if (rc < 0)
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);
1146 return 1;
1147 }
1148
1149 if (strcmp(key, "prog_type") == 0) {
1150 int32_t t = -1;
1151 int rc = ul_strtos32(value, &t, 10);
1152 if (rc < 0)
1153 return 0; /* ignore -- parse failed */
1154 ((struct anon_bpf_prog_data *)unkn->anon_data)->type = (int)t;
1155 return 1;
1156 }
1157
1158 return 0;
1159 }
1160
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,
1169 };
1170
1171 /*
1172 * bpf-map
1173 */
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 */
1208 };
1209
1210 struct anon_bpf_map_data {
1211 int type;
1212 int id;
1213 char name[BPF_OBJ_NAME_LEN + 1];
1214 };
1215
1216 static bool anon_bpf_map_probe(const char *str)
1217 {
1218 return strncmp(str, "bpf-map", 8) == 0;
1219 }
1220
1221 static const char *anon_bpf_map_get_map_type_name(int type)
1222 {
1223 if (0 <= type && type < (int)ARRAY_SIZE(bpf_map_type_table))
1224 return bpf_map_type_table[type];
1225 return NULL;
1226 }
1227
1228 static bool anon_bpf_map_fill_column(struct proc *proc __attribute__((__unused__)),
1229 struct unkn *unkn,
1230 struct libscols_line *ln __attribute__((__unused__)),
1231 int column_id,
1232 size_t column_index __attribute__((__unused__)),
1233 char **str)
1234 {
1235 struct anon_bpf_prog_data *data = (struct anon_bpf_prog_data *)unkn->anon_data;
1236 const char *t;
1237
1238 switch(column_id) {
1239 case COL_BPF_MAP_ID:
1240 xasprintf(str, "%d", data->id);
1241 return true;
1242 case COL_BPF_MAP_TYPE_RAW:
1243 xasprintf(str, "%d", data->type);
1244 return true;
1245 case COL_BPF_MAP_TYPE:
1246 t = anon_bpf_map_get_map_type_name(data->type);
1247 if (t)
1248 *str = xstrdup(t);
1249 else
1250 xasprintf(str, "UNKNOWN(%d)", data->type);
1251 return true;
1252 case COL_BPF_NAME:
1253 *str = xstrdup(data->name);
1254 return true;
1255 default:
1256 return false;
1257 }
1258 }
1259
1260 static char *anon_bpf_map_get_name(struct unkn *unkn)
1261 {
1262 const char *t;
1263 char *str = NULL;
1264 struct anon_bpf_map_data *data = (struct anon_bpf_map_data *)unkn->anon_data;
1265
1266 t = anon_bpf_map_get_map_type_name(data->type);
1267 if (t)
1268 xasprintf(&str, "id=%d type=%s", data->id, t);
1269 else
1270 xasprintf(&str, "id=%d type=UNKNOWN(%d)", data->id, data->type);
1271
1272 if (*data->name)
1273 xstrfappend(&str, " name=%s", data->name);
1274
1275 return str;
1276 }
1277
1278 static void anon_bpf_map_init(struct unkn *unkn)
1279 {
1280 struct anon_bpf_map_data *data = xmalloc(sizeof(*data));
1281 data->type = -1;
1282 data->id = -1;
1283 data->name[0] = '\0';
1284 unkn->anon_data = data;
1285 }
1286
1287 static void anon_bpf_map_free(struct unkn *unkn)
1288 {
1289 struct anon_bpf_map_data *data = (struct anon_bpf_map_data *)unkn->anon_data;
1290 free(data);
1291 }
1292
1293 static void anon_bpf_map_get_more_info(struct anon_bpf_map_data *map_data)
1294 {
1295 union bpf_attr attr = {
1296 .map_id = (int32_t)map_data->id,
1297 .next_id = 0,
1298 .open_flags = 0,
1299 };
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,
1304 };
1305
1306 int bpf_fd = syscall(SYS_bpf, BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
1307 if (bpf_fd < 0)
1308 return;
1309
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,
1313 info.name,
1314 BPF_OBJ_NAME_LEN);
1315 map_data->name[BPF_OBJ_NAME_LEN] = '\0';
1316 }
1317 close (bpf_fd);
1318 }
1319
1320 static int anon_bpf_map_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
1321 {
1322 if (strcmp(key, "map_id") == 0) {
1323 int32_t t = -1;
1324 int rc = ul_strtos32(value, &t, 10);
1325 if (rc < 0)
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);
1329 return 1;
1330 }
1331
1332 if (strcmp(key, "map_type") == 0) {
1333 int32_t t = -1;
1334 int rc = ul_strtos32(value, &t, 10);
1335 if (rc < 0)
1336 return 0; /* ignore -- parse failed */
1337 ((struct anon_bpf_map_data *)unkn->anon_data)->type = (int)t;
1338 return 1;
1339 }
1340
1341 return 0;
1342 }
1343
1344 static const struct anon_ops anon_bpf_map_ops = {
1345 .class = "bpf-map",
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,
1352 };
1353
1354 /*
1355 * generic (fallback implementation)
1356 */
1357 static const struct anon_ops anon_generic_ops = {
1358 .class = NULL,
1359 .get_name = NULL,
1360 .fill_column = NULL,
1361 .init = NULL,
1362 .free = NULL,
1363 .handle_fdinfo = NULL,
1364 };
1365
1366 static const struct anon_ops *anon_ops[] = {
1367 &anon_pidfd_ops,
1368 &anon_eventfd_ops,
1369 &anon_eventpoll_ops,
1370 &anon_timerfd_ops,
1371 &anon_signalfd_ops,
1372 &anon_inotify_ops,
1373 &anon_bpf_prog_ops,
1374 &anon_bpf_map_ops,
1375 };
1376
1377 static const struct anon_ops *anon_probe(const char *str)
1378 {
1379 for (size_t i = 0; i < ARRAY_SIZE(anon_ops); i++)
1380 if (anon_ops[i]->probe(str))
1381 return anon_ops[i];
1382 return &anon_generic_ops;
1383 }
1384
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,
1394 };