]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsns: interpolate missing namespaces for converting forests to a tree
authorMasatake YAMATO <yamato@redhat.com>
Wed, 28 Apr 2021 20:49:33 +0000 (05:49 +0900)
committerKarel Zak <kzak@redhat.com>
Mon, 24 May 2021 11:39:53 +0000 (13:39 +0200)
The tree of *parent* and *owner* could be forests because *lsns*
cannot track a namespace having no process.

This change tries interpolating the missing namespaces by calling
ioctl(NS_GET_PARENT) and ioctl(NS_GET_USERNS) recursively.

The original output for -Tparent:

    # ./lsns -Tparent
    NS             TYPE   NPROCS   PID USER   COMMAND
    4026531837     user      404     1 root   /usr/lib/systemd/sy
    ├─4026532508   user        1 29376 yamato /usr/lib64/firefox/
    ...
    └─4026533513   user        1 24245 yamato /usr/lib64/firefox/
    ...
    4026533733     user        1 30839 yamato /opt/google/chrome-
    4026533734     user       15 10076 yamato /opt/google/chrome-

user namespaces 4026533733 and 4026533734 are orphans.
lsns could not find their parents.

With this change:

    # ./lsns-with-changes -Tparent
    NS               TYPE   NPROCS   PID USER   COMMAND
    4026531837       user      404     1 root   /usr/lib/systemd/
    ├─4026532508     user        1 29376 yamato /usr/lib64/firefo
    ...
    ├─4026532639     user        0
    │ ├─4026532637   user        0
    │ │ └─4026533733 user        1 30839 yamato /opt/google/chrom
    │ └─4026533734   user       14 10076 yamato /opt/google/chrom

Now user namespaces 4026533733 and 4026533734 are integrated to the
tree. lsns interpolates the missing namespace 4026532639 and
4026532637 for the integration.

The original output for -Towner:

    # ./lsns -Towner
    NS             TYPE   NPROCS   PID USER   COMMAND
    4026531837     user      405     1 root   /usr/lib/systemd/s
    ├─4026531835   cgroup    431     1 root   /usr/lib/systemd/s
    ...
    4026532638     pid         1 30839 yamato /opt/google/chrome
    4026532640     pid         2 30837 yamato /opt/google/chrome
    ...

pid namespaces 4026532638 and 4026532640 are orphans.
lsns could not find their owners.

With this change:

    # ./lsns-with-changes -Towner
    NS               TYPE   NPROCS   PID USER   COMMAND
    4026531837       user      403     1 root   /usr/lib/systemd
    ├─4026531835     cgroup    429     1 root   /usr/lib/systemd
    ...
    ├─4026532639     user        0
    ...
    │ ├─4026532637   user        0
    │ │ ├─4026532638 pid         1 30839 yamato /opt/google/chro
    │ │ ├─4026533638 net         1 30839 yamato /opt/google/chro
    │ │ └─4026533733 user        1 30839 yamato /opt/google/chro
    │ ├─4026532640   pid         2 30837 yamato /opt/google/chro

Now pid namespaces 4026532638 and 4026532640 are integrated to the
tree. lsns interpolates the missing namespace 4026532639 and
4026532637 for the integration.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
sys-utils/lsns.8.adoc
sys-utils/lsns.c

index 738f8710616ecbef7e0c570f1d4185cb811212c6..651d3fe9e879fb8016c8989e781e33f3d9633f56 100644 (file)
@@ -69,7 +69,6 @@ If *process* is given as _rel_, print proecss tree(s) in each name space. This i
 If *parent* is given, print tree(s) constructed by the parent/child relationship.
 If *owner* is given, print tree(s) constructed by the owner/owned relationship.
 *owner* is used as default when _rel_ is omitted.
-The result of *parent* and *owner* can be forests because *lsns* cannot track a namespace having no process.
 
 
 *-V*, *--version*::
index a09c84dfb3f4d358e163cb08e3ba3e7e05209b54..8905a13bd1d954e322f60fed6f994ace10937208 100644 (file)
@@ -676,9 +676,124 @@ static int netnsid_xasputs(char **str, int netnsid)
        return 0;
 }
 
+static int clone_type_to_lsns_type(int clone_type)
+{
+       switch (clone_type) {
+       case CLONE_NEWNS:
+               return LSNS_ID_MNT;
+       case CLONE_NEWCGROUP:
+               return LSNS_ID_CGROUP;
+       case CLONE_NEWUTS:
+               return LSNS_ID_UTS;
+       case CLONE_NEWIPC:
+               return LSNS_ID_IPC;
+       case CLONE_NEWUSER:
+               return LSNS_ID_USER;
+       case CLONE_NEWPID:
+               return LSNS_ID_PID;
+       case CLONE_NEWNET:
+               return LSNS_ID_NET;
+       default:
+               return -1;
+       }
+}
+
+static struct lsns_namespace *add_namespace_for_nsfd(struct lsns *ls, int fd, ino_t ino)
+{
+       int fd_owner = -1, fd_parent = -1;
+       struct stat st_owner, st_parent;
+       ino_t ino_owner = 0, ino_parent = 0;
+       struct lsns_namespace *ns;
+       int clone_type, lsns_type;
+
+       clone_type = ioctl(fd, NS_GET_NSTYPE);
+       if (clone_type < 0)
+               return NULL;
+       lsns_type = clone_type_to_lsns_type(clone_type);
+       if (lsns_type < 0)
+               return NULL;
+
+       fd_owner = ioctl(fd, NS_GET_USERNS);
+       if (fd_owner < 0)
+               goto parent;
+       if (fstat(fd_owner, &st_owner) < 0)
+               goto parent;
+       ino_owner = st_owner.st_ino;
+
+ parent:
+       fd_parent = ioctl(fd, NS_GET_PARENT);
+       if (fd_parent < 0)
+               goto add_ns;
+       if (fstat(fd_parent, &st_parent) < 0)
+               goto add_ns;
+       ino_parent = st_parent.st_ino;
+
+ add_ns:
+       ns = add_namespace(ls, lsns_type, ino, ino_parent, ino_owner);
+
+       if ((lsns_type == LSNS_ID_USER || lsns_type == LSNS_ID_PID)
+           && ino_parent != ino && ino_parent != 0) {
+               ns->related_ns[RELA_PARENT] = get_namespace(ls, ino_parent);
+               if (!ns->related_ns[RELA_PARENT]) {
+                       ns->related_ns[RELA_PARENT] = add_namespace_for_nsfd(ls, fd_parent, ino_parent);
+                       if (ino_parent == ino_owner)
+                               ns->related_ns[RELA_OWNER] = ns->related_ns[RELA_PARENT];
+               }
+       }
+
+       if (ns->related_ns[RELA_OWNER] == NULL && ino_owner != 0) {
+               ns->related_ns[RELA_OWNER] = get_namespace(ls, ino_owner);
+               if (!ns->related_ns[RELA_OWNER])
+                       ns->related_ns[RELA_OWNER] = add_namespace_for_nsfd(ls, fd_owner, ino_owner);
+       }
+
+       if (fd_owner >= 0)
+               close(fd_owner);
+       if (fd_parent >= 0)
+               close(fd_parent);
+
+       return ns;
+}
+
+static void interpolate_missing_namespaces (struct lsns *ls, struct lsns_namespace *orphan, int rela)
+{
+       const int cmd[MAX_RELA] = {
+               [RELA_PARENT] = NS_GET_PARENT,
+               [RELA_OWNER] = NS_GET_USERNS
+       };
+       char buf[BUFSIZ];
+       int fd_orphan, fd_missing;
+       struct stat st;
+
+       orphan->related_ns[rela] = get_namespace(ls, orphan->related_id[rela]);
+       if (orphan->related_ns[rela])
+               return;
+
+       snprintf(buf, sizeof(buf), "/proc/%d/ns/%s", orphan->proc->pid, ns_names [orphan->type]);
+       fd_orphan = open(buf, O_RDONLY);
+       if (fd_orphan < 0)
+               return;
+
+       fd_missing = ioctl (fd_orphan, cmd[rela]);
+       close (fd_orphan);
+       if (fd_missing < 0)
+               return;
+
+       if (fstat(fd_missing, &st) < 0
+           || st.st_ino != orphan->related_id[rela]) {
+               close (fd_missing);
+               return;
+       }
+
+       orphan->related_ns[rela] = add_namespace_for_nsfd(ls, fd_missing, orphan->related_id[rela]);
+       close (fd_missing);
+}
+
 static int read_namespaces(struct lsns *ls)
 {
        struct list_head *p;
+       struct lsns_namespace *orphan[2] = {NULL, NULL};
+       int rela;
 
        DBG(NS, ul_debug("reading namespace"));
 
@@ -721,6 +836,32 @@ static int read_namespaces(struct lsns *ls)
                                        }
                                }
                        }
+
+                       /* lsns scans /proc/[0-9]+ for finding namespaces.
+                        * So if a namespace has no process, lsns cannot
+                        * find it. Here we call it a missing namespace.
+                        *
+                        * If the id for a related namesspce is known but
+                        * namespace for the id is not found, there must
+                        * be orphan namespaces. A missing namespace is an
+                        * owner or a parent of the orphan namespace.
+                        */
+                       for (rela = 0; rela < MAX_RELA; rela++) {
+                               if (ns->related_id[rela] != 0
+                                   && ns->related_ns[rela] == NULL) {
+                                       ns->related_ns[rela] = orphan[rela];
+                                       orphan[rela] = ns;
+                               }
+                       }
+               }
+       }
+
+       for (rela = 0; rela < MAX_RELA; rela++) {
+               while (orphan[rela]) {
+                       struct lsns_namespace *current = orphan[rela];
+                       orphan[rela] = orphan[rela]->related_ns[rela];
+                       current->related_ns[rela] = NULL;
+                       interpolate_missing_namespaces (ls, current, rela);
                }
        }