From: Masatake YAMATO Date: Thu, 10 Nov 2022 21:09:57 +0000 (+0900) Subject: lsfd: show extra information returned from ioctl(..., NS_GET_NSTYPE) X-Git-Tag: v2.39-rc1~406^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c5eb81b39d1d298c8e07a31ce8fccf69dcf3e5fa;p=thirdparty%2Futil-linux.git lsfd: show extra information returned from ioctl(..., NS_GET_NSTYPE) lsfd uses the information to fill the newly added NS.TYPE and NS.NAME columns: # ./lsfd -oCOMMAND,PID,USER,ASSOC,SOURCE,MNTID,NS.TYPE,NS.NAME,NAME -Q '(FD >= 0) and (SOURCE == "nsfs")' COMMAND PID USER ASSOC SOURCE MNTID NS.TYPE NS.NAME NAME NetworkManager 1114 root 8 nsfs 3 net net:[4026531840] net:[4026531840] NetworkManager 1114 root 9 nsfs 3 mnt mnt:[4026532758] mnt:[4026532758] lsfd 14379 root 3 nsfs 3 net net:[4026531840] net:[4026531840] podman 1532759 root 7 nsfs 1546 net net:[4026533029] /run/netns/netns-4f5ecc46-d6a0-175c-46a8-2e4ba180b94a Signed-off-by: Masatake YAMATO --- diff --git a/misc-utils/lsfd-file.c b/misc-utils/lsfd-file.c index ce310cc0f2..f62aa7cc50 100644 --- a/misc-utils/lsfd-file.c +++ b/misc-utils/lsfd-file.c @@ -24,6 +24,15 @@ #include +#ifdef HAVE_LINUX_NSFS_H +# include +# if defined(NS_GET_NSTYPE) +# define USE_NS_GET_API 1 +# include +# endif +#endif +#include + #include "xalloc.h" #include "nls.h" #include "buffer.h" @@ -316,3 +325,141 @@ const struct file_class file_class = { .handle_fdinfo = file_handle_fdinfo, .free_content = file_free_content, }; + +/* + * Regular files on NSFS + */ + +struct nsfs_file { + struct file file; + int clone_type; +}; + +static const char *get_ns_type_name(int clone_type) +{ + switch (clone_type) { +#ifdef USE_NS_GET_API + case CLONE_NEWNS: + return "mnt"; + case CLONE_NEWCGROUP: + return "cgroup"; + case CLONE_NEWUTS: + return "uts"; + case CLONE_NEWIPC: + return "ipc"; + case CLONE_NEWUSER: + return "user"; + case CLONE_NEWPID: + return "pid"; + case CLONE_NEWNET: + return "net"; +#ifdef CLONE_NEWTIME + case CLONE_NEWTIME: + return "time"; +#endif /* CLONE_NEWTIME */ +#endif /* USE_NS_GET_API */ + default: + return "unknown"; + } +} + +static void init_nsfs_file_content(struct file *file) +{ + struct nsfs_file *nsfs_file = (struct nsfs_file *)file; + nsfs_file->clone_type = -1; + +#ifdef USE_NS_GET_API + char *proc_fname = NULL; + int ns_fd; + int ns_type; + + if (is_association (file, NS_CGROUP)) + nsfs_file->clone_type = CLONE_NEWCGROUP; + else if (is_association (file, NS_IPC)) + nsfs_file->clone_type = CLONE_NEWIPC; + else if (is_association (file, NS_MNT)) + nsfs_file->clone_type = CLONE_NEWNS; + else if (is_association (file, NS_NET)) + nsfs_file->clone_type = CLONE_NEWNET; + else if (is_association (file, NS_PID) + || is_association (file, NS_PID4C)) + nsfs_file->clone_type = CLONE_NEWPID; +#ifdef CLONE_NEWTIME + else if (is_association (file, NS_TIME) + || is_association (file, NS_TIME4C)) + nsfs_file->clone_type = CLONE_NEWTIME; +#endif + else if (is_association (file, NS_USER)) + nsfs_file->clone_type = CLONE_NEWUSER; + else if (is_association (file, NS_UTS)) + nsfs_file->clone_type = CLONE_NEWUTS; + + if (nsfs_file->clone_type != -1) + return; + + if (!is_opened_file(file)) + return; + + if (!file->name) + return; + + xasprintf(&proc_fname, "/proc/%d/fd/%d", + file->proc->pid, file->association); + ns_fd = open(proc_fname, O_RDONLY); + free(proc_fname); + if (ns_fd < 0) + return; + + ns_type = ioctl(ns_fd, NS_GET_NSTYPE); + close(ns_fd); + if (ns_type < 0) + return; + + nsfs_file->clone_type = ns_type; +#endif /* USE_NS_GET_API */ +} + + +static bool nsfs_file_fill_column(struct proc *proc __attribute__((__unused__)), + struct file *file, + struct libscols_line *ln, + int column_id, + size_t column_index) +{ + struct nsfs_file *nsfs_file = (struct nsfs_file *)file; + char *name = NULL; + + if (nsfs_file->clone_type == -1) + return false; + + switch (column_id) { + case COL_NS_NAME: + xasprintf(&name, "%s:[%llu]", + get_ns_type_name(nsfs_file->clone_type), + (unsigned long long)file->stat.st_ino); + break; + case COL_NS_TYPE: + if (scols_line_set_data(ln, column_index, + get_ns_type_name(nsfs_file->clone_type))) + err(EXIT_FAILURE, _("failed to add output data")); + return true; + default: + return false; + } + + if (name && scols_line_refer_data(ln, column_index, name)) + err(EXIT_FAILURE, _("failed to add output data")); + + return true; +} + +const struct file_class nsfs_file_class = { + .super = &file_class, + .size = sizeof(struct nsfs_file), + .initialize_class = NULL, + .finalize_class = NULL, + .initialize_content = init_nsfs_file_content, + .free_content = NULL, + .fill_column = nsfs_file_fill_column, + .handle_fdinfo = NULL, +}; diff --git a/misc-utils/lsfd.1.adoc b/misc-utils/lsfd.1.adoc index ce833a5a98..b65e5ec6a0 100644 --- a/misc-utils/lsfd.1.adoc +++ b/misc-utils/lsfd.1.adoc @@ -228,6 +228,14 @@ state=_SOCK.STATE_[ path=_UNIX.PATH_] type=_SOCK.TYPE_ NLINK <``number``>:: Link count. +NS.NAME <``string``>:: +Name (_NS.TYPE_:[_INODE_]) of the namespace specified with the file. + +NS.TYPE <``string``>:: +Type of the namespace specified with the file. +The type is `mnt`, `cgroup`, `uts`, `ipc`, `user`, `pid`, `net`, +`time`, or `unknown`. + OWNER <``string``>:: Owner of the file. diff --git a/misc-utils/lsfd.c b/misc-utils/lsfd.c index 2e2728791b..256ff9a923 100644 --- a/misc-utils/lsfd.c +++ b/misc-utils/lsfd.c @@ -169,6 +169,10 @@ static struct colinfo infos[] = { N_("name of the file (cooked)") }, [COL_NLINK] = { "NLINK", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("link count") }, + [COL_NS_NAME] = { "NS.NAME", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, + N_("name of the namespace (NS.TYPE:[INODE])") }, + [COL_NS_TYPE] = { "NS.TYPE", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, + N_("type of the namespace") }, [COL_OWNER] = { "OWNER", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, N_("owner of the file") }, [COL_PARTITION]={ "PARTITION",0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, @@ -473,6 +477,9 @@ static void add_mnt_ns(ino_t id) static const struct file_class *stat2class(struct stat *sb) { + const char *fs; + dev_t dev; + assert(sb); switch (sb->st_mode & S_IFMT) { @@ -485,8 +492,17 @@ static const struct file_class *stat2class(struct stat *sb) case S_IFIFO: return &fifo_class; case S_IFLNK: - case S_IFREG: case S_IFDIR: + return &file_class; + case S_IFREG: + dev = sb->st_dev; + if (major(dev) != 0) + return &file_class; + + fs = get_nodev_filesystem(minor(dev)); + if (fs && strcmp(fs, "nsfs") == 0) + return &nsfs_file_class; + return &file_class; default: break; diff --git a/misc-utils/lsfd.h b/misc-utils/lsfd.h index 163715e58e..c73a66298b 100644 --- a/misc-utils/lsfd.h +++ b/misc-utils/lsfd.h @@ -60,6 +60,8 @@ enum { COL_MODE, COL_NAME, COL_NLINK, + COL_NS_NAME, + COL_NS_TYPE, COL_PARTITION, COL_PID, COL_PIDFD_COMM, @@ -169,7 +171,8 @@ struct file_class { struct ipc_class *(*get_ipc_class)(struct file *file); }; -extern const struct file_class file_class, cdev_class, bdev_class, sock_class, unkn_class, fifo_class; +extern const struct file_class file_class, cdev_class, bdev_class, sock_class, unkn_class, fifo_class, + nsfs_file_class; /* * IPC diff --git a/tests/expected/lsfd/mkfds-netns b/tests/expected/lsfd/mkfds-netns new file mode 100644 index 0000000000..3df8ce7f7d --- /dev/null +++ b/tests/expected/lsfd/mkfds-netns @@ -0,0 +1,4 @@ + net +net:[4026531840] == net:[4026531840]: 0 +net:[4026531840] == net:[4026531840]: 0 +net:[4026531840] == net:[4026531840]: 0 diff --git a/tests/helpers/test_mkfds.c b/tests/helpers/test_mkfds.c index 9c072ea97e..e37225aaf2 100644 --- a/tests/helpers/test_mkfds.c +++ b/tests/helpers/test_mkfds.c @@ -25,6 +25,7 @@ #include #include #include +#include /* SIOCGSKNS */ #include #include #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -1503,6 +1505,37 @@ static void *make_udp(const struct factory *factory, struct fdesc fdescs[], return NULL; } +static void *make_netns(const struct factory *factory _U_, struct fdesc fdescs[], + int argc _U_, char ** argv _U_) +{ + int sd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sd < 0) + err(EXIT_FAILURE, "failed in socket()"); + + int ns = ioctl(sd, SIOCGSKNS); + if (ns < 0) + err(EXIT_FAILURE, "failed in ioctl(SIOCGSKNS)"); + close(sd); + + if (ns != fdescs[0].fd) { + if (dup2(ns, fdescs[0].fd) < 0) { + int e = errno; + close(ns); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", ns, fdescs[0].fd); + } + close(ns); + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + + return NULL; +} + #define PARAM_END { .name = NULL, } static const struct factory factories[] = { { @@ -1861,7 +1894,18 @@ static const struct factory factories[] = { }, PARAM_END } - } + }, + { + .name = "netns", + .desc = "open a file specifying a netns", + .priv = true, + .N = 1, + .EX_N = 0, + .make = make_netns, + .params = (struct parameter []) { + PARAM_END + } + }, }; static int count_parameters(const struct factory *factory) diff --git a/tests/ts/lsfd/mkfds-netns b/tests/ts/lsfd/mkfds-netns new file mode 100644 index 0000000000..11a312a78d --- /dev/null +++ b/tests/ts/lsfd/mkfds-netns @@ -0,0 +1,58 @@ +#!/bin/bash +# +# Copyright (C) 2022 Masatake YAMATO +# +# This file is part of util-linux. +# +# This file 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 file is distributed in the hope that it will 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. +# +TS_TOPDIR="${0%/*}/../.." +TS_DESC="netns associated with a fd" + +. "$TS_TOPDIR"/functions.sh +ts_init "$*" +ts_skip_nonroot + +ts_check_test_command "$TS_CMD_LSFD" +ts_check_test_command "$TS_HELPER_MKFDS" + +ts_cd "$TS_OUTDIR" + +PID= +FD=3 +NAME_FD= +NAME_NS= +INO_FD= +INO_NS= +{ + coproc MKFDS { "$TS_HELPER_MKFDS" netns "$FD"; } + if read -r -u "${MKFDS[0]}" PID; then + "${TS_CMD_LSFD}" -n -oNS.TYPE -p "${PID}" -Q "(FD == $FD)" + NAME_FD=$("${TS_CMD_LSFD}" -n -oNS.NAME -p "${PID}" -Q "(FD == $FD)") + NAME_NS=$("${TS_CMD_LSFD}" -n -oNS.NAME -p "${PID}" -Q '(ASSOC == "net")') + INO_FD=$( "${TS_CMD_LSFD}" -n -oINODE -p "${PID}" -Q "(FD == $FD)") + INO_NS=$( "${TS_CMD_LSFD}" -n -oINODE -p "${PID}" -Q '(ASSOC == "net")') + + [[ "$NAME_FD" = "$NAME_NS" ]] + echo "$NAME_FD" == "$NAME_NS": $? + + [[ "$NAME_FD" = "net:[$INO_FD]" ]] + echo "$NAME_FD" == "net:[$INO_FD]": $? + + [[ "$NAME_FD" = "net:[$INO_NS]" ]] + echo "$NAME_FD" == "net:[$INO_NS]": $? + + kill -CONT ${PID} + fi + wait "${MKFDS_PID}" +} > "$TS_OUTPUT" 2>&1 + +ts_finalize