]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lsfd-unkn.c
lsblk: rename sortdata to rawdata
[thirdparty/util-linux.git] / misc-utils / lsfd-unkn.c
CommitLineData
2da06d3c
MY
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
bc808368
MY
22#include <linux/bpf.h>
23#include <stdalign.h>
24#include <sys/syscall.h>
ef4fb7fa
MY
25#include <sys/timerfd.h>
26#include <time.h>
27
b22d1e54 28#include "signames.h"
765b4a14 29#include "timeutils.h"
2da06d3c
MY
30
31#include "lsfd.h"
32
bc808368
MY
33#define offsetofend(TYPE, MEMBER) \
34 (offsetof(TYPE, MEMBER) + sizeof_member(TYPE, MEMBER))
35
9a9c96b7
MY
36struct unkn {
37 struct file file;
54a06438 38 const struct anon_ops *anon_ops;
9a9c96b7
MY
39 void *anon_data;
40};
41
42struct anon_ops {
4cd4ed18 43 const char *class;
007e405b 44 bool (*probe)(const char *);
9a9c96b7 45 char * (*get_name)(struct unkn *);
553a6817
MY
46 /* Return true is handled the column. */
47 bool (*fill_column)(struct proc *,
48 struct unkn *,
49 struct libscols_line *,
50 int,
51 size_t,
52 char **str);
9a9c96b7
MY
53 void (*init)(struct unkn *);
54 void (*free)(struct unkn *);
55 int (*handle_fdinfo)(struct unkn *, const char *, const char *);
d4fea4d0
MY
56 void (*attach_xinfo)(struct unkn *);
57 const struct ipc_class *ipc_class;
9a9c96b7
MY
58};
59
007e405b 60static const struct anon_ops *anon_probe(const char *);
9a9c96b7 61
4cd4ed18
MY
62static char * anon_get_class(struct unkn *unkn)
63{
64 char *name;
65
66 if (unkn->anon_ops->class)
e0dc84da 67 return xstrdup(unkn->anon_ops->class);
4cd4ed18
MY
68
69 /* See unkn_init_content() */
70 name = ((struct file *)unkn)->name + 11;
71 /* Does it have the form anon_inode:[class]? */
72 if (*name == '[') {
73 size_t len = strlen(name + 1);
74 if (*(name + 1 + len - 1) == ']')
75 return strndup(name + 1, len - 1);
76 }
77
e0dc84da 78 return xstrdup(name);
4cd4ed18
MY
79}
80
553a6817 81static bool unkn_fill_column(struct proc *proc,
a9e36b09 82 struct file *file,
2da06d3c
MY
83 struct libscols_line *ln,
84 int column_id,
85 size_t column_index)
86{
a500de34 87 char *str = NULL;
9a9c96b7 88 struct unkn *unkn = (struct unkn *)file;
a500de34 89
2da06d3c 90 switch(column_id) {
9a9c96b7
MY
91 case COL_NAME:
92 if (unkn->anon_ops && unkn->anon_ops->get_name) {
93 str = unkn->anon_ops->get_name(unkn);
94 if (str)
95 break;
96 }
97 return false;
2da06d3c 98 case COL_TYPE:
be019f66
MY
99 if (!unkn->anon_ops)
100 return false;
101 /* FALL THROUGH */
4cd4ed18
MY
102 case COL_AINODECLASS:
103 if (unkn->anon_ops) {
104 str = anon_get_class(unkn);
105 break;
106 }
107 return false;
618db964 108 case COL_SOURCE:
9a9c96b7 109 if (unkn->anon_ops) {
e0dc84da 110 str = xstrdup("anon_inodefs");
a500de34
MY
111 break;
112 }
113 return false;
2da06d3c 114 default:
553a6817
MY
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))
118 break;
119 }
2da06d3c
MY
120 return false;
121 }
a500de34
MY
122
123 if (!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"));
127 return true;
2da06d3c
MY
128}
129
d4fea4d0
MY
130static void unkn_attach_xinfo(struct file *file)
131{
132 struct unkn *unkn = (struct unkn *)file;
133 if (unkn->anon_ops && unkn->anon_ops->attach_xinfo)
134 unkn->anon_ops->attach_xinfo(unkn);
135}
136
137static const struct ipc_class *unkn_get_ipc_class(struct file *file)
138{
139 struct unkn *unkn = (struct unkn *)file;
140
141 if (unkn->anon_ops && unkn->anon_ops->ipc_class)
142 return unkn->anon_ops->ipc_class;
143 return NULL;
144}
145
9a9c96b7
MY
146static void unkn_init_content(struct file *file)
147{
148 struct unkn *unkn = (struct unkn *)file;
149
150 assert(file);
151 unkn->anon_ops = NULL;
152 unkn->anon_data = NULL;
153
154 if (major(file->stat.st_dev) == 0
155 && strncmp(file->name, "anon_inode:", 11) == 0) {
ba8c749f
MY
156 const char *rest = file->name + 11;
157
007e405b 158 unkn->anon_ops = anon_probe(rest);
ba8c749f 159
9a9c96b7
MY
160 if (unkn->anon_ops->init)
161 unkn->anon_ops->init(unkn);
162 }
163}
164
165static void unkn_content_free(struct file *file)
166{
167 struct unkn *unkn = (struct unkn *)file;
168
169 assert(file);
170 if (unkn->anon_ops && unkn->anon_ops->free)
171 unkn->anon_ops->free((struct unkn *)file);
172}
173
174static int unkn_handle_fdinfo(struct file *file, const char *key, const char *value)
175{
176 struct unkn *unkn = (struct unkn *)file;
177
178 assert(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 */
182}
183
ba8c749f
MY
184/*
185 * pidfd
186 */
187struct anon_pidfd_data {
188 pid_t pid;
189 char *nspid;
190};
191
007e405b
MY
192static bool anon_pidfd_probe(const char *str)
193{
4a3d70fe 194 return strncmp(str, "[pidfd]", 7) == 0;
007e405b
MY
195}
196
ba8c749f
MY
197static char *anon_pidfd_get_name(struct unkn *unkn)
198{
199 char *str = NULL;
200 struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
201
202 char *comm = NULL;
203 struct proc *proc = get_proc(data->pid);
204 if (proc)
205 comm = proc->command;
206
f3609f0a 207 xasprintf(&str, "pid=%d comm=%s nspid=%s",
ba8c749f
MY
208 data->pid,
209 comm? comm: "",
210 data->nspid? data->nspid: "");
211 return str;
212}
213
214static void anon_pidfd_init(struct unkn *unkn)
215{
216 unkn->anon_data = xcalloc(1, sizeof(struct anon_pidfd_data));
217}
218
219static void anon_pidfd_free(struct unkn *unkn)
220{
221 struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
222
223 if (data->nspid)
224 free(data->nspid);
225 free(data);
226}
227
228static int anon_pidfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
229{
230 if (strcmp(key, "Pid") == 0) {
231 uint64_t pid;
232
233 int rc = ul_strtou64(value, &pid, 10);
234 if (rc < 0)
235 return 0; /* ignore -- parse failed */
236 ((struct anon_pidfd_data *)unkn->anon_data)->pid = (pid_t)pid;
237 return 1;
dd458e65 238 } else if (strcmp(key, "NSpid") == 0) {
ba8c749f
MY
239 ((struct anon_pidfd_data *)unkn->anon_data)->nspid = xstrdup(value);
240 return 1;
241
242 }
243 return 0;
244}
245
553a6817
MY
246static bool anon_pidfd_fill_column(struct proc *proc __attribute__((__unused__)),
247 struct unkn *unkn,
248 struct libscols_line *ln __attribute__((__unused__)),
249 int column_id,
250 size_t column_index __attribute__((__unused__)),
251 char **str)
252{
253 struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
254
255 switch(column_id) {
256 case COL_PIDFD_COMM: {
257 struct proc *pidfd_proc = get_proc(data->pid);
258 char *pidfd_comm = NULL;
259 if (pidfd_proc)
260 pidfd_comm = pidfd_proc->command;
261 if (pidfd_comm) {
e0dc84da 262 *str = xstrdup(pidfd_comm);
553a6817
MY
263 return true;
264 }
265 break;
266 }
267 case COL_PIDFD_NSPID:
268 if (data->nspid) {
e0dc84da 269 *str = xstrdup(data->nspid);
553a6817
MY
270 return true;
271 }
272 break;
273 case COL_PIDFD_PID:
274 xasprintf(str, "%d", (int)data->pid);
275 return true;
276 }
277
278 return false;
279}
280
54a06438 281static const struct anon_ops anon_pidfd_ops = {
4cd4ed18 282 .class = "pidfd",
007e405b 283 .probe = anon_pidfd_probe,
ba8c749f 284 .get_name = anon_pidfd_get_name,
553a6817 285 .fill_column = anon_pidfd_fill_column,
ba8c749f
MY
286 .init = anon_pidfd_init,
287 .free = anon_pidfd_free,
288 .handle_fdinfo = anon_pidfd_handle_fdinfo,
289};
290
8c2ca9c9
MY
291/*
292 * eventfd
293 */
294struct anon_eventfd_data {
295 int id;
e283500c 296 struct unkn *backptr;
d4fea4d0
MY
297 struct ipc_endpoint endpoint;
298};
299
300struct eventfd_ipc {
301 struct ipc ipc;
302 int id;
303};
304
305static unsigned int anon_eventfd_get_hash(struct file *file)
306{
307 struct unkn *unkn = (struct unkn *)file;
308 struct anon_eventfd_data *data = (struct anon_eventfd_data *)unkn->anon_data;
309
310 return (unsigned int)data->id;
311}
312
313static bool anon_eventfd_is_suitable_ipc(struct ipc *ipc, struct file *file)
314{
315 struct unkn *unkn = (struct unkn *)file;
316 struct anon_eventfd_data *data = (struct anon_eventfd_data *)unkn->anon_data;
317
318 return ((struct eventfd_ipc *)ipc)->id == data->id;
319}
320
321static const struct ipc_class anon_eventfd_ipc_class = {
322 .size = sizeof(struct eventfd_ipc),
323 .get_hash = anon_eventfd_get_hash,
324 .is_suitable_ipc = anon_eventfd_is_suitable_ipc,
325 .free = NULL,
8c2ca9c9
MY
326};
327
328static bool anon_eventfd_probe(const char *str)
329{
dd458e65 330 return strncmp(str, "[eventfd]", 9) == 0;
8c2ca9c9
MY
331}
332
333static char *anon_eventfd_get_name(struct unkn *unkn)
334{
335 char *str = NULL;
336 struct anon_eventfd_data *data = (struct anon_eventfd_data *)unkn->anon_data;
337
338 xasprintf(&str, "id=%d", data->id);
339 return str;
340}
341
342static void anon_eventfd_init(struct unkn *unkn)
343{
d4fea4d0
MY
344 struct anon_eventfd_data *data = xcalloc(1, sizeof(struct anon_eventfd_data));
345 init_endpoint(&data->endpoint);
346 data->backptr = unkn;
347 unkn->anon_data = data;
8c2ca9c9
MY
348}
349
350static void anon_eventfd_free(struct unkn *unkn)
351{
352 free(unkn->anon_data);
353}
354
d4fea4d0
MY
355static void anon_eventfd_attach_xinfo(struct unkn *unkn)
356{
357 struct anon_eventfd_data *data = (struct anon_eventfd_data *)unkn->anon_data;
358 unsigned int hash;
359 struct ipc *ipc = get_ipc(&unkn->file);
360 if (ipc)
361 goto link;
362
363 ipc = new_ipc(&anon_eventfd_ipc_class);
364 ((struct eventfd_ipc *)ipc)->id = data->id;
365
366 hash = anon_eventfd_get_hash(&unkn->file);
367 add_ipc(ipc, hash);
368
369 link:
370 add_endpoint(&data->endpoint, ipc);
371}
372
8c2ca9c9
MY
373static int anon_eventfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
374{
375 if (strcmp(key, "eventfd-id") == 0) {
376 int64_t id;
377
378 int rc = ul_strtos64(value, &id, 10);
379 if (rc < 0)
380 return 0;
381 ((struct anon_eventfd_data *)unkn->anon_data)->id = (int)id;
382 return 1;
383 }
384 return 0;
385}
386
d4fea4d0
MY
387static inline char *anon_eventfd_data_xstrendpoint(struct file *file)
388{
389 char *str = NULL;
390 xasprintf(&str, "%d,%s,%d",
391 file->proc->pid, file->proc->command, file->association);
392 return str;
393}
394
8c2ca9c9
MY
395static bool anon_eventfd_fill_column(struct proc *proc __attribute__((__unused__)),
396 struct unkn *unkn,
397 struct libscols_line *ln __attribute__((__unused__)),
398 int column_id,
399 size_t column_index __attribute__((__unused__)),
400 char **str)
401{
402 struct anon_eventfd_data *data = (struct anon_eventfd_data *)unkn->anon_data;
403
404 switch(column_id) {
405 case COL_EVENTFD_ID:
406 xasprintf(str, "%d", data->id);
407 return true;
d4fea4d0
MY
408 case COL_ENDPOINTS: {
409 struct list_head *e;
410 char *estr;
411 foreach_endpoint(e, data->endpoint) {
412 struct anon_eventfd_data *other = list_entry(e,
413 struct anon_eventfd_data,
414 endpoint.endpoints);
415 if (data == other)
416 continue;
417 if (*str)
418 xstrputc(str, '\n');
419 estr = anon_eventfd_data_xstrendpoint(&other->backptr->file);
420 xstrappend(str, estr);
421 free(estr);
422 }
423 if (!*str)
424 return false;
425 return true;
426 }
8c2ca9c9
MY
427 default:
428 return false;
429 }
430}
431
432static const struct anon_ops anon_eventfd_ops = {
433 .class = "eventfd",
434 .probe = anon_eventfd_probe,
435 .get_name = anon_eventfd_get_name,
436 .fill_column = anon_eventfd_fill_column,
437 .init = anon_eventfd_init,
438 .free = anon_eventfd_free,
439 .handle_fdinfo = anon_eventfd_handle_fdinfo,
d4fea4d0
MY
440 .attach_xinfo = anon_eventfd_attach_xinfo,
441 .ipc_class = &anon_eventfd_ipc_class,
8c2ca9c9
MY
442};
443
c2e2dd60
MY
444/*
445 * eventpoll
446 */
447struct anon_eventpoll_data {
448 size_t count;
449 int *tfds;
da901314 450 struct list_head siblings;
c2e2dd60
MY
451};
452
c2e2dd60
MY
453static bool anon_eventpoll_probe(const char *str)
454{
455 return strncmp(str, "[eventpoll]", 11) == 0;
456}
457
458static void anon_eventpoll_init(struct unkn *unkn)
459{
da901314
MY
460 struct anon_eventpoll_data *data = xcalloc(1, sizeof(struct anon_eventpoll_data));
461 INIT_LIST_HEAD(&data->siblings);
462 unkn->anon_data = data;
c2e2dd60
MY
463}
464
465static void anon_eventpoll_free(struct unkn *unkn)
466{
467 struct anon_eventpoll_data *data = unkn->anon_data;
dd458e65
MY
468 free(data->tfds);
469 free(data);
c2e2dd60
MY
470}
471
472static int anon_eventpoll_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
473{
474 struct anon_eventpoll_data *data;
475 if (strcmp(key, "tfd") == 0) {
476 unsigned long tfd;
477 char *end = NULL;
478
479 errno = 0;
480 tfd = strtoul(value, &end, 0);
481 if (errno != 0)
482 return 0; /* ignore -- parse failed */
483
484 data = (struct anon_eventpoll_data *)unkn->anon_data;
dd458e65 485 data->tfds = xreallocarray(data->tfds, ++data->count, sizeof(int));
c2e2dd60
MY
486 data->tfds[data->count - 1] = (int)tfd;
487 return 1;
488 }
489 return 0;
490}
491
dd458e65 492static int intcmp(const void *a, const void *b)
c2e2dd60
MY
493{
494 int ai = *(int *)a;
495 int bi = *(int *)b;
496
497 return ai - bi;
498}
499
500static void anon_eventpoll_attach_xinfo(struct unkn *unkn)
501{
502 struct anon_eventpoll_data *data = (struct anon_eventpoll_data *)unkn->anon_data;
baef2659
TW
503 if (data->count > 0) {
504 qsort(data->tfds, data->count, sizeof(data->tfds[0]),
505 intcmp);
da901314
MY
506 list_add_tail(&data->siblings,
507 &unkn->file.proc->eventpolls);
baef2659 508 }
c2e2dd60
MY
509}
510
511static char *anon_eventpoll_make_tfds_string(struct anon_eventpoll_data *data,
92e0e017
MY
512 const char *prefix,
513 const char sep)
c2e2dd60
MY
514{
515 char *str = prefix? xstrdup(prefix): NULL;
516
517 char buf[256];
518 for (size_t i = 0; i < data->count; i++) {
519 size_t offset = 0;
520
521 if (i > 0) {
92e0e017 522 buf[0] = sep;
c2e2dd60
MY
523 offset = 1;
524 }
525 snprintf(buf + offset, sizeof(buf) - offset, "%d", data->tfds[i]);
526 xstrappend(&str, buf);
527 }
528 return str;
529}
530
531static char *anon_eventpoll_get_name(struct unkn *unkn)
532{
dd458e65 533 return anon_eventpoll_make_tfds_string((struct anon_eventpoll_data *)unkn->anon_data,
92e0e017 534 "tfds=", ',');
c2e2dd60
MY
535}
536
537static bool anon_eventpoll_fill_column(struct proc *proc __attribute__((__unused__)),
538 struct unkn *unkn,
539 struct libscols_line *ln __attribute__((__unused__)),
540 int column_id,
541 size_t column_index __attribute__((__unused__)),
542 char **str)
543{
544 struct anon_eventpoll_data *data = (struct anon_eventpoll_data *)unkn->anon_data;
545
546 switch(column_id) {
547 case COL_EVENTPOLL_TFDS:
92e0e017 548 *str =anon_eventpoll_make_tfds_string(data, NULL, '\n');
c2e2dd60
MY
549 if (*str)
550 return true;
551 break;
552 }
553
554 return false;
555}
556
557static const struct anon_ops anon_eventpoll_ops = {
558 .class = "eventpoll",
559 .probe = anon_eventpoll_probe,
560 .get_name = anon_eventpoll_get_name,
561 .fill_column = anon_eventpoll_fill_column,
562 .init = anon_eventpoll_init,
563 .free = anon_eventpoll_free,
564 .handle_fdinfo = anon_eventpoll_handle_fdinfo,
565 .attach_xinfo = anon_eventpoll_attach_xinfo,
566};
567
da901314
MY
568static int numcomp(const void *a, const void *b)
569{
570 return *(int *)a - *(int *)b;
571}
572
573bool is_multiplexed_by_eventpoll(int fd, struct list_head *eventpolls)
574{
575 struct list_head *t;
576 list_for_each (t, eventpolls) {
577 struct anon_eventpoll_data *data = list_entry(t, struct anon_eventpoll_data, siblings);
578 if (data->count) {
579 if (bsearch(&fd, data->tfds,
580 data->count, sizeof (data->tfds[0]),
581 numcomp))
582 return true;
583 }
584 }
585 return false;
586}
587
188b7d64
MY
588/*
589 * timerfd
590 */
591struct anon_timerfd_data {
592 int clockid;
593 struct itimerspec itimerspec;
594};
595
596static bool anon_timerfd_probe(const char *str)
597{
598 return strncmp(str, "[timerfd]", 9) == 0;
599}
600
601static void anon_timerfd_init(struct unkn *unkn)
602{
603 unkn->anon_data = xcalloc(1, sizeof(struct anon_timerfd_data));
604}
605
606static void anon_timerfd_free(struct unkn *unkn)
607{
608 struct anon_timerfd_data *data = unkn->anon_data;
609 free(data);
610}
611
612static int anon_timerfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
613{
614 struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data;
615
616 if (strcmp(key, "clockid") == 0) {
617 unsigned long clockid;
618 char *end = NULL;
619
620 errno = 0;
621 clockid = strtoul(value, &end, 0);
622 if (errno != 0)
623 return 0; /* ignore -- parse failed */
624 if (*end != '\0')
625 return 0; /* ignore -- garbage remains. */
626
627 data->clockid = clockid;
628 return 1;
629 } else {
630 struct timespec *t;
631 uint64_t tv_sec;
632 uint64_t tv_nsec;
633
634 if (strcmp(key, "it_value") == 0)
635 t = &data->itimerspec.it_value;
636 else if (strcmp(key, "it_interval") == 0)
637 t = &data->itimerspec.it_interval;
638 else
639 return 0;
640
641 if (sscanf(value, "(%"SCNu64", %"SCNu64")",
642 &tv_sec, &tv_nsec) == 2) {
643 t->tv_sec = (time_t)tv_sec;
644 t->tv_nsec = (long)tv_nsec;
645 return 1;
646 }
647
648 return 0;
649 }
650}
651
188b7d64
MY
652static const char *anon_timerfd_decode_clockid(int clockid)
653{
654 switch (clockid) {
655 case CLOCK_REALTIME:
656 return "realtime";
657 case CLOCK_MONOTONIC:
658 return "monotonic";
659 case CLOCK_BOOTTIME:
660 return "boottime";
661 case CLOCK_REALTIME_ALARM:
662 return "realtime-alarm";
663 case CLOCK_BOOTTIME_ALARM:
664 return "boottime-alarm";
665 default:
666 return "unknown";
667 }
668}
669
670static void anon_timerfd_render_timespec_string(char *buf, size_t size,
671 const char *prefix,
672 const struct timespec *t)
673{
674 snprintf(buf, size, "%s%llu.%09ld",
675 prefix? prefix: "",
676 (unsigned long long)t->tv_sec, t->tv_nsec);
677}
678
679static char *anon_timerfd_get_name(struct unkn *unkn)
680{
681 char *str = NULL;
682
683 struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data;
684 const struct timespec *exp;
685 const struct timespec *ival;
686
687 const char *clockid_name;
688 char exp_buf[BUFSIZ] = {'\0'};
689 char ival_buf[BUFSIZ] = {'\0'};
690
691 clockid_name = anon_timerfd_decode_clockid(data->clockid);
692
693 exp = &data->itimerspec.it_value;
765b4a14 694 if (is_timespecset(exp))
188b7d64
MY
695 anon_timerfd_render_timespec_string(exp_buf, sizeof(exp_buf),
696 " remaining=", exp);
697
698 ival = &data->itimerspec.it_interval;
765b4a14 699 if (is_timespecset(ival))
188b7d64
MY
700 anon_timerfd_render_timespec_string(ival_buf, sizeof(ival_buf),
701 " interval=", ival);
702
703 xasprintf(&str, "clockid=%s%s%s", clockid_name, exp_buf, ival_buf);
704 return str;
705}
706
707static bool anon_timerfd_fill_column(struct proc *proc __attribute__((__unused__)),
708 struct unkn *unkn,
709 struct libscols_line *ln __attribute__((__unused__)),
710 int column_id,
711 size_t column_index __attribute__((__unused__)),
712 char **str)
713{
714 struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data;
715 char buf[BUFSIZ] = {'\0'};
716
717 switch(column_id) {
718 case COL_TIMERFD_CLOCKID:
719 *str = xstrdup(anon_timerfd_decode_clockid(data->clockid));
720 return true;
721 case COL_TIMERFD_INTERVAL:
722 anon_timerfd_render_timespec_string(buf, sizeof(buf), NULL,
723 &data->itimerspec.it_interval);
724 *str = xstrdup(buf);
725 return true;
726 case COL_TIMERFD_REMAINING:
727 anon_timerfd_render_timespec_string(buf, sizeof(buf), NULL,
728 &data->itimerspec.it_value);
729 *str = xstrdup(buf);
730 return true;
731 }
732
733 return false;
734}
735
736static const struct anon_ops anon_timerfd_ops = {
737 .class = "timerfd",
738 .probe = anon_timerfd_probe,
739 .get_name = anon_timerfd_get_name,
740 .fill_column = anon_timerfd_fill_column,
741 .init = anon_timerfd_init,
742 .free = anon_timerfd_free,
743 .handle_fdinfo = anon_timerfd_handle_fdinfo,
744};
745
b22d1e54
MY
746/*
747 * signalfd
748 */
749struct anon_signalfd_data {
750 uint64_t sigmask;
751};
752
753static bool anon_signalfd_probe(const char *str)
754{
755 return strncmp(str, "[signalfd]", 10) == 0;
756}
757
758static void anon_signalfd_init(struct unkn *unkn)
759{
760 unkn->anon_data = xcalloc(1, sizeof(struct anon_signalfd_data));
761}
762
763static void anon_signalfd_free(struct unkn *unkn)
764{
765 struct anon_signalfd_data *data = unkn->anon_data;
766 free(data);
767}
768
769static int anon_signalfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
770{
771 struct anon_signalfd_data *data = (struct anon_signalfd_data *)unkn->anon_data;
772
773 if (strcmp(key, "sigmask") == 0) {
774 if (ul_strtou64(value, &data->sigmask, 16) < 0) {
775 data->sigmask = 0;
776 return 0;
777 }
778 }
779 return 0;
780}
781
782static char *anon_signalfd_make_mask_string(const char* prefix, uint64_t sigmask)
783{
784 char *str = NULL;
785
786 for (size_t i = 0; i < sizeof(sigmask) * 8; i++) {
787 if ((((uint64_t)0x1) << i) & sigmask) {
788 const int signum = i + 1;
789 const char *signame = signum_to_signame(signum);
790
791 if (str)
792 xstrappend(&str, ",");
793 else if (prefix)
794 xstrappend(&str, prefix);
795
796 if (signame) {
797 xstrappend(&str, signame);
798 } else {
799 char buf[BUFSIZ];
800 snprintf(buf, sizeof(buf), "%d", signum);
801 xstrappend(&str, buf);
802 }
803 }
804 }
805
806 return str;
807}
808
809static char *anon_signalfd_get_name(struct unkn *unkn)
810{
811 struct anon_signalfd_data *data = (struct anon_signalfd_data *)unkn->anon_data;
812 return anon_signalfd_make_mask_string("mask=", data->sigmask);
813}
814
815static bool anon_signalfd_fill_column(struct proc *proc __attribute__((__unused__)),
816 struct unkn *unkn,
817 struct libscols_line *ln __attribute__((__unused__)),
818 int column_id,
819 size_t column_index __attribute__((__unused__)),
820 char **str)
821{
822 struct anon_signalfd_data *data = (struct anon_signalfd_data *)unkn->anon_data;
823
824 switch(column_id) {
825 case COL_SIGNALFD_MASK:
826 *str = anon_signalfd_make_mask_string(NULL, data->sigmask);
827 return true;
828 default:
829 return false;
830 }
831}
832
833static const struct anon_ops anon_signalfd_ops = {
834 .class = "signalfd",
835 .probe = anon_signalfd_probe,
836 .get_name = anon_signalfd_get_name,
837 .fill_column = anon_signalfd_fill_column,
838 .init = anon_signalfd_init,
839 .free = anon_signalfd_free,
840 .handle_fdinfo = anon_signalfd_handle_fdinfo,
841};
842
c1a26ac8
MY
843/*
844 * inotify
845 */
846struct anon_inotify_data {
847 struct list_head inodes;
848};
849
850struct anon_inotify_inode {
851 ino_t ino;
852 dev_t sdev;
853 struct list_head inodes;
854};
855
856static bool anon_inotify_probe(const char *str)
857{
858 return strncmp(str, "inotify", 7) == 0;
859}
860
861/* A device number appeared in fdinfo of an inotify file uses the kernel
862 * internal representation. It is different from what we are familiar with;
863 * major(3) and minor(3) don't work with the representation.
864 * See linux/include/linux/kdev_t.h. */
865#define ANON_INOTIFY_MINORBITS 20
866#define ANON_INOTIFY_MINORMASK ((1U << ANON_INOTIFY_MINORBITS) - 1)
867
868#define ANON_INOTIFY_MAJOR(dev) ((unsigned int) ((dev) >> ANON_INOTIFY_MINORBITS))
869#define ANON_INOTIFY_MINOR(dev) ((unsigned int) ((dev) & ANON_INOTIFY_MINORMASK))
870
871static char *anon_inotify_make_inodes_string(const char *prefix,
c34f8632 872 const char *sep,
c1a26ac8
MY
873 enum decode_source_level decode_level,
874 struct anon_inotify_data *data)
875{
876 char *str = NULL;
877 char buf[BUFSIZ] = {'\0'};
878 bool first_element = true;
879
880 struct list_head *i;
881 list_for_each(i, &data->inodes) {
882 char source[BUFSIZ/2] = {'\0'};
883 struct anon_inotify_inode *inode = list_entry(i,
884 struct anon_inotify_inode,
885 inodes);
886
887 decode_source(source, sizeof(source),
888 ANON_INOTIFY_MAJOR(inode->sdev), ANON_INOTIFY_MINOR(inode->sdev),
889 decode_level);
c34f8632 890 snprintf(buf, sizeof(buf), "%s%llu@%s", first_element? prefix: sep,
c1a26ac8
MY
891 (unsigned long long)inode->ino, source);
892 first_element = false;
893
894 xstrappend(&str, buf);
895 }
896
897 return str;
898}
899
900static char *anon_inotify_get_name(struct unkn *unkn)
901{
c34f8632 902 return anon_inotify_make_inodes_string("inodes=", ",", DECODE_SOURCE_FULL,
c1a26ac8
MY
903 (struct anon_inotify_data *)unkn->anon_data);
904}
905
906static void anon_inotify_init(struct unkn *unkn)
907{
908 struct anon_inotify_data *data = xcalloc(1, sizeof(struct anon_inotify_data));
909 INIT_LIST_HEAD (&data->inodes);
910 unkn->anon_data = data;
911}
912
913static void anon_inotify_free(struct unkn *unkn)
914{
915 struct anon_inotify_data *data = unkn->anon_data;
916
917 list_free(&data->inodes, struct anon_inotify_inode, inodes,
918 free);
919 free(data);
920}
921
922static void add_inode(struct anon_inotify_data *data, ino_t ino, dev_t sdev)
923{
924 struct anon_inotify_inode *inode = xmalloc(sizeof(*inode));
925
926 INIT_LIST_HEAD (&inode->inodes);
927 inode->ino = ino;
928 inode->sdev = sdev;
929
930 list_add_tail(&inode->inodes, &data->inodes);
931}
932
933static int anon_inotify_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
934{
935 struct anon_inotify_data *data = (struct anon_inotify_data *)unkn->anon_data;
936
937 if (strcmp(key, "inotify wd") == 0) {
938 unsigned long long ino;
939 unsigned long long sdev;
940
941 if (sscanf(value, "%*d ino:%llx sdev:%llx %*s", &ino, &sdev) == 2) {
942 add_inode(data, (ino_t)ino, (dev_t)sdev);
943 return 1;
944 }
945 }
946 return 0;
947}
948
949static bool anon_inotify_fill_column(struct proc *proc __attribute__((__unused__)),
950 struct unkn *unkn,
951 struct libscols_line *ln __attribute__((__unused__)),
952 int column_id,
953 size_t column_index __attribute__((__unused__)),
954 char **str)
955{
956 struct anon_inotify_data *data = (struct anon_inotify_data *)unkn->anon_data;
957
958 switch(column_id) {
959 case COL_INOTIFY_INODES:
c34f8632 960 *str = anon_inotify_make_inodes_string("", "\n", DECODE_SOURCE_FULL,
c1a26ac8
MY
961 data);
962 if (*str)
963 return true;
964 break;
965 case COL_INOTIFY_INODES_RAW:
c34f8632 966 *str = anon_inotify_make_inodes_string("", "\n", DECODE_SOURCE_MAJMIN,
c1a26ac8
MY
967 data);
968 if (*str)
969 return true;
970 break;
971 }
972
973 return false;
974}
975
976static const struct anon_ops anon_inotify_ops = {
977 .class = "inotify",
978 .probe = anon_inotify_probe,
979 .get_name = anon_inotify_get_name,
980 .fill_column = anon_inotify_fill_column,
981 .init = anon_inotify_init,
982 .free = anon_inotify_free,
983 .handle_fdinfo = anon_inotify_handle_fdinfo,
984};
985
ec2c77da
MY
986/*
987 * bpf-prog
988 *
989 * Generally, we use "-" as the word separators in lsfd's output.
990 * However, about bpf*, we use "_" because bpftool uses "_".
991 */
992static const char *bpf_prog_type_table[] = {
993 [0] = "unspec", /* BPF_PROG_TYPE_UNSPEC*/
994 [1] = "socket_filter", /* BPF_PROG_TYPE_SOCKET_FILTER*/
995 [2] = "kprobe", /* BPF_PROG_TYPE_KPROBE*/
996 [3] = "sched_cls", /* BPF_PROG_TYPE_SCHED_CLS*/
997 [4] = "sched_act", /* BPF_PROG_TYPE_SCHED_ACT*/
998 [5] = "tracepoint", /* BPF_PROG_TYPE_TRACEPOINT*/
999 [6] = "xdp", /* BPF_PROG_TYPE_XDP*/
1000 [7] = "perf_event", /* BPF_PROG_TYPE_PERF_EVENT*/
1001 [8] = "cgroup_skb", /* BPF_PROG_TYPE_CGROUP_SKB*/
1002 [9] = "cgroup_sock", /* BPF_PROG_TYPE_CGROUP_SOCK*/
1003 [10] = "lwt_in", /* BPF_PROG_TYPE_LWT_IN*/
1004 [11] = "lwt_out", /* BPF_PROG_TYPE_LWT_OUT*/
1005 [12] = "lwt_xmit", /* BPF_PROG_TYPE_LWT_XMIT*/
1006 [13] = "sock_ops", /* BPF_PROG_TYPE_SOCK_OPS*/
1007 [14] = "sk_skb", /* BPF_PROG_TYPE_SK_SKB*/
1008 [15] = "cgroup_device", /* BPF_PROG_TYPE_CGROUP_DEVICE*/
1009 [16] = "sk_msg", /* BPF_PROG_TYPE_SK_MSG*/
1010 [17] = "raw_tracepoint", /* BPF_PROG_TYPE_RAW_TRACEPOINT*/
1011 [18] = "cgroup_sock_addr", /* BPF_PROG_TYPE_CGROUP_SOCK_ADDR*/
1012 [19] = "lwt_seg6local", /* BPF_PROG_TYPE_LWT_SEG6LOCAL*/
1013 [20] = "lirc_mode2", /* BPF_PROG_TYPE_LIRC_MODE2*/
1014 [21] = "sk_reuseport", /* BPF_PROG_TYPE_SK_REUSEPORT*/
1015 [22] = "flow_dissector", /* BPF_PROG_TYPE_FLOW_DISSECTOR*/
1016 [23] = "cgroup_sysctl", /* BPF_PROG_TYPE_CGROUP_SYSCTL*/
1017 [24] = "raw_tracepoint_writable", /* BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE*/
1018 [25] = "cgroup_sockopt", /* BPF_PROG_TYPE_CGROUP_SOCKOPT*/
1019 [26] = "tracing", /* BPF_PROG_TYPE_TRACING*/
1020 [27] = "struct_ops", /* BPF_PROG_TYPE_STRUCT_OPS*/
1021 [28] = "ext", /* BPF_PROG_TYPE_EXT*/
1022 [29] = "lsm", /* BPF_PROG_TYPE_LSM*/
1023 [30] = "sk_lookup", /* BPF_PROG_TYPE_SK_LOOKUP*/
1024 [31] = "syscall", /* BPF_PROG_TYPE_SYSCALL*/
1025};
1026
1027struct anon_bpf_prog_data {
1028 int type;
1029 int id;
bc808368 1030 char name[BPF_OBJ_NAME_LEN + 1];
ec2c77da
MY
1031};
1032
1033static bool anon_bpf_prog_probe(const char *str)
1034{
1035 return strncmp(str, "bpf-prog", 8) == 0;
1036}
1037
1038static const char *anon_bpf_prog_get_prog_type_name(int type)
1039{
1040 if (0 <= type && type < (int)ARRAY_SIZE(bpf_prog_type_table))
1041 return bpf_prog_type_table[type];
1042 return NULL;
1043}
1044
1045static bool anon_bpf_prog_fill_column(struct proc *proc __attribute__((__unused__)),
1046 struct unkn *unkn,
1047 struct libscols_line *ln __attribute__((__unused__)),
1048 int column_id,
1049 size_t column_index __attribute__((__unused__)),
1050 char **str)
1051{
1052 struct anon_bpf_prog_data *data = (struct anon_bpf_prog_data *)unkn->anon_data;
1053 const char *t;
1054
1055 switch(column_id) {
1056 case COL_BPF_PROG_ID:
1057 xasprintf(str, "%d", data->id);
1058 return true;
1059 case COL_BPF_PROG_TYPE_RAW:
1060 xasprintf(str, "%d", data->type);
1061 return true;
1062 case COL_BPF_PROG_TYPE:
1063 t = anon_bpf_prog_get_prog_type_name(data->type);
1064 if (t)
1065 *str = xstrdup(t);
1066 else
1067 xasprintf(str, "UNKNOWN(%d)", data->type);
1068 return true;
bc808368
MY
1069 case COL_BPF_NAME:
1070 *str = xstrdup(data->name);
1071 return true;
ec2c77da
MY
1072 default:
1073 return false;
1074 }
1075}
1076
1077static char *anon_bpf_prog_get_name(struct unkn *unkn)
1078{
1079 const char *t;
1080 char *str = NULL;
1081 struct anon_bpf_prog_data *data = (struct anon_bpf_prog_data *)unkn->anon_data;
1082
1083 t = anon_bpf_prog_get_prog_type_name(data->type);
1084 if (t)
1085 xasprintf(&str, "id=%d type=%s", data->id, t);
1086 else
1087 xasprintf(&str, "id=%d type=UNKNOWN(%d)", data->id, data->type);
1088
bc808368
MY
1089 if (*data->name)
1090 xstrfappend(&str, " name=%s", data->name);
1091
ec2c77da
MY
1092 return str;
1093}
1094
1095
1096static void anon_bpf_prog_init(struct unkn *unkn)
1097{
1098 struct anon_bpf_prog_data *data = xmalloc(sizeof(*data));
1099 data->type = -1;
1100 data->id = -1;
bc808368 1101 data->name[0] = '\0';
ec2c77da
MY
1102 unkn->anon_data = data;
1103}
1104
1105static void anon_bpf_prog_free(struct unkn *unkn)
1106{
1107 struct anon_bpf_prog_data *data = (struct anon_bpf_prog_data *)unkn->anon_data;
1108 free(data);
1109}
1110
bc808368
MY
1111static void anon_bpf_prog_get_more_info(struct anon_bpf_prog_data *prog_data)
1112{
1113 union bpf_attr attr = {
1114 .prog_id = (int32_t)prog_data->id,
1115 .next_id = 0,
1116 .open_flags = 0,
1117 };
1118 struct bpf_prog_info info = { 0 };
1119 union bpf_attr info_attr = {
1120 .info.info_len = sizeof(info),
1121 .info.info = (uint64_t)(uintptr_t)&info,
1122 };
1123
1124 int bpf_fd = syscall(SYS_bpf, BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
1125 if (bpf_fd < 0)
1126 return;
1127
1128 info_attr.info.bpf_fd = bpf_fd;
1129 if (syscall(SYS_bpf, BPF_OBJ_GET_INFO_BY_FD, &info_attr, offsetofend(union bpf_attr, info)) == 0) {
1130 memcpy(prog_data->name,
1131 info.name,
1132 BPF_OBJ_NAME_LEN);
1133 prog_data->name[BPF_OBJ_NAME_LEN] = '\0';
1134 }
1135 close (bpf_fd);
1136}
1137
ec2c77da
MY
1138static int anon_bpf_prog_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
1139{
1140 if (strcmp(key, "prog_id") == 0) {
1141 int32_t t = -1;
1142 int rc = ul_strtos32(value, &t, 10);
1143 if (rc < 0)
1144 return 0; /* ignore -- parse failed */
1145 ((struct anon_bpf_prog_data *)unkn->anon_data)->id = (int)t;
bc808368 1146 anon_bpf_prog_get_more_info((struct anon_bpf_prog_data *)unkn->anon_data);
ec2c77da
MY
1147 return 1;
1148 }
1149
1150 if (strcmp(key, "prog_type") == 0) {
1151 int32_t t = -1;
1152 int rc = ul_strtos32(value, &t, 10);
1153 if (rc < 0)
1154 return 0; /* ignore -- parse failed */
1155 ((struct anon_bpf_prog_data *)unkn->anon_data)->type = (int)t;
1156 return 1;
1157 }
1158
1159 return 0;
1160}
1161
1162static const struct anon_ops anon_bpf_prog_ops = {
1163 .class = "bpf-prog",
1164 .probe = anon_bpf_prog_probe,
1165 .get_name = anon_bpf_prog_get_name,
1166 .fill_column = anon_bpf_prog_fill_column,
1167 .init = anon_bpf_prog_init,
1168 .free = anon_bpf_prog_free,
1169 .handle_fdinfo = anon_bpf_prog_handle_fdinfo,
1170};
1171
ea9466d4
MY
1172/*
1173 * bpf-map
1174 */
1175static const char *bpf_map_type_table[] = {
1176 [0] = "unspec", /* BPF_MAP_TYPE_UNSPEC */
1177 [1] = "hash", /* BPF_MAP_TYPE_HASH */
1178 [2] = "array", /* BPF_MAP_TYPE_ARRAY */
1179 [3] = "prog-array", /* BPF_MAP_TYPE_PROG_ARRAY */
1180 [4] = "perf-event-array", /* BPF_MAP_TYPE_PERF_EVENT_ARRAY */
1181 [5] = "percpu-hash", /* BPF_MAP_TYPE_PERCPU_HASH */
1182 [6] = "percpu-array", /* BPF_MAP_TYPE_PERCPU_ARRAY */
1183 [7] = "stack-trace", /* BPF_MAP_TYPE_STACK_TRACE */
1184 [8] = "cgroup-array", /* BPF_MAP_TYPE_CGROUP_ARRAY */
1185 [9] = "lru-hash", /* BPF_MAP_TYPE_LRU_HASH */
1186 [10] = "lru-percpu-hash", /* BPF_MAP_TYPE_LRU_PERCPU_HASH */
1187 [11] = "lpm-trie", /* BPF_MAP_TYPE_LPM_TRIE */
1188 [12] = "array-of-maps", /* BPF_MAP_TYPE_ARRAY_OF_MAPS */
1189 [13] = "hash-of-maps", /* BPF_MAP_TYPE_HASH_OF_MAPS */
1190 [14] = "devmap", /* BPF_MAP_TYPE_DEVMAP */
1191 [15] = "sockmap", /* BPF_MAP_TYPE_SOCKMAP */
1192 [16] = "cpumap", /* BPF_MAP_TYPE_CPUMAP */
1193 [17] = "xskmap", /* BPF_MAP_TYPE_XSKMAP */
1194 [18] = "sockhash", /* BPF_MAP_TYPE_SOCKHASH */
1195 [19] = "cgroup-storage", /* BPF_MAP_TYPE_CGROUP_STORAGE */
1196 [20] = "reuseport-sockarray", /* BPF_MAP_TYPE_REUSEPORT_SOCKARRAY */
1197 [21] = "percpu-cgroup-storage", /* BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE */
1198 [22] = "queue", /* BPF_MAP_TYPE_QUEUE */
1199 [23] = "stack", /* BPF_MAP_TYPE_STACK */
1200 [24] = "sk-storage", /* BPF_MAP_TYPE_SK_STORAGE */
1201 [25] = "devmap-hash", /* BPF_MAP_TYPE_DEVMAP_HASH */
1202 [26] = "struct-ops", /* BPF_MAP_TYPE_STRUCT_OPS */
1203 [27] = "ringbuf", /* BPF_MAP_TYPE_RINGBUF */
1204 [28] = "inode-storage", /* BPF_MAP_TYPE_INODE_STORAGE */
1205 [29] = "task-storage", /* BPF_MAP_TYPE_TASK_STORAGE */
1206 [30] = "bloom-filter", /* BPF_MAP_TYPE_BLOOM_FILTER */
1207 [31] = "user-ringbuf", /* BPF_MAP_TYPE_USER_RINGBUF */
1208 [32] = "cgrp-storage", /* BPF_MAP_TYPE_CGRP_STORAGE */
1209};
1210
1211struct anon_bpf_map_data {
1212 int type;
1213 int id;
bc808368 1214 char name[BPF_OBJ_NAME_LEN + 1];
ea9466d4
MY
1215};
1216
1217static bool anon_bpf_map_probe(const char *str)
1218{
1219 return strncmp(str, "bpf-map", 8) == 0;
1220}
1221
1222static const char *anon_bpf_map_get_map_type_name(int type)
1223{
1224 if (0 <= type && type < (int)ARRAY_SIZE(bpf_map_type_table))
1225 return bpf_map_type_table[type];
1226 return NULL;
1227}
1228
1229static bool anon_bpf_map_fill_column(struct proc *proc __attribute__((__unused__)),
1230 struct unkn *unkn,
1231 struct libscols_line *ln __attribute__((__unused__)),
1232 int column_id,
1233 size_t column_index __attribute__((__unused__)),
1234 char **str)
1235{
1236 struct anon_bpf_prog_data *data = (struct anon_bpf_prog_data *)unkn->anon_data;
1237 const char *t;
1238
1239 switch(column_id) {
1240 case COL_BPF_MAP_ID:
1241 xasprintf(str, "%d", data->id);
1242 return true;
1243 case COL_BPF_MAP_TYPE_RAW:
1244 xasprintf(str, "%d", data->type);
1245 return true;
1246 case COL_BPF_MAP_TYPE:
1247 t = anon_bpf_map_get_map_type_name(data->type);
1248 if (t)
1249 *str = xstrdup(t);
1250 else
1251 xasprintf(str, "UNKNOWN(%d)", data->type);
1252 return true;
bc808368
MY
1253 case COL_BPF_NAME:
1254 *str = xstrdup(data->name);
1255 return true;
ea9466d4
MY
1256 default:
1257 return false;
1258 }
1259}
1260
1261static char *anon_bpf_map_get_name(struct unkn *unkn)
1262{
1263 const char *t;
1264 char *str = NULL;
1265 struct anon_bpf_map_data *data = (struct anon_bpf_map_data *)unkn->anon_data;
1266
1267 t = anon_bpf_map_get_map_type_name(data->type);
1268 if (t)
1269 xasprintf(&str, "id=%d type=%s", data->id, t);
1270 else
1271 xasprintf(&str, "id=%d type=UNKNOWN(%d)", data->id, data->type);
bc808368
MY
1272
1273 if (*data->name)
1274 xstrfappend(&str, " name=%s", data->name);
1275
ea9466d4
MY
1276 return str;
1277}
1278
1279static void anon_bpf_map_init(struct unkn *unkn)
1280{
1281 struct anon_bpf_map_data *data = xmalloc(sizeof(*data));
1282 data->type = -1;
1283 data->id = -1;
bc808368 1284 data->name[0] = '\0';
ea9466d4
MY
1285 unkn->anon_data = data;
1286}
1287
1288static void anon_bpf_map_free(struct unkn *unkn)
1289{
1290 struct anon_bpf_map_data *data = (struct anon_bpf_map_data *)unkn->anon_data;
1291 free(data);
1292}
1293
bc808368
MY
1294static void anon_bpf_map_get_more_info(struct anon_bpf_map_data *map_data)
1295{
1296 union bpf_attr attr = {
1297 .map_id = (int32_t)map_data->id,
1298 .next_id = 0,
1299 .open_flags = 0,
1300 };
1301 struct bpf_map_info info = { 0 };
1302 union bpf_attr info_attr = {
1303 .info.info_len = sizeof(info),
1304 .info.info = (uint64_t)(uintptr_t)&info,
1305 };
1306
1307 int bpf_fd = syscall(SYS_bpf, BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
1308 if (bpf_fd < 0)
1309 return;
1310
1311 info_attr.info.bpf_fd = bpf_fd;
1312 if (syscall(SYS_bpf, BPF_OBJ_GET_INFO_BY_FD, &info_attr, offsetofend(union bpf_attr, info)) == 0) {
1313 memcpy(map_data->name,
1314 info.name,
1315 BPF_OBJ_NAME_LEN);
1316 map_data->name[BPF_OBJ_NAME_LEN] = '\0';
1317 }
1318 close (bpf_fd);
1319}
1320
ea9466d4
MY
1321static int anon_bpf_map_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
1322{
1323 if (strcmp(key, "map_id") == 0) {
1324 int32_t t = -1;
1325 int rc = ul_strtos32(value, &t, 10);
1326 if (rc < 0)
1327 return 0; /* ignore -- parse failed */
1328 ((struct anon_bpf_map_data *)unkn->anon_data)->id = (int)t;
bc808368 1329 anon_bpf_map_get_more_info((struct anon_bpf_map_data *)unkn->anon_data);
ea9466d4
MY
1330 return 1;
1331 }
1332
1333 if (strcmp(key, "map_type") == 0) {
1334 int32_t t = -1;
1335 int rc = ul_strtos32(value, &t, 10);
1336 if (rc < 0)
1337 return 0; /* ignore -- parse failed */
1338 ((struct anon_bpf_map_data *)unkn->anon_data)->type = (int)t;
1339 return 1;
1340 }
1341
1342 return 0;
1343}
1344
1345static const struct anon_ops anon_bpf_map_ops = {
1346 .class = "bpf-map",
1347 .probe = anon_bpf_map_probe,
1348 .get_name = anon_bpf_map_get_name,
1349 .fill_column = anon_bpf_map_fill_column,
1350 .init = anon_bpf_map_init,
1351 .free = anon_bpf_map_free,
1352 .handle_fdinfo = anon_bpf_map_handle_fdinfo,
1353};
1354
9a9c96b7
MY
1355/*
1356 * generic (fallback implementation)
1357 */
54a06438 1358static const struct anon_ops anon_generic_ops = {
4cd4ed18 1359 .class = NULL,
9a9c96b7 1360 .get_name = NULL,
553a6817 1361 .fill_column = NULL,
9a9c96b7
MY
1362 .init = NULL,
1363 .free = NULL,
1364 .handle_fdinfo = NULL,
1365};
1366
007e405b
MY
1367static const struct anon_ops *anon_ops[] = {
1368 &anon_pidfd_ops,
8c2ca9c9 1369 &anon_eventfd_ops,
c2e2dd60 1370 &anon_eventpoll_ops,
188b7d64 1371 &anon_timerfd_ops,
b22d1e54 1372 &anon_signalfd_ops,
c1a26ac8 1373 &anon_inotify_ops,
ec2c77da 1374 &anon_bpf_prog_ops,
ea9466d4 1375 &anon_bpf_map_ops,
007e405b
MY
1376};
1377
1378static const struct anon_ops *anon_probe(const char *str)
1379{
1380 for (size_t i = 0; i < ARRAY_SIZE(anon_ops); i++)
1381 if (anon_ops[i]->probe(str))
1382 return anon_ops[i];
1383 return &anon_generic_ops;
1384}
1385
14e16696
MY
1386const struct file_class unkn_class = {
1387 .super = &file_class,
9a9c96b7 1388 .size = sizeof(struct unkn),
14e16696 1389 .fill_column = unkn_fill_column,
9a9c96b7
MY
1390 .initialize_content = unkn_init_content,
1391 .free_content = unkn_content_free,
1392 .handle_fdinfo = unkn_handle_fdinfo,
d4fea4d0
MY
1393 .attach_xinfo = unkn_attach_xinfo,
1394 .get_ipc_class = unkn_get_ipc_class,
14e16696 1395};