diff options
author | Karel Zak <kzak@redhat.com> | 2024-04-08 09:41:03 +0200 |
---|---|---|
committer | Karel Zak <kzak@redhat.com> | 2024-04-08 09:41:03 +0200 |
commit | 5ce970df73acacbc56f2eb9152ccc56e43fd0f3e (patch) | |
tree | b983e21cdf6d9cf526566668015d58f41de715f7 | |
parent | b1a76c226b276f49c6a7e74082a23f06f5b99966 (diff) | |
parent | 7d5036fdafe0498a09146a5637a9f7f8351e6e72 (diff) | |
download | util-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.c | 126 | ||||
-rwxr-xr-x | tests/ts/lsns/filedesc | 71 |
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 |