From: Masatake YAMATO Date: Tue, 6 Apr 2021 19:28:32 +0000 (+0900) Subject: lsns: add columns for parent namespaces and owner namespaces X-Git-Tag: v2.37-rc2~70^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d652d4c666d61bdd4b5f44d2d377d35ab51b6c9a;p=thirdparty%2Futil-linux.git lsns: add columns for parent namespaces and owner namespaces See ioctl_ns(2) about parent and owner namespaces. Signed-off-by: Masatake YAMATO --- diff --git a/configure.ac b/configure.ac index bebb408542..abb96e6280 100644 --- a/configure.ac +++ b/configure.ac @@ -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 \ diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c index eec8e273b3..8e25ff1831 100644 --- a/sys-utils/lsns.c +++ b/sys-utils/lsns.c @@ -38,6 +38,10 @@ #include #endif +#ifdef HAVE_LINUX_NSFS_H +#include +#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; } diff --git a/tests/commands.sh b/tests/commands.sh index e09fe5e40f..4b0e2f15a2 100644 --- a/tests/commands.sh +++ b/tests/commands.sh @@ -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 index 0000000000..573541ac97 --- /dev/null +++ b/tests/expected/lsns/ioctl_ns @@ -0,0 +1 @@ +0 diff --git a/tests/ts/lsns/ioctl_ns b/tests/ts/lsns/ioctl_ns new file mode 100644 index 0000000000..8adff080dc --- /dev/null +++ b/tests/ts/lsns/ioctl_ns @@ -0,0 +1,108 @@ +#!/bin/bash +# +# Copyright (C) 2021 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="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