lsns: interpolate missing namespaces for converting forests to a tree

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>
This commit is contained in:
Masatake YAMATO 2021-04-29 05:49:33 +09:00 committed by Karel Zak
parent c67b83c10d
commit de72df79d7
2 changed files with 141 additions and 1 deletions

View 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*::

View 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);
}
}