lsns: print namespace tree based on the relationship (parent/child or owner/owned)

Introduce new option -T[parent|owner]|--nstree[=parent|=owner].

With this change, lsns prints parent/child relationship tree if
"-T parent" is given and owner/owned relationship tree if "-T owner is given.

Passing only "-T" is same as passing "-Towner."

Example sessions:

    # ./lsns -Tparent -ons,type,pns| head -20
    NS             TYPE          PNS
    4026531837     user            0
    ├─4026532508   user   4026531837
    ├─4026532609   user   4026531837
    ├─4026532610   user   4026531837
    ├─4026532629   user   4026531837
    ├─4026532705   user   4026531837
    ├─4026532901   user   4026531837
    ├─4026533090   user   4026531837
    ├─4026533185   user   4026531837
    ├─4026533280   user   4026531837
    └─4026533468   user   4026531837
    4026531835     cgroup          0
    4026531836     pid             0
    └─4026533038   pid    4026531836
      ├─4026532934 pid    4026533038
      ├─4026533715 pid    4026533038
      ├─4026533716 pid    4026533038
      ...

    # ./lsns -Towner -ons,type,ons| head -20
    NS             TYPE          ONS
    4026531837     user            0
    ├─4026531835   cgroup 4026531837
    ├─4026531836   pid    4026531837
    ├─4026531838   uts    4026531837
    ├─4026531839   ipc    4026531837
    ├─4026531840   mnt    4026531837
    ├─4026531861   mnt    4026531837
    ├─4026532001   net    4026531837
    ├─4026532219   mnt    4026531837
    ├─4026532357   mnt    4026531837
    ├─4026532383   net    4026531837
    ├─4026532475   mnt    4026531837
    ├─4026532476   mnt    4026531837
    ├─4026532504   mnt    4026531837
    ├─4026532508   user   4026531837
    │ ├─4026532509 ipc    4026532508
    │ └─4026532511 net    4026532508
    ├─4026532573   mnt    4026531837
    ├─4026532574   mnt    4026531837

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
This commit is contained in:
Masatake YAMATO 2021-04-15 18:03:57 +09:00 committed by Karel Zak
parent aa049eabb3
commit e8c56ae2d7
2 changed files with 90 additions and 5 deletions

View File

@ -63,6 +63,12 @@ Do not truncate text in columns.
*-W*, *--nowrap*::
Do not use multi-line text in columns.
*-T*, *--nstree* _rel_::
Print namespace tree(s).
If *parent* is given as _rel_, print tree(s) constructed by the parent/child relationship.
If *owner* is given, print tree(s) constructed by the owner/owned relationship.
The result can be forests because *lsns* cannot track a namespace having no process.
*-V*, *--version*::
Display version information and exit.

View File

@ -152,6 +152,10 @@ struct lsns_namespace {
struct lsns_process *proc;
struct lsns_namespace *parentns;
struct lsns_namespace *ownerns;
struct libscols_line *ns_outline;
struct list_head namespaces; /* lsns->processes member */
struct list_head processes; /* head of lsns_process *siblings */
};
@ -177,6 +181,11 @@ struct lsns_process {
int netnsid;
};
enum {
LSNS_NSTREE_OWNER = 1,
LSNS_NSTREE_PARENT,
};
struct lsns {
struct list_head processes;
struct list_head namespaces;
@ -192,7 +201,9 @@ struct lsns {
list : 1,
no_trunc : 1,
no_headings: 1,
no_wrap : 1;
no_wrap : 1,
nstree : 2;
struct libmnt_table *tab;
};
@ -684,6 +695,30 @@ static int read_namespaces(struct lsns *ls)
}
}
if (ls->nstree) {
list_for_each(p, &ls->namespaces) {
struct lsns_namespace *ns = list_entry(p, struct lsns_namespace, namespaces);
struct list_head *pp;
list_for_each(pp, &ls->namespaces) {
struct lsns_namespace *pns = list_entry(pp, struct lsns_namespace, namespaces);
if (ns->type == LSNS_ID_USER
|| ns->type == LSNS_ID_PID) {
if (ns->parentid == pns->id)
ns->parentns = pns;
if (ns->ownerid == pns->id)
ns->ownerns = pns;
if (ns->parentns && ns->ownerns)
break;
} else {
if (ns->ownerid == pns->id) {
ns->ownerns = pns;
break;
}
}
}
}
}
list_sort(&ls->namespaces, cmp_namespaces, NULL);
return 0;
@ -774,7 +809,10 @@ static void add_scols_line(struct lsns *ls, struct libscols_table *table,
assert(table);
line = scols_table_new_line(table,
ls->tree && proc->parent ? proc->parent->outline : NULL);
ls->tree && proc->parent ? proc->parent->outline:
ls->nstree == LSNS_NSTREE_PARENT && ns->parentns ? ns->parentns->ns_outline:
ls->nstree == LSNS_NSTREE_OWNER && ns->ownerns ? ns->ownerns->ns_outline:
NULL);
if (!line) {
warn(_("failed to add line to output"));
return;
@ -834,7 +872,10 @@ static void add_scols_line(struct lsns *ls, struct libscols_table *table,
err_oom();
}
proc->outline = line;
if (ls->nstree)
ns->ns_outline = line;
else
proc->outline = line;
}
static struct libscols_table *init_scols_table(struct lsns *ls)
@ -866,6 +907,10 @@ static struct libscols_table *init_scols_table(struct lsns *ls)
flags |= SCOLS_FL_TREE;
if (ls->no_wrap)
flags &= ~SCOLS_FL_WRAP;
if (ls->nstree && get_column_id(i) == COL_NS) {
flags |= SCOLS_FL_TREE;
flags &= ~SCOLS_FL_RIGHT;
}
cl = scols_table_new_column(tab, col->name, col->whint, flags);
if (cl == NULL) {
@ -890,6 +935,28 @@ err:
return NULL;
}
static void show_namespace(struct lsns *ls, struct libscols_table *tab,
struct lsns_namespace *ns, struct lsns_process *proc)
{
/*
* create a tree from owner->owned and/or parent->child relation
*/
if (ls->nstree == LSNS_NSTREE_OWNER
&& ns->ownerns
&& !ns->ownerns->ns_outline)
show_namespace(ls, tab, ns->ownerns, proc);
else if (ls->nstree == LSNS_NSTREE_PARENT) {
if (ns->parentns) {
if (!ns->parentns->ns_outline)
show_namespace(ls, tab, ns->parentns, proc);
}
else if (ns->ownerns && !ns->ownerns->ns_outline)
show_namespace(ls, tab, ns->ownerns, proc);
}
add_scols_line(ls, tab, ns, proc);
}
static int show_namespaces(struct lsns *ls)
{
struct libscols_table *tab;
@ -906,7 +973,8 @@ static int show_namespaces(struct lsns *ls)
if (ls->fltr_pid != 0 && !namespace_has_process(ns, ls->fltr_pid))
continue;
add_scols_line(ls, tab, ns, ns->proc);
if (!ns->ns_outline)
show_namespace(ls, tab, ns, ns->proc);
}
scols_print_table(tab);
@ -977,6 +1045,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -u, --notruncate don't truncate text in columns\n"), out);
fputs(_(" -W, --nowrap don't use multi-line representation\n"), out);
fputs(_(" -t, --type <name> namespace type (mnt, net, ipc, user, pid, uts, cgroup, time)\n"), out);
fputs(_(" -T, --nstree <rel> print namespace tree based on the relationship (parent or owner)\n"), out);
fputs(USAGE_SEPARATOR, out);
printf(USAGE_HELP_OPTIONS(24));
@ -1013,6 +1082,7 @@ int main(int argc, char *argv[])
{ "list", no_argument, NULL, 'l' },
{ "raw", no_argument, NULL, 'r' },
{ "type", required_argument, NULL, 't' },
{ "nstree", optional_argument, NULL, 'T' },
{ NULL, 0, NULL, 0 }
};
@ -1036,7 +1106,7 @@ int main(int argc, char *argv[])
INIT_LIST_HEAD(&netnsids_cache);
while ((c = getopt_long(argc, argv,
"Jlp:o:nruhVt:W", long_opts, NULL)) != -1) {
"Jlp:o:nruhVt:T::W", long_opts, NULL)) != -1) {
err_exclusive_options(c, long_opts, excl, excl_st);
@ -1080,6 +1150,15 @@ int main(int argc, char *argv[])
case 'W':
ls.no_wrap = 1;
break;
case 'T':
ls.nstree = LSNS_NSTREE_OWNER;
if (optarg) {
if (strcmp (optarg, "parent") == 0)
ls.nstree = LSNS_NSTREE_PARENT;
else if (strcmp (optarg, "owner") != 0)
errx(EXIT_FAILURE, _("unknown nstree type: %s"), optarg);
}
break;
case 'h':
usage();