aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarel Zak <kzak@redhat.com>2024-04-08 09:41:03 +0200
committerKarel Zak <kzak@redhat.com>2024-04-08 09:41:03 +0200
commit5ce970df73acacbc56f2eb9152ccc56e43fd0f3e (patch)
treeb983e21cdf6d9cf526566668015d58f41de715f7
parentb1a76c226b276f49c6a7e74082a23f06f5b99966 (diff)
parent7d5036fdafe0498a09146a5637a9f7f8351e6e72 (diff)
downloadutil-linux-5ce970df73acacbc56f2eb9152ccc56e43fd0f3e.tar.gz
Merge branch 'lsns--opened-ns' of https://github.com/masatake/util-linux
* 'lsns--opened-ns' of https://github.com/masatake/util-linux: lsns: show namespaces only kept alive by open file descriptors lsns: (refactor) use ul_new_path and procfs_process_init_path
-rw-r--r--sys-utils/lsns.c126
-rwxr-xr-xtests/ts/lsns/filedesc71
2 files changed, 146 insertions, 51 deletions
diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c
index f444f5e1a5..9d12ca205b 100644
--- a/sys-utils/lsns.c
+++ b/sys-utils/lsns.c
@@ -218,6 +218,7 @@ struct lsns {
no_headings: 1,
no_wrap : 1;
+ dev_t nsfs_dev;
struct libmnt_table *tab;
struct libscols_filter *filter;
@@ -297,14 +298,14 @@ 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, ino_t *pino, ino_t *oino)
+static int get_ns_ino(struct path_cxt *pc, const char *nsname, ino_t *ino, ino_t *pino, ino_t *oino)
{
struct stat st;
char path[16];
snprintf(path, sizeof(path), "ns/%s", nsname);
- if (fstatat(dir, path, &st, 0) != 0)
+ if (ul_path_stat(pc, &st, 0, path) != 0)
return -errno;
*ino = st.st_ino;
@@ -313,7 +314,7 @@ static int get_ns_ino(int dir, const char *nsname, ino_t *ino, ino_t *pino, ino_
#ifdef USE_NS_GET_API
int fd, pfd, ofd;
- fd = openat(dir, path, 0);
+ fd = ul_path_open(pc, 0, path);
if (fd < 0)
return -errno;
if (strcmp(nsname, "pid") == 0 || strcmp(nsname, "user") == 0) {
@@ -351,17 +352,11 @@ static int get_ns_ino(int dir, const char *nsname, ino_t *ino, ino_t *pino, ino_
return 0;
}
-static int parse_proc_stat(FILE *fp, pid_t *pid, char *state, pid_t *ppid)
+static int parse_proc_stat(char *line, pid_t *pid, char *state, pid_t *ppid)
{
- char *line = NULL, *p;
- size_t len = 0;
+ char *p;
int rc;
- if (getline(&line, &len, fp) < 0) {
- rc = -errno;
- goto error;
- }
-
p = strrchr(line, ')');
if (p == NULL ||
sscanf(line, "%d (", pid) != 1 ||
@@ -372,7 +367,6 @@ static int parse_proc_stat(FILE *fp, pid_t *pid, char *state, pid_t *ppid)
rc = 0;
error:
- free(line);
return rc;
}
@@ -462,7 +456,7 @@ static int get_netnsid_via_netlink_recv_response(int *netnsid)
return 0;
}
-static int get_netnsid_via_netlink(int dir, const char *path)
+static int get_netnsid_via_netlink(struct path_cxt *pc, const char *path)
{
int netnsid;
int target_fd;
@@ -470,7 +464,7 @@ static int get_netnsid_via_netlink(int dir, const char *path)
if (netlink_fd < 0)
return LSNS_NETNS_UNUSABLE;
- target_fd = openat(dir, path, O_RDONLY);
+ target_fd = ul_path_open(pc, O_RDONLY, path);
if (target_fd < 0)
return LSNS_NETNS_UNUSABLE;
@@ -489,61 +483,68 @@ static int get_netnsid_via_netlink(int dir, const char *path)
return netnsid;
}
-static int get_netnsid(int dir, ino_t netino)
+static int get_netnsid(struct path_cxt *pc, ino_t netino)
{
int netnsid;
if (!netnsid_cache_find(netino, &netnsid)) {
- netnsid = get_netnsid_via_netlink(dir, "ns/net");
+ netnsid = get_netnsid_via_netlink(pc, "ns/net");
netnsid_cache_add(netino, netnsid);
}
return netnsid;
}
#else
-static int get_netnsid(int dir __attribute__((__unused__)),
+static int get_netnsid(struct path_cxt *pc __attribute__((__unused__)),
ino_t netino __attribute__((__unused__)))
{
return LSNS_NETNS_UNUSABLE;
}
#endif /* HAVE_LINUX_NET_NAMESPACE_H */
-static int read_process(struct lsns *ls, pid_t pid)
+static struct lsns_namespace *add_namespace_for_nsfd(struct lsns *ls, int fd, ino_t ino);
+
+static void read_open_ns_inos(struct lsns *ls, struct path_cxt *pc)
+{
+ DIR *sub = NULL;
+ struct dirent *d = NULL;
+ char path[sizeof("fd/") + sizeof(stringify_value(UINT64_MAX))];
+
+ while (ul_path_next_dirent(pc, &sub, "fd", &d) == 0) {
+ uint64_t num;
+ struct stat st;
+
+ if (ul_strtou64(d->d_name, &num, 10) != 0) /* only numbers */
+ continue;
+
+ snprintf(path, sizeof(path), "fd/%ju", (uintmax_t) num);
+ ul_path_stat(pc, &st, 0, path);
+ if (st.st_dev == ls->nsfs_dev) {
+ int fd = ul_path_open(pc, O_RDONLY, path);
+ if (fd >= 0) {
+ add_namespace_for_nsfd(ls, fd, st.st_ino);
+ close(fd);
+ }
+ }
+ }
+}
+
+static int read_process(struct lsns *ls, struct path_cxt *pc)
{
struct lsns_process *p = NULL;
+ int rc = 0;
char buf[BUFSIZ];
- DIR *dir;
- int rc = 0, fd;
- FILE *f = NULL;
size_t i;
- struct stat st;
-
- DBG(PROC, ul_debug("reading %d", (int) pid));
-
- snprintf(buf, sizeof(buf), "/proc/%d", pid);
- dir = opendir(buf);
- if (!dir)
- return -errno;
p = xcalloc(1, sizeof(*p));
p->netnsid = LSNS_NETNS_UNUSABLE;
- if (fstat(dirfd(dir), &st) == 0) {
- p->uid = st.st_uid;
- add_uid(uid_cache, st.st_uid);
- }
+ if (procfs_process_get_uid(pc, &p->uid) == 0)
+ add_uid(uid_cache, p->uid);
- fd = openat(dirfd(dir), "stat", O_RDONLY);
- if (fd < 0) {
- rc = -errno;
+ if ((rc = procfs_process_get_stat(pc, buf, sizeof(buf))) < 0)
goto done;
- }
- if (!(f = fdopen(fd, "r"))) {
- rc = -errno;
- goto done;
- }
- rc = parse_proc_stat(f, &p->pid, &p->state, &p->ppid);
- if (rc < 0)
+ if ((rc = parse_proc_stat(buf, &p->pid, &p->state, &p->ppid)) < 0)
goto done;
rc = 0;
@@ -553,12 +554,12 @@ 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(pc, 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)
- p->netnsid = get_netnsid(dirfd(dir), p->ns_ids[i]);
+ p->netnsid = get_netnsid(pc, p->ns_ids[i]);
rc = 0;
}
@@ -566,10 +567,9 @@ static int read_process(struct lsns *ls, pid_t pid)
DBG(PROC, ul_debugobj(p, "new pid=%d", p->pid));
list_add_tail(&p->processes, &ls->processes);
+
+ read_open_ns_inos(ls, pc);
done:
- if (f)
- fclose(f);
- closedir(dir);
if (rc)
free(p);
return rc;
@@ -580,6 +580,7 @@ static int read_processes(struct lsns *ls)
DIR *dir;
struct dirent *d;
int rc = 0;
+ struct path_cxt *pc;
DBG(PROC, ul_debug("opening /proc"));
@@ -587,20 +588,31 @@ static int read_processes(struct lsns *ls)
if (!dir)
return -errno;
+ pc = ul_new_path(NULL);
+ if (!pc)
+ err(EXIT_FAILURE, _("failed to alloc procfs handler"));
+
while ((d = xreaddir(dir))) {
pid_t pid = 0;
if (procfs_dirent_get_pid(d, &pid) != 0)
continue;
- /* TODO: use ul_new_procfs_path(pid, NULL) to read files from /proc/pid/
- */
- rc = read_process(ls, pid);
+ DBG(PROC, ul_debug("reading %d", (int) pid));
+ rc = procfs_process_init_path(pc, pid);
+ if (rc < 0) {
+ DBG(PROC, ul_debug("failed in reading /proc/%d", (int) pid));
+ continue;
+ }
+
+ rc = read_process(ls, pc);
if (rc && rc != -EACCES && rc != -ENOENT)
break;
rc = 0;
}
+ ul_unref_path(pc);
+
DBG(PROC, ul_debug("closing /proc"));
closedir(dir);
return rc;
@@ -1439,6 +1451,16 @@ static void __attribute__((__noreturn__)) list_colunms(bool raw, bool json)
exit(EXIT_SUCCESS);
}
+static dev_t read_nsfs_dev(void)
+{
+ struct stat st;
+
+ if (stat("/proc/self/ns/user", &st) < 0)
+ err(EXIT_FAILURE, _("failed to do stat /proc/self/ns/user"));
+
+ return st.st_dev;
+}
+
int main(int argc, char *argv[])
{
struct lsns ls;
@@ -1625,6 +1647,8 @@ int main(int argc, char *argv[])
if (!ls.tab)
err(MNT_EX_FAIL, _("failed to parse %s"), _PATH_PROC_MOUNTINFO);
+ ls.nsfs_dev = read_nsfs_dev();
+
r = read_processes(&ls);
if (!r)
r = read_namespaces(&ls);
diff --git a/tests/ts/lsns/filedesc b/tests/ts/lsns/filedesc
new file mode 100755
index 0000000000..285ad6312c
--- /dev/null
+++ b/tests/ts/lsns/filedesc
@@ -0,0 +1,71 @@
+#!/bin/bash
+#
+# Copyright (C) 2024 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.
+#
+
+# This test case is based on the issue (#1884) submitted by @hesch
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="list the namespace pointed by a file descriptor"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_LSNS"
+ts_check_test_command "$TS_CMD_LSFD"
+ts_check_test_command "$TS_HELPER_MKFDS"
+
+ts_check_prog "ip"
+
+ts_skip_nonroot
+
+FD=4
+NS=LSNS-TEST-FILEDESC-NS
+FILE=/run/netns/$NS
+PID=
+
+cleanup()
+{
+ ip netns delete $NS 2> /dev/null || :
+}
+
+cleanup
+
+if ! ip netns add $NS; then
+ ts_skip "failed to make a namespace"
+fi
+trap "cleanup" EXIT
+
+{
+ coproc MKFDS { "$TS_HELPER_MKFDS" ro-regular-file $FD file=$FILE; }
+ if read -u ${MKFDS[0]} PID; then
+ # Make the namespace invisible from the file system tree.
+ cleanup
+ lsfd_expr="PID == ${PID} and FD == $FD"
+ lsfd_inode=$(${TS_CMD_LSFD} -n --raw -o INODE -Q "${lsfd_expr}")
+ lsns_expr="NS == $lsfd_inode"
+ lsns_output=$(${TS_CMD_LSNS} -n --raw -o TYPE,NPROCS,USER -Q "${lsns_expr}")
+ if ! [ "${lsns_output}" == "net 0 root" ]; then
+ echo lsfd_inode: $lsfd_inode
+ echo lsns_output: $lsns_output
+ echo LSFD:
+ ${TS_CMD_LSFD} -Q "PID == $PID"
+ echo LSNS:
+ ${TS_CMD_LSNS}
+ fi
+ echo DONE >&"${MKFDS[1]}"
+ fi
+ wait "${MKFDS_PID}"
+} > $TS_OUTPUT 2>&1
+ts_finalize