]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: show extra information returned from ioctl(..., NS_GET_NSTYPE)
authorMasatake YAMATO <yamato@redhat.com>
Thu, 10 Nov 2022 21:09:57 +0000 (06:09 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Wed, 23 Nov 2022 11:05:05 +0000 (20:05 +0900)
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 <yamato@redhat.com>
misc-utils/lsfd-file.c
misc-utils/lsfd.1.adoc
misc-utils/lsfd.c
misc-utils/lsfd.h
tests/expected/lsfd/mkfds-netns [new file with mode: 0644]
tests/helpers/test_mkfds.c
tests/ts/lsfd/mkfds-netns [new file with mode: 0644]

index ce310cc0f206258cf234be1733fab0a12eac0580..f62aa7cc50c678d550a61b56b2710e16c84af61e 100644 (file)
 
 #include <unistd.h>
 
+#ifdef HAVE_LINUX_NSFS_H
+# include <linux/nsfs.h>
+# if defined(NS_GET_NSTYPE)
+#  define USE_NS_GET_API       1
+#  include <sys/ioctl.h>
+# endif
+#endif
+#include <linux/sched.h>
+
 #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,
+};
index ce833a5a9858f1185cfa85a908f262b826db2984..b65e5ec6a039a5aef7f594dc415c404806386123 100644 (file)
@@ -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.
 
index 2e2728791b5a46aae9724e7afa506254a8f78032..256ff9a923759fad867eecaf3143d4d7e85e9d6a 100644 (file)
@@ -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;
index 163715e58e11bf3fd14b8d047ed4dbec12081ad1..c73a66298b5ccf1b3c56d59d83b7bf8991b27a20 100644 (file)
@@ -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 (file)
index 0000000..3df8ce7
--- /dev/null
@@ -0,0 +1,4 @@
+    net
+net:[4026531840] == net:[4026531840]: 0
+net:[4026531840] == net:[4026531840]: 0
+net:[4026531840] == net:[4026531840]: 0
index 9c072ea97ed8b4522c5e7b74619bef02e0c3eff8..e37225aaf27af73d069654d88f32e84c901e8930 100644 (file)
@@ -25,6 +25,7 @@
 #include <getopt.h>
 #include <linux/if_ether.h>
 #include <linux/if_packet.h>
+#include <linux/sockios.h>  /* SIOCGSKNS */
 #include <net/if.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
@@ -35,6 +36,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/inotify.h>
+#include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/prctl.h>
 #include <sys/select.h>
@@ -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 (file)
index 0000000..11a312a
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 Masatake YAMATO <yamato@redhat.com>
+#
+# 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