]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - sys-utils/lsns.c
lsns: fill the netsid member of lsns_process with reliable value
[thirdparty/util-linux.git] / sys-utils / lsns.c
index c328f046e168e5cbaefd3dce423acfeecfbe1145..0ab5e26a3079f743c5f2cc4474e95eb48ad2b85b 100644 (file)
@@ -218,6 +218,7 @@ struct lsns {
                     no_headings: 1,
                     no_wrap    : 1;
 
+       dev_t nsfs_dev;
 
        struct libmnt_table *tab;
        struct libscols_filter *filter;
@@ -304,6 +305,7 @@ static int get_ns_ino(struct path_cxt *pc, const char *nsname, ino_t *ino, ino_t
 
        snprintf(path, sizeof(path), "ns/%s", nsname);
 
+       *ino = 0;
        if (ul_path_stat(pc, &st, 0, path) != 0)
                return -errno;
        *ino = st.st_ino;
@@ -318,7 +320,11 @@ static int get_ns_ino(struct path_cxt *pc, const char *nsname, ino_t *ino, ino_t
                return -errno;
        if (strcmp(nsname, "pid") == 0 || strcmp(nsname, "user") == 0) {
                if ((pfd = lsns_ioctl(fd, NS_GET_PARENT)) < 0) {
-                       if (errno == EPERM)
+                       if (errno == EPERM
+                           /* On the test platforms, "build (qemu-user, s390x)" and
+                            * "build (qemu-user, riscv64)", the ioctl reported ENOSYS.
+                            */
+                           || errno == ENOSYS)
                                goto user;
                        close(fd);
                        return -errno;
@@ -333,7 +339,11 @@ static int get_ns_ino(struct path_cxt *pc, const char *nsname, ino_t *ino, ino_t
        }
  user:
        if ((ofd = lsns_ioctl(fd, NS_GET_USERNS)) < 0) {
-               if (errno == EPERM)
+               if (errno == EPERM
+                   /* On the test platforms, "build (qemu-user, s390x)" and
+                    * "build (qemu-user, riscv64)", the ioctl reported ENOSYS.
+                    */
+                   || errno == ENOSYS)
                        goto out;
                close(fd);
                return -errno;
@@ -501,6 +511,34 @@ static int get_netnsid(struct path_cxt *pc __attribute__((__unused__)),
 }
 #endif /* HAVE_LINUX_NET_NAMESPACE_H */
 
+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);
+
+               if (ul_path_stat(pc, &st, 0, path) == 0
+                   && 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;
@@ -514,10 +552,14 @@ static int read_process(struct lsns *ls, struct path_cxt *pc)
        if (procfs_process_get_uid(pc, &p->uid) == 0)
                add_uid(uid_cache, p->uid);
 
-       if ((rc = procfs_process_get_stat(pc, buf, sizeof(buf))) < 0)
+       if ((rc = procfs_process_get_stat(pc, buf, sizeof(buf))) < 0) {
+               DBG(PROC, ul_debug("failed in procfs_process_get_stat() (rc: %d)", rc));
                goto done;
-       if ((rc = parse_proc_stat(buf, &p->pid, &p->state, &p->ppid)) < 0)
+       }
+       if ((rc = parse_proc_stat(buf, &p->pid, &p->state, &p->ppid)) < 0) {
+               DBG(PROC, ul_debug("failed in parse_proc_stat() (rc: %d)", rc));
                goto done;
+       }
        rc = 0;
 
        for (i = 0; i < ARRAY_SIZE(p->ns_ids); i++) {
@@ -528,9 +570,11 @@ static int read_process(struct lsns *ls, struct path_cxt *pc)
 
                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)
+               if (rc && rc != -EACCES && rc != -ENOENT) {
+                       DBG(PROC, ul_debug("failed in get_ns_ino (rc: %d)", rc));
                        goto done;
-               if (i == LSNS_ID_NET)
+               }
+               if (p->ns_ids[i] && i == LSNS_ID_NET)
                        p->netnsid = get_netnsid(pc, p->ns_ids[i]);
                rc = 0;
        }
@@ -539,6 +583,8 @@ static int read_process(struct lsns *ls, struct path_cxt *pc)
 
        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 (rc)
                free(p);
@@ -571,13 +617,25 @@ static int read_processes(struct lsns *ls)
                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));
+                       DBG(PROC, ul_debug("failed in initializing path_cxt for /proc/%d (rc: %d)", (int) pid, rc));
+                       /* This failure is acceptable. If a process ($pid) owning
+                        * a namespace is gone while running this lsns process,
+                        * procfs_process_init_path(pc, $pid) may fail.
+                        *
+                        * We must reset this `rc' here. If this `d' is the last
+                        * dentry in `dir', this read_processes() invocation
+                        * returns this `rc'. In the caller context, the
+                        * non-zero value returned from read_processes() makes
+                        * lsns prints nothing. We should avoid the behavior. */
+                       rc = 0;
                        continue;
                }
 
                rc = read_process(ls, pc);
-               if (rc && rc != -EACCES && rc != -ENOENT)
+               if (rc && rc != -EACCES && rc != -ENOENT) {
+                       DBG(PROC, ul_debug("failed in read_process() (pid: %d, rc: %d)", (int) pid, rc));
                        break;
+               }
                rc = 0;
        }
 
@@ -1421,6 +1479,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;
@@ -1607,6 +1675,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);