2 * lsns(8) - list system namespaces
4 * Copyright (C) 2015 Karel Zak <kzak@redhat.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it would be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include <sys/types.h>
30 #include <libsmartcols.h>
32 #include "pathnames.h"
37 #include "closestream.h"
39 #include "procutils.h"
41 #include "namespace.h"
47 static UL_DEBUG_DEFINE_MASK(lsns
);
48 UL_DEBUG_DEFINE_MASKNAMES(lsns
) = UL_DEBUG_EMPTY_MASKNAMES
;
50 #define LSNS_DEBUG_INIT (1 << 1)
51 #define LSNS_DEBUG_PROC (1 << 2)
52 #define LSNS_DEBUG_NS (1 << 3)
53 #define LSNS_DEBUG_ALL 0xFFFF
55 #define DBG(m, x) __UL_DBG(lsns, LSNS_DEBUG_, m, x)
56 #define ON_DBG(m, x) __UL_DBG_CALL(lsns, LSNS_DEBUG_, m, x)
58 static struct idcache
*uid_cache
= NULL
;
75 const char *name
; /* header */
76 double whint
; /* width hint (N < 1 is in percent of termwidth) */
77 int flags
; /* SCOLS_FL_* */
81 /* columns descriptions */
82 static const struct colinfo infos
[] = {
83 [COL_NS
] = { "NS", 10, SCOLS_FL_RIGHT
, N_("namespace identifier (inode number)") },
84 [COL_TYPE
] = { "TYPE", 5, 0, N_("kind of namespace") },
85 [COL_PATH
] = { "PATH", 0, 0, N_("path to the namespace")},
86 [COL_NPROCS
] = { "NPROCS", 5, SCOLS_FL_RIGHT
, N_("number of processes in the namespace") },
87 [COL_PID
] = { "PID", 5, SCOLS_FL_RIGHT
, N_("lowest PID in the namespace") },
88 [COL_PPID
] = { "PPID", 5, SCOLS_FL_RIGHT
, N_("PPID of the PID") },
89 [COL_COMMAND
] = { "COMMAND", 0, SCOLS_FL_TRUNC
, N_("command line of the PID")},
90 [COL_UID
] = { "UID", 0, SCOLS_FL_RIGHT
, N_("UID of the PID")},
91 [COL_USER
] = { "USER", 0, 0, N_("username of the PID")}
94 static int columns
[ARRAY_SIZE(infos
) * 2];
95 static size_t ncolumns
;
107 static char *ns_names
[] = {
108 [LSNS_ID_MNT
] = "mnt",
109 [LSNS_ID_NET
] = "net",
110 [LSNS_ID_PID
] = "pid",
111 [LSNS_ID_UTS
] = "uts",
112 [LSNS_ID_IPC
] = "ipc",
113 [LSNS_ID_USER
] = "user",
114 [LSNS_ID_CGROUP
] = "cgroup"
117 struct lsns_namespace
{
119 int type
; /* LSNS_* */
122 struct lsns_process
*proc
;
124 struct list_head namespaces
; /* lsns->processes member */
125 struct list_head processes
; /* head of lsns_process *siblings */
128 struct lsns_process
{
129 pid_t pid
; /* process PID */
130 pid_t ppid
; /* parent's PID */
131 pid_t tpid
; /* thread group */
135 ino_t ns_ids
[ARRAY_SIZE(ns_names
)];
136 struct list_head ns_siblings
[ARRAY_SIZE(ns_names
)];
138 struct list_head processes
; /* list of processes */
140 struct libscols_line
*outline
;
141 struct lsns_process
*parent
;
145 struct list_head processes
;
146 struct list_head namespaces
;
148 pid_t fltr_pid
; /* filter out by PID */
149 ino_t fltr_ns
; /* filter out by namespace */
150 int fltr_types
[ARRAY_SIZE(ns_names
)];
153 unsigned int raw
: 1,
161 static void lsns_init_debug(void)
163 __UL_INIT_DEBUG(lsns
, LSNS_DEBUG_
, 0, LSNS_DEBUG
);
166 static int ns_name2type(const char *name
)
170 for (i
= 0; i
< ARRAY_SIZE(ns_names
); i
++) {
171 if (strcmp(ns_names
[i
], name
) == 0)
177 static int column_name_to_id(const char *name
, size_t namesz
)
183 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
184 const char *cn
= infos
[i
].name
;
186 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
189 warnx(_("unknown column: %s"), name
);
193 static inline int get_column_id(int num
)
196 assert((size_t) num
< ncolumns
);
197 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
202 static inline const struct colinfo
*get_column_info(unsigned num
)
204 return &infos
[ get_column_id(num
) ];
207 static int get_ns_ino(int dir
, const char *nsname
, ino_t
*ino
)
212 snprintf(path
, sizeof(path
), "ns/%s", nsname
);
214 if (fstatat(dir
, path
, &st
, 0) != 0)
220 static int parse_proc_stat(FILE *fp
, pid_t
*pid
, char *state
, pid_t
*ppid
)
222 char *line
= NULL
, *p
;
226 if (getline(&line
, &len
, fp
) < 0) {
231 p
= strrchr(line
, ')');
233 sscanf(line
, "%d (", pid
) != 1 ||
234 sscanf(p
, ") %c %d*[^\n]", state
, ppid
) != 2) {
245 static int read_process(struct lsns
*ls
, pid_t pid
)
247 struct lsns_process
*p
= NULL
;
255 DBG(PROC
, ul_debug("reading %d", (int) pid
));
257 snprintf(buf
, sizeof(buf
), "/proc/%d", pid
);
262 p
= xcalloc(1, sizeof(*p
));
268 if (fstat(dirfd(dir
), &st
) == 0) {
270 add_uid(uid_cache
, st
.st_uid
);
273 fd
= openat(dirfd(dir
), "stat", O_RDONLY
);
278 if (!(f
= fdopen(fd
, "r"))) {
282 rc
= parse_proc_stat(f
, &p
->pid
, &p
->state
, &p
->ppid
);
287 for (i
= 0; i
< ARRAY_SIZE(p
->ns_ids
); i
++) {
288 INIT_LIST_HEAD(&p
->ns_siblings
[i
]);
290 if (!ls
->fltr_types
[i
])
293 rc
= get_ns_ino(dirfd(dir
), ns_names
[i
], &p
->ns_ids
[i
]);
294 if (rc
&& rc
!= -EACCES
&& rc
!= -ENOENT
)
299 INIT_LIST_HEAD(&p
->processes
);
301 DBG(PROC
, ul_debugobj(p
, "new pid=%d", p
->pid
));
302 list_add_tail(&p
->processes
, &ls
->processes
);
312 static int read_processes(struct lsns
*ls
)
314 struct proc_processes
*proc
= NULL
;
318 DBG(PROC
, ul_debug("opening /proc"));
320 if (!(proc
= proc_open_processes())) {
325 while (proc_next_pid(proc
, &pid
) == 0) {
326 rc
= read_process(ls
, pid
);
327 if (rc
&& rc
!= -EACCES
&& rc
!= -ENOENT
)
332 DBG(PROC
, ul_debug("closing /proc"));
333 proc_close_processes(proc
);
337 static struct lsns_namespace
*get_namespace(struct lsns
*ls
, ino_t ino
)
341 list_for_each(p
, &ls
->namespaces
) {
342 struct lsns_namespace
*ns
= list_entry(p
, struct lsns_namespace
, namespaces
);
350 static int namespace_has_process(struct lsns_namespace
*ns
, pid_t pid
)
354 list_for_each(p
, &ns
->processes
) {
355 struct lsns_process
*proc
= list_entry(p
, struct lsns_process
, ns_siblings
[ns
->type
]);
357 if (proc
->pid
== pid
)
363 static struct lsns_namespace
*add_namespace(struct lsns
*ls
, int type
, ino_t ino
)
365 struct lsns_namespace
*ns
= xcalloc(1, sizeof(*ns
));
370 DBG(NS
, ul_debugobj(ns
, "new %s[%ju]", ns_names
[type
], (uintmax_t)ino
));
372 INIT_LIST_HEAD(&ns
->processes
);
373 INIT_LIST_HEAD(&ns
->namespaces
);
378 list_add_tail(&ns
->namespaces
, &ls
->namespaces
);
382 static int add_process_to_namespace(struct lsns
*ls
, struct lsns_namespace
*ns
, struct lsns_process
*proc
)
386 DBG(NS
, ul_debugobj(ns
, "add process [%p] pid=%d to %s[%ju]",
387 proc
, proc
->pid
, ns_names
[ns
->type
], (uintmax_t)ns
->id
));
389 list_for_each(p
, &ls
->processes
) {
390 struct lsns_process
*xproc
= list_entry(p
, struct lsns_process
, processes
);
392 if (xproc
->pid
== proc
->ppid
) /* my parent */
393 proc
->parent
= xproc
;
394 else if (xproc
->ppid
== proc
->pid
) /* my child */
395 xproc
->parent
= proc
;
398 list_add_tail(&proc
->ns_siblings
[ns
->type
], &ns
->processes
);
401 if (!ns
->proc
|| ns
->proc
->pid
> proc
->pid
)
407 static int cmp_namespaces(struct list_head
*a
, struct list_head
*b
,
408 __attribute__((__unused__
)) void *data
)
410 struct lsns_namespace
*xa
= list_entry(a
, struct lsns_namespace
, namespaces
),
411 *xb
= list_entry(b
, struct lsns_namespace
, namespaces
);
413 return cmp_numbers(xa
->id
, xb
->id
);
416 static int read_namespaces(struct lsns
*ls
)
420 DBG(NS
, ul_debug("reading namespace"));
422 list_for_each(p
, &ls
->processes
) {
424 struct lsns_namespace
*ns
;
425 struct lsns_process
*proc
= list_entry(p
, struct lsns_process
, processes
);
427 for (i
= 0; i
< ARRAY_SIZE(proc
->ns_ids
); i
++) {
428 if (proc
->ns_ids
[i
] == 0)
430 if (!(ns
= get_namespace(ls
, proc
->ns_ids
[i
]))) {
431 ns
= add_namespace(ls
, i
, proc
->ns_ids
[i
]);
435 add_process_to_namespace(ls
, ns
, proc
);
439 list_sort(&ls
->namespaces
, cmp_namespaces
, NULL
);
444 static void add_scols_line(struct lsns
*ls
, struct libscols_table
*table
,
445 struct lsns_namespace
*ns
, struct lsns_process
*proc
)
448 struct libscols_line
*line
;
453 line
= scols_table_new_line(table
,
454 ls
->tree
&& proc
->parent
? proc
->parent
->outline
: NULL
);
456 warn(_("failed to add line to output"));
460 for (i
= 0; i
< ncolumns
; i
++) {
463 switch (get_column_id(i
)) {
465 xasprintf(&str
, "%ju", (uintmax_t)ns
->id
);
468 xasprintf(&str
, "%d", (int) proc
->pid
);
471 xasprintf(&str
, "%d", (int) proc
->ppid
);
474 xasprintf(&str
, "%s", ns_names
[ns
->type
]);
477 xasprintf(&str
, "%d", ns
->nprocs
);
480 str
= proc_get_command(proc
->pid
);
482 str
= proc_get_command_name(proc
->pid
);
485 xasprintf(&str
, "/proc/%d/ns/%s", (int) proc
->pid
, ns_names
[ns
->type
]);
488 xasprintf(&str
, "%d", (int) proc
->uid
);
491 xasprintf(&str
, "%s", get_id(uid_cache
, proc
->uid
)->name
);
497 if (str
&& scols_line_refer_data(line
, i
, str
) != 0)
501 proc
->outline
= line
;
504 static struct libscols_table
*init_scols_table(struct lsns
*ls
)
506 struct libscols_table
*tab
;
509 tab
= scols_new_table();
511 warn(_("failed to initialize output table"));
515 scols_table_enable_raw(tab
, ls
->raw
);
516 scols_table_enable_json(tab
, ls
->json
);
517 scols_table_enable_noheadings(tab
, ls
->no_headings
);
520 scols_table_set_name(tab
, "namespaces");
522 for (i
= 0; i
< ncolumns
; i
++) {
523 const struct colinfo
*col
= get_column_info(i
);
524 int flags
= col
->flags
;
527 flags
&= ~SCOLS_FL_TRUNC
;
528 if (ls
->tree
&& get_column_id(i
) == COL_COMMAND
)
529 flags
|= SCOLS_FL_TREE
;
531 if (!scols_table_new_column(tab
, col
->name
, col
->whint
, flags
)) {
532 warnx(_("failed to initialize output column"));
539 scols_unref_table(tab
);
543 static int show_namespaces(struct lsns
*ls
)
545 struct libscols_table
*tab
;
549 tab
= init_scols_table(ls
);
553 list_for_each(p
, &ls
->namespaces
) {
554 struct lsns_namespace
*ns
= list_entry(p
, struct lsns_namespace
, namespaces
);
556 if (ls
->fltr_pid
!= 0 && !namespace_has_process(ns
, ls
->fltr_pid
))
559 add_scols_line(ls
, tab
, ns
, ns
->proc
);
562 scols_print_table(tab
);
563 scols_unref_table(tab
);
567 static void show_process(struct lsns
*ls
, struct libscols_table
*tab
,
568 struct lsns_process
*proc
, struct lsns_namespace
*ns
)
571 * create a tree from parent->child relation, but only if the parent is
572 * within the same namespace
576 && !proc
->parent
->outline
577 && proc
->parent
->ns_ids
[ns
->type
] == proc
->ns_ids
[ns
->type
])
578 show_process(ls
, tab
, proc
->parent
, ns
);
580 add_scols_line(ls
, tab
, ns
, proc
);
584 static int show_namespace_processes(struct lsns
*ls
, struct lsns_namespace
*ns
)
586 struct libscols_table
*tab
;
589 tab
= init_scols_table(ls
);
593 list_for_each(p
, &ns
->processes
) {
594 struct lsns_process
*proc
= list_entry(p
, struct lsns_process
, ns_siblings
[ns
->type
]);
597 show_process(ls
, tab
, proc
, ns
);
601 scols_print_table(tab
);
602 scols_unref_table(tab
);
606 static void __attribute__((__noreturn__
)) usage(void)
611 fputs(USAGE_HEADER
, out
);
614 _(" %s [options] [<namespace>]\n"), program_invocation_short_name
);
616 fputs(USAGE_SEPARATOR
, out
);
617 fputs(_("List system namespaces.\n"), out
);
619 fputs(USAGE_OPTIONS
, out
);
620 fputs(_(" -J, --json use JSON output format\n"), out
);
621 fputs(_(" -l, --list use list format output\n"), out
);
622 fputs(_(" -n, --noheadings don't print headings\n"), out
);
623 fputs(_(" -o, --output <list> define which output columns to use\n"), out
);
624 fputs(_(" -p, --task <pid> print process namespaces\n"), out
);
625 fputs(_(" -r, --raw use the raw output format\n"), out
);
626 fputs(_(" -u, --notruncate don't truncate text in columns\n"), out
);
627 fputs(_(" -t, --type <name> namespace type (mnt, net, ipc, user, pid, uts, cgroup)\n"), out
);
629 fputs(USAGE_SEPARATOR
, out
);
630 printf(USAGE_HELP_OPTIONS(24));
632 fputs(USAGE_COLUMNS
, out
);
633 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
634 fprintf(out
, " %11s %s\n", infos
[i
].name
, _(infos
[i
].help
));
636 printf(USAGE_MAN_TAIL("lsns(8)"));
642 int main(int argc
, char *argv
[])
648 static const struct option long_opts
[] = {
649 { "json", no_argument
, NULL
, 'J' },
650 { "task", required_argument
, NULL
, 'p' },
651 { "help", no_argument
, NULL
, 'h' },
652 { "output", required_argument
, NULL
, 'o' },
653 { "notruncate", no_argument
, NULL
, 'u' },
654 { "version", no_argument
, NULL
, 'V' },
655 { "noheadings", no_argument
, NULL
, 'n' },
656 { "list", no_argument
, NULL
, 'l' },
657 { "raw", no_argument
, NULL
, 'r' },
658 { "type", required_argument
, NULL
, 't' },
662 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
666 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
668 setlocale(LC_ALL
, "");
669 bindtextdomain(PACKAGE
, LOCALEDIR
);
671 atexit(close_stdout
);
674 memset(&ls
, 0, sizeof(ls
));
676 INIT_LIST_HEAD(&ls
.processes
);
677 INIT_LIST_HEAD(&ls
.namespaces
);
679 while ((c
= getopt_long(argc
, argv
,
680 "Jlp:o:nruhVt:", long_opts
, NULL
)) != -1) {
682 err_exclusive_options(c
, long_opts
, excl
, excl_st
);
695 printf(UTIL_LINUX_VERSION
);
698 ls
.fltr_pid
= strtos32_or_err(optarg
, _("invalid PID argument"));
713 int type
= ns_name2type(optarg
);
715 errx(EXIT_FAILURE
, _("unknown namespace type: %s"), optarg
);
716 ls
.fltr_types
[type
] = 1;
721 errtryhelp(EXIT_FAILURE
);
725 if (!ls
.fltr_ntypes
) {
727 for (i
= 0; i
< ARRAY_SIZE(ns_names
); i
++)
728 ls
.fltr_types
[i
] = 1;
733 errx(EXIT_FAILURE
, _("--task is mutually exclusive with <namespace>"));
734 ls
.fltr_ns
= strtou64_or_err(argv
[optind
], _("invalid namespace argument"));
735 ls
.tree
= ls
.list
? 0 : 1;
738 columns
[ncolumns
++] = COL_PID
;
739 columns
[ncolumns
++] = COL_PPID
;
740 columns
[ncolumns
++] = COL_USER
;
741 columns
[ncolumns
++] = COL_COMMAND
;
746 columns
[ncolumns
++] = COL_NS
;
747 columns
[ncolumns
++] = COL_TYPE
;
748 columns
[ncolumns
++] = COL_NPROCS
;
749 columns
[ncolumns
++] = COL_PID
;
750 columns
[ncolumns
++] = COL_USER
;
751 columns
[ncolumns
++] = COL_COMMAND
;
754 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
755 &ncolumns
, column_name_to_id
) < 0)
760 uid_cache
= new_idcache();
762 err(EXIT_FAILURE
, _("failed to allocate UID cache"));
764 r
= read_processes(&ls
);
766 r
= read_namespaces(&ls
);
769 struct lsns_namespace
*ns
= get_namespace(&ls
, ls
.fltr_ns
);
772 errx(EXIT_FAILURE
, _("not found namespace: %ju"), (uintmax_t) ls
.fltr_ns
);
773 r
= show_namespace_processes(&ls
, ns
);
775 r
= show_namespaces(&ls
);
778 free_idcache(uid_cache
);
779 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;