]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsns: add columns for parent namespaces and owner namespaces
authorMasatake YAMATO <yamato@redhat.com>
Tue, 6 Apr 2021 19:28:32 +0000 (04:28 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Mon, 12 Apr 2021 21:22:51 +0000 (06:22 +0900)
See ioctl_ns(2) about parent and owner namespaces.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
configure.ac
sys-utils/lsns.c
tests/commands.sh
tests/expected/lsns/ioctl_ns [new file with mode: 0644]
tests/ts/lsns/ioctl_ns [new file with mode: 0644]

index bebb4085425a7cc84e20b6d1b4bda599d89a1784..abb96e6280a2a6b52ef826e374fa8798ba5d7e3f 100644 (file)
@@ -308,6 +308,7 @@ AC_CHECK_HEADERS([ \
        linux/fd.h \
        linux/fiemap.h \
        linux/net_namespace.h \
+       linux/nsfs.h \
        linux/raw.h \
        linux/securebits.h \
        linux/tiocl.h \
index eec8e273b379c4ca200b28c5a5475b5e76b33fc8..8e25ff1831b9a70c30752c24bdbe83cad7143c9b 100644 (file)
 #include <linux/net_namespace.h>
 #endif
 
+#ifdef HAVE_LINUX_NSFS_H
+#include <linux/nsfs.h>
+#endif
+
 #include "pathnames.h"
 #include "nls.h"
 #include "xalloc.h"
@@ -83,6 +87,8 @@ enum {
        COL_USER,
        COL_NETNSID,
        COL_NSFS,
+       COL_PNS,                /* parent namespace */
+       COL_ONS,                /* owner namespace */
 };
 
 /* column names */
@@ -106,7 +112,9 @@ static const struct colinfo infos[] = {
        [COL_UID]     = { "UID",     0, SCOLS_FL_RIGHT, N_("UID of the PID"), SCOLS_JSON_NUMBER},
        [COL_USER]    = { "USER",    0, 0, N_("username of the PID")},
        [COL_NETNSID] = { "NETNSID", 0, SCOLS_FL_RIGHT, N_("namespace ID as used by network subsystem")},
-       [COL_NSFS]    = { "NSFS",    0, SCOLS_FL_WRAP, N_("nsfs mountpoint (usually used network subsystem)")}
+       [COL_NSFS]    = { "NSFS",    0, SCOLS_FL_WRAP, N_("nsfs mountpoint (usually used network subsystem)")},
+       [COL_PNS]     = { "PNS",   10, SCOLS_FL_RIGHT, N_("parent namespace identifier (inode number)"), SCOLS_JSON_NUMBER },
+       [COL_ONS]     = { "ONS",   10, SCOLS_FL_RIGHT, N_("owner namespace identifier (inode number)"), SCOLS_JSON_NUMBER },
 };
 
 static int columns[ARRAY_SIZE(infos) * 2];
@@ -139,6 +147,8 @@ struct lsns_namespace {
        int type;                       /* LSNS_* */
        int nprocs;
        int netnsid;
+       ino_t parentid;
+       ino_t ownerid;
 
        struct lsns_process *proc;
 
@@ -154,6 +164,9 @@ struct lsns_process {
        uid_t uid;
 
        ino_t            ns_ids[ARRAY_SIZE(ns_names)];
+       ino_t            ns_pids[ARRAY_SIZE(ns_names)];
+       ino_t            ns_oids[ARRAY_SIZE(ns_names)];
+
        struct list_head ns_siblings[ARRAY_SIZE(ns_names)];
 
        struct list_head processes;     /* list of processes */
@@ -251,7 +264,7 @@ static inline const struct colinfo *get_column_info(unsigned num)
        return &infos[ get_column_id(num) ];
 }
 
-static int get_ns_ino(int dir, const char *nsname, ino_t *ino)
+static int get_ns_ino(int dir, const char *nsname, ino_t *ino, ino_t *pino, ino_t *oino)
 {
        struct stat st;
        char path[16];
@@ -261,6 +274,47 @@ static int get_ns_ino(int dir, const char *nsname, ino_t *ino)
        if (fstatat(dir, path, &st, 0) != 0)
                return -errno;
        *ino = st.st_ino;
+
+       *pino = 0;
+       *oino = 0;
+
+#ifdef HAVE_LINUX_NSFS_H
+       int fd, pfd, ofd;
+       fd = openat(dir, path, 0);
+       if (fd < 0)
+               return -errno;
+       if (strcmp(nsname, "pid") == 0 || strcmp(nsname, "user") == 0) {
+               if ((pfd = ioctl(fd, NS_GET_PARENT)) < 0) {
+                       if (errno == EPERM)
+                               goto user;
+                       close(fd);
+                       return -errno;
+               }
+               if (fstat(pfd, &st) < 0) {
+                       close(pfd);
+                       close(fd);
+                       return -errno;
+               }
+               *pino = st.st_ino;
+               close(pfd);
+       }
+ user:
+       if ((ofd = ioctl(fd, NS_GET_USERNS)) < 0) {
+               if (errno == EPERM)
+                       goto out;
+               close(fd);
+               return -errno;
+       }
+       if (fstat(ofd, &st) < 0) {
+               close(ofd);
+               close(fd);
+               return -errno;
+       }
+       *oino = st.st_ino;
+       close(ofd);
+ out:
+       close(fd);
+#endif
        return 0;
 }
 
@@ -466,7 +520,8 @@ static int read_process(struct lsns *ls, pid_t pid)
                if (!ls->fltr_types[i])
                        continue;
 
-               rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i]);
+               rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i],
+                               &p->ns_pids[i], &p->ns_oids[i]);
                if (rc && rc != -EACCES && rc != -ENOENT)
                        goto done;
                if (i == LSNS_ID_NET)
@@ -538,7 +593,8 @@ static int namespace_has_process(struct lsns_namespace *ns, pid_t pid)
        return 0;
 }
 
-static struct lsns_namespace *add_namespace(struct lsns *ls, int type, ino_t ino)
+static struct lsns_namespace *add_namespace(struct lsns *ls, int type, ino_t ino,
+                                           ino_t parent_ino, ino_t owner_ino)
 {
        struct lsns_namespace *ns = xcalloc(1, sizeof(*ns));
 
@@ -552,6 +608,8 @@ static struct lsns_namespace *add_namespace(struct lsns *ls, int type, ino_t ino
 
        ns->type = type;
        ns->id = ino;
+       ns->parentid = parent_ino;
+       ns->ownerid = owner_ino;
 
        list_add_tail(&ns->namespaces, &ls->namespaces);
        return ns;
@@ -617,7 +675,8 @@ static int read_namespaces(struct lsns *ls)
                        if (proc->ns_ids[i] == 0)
                                continue;
                        if (!(ns = get_namespace(ls, proc->ns_ids[i]))) {
-                               ns = add_namespace(ls, i, proc->ns_ids[i]);
+                               ns = add_namespace(ls, i, proc->ns_ids[i],
+                                                  proc->ns_pids[i], proc->ns_oids[i]);
                                if (!ns)
                                        return -ENOMEM;
                        }
@@ -761,6 +820,12 @@ static void add_scols_line(struct lsns *ls, struct libscols_table *table,
                case COL_NSFS:
                        nsfs_xasputs(&str, ns, ls->tab, ls->no_wrap ? ',' : '\n');
                        break;
+               case COL_PNS:
+                       xasprintf(&str, "%ju", (uintmax_t)ns->parentid);
+                       break;
+               case COL_ONS:
+                       xasprintf(&str, "%ju", (uintmax_t)ns->ownerid);
+                       break;
                default:
                        break;
                }
index e09fe5e40fb3cce753d95f607e90c581d1d6e7e4..4b0e2f15a2aae58be4891cf73fe2e28997443565 100644 (file)
@@ -103,6 +103,7 @@ TS_CMD_SWAPOFF=${TS_CMD_SWAPOFF:-"${ts_commandsdir}swapoff"}
 TS_CMD_SWAPON=${TS_CMD_SWAPON:-"${ts_commandsdir}swapon"}
 TS_CMD_UL=${TS_CMD_UL-"${ts_commandsdir}ul"}
 TS_CMD_UMOUNT=${TS_CMD_UMOUNT:-"${ts_commandsdir}umount"}
+TS_CMD_UNSHARE=${TS_CMD_UNSHARE:-"${ts_commandsdir}unshare"}
 TS_CMD_UTMPDUMP=${TS_CMD_UTMPDUMP-"${ts_commandsdir}utmpdump"}
 TS_CMD_UUIDD=${TS_CMD_UUIDD-"${ts_commandsdir}uuidd"}
 TS_CMD_UUIDGEN=${TS_CMD_UUIDGEN-"${ts_commandsdir}uuidgen"}
diff --git a/tests/expected/lsns/ioctl_ns b/tests/expected/lsns/ioctl_ns
new file mode 100644 (file)
index 0000000..573541a
--- /dev/null
@@ -0,0 +1 @@
+0
diff --git a/tests/ts/lsns/ioctl_ns b/tests/ts/lsns/ioctl_ns
new file mode 100644 (file)
index 0000000..8adff08
--- /dev/null
@@ -0,0 +1,108 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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="namespace ownership and hierarchy"
+
+. $TS_TOPDIR/functions.sh
+ts_init "$*"
+
+# ts_skip_nonroot
+grep -q '#define HAVE_LINUX_NSFS_H' ${top_builddir}/config.h || ts_skip "no ioctl_ns support"
+
+ts_check_test_command "$TS_CMD_LSNS"
+ts_check_test_command "$TS_CMD_UNSHARE"
+ts_check_prog "stat"
+ts_check_prog "mkfifo"
+ts_check_prog "touch"
+ts_check_prog "uniq"
+
+ts_cd "$TS_OUTDIR"
+
+# The parent process receives namespaces ids via FIFO_DATA from bash
+# run by unshare. The parent can wait till the bash process provides
+# data.
+FIFO_DATA=$TS_OUTDIR/FIFO_DATA-HIERARCHY
+
+# The bash process run by unshare waits with this fifo till the parent
+# process exits.  As a result, namespaecs referred by the bash
+# process can be alive till the parent process exits.
+FIFO_WAIT=$TS_OUTDIR/FIFO_WAIT-HIERARCHY
+
+function cleanup {
+    rm -f $FIFO_DATA
+    rm -f $FIFO_WAIT
+}
+
+function init {
+    cleanup
+    mkfifo $FIFO_DATA
+    mkfifo $FIFO_WAIT
+}
+
+init
+(
+    exec 4> $FIFO_WAIT
+    my_userns=$(stat -c %i -L /proc/self/ns/user)
+    my_pidns=$(stat -c %i -L /proc/self/ns/pid)
+    read child_userns < $FIFO_DATA
+    read child_pidns  < $FIFO_DATA
+
+    expected="$child_userns $my_userns $my_userns"
+    actual=$("$TS_CMD_LSNS" -t user -n -o NS,PNS,ONS "$child_userns" | uniq)
+    test "$expected" = "$actual"
+    RESULT=$?
+    if [ $RESULT -ne 0 ]; then
+       echo
+       echo userns expected: "$expected"
+       echo userns actual:   "$actual"
+       cleanup
+       exit $RESULT
+    fi
+
+    expected="$child_pidns $my_pidns $child_userns"
+    actual=$("$TS_CMD_LSNS" -t pid -n -o NS,PNS,ONS "$child_pidns" | uniq)
+    test "$expected" = "$actual"
+    RESULT=$?
+    if [ $RESULT -ne 0 ]; then
+       echo
+       echo pidns expected: "$expected"
+       echo pidns actual:   "$actual"
+       cleanup
+    fi
+    exit $RESULT
+) &
+mainpid=$!
+(
+    exec 4< $FIFO_WAIT
+    $TS_CMD_UNSHARE --user --pid --mount-proc --fork bash -s > $FIFO_DATA <<'EOF'
+    stat -c %i -L /proc/self/ns/user
+    stat -c %i -L /proc/self/ns/pid
+    # Wait till FIFO_WAIT is clsoed.
+    read -u 4
+EOF
+) &
+subpid=$!
+
+wait $subpid
+wait $mainpid
+
+RESULT=$?
+echo $RESULT >> $TS_OUTPUT
+
+cleanup
+ts_finalize