diff options
Diffstat (limited to 'misc-utils/lsfd-unkn.c')
-rw-r--r-- | misc-utils/lsfd-unkn.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/misc-utils/lsfd-unkn.c b/misc-utils/lsfd-unkn.c new file mode 100644 index 0000000..087e31d --- /dev/null +++ b/misc-utils/lsfd-unkn.c @@ -0,0 +1,282 @@ +/* + * lsfd-unkn.c - handle associations opening unknown objects + * + * Copyright (C) 2021 Red Hat, Inc. All rights reserved. + * Written by Masatake YAMATO <yamato@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "xalloc.h" +#include "nls.h" +#include "libsmartcols.h" + +#include "lsfd.h" + +struct unkn { + struct file file; + const struct anon_ops *anon_ops; + void *anon_data; +}; + +struct anon_ops { + const char *class; + char * (*get_name)(struct unkn *); + /* Return true is handled the column. */ + bool (*fill_column)(struct proc *, + struct unkn *, + struct libscols_line *, + int, + size_t, + char **str); + void (*init)(struct unkn *); + void (*free)(struct unkn *); + int (*handle_fdinfo)(struct unkn *, const char *, const char *); +}; + +static const struct anon_ops anon_generic_ops; +static const struct anon_ops anon_pidfd_ops; + +static char * anon_get_class(struct unkn *unkn) +{ + char *name; + + if (unkn->anon_ops->class) + return xstrdup(unkn->anon_ops->class); + + /* See unkn_init_content() */ + name = ((struct file *)unkn)->name + 11; + /* Does it have the form anon_inode:[class]? */ + if (*name == '[') { + size_t len = strlen(name + 1); + if (*(name + 1 + len - 1) == ']') + return strndup(name + 1, len - 1); + } + + return xstrdup(name); +} + +static bool unkn_fill_column(struct proc *proc, + struct file *file, + struct libscols_line *ln, + int column_id, + size_t column_index) +{ + char *str = NULL; + struct unkn *unkn = (struct unkn *)file; + + switch(column_id) { + case COL_NAME: + if (unkn->anon_ops && unkn->anon_ops->get_name) { + str = unkn->anon_ops->get_name(unkn); + if (str) + break; + } + return false; + case COL_TYPE: + if (!unkn->anon_ops) + return false; + /* FALL THROUGH */ + case COL_AINODECLASS: + if (unkn->anon_ops) { + str = anon_get_class(unkn); + break; + } + return false; + case COL_SOURCE: + if (unkn->anon_ops) { + str = xstrdup("anon_inodefs"); + break; + } + return false; + default: + if (unkn->anon_ops && unkn->anon_ops->fill_column) { + if (unkn->anon_ops->fill_column(proc, unkn, ln, + column_id, column_index, &str)) + break; + } + return false; + } + + if (!str) + err(EXIT_FAILURE, _("failed to add output data")); + if (scols_line_refer_data(ln, column_index, str)) + err(EXIT_FAILURE, _("failed to add output data")); + return true; +} + +static void unkn_init_content(struct file *file) +{ + struct unkn *unkn = (struct unkn *)file; + + assert(file); + unkn->anon_ops = NULL; + unkn->anon_data = NULL; + + if (major(file->stat.st_dev) == 0 + && strncmp(file->name, "anon_inode:", 11) == 0) { + const char *rest = file->name + 11; + + if (strncmp(rest, "[pidfd]", 7) == 0) + unkn->anon_ops = &anon_pidfd_ops; + else + unkn->anon_ops = &anon_generic_ops; + + if (unkn->anon_ops->init) + unkn->anon_ops->init(unkn); + } +} + +static void unkn_content_free(struct file *file) +{ + struct unkn *unkn = (struct unkn *)file; + + assert(file); + if (unkn->anon_ops && unkn->anon_ops->free) + unkn->anon_ops->free((struct unkn *)file); +} + +static int unkn_handle_fdinfo(struct file *file, const char *key, const char *value) +{ + struct unkn *unkn = (struct unkn *)file; + + assert(file); + if (unkn->anon_ops && unkn->anon_ops->handle_fdinfo) + return unkn->anon_ops->handle_fdinfo(unkn, key, value); + return 0; /* Should be handled in parents */ +} + +/* + * pidfd + */ +struct anon_pidfd_data { + pid_t pid; + char *nspid; +}; + +static char *anon_pidfd_get_name(struct unkn *unkn) +{ + char *str = NULL; + struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data; + + char *comm = NULL; + struct proc *proc = get_proc(data->pid); + if (proc) + comm = proc->command; + + xasprintf(&str, "pid=%d comm=%s nspid=%s", + data->pid, + comm? comm: "", + data->nspid? data->nspid: ""); + return str; +} + +static void anon_pidfd_init(struct unkn *unkn) +{ + unkn->anon_data = xcalloc(1, sizeof(struct anon_pidfd_data)); +} + +static void anon_pidfd_free(struct unkn *unkn) +{ + struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data; + + if (data->nspid) + free(data->nspid); + free(data); +} + +static int anon_pidfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value) +{ + if (strcmp(key, "Pid") == 0) { + uint64_t pid; + + int rc = ul_strtou64(value, &pid, 10); + if (rc < 0) + return 0; /* ignore -- parse failed */ + ((struct anon_pidfd_data *)unkn->anon_data)->pid = (pid_t)pid; + return 1; + } + else if (strcmp(key, "NSpid") == 0) { + ((struct anon_pidfd_data *)unkn->anon_data)->nspid = xstrdup(value); + return 1; + + } + return 0; +} + +static bool anon_pidfd_fill_column(struct proc *proc __attribute__((__unused__)), + struct unkn *unkn, + struct libscols_line *ln __attribute__((__unused__)), + int column_id, + size_t column_index __attribute__((__unused__)), + char **str) +{ + struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data; + + switch(column_id) { + case COL_PIDFD_COMM: { + struct proc *pidfd_proc = get_proc(data->pid); + char *pidfd_comm = NULL; + if (pidfd_proc) + pidfd_comm = pidfd_proc->command; + if (pidfd_comm) { + *str = xstrdup(pidfd_comm); + return true; + } + break; + } + case COL_PIDFD_NSPID: + if (data->nspid) { + *str = xstrdup(data->nspid); + return true; + } + break; + case COL_PIDFD_PID: + xasprintf(str, "%d", (int)data->pid); + return true; + } + + return false; +} + +static const struct anon_ops anon_pidfd_ops = { + .class = "pidfd", + .get_name = anon_pidfd_get_name, + .fill_column = anon_pidfd_fill_column, + .init = anon_pidfd_init, + .free = anon_pidfd_free, + .handle_fdinfo = anon_pidfd_handle_fdinfo, +}; + +/* + * generic (fallback implementation) + */ +static const struct anon_ops anon_generic_ops = { + .class = NULL, + .get_name = NULL, + .fill_column = NULL, + .init = NULL, + .free = NULL, + .handle_fdinfo = NULL, +}; + +const struct file_class unkn_class = { + .super = &file_class, + .size = sizeof(struct unkn), + .fill_column = unkn_fill_column, + .initialize_content = unkn_init_content, + .free_content = unkn_content_free, + .handle_fdinfo = unkn_handle_fdinfo, +}; |