From c9f6f3e60d1770a95eb491dd503fdbe881ee8740 Mon Sep 17 00:00:00 2001 From: Aristeu Rozanski Date: Wed, 24 Apr 2013 15:32:35 -0400 Subject: [PATCH 1/3] pstree: introduce support for namespaces Options -N and --ns-sort were added which require one of the namespaces: ipc, mnt, net, pid, user, uts and will show separated trees per namespace Signed-off-by: Aristeu Rozanski Signed-off-by: Craig Small --- doc/pstree.1 | 6 ++ src/pstree.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 173 insertions(+), 3 deletions(-) --- psmisc-22.20.orig/doc/pstree.1 2012-09-19 06:54:03.000000000 -0400 +++ psmisc-22.20/doc/pstree.1 2013-09-17 16:26:00.210118465 -0400 @@ -18,6 +18,7 @@ pstree \- display a tree of processes .RB [ \-g ] \ \-\-show\-pgids ] .RB [ \-l , \ \-\-long ] .RB [ \-n , \ \-\-numeric\-sort ] +.RB [ \-N , \ \-\-ns\-sort \fIns\fB .RB [ \-p , \ \-\-show\-pids ] .RB [ \-s , \ \-\-show\-parents ] .RB [ \-u , \ \-\-uid\-changes ] @@ -121,6 +122,11 @@ unknown. .IP \fB\-n\fP Sort processes with the same ancestor by PID instead of by name. (Numeric sort.) +.IP \fB\-N\fP +Show individual trees for each namespace of the type specified. The +available types are: ipc, mnt, net, pid, user, uts. Regular users don't +have access to other users' processes information, so the output will be +limited. .IP \fB\-p\fP Show PIDs. PIDs are shown as decimal numbers in parentheses after each process name. --- psmisc-22.20.orig/src/pstree.c 2013-09-17 16:25:01.000000000 -0400 +++ psmisc-22.20/src/pstree.c 2013-09-17 16:27:12.356037690 -0400 @@ -74,6 +74,8 @@ #define VT_END "\033(B" /* #define VT_UR "m" #define VT_HD "w" +#define NUM_NS 6 + typedef struct _proc { char comm[COMM_LEN + 2 + 1]; /* add another 2 for thread brackets */ char **argv; /* only used : argv[0] is 1st arg; undef if argc < 1 */ @@ -84,6 +86,7 @@ char **argv; /* only #ifdef WITH_SELINUX security_context_t scontext; #endif /*WITH_SELINUX */ + ino_t ns[NUM_NS]; char flags; struct _child *children; struct _proc *parent; @@ -140,6 +143,133 @@ static char last_char = 0; static int dumped = 0; /* used by dump_by_user */ static int charlen = 0; /* length of character */ +enum ns_type { + IPCNS = 0, + MNTNS, + NETNS, + PIDNS, + USERNS, + UTSNS +}; +struct ns_entry; +struct ns_entry { + ino_t number; + CHILD *children; + struct ns_entry *next; +}; + +static const char *ns_names[] = { + [IPCNS] = "ipc", + [MNTNS] = "mnt", + [NETNS] = "net", + [PIDNS] = "pid", + [USERNS] = "user", + [UTSNS] = "uts", +}; + +const char *get_ns_name(int id) { + if (id >= NUM_NS) + return NULL; + return ns_names[id]; +} + +static int get_ns_id(const char *name) { + int i; + + for (i = 0; i < NUM_NS; i++) + if (!strcmp(ns_names[i], name)) + return i; + return -1; +} + +static int verify_ns(int id) +{ + char filename[50]; + struct stat s; + + snprintf(filename, 50, "/proc/%i/ns/%s", getpid(), get_ns_name(id)); + + return stat(filename, &s); +} + +static inline void new_proc_ns(PROC *ns_task) +{ + struct stat st; + char buff[50]; + pid_t pid = ns_task->pid; + int i; + + for (i = 0; i < NUM_NS; i++) { + snprintf(buff, sizeof(buff), "/proc/%i/ns/%s", pid, + get_ns_name(i)); + if (stat(buff, &st)) { + ns_task->ns[i] = 0; + continue; + } + ns_task->ns[i] = st.st_ino; + } +} + +static void find_ns_and_add(struct ns_entry **root, PROC *r, enum ns_type id) +{ + struct ns_entry *ptr, *last = NULL; + CHILD **c; + + for (ptr = *root; ptr; ptr = ptr->next) { + if (ptr->number == r->ns[id]) + break; + last = ptr; + } + + if (!ptr) { + ptr = malloc(sizeof(*ptr)); + memset(ptr, 0, sizeof(*ptr)); + ptr->number = r->ns[id]; + if (*root == NULL) + *root = ptr; + else + last->next = ptr; + } + + /* move the child to under the namespace's umbrella */ + for (c = &ptr->children; *c; c = &(*c)->next) + ; + *c = malloc(sizeof(CHILD)); + (*c)->child = r; + (*c)->next = NULL; + + /* detaching from parent */ + if (r->parent) { + for (c = &r->parent->children; *c; c = &(*c)->next) { + if ((*c)->child == r) { + *c = (*c)->next; + break; + } + } + r->parent = NULL; + } + +} + +static PROC *find_proc(pid_t pid); +static void sort_by_namespace(PROC *r, enum ns_type id, struct ns_entry **root) +{ + CHILD *walk; + + /* first run, find the first process */ + if (!r) { + r = find_proc(1); + if (!r) + return; + } + + if (r->parent == NULL || r->parent->ns[id] != r->ns[id]) + find_ns_and_add(root, r, id); + + for (walk = r->children; walk; walk = walk->next) + sort_by_namespace(walk->child, id, root); +} + #ifdef WITH_SELINUX static void fix_orphans(security_context_t scontext); #else @@ -290,6 +420,7 @@ new->argc = 0; new->children = NULL; new->parent = NULL; new->next = list; + new_proc_ns(new); return list = new; } @@ -624,6 +755,20 @@ dump_tree(current, 0, 1, 1, 1, u dump_by_user(walk->child, uid); } +static void dump_by_namespace(struct ns_entry *root) +{ + struct ns_entry *ptr = root; + CHILD *c; + char buff[14]; + + for ( ; ptr; ptr = ptr->next) { + snprintf(buff, sizeof(buff), "[%li]\n", ptr->number); + out_string(buff); + for (c = ptr->children; c; c = c->next) + dump_tree(c->child, 0, 1, 1, 1, 0, 0); + } +} + static void trim_tree_by_parent(PROC * current) { if (!current) @@ -853,6 +998,8 @@ static void usage(void) " -G, --vt100 use VT100 line drawing characters\n" " -l, --long don't truncate long lines\n" " -n, --numeric-sort sort output by PID\n" + " -N type,\n" + " --ns-sort=type sort by namespace type (ipc, mnt, net, pid, user, uts)\n" " -p, --show-pids show PIDs; implies -c\n" " -s, --show-parents show parents of the selected process\n" " -u, --uid-changes show uid transitions\n" @@ -886,11 +1033,13 @@ int main(int argc, char **argv) { PROC *current; struct winsize winsz; + struct ns_entry *nsroot = NULL; const struct passwd *pw; pid_t pid, highlight; char termcap_area[1024]; char *termname, *endptr; int c, pid_set; + enum ns_type nsid = -1; struct option options[] = { {"arguments", 0, NULL, 'a'}, @@ -901,6 +1050,7 @@ {"highlight-all", 0, NULL, 'h'}, {"highlight-pid", 1, NULL, 'H'}, {"long", 0, NULL, 'l'}, {"numeric-sort", 0, NULL, 'n'}, + {"ns-sort", 1, NULL, 'N' }, {"show-pids", 0, NULL, 'p'}, {"show-pgids", 0, NULL, 'g'}, {"show-parents", 0, NULL, 's'}, @@ -956,11 +1106,11 @@ /* problems with VT100 on some t #ifdef WITH_SELINUX while ((c = - getopt_long(argc, argv, "aAcGhH:npglsuUVZ", options, + getopt_long(argc, argv, "aAcGhH:nN:pglsuUVZ", options, NULL)) != -1) #else /*WITH_SELINUX */ while ((c = - getopt_long(argc, argv, "aAcGhH:npglsuUV", options, NULL)) != -1) + getopt_long(argc, argv, "aAcGhH:nN:pglsuUV", options, NULL)) != -1) #endif /*WITH_SELINUX */ switch (c) { case 'a': @@ -1002,6 +1152,17 @@ trunc = 0; case 'n': by_pid = 1; break; + case 'N': + nsid = get_ns_id(optarg); + if (nsid == -1) + usage(); + if (verify_ns(nsid)) { + fprintf(stderr, + _("procfs file for %s namespace not available\n"), + optarg); + return 1; + } + break; case 'p': pids = 1; compact = 0; @@ -1059,7 +1220,10 @@ if (endptr[0] != '\0') pid = ROOT_PID; } - if (!pw) + if (nsid != -1) { + sort_by_namespace(NULL, nsid, &nsroot); + dump_by_namespace(nsroot); + } else if (!pw) dump_tree(find_proc(pid), 0, 1, 1, 1, 0, 0); else { dump_by_user(find_proc(ROOT_PID), pw->pw_uid);