2 * SPDX-License-Identifier: GPL-2.0-or-later
4 * lsns(8) - list system namespaces
6 * Copyright (C) 2015 Karel Zak <kzak@redhat.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
21 #include <sys/types.h>
23 #include <libsmartcols.h>
27 #ifdef HAVE_LINUX_NET_NAMESPACE_H
28 # include <sys/socket.h>
29 # include <linux/netlink.h>
30 # include <linux/rtnetlink.h>
31 # include <linux/net_namespace.h>
34 #ifdef HAVE_LINUX_NSFS_H
35 # include <linux/nsfs.h>
36 # if defined(NS_GET_NSTYPE) && defined(NS_GET_OWNER_UID)
37 # define USE_NS_GET_API 1
41 #include "pathnames.h"
46 #include "closestream.h"
50 #include "namespace.h"
52 #include "fileutils.h"
53 #include "column-list-table.h"
57 static UL_DEBUG_DEFINE_MASK(lsns
);
58 UL_DEBUG_DEFINE_MASKNAMES(lsns
) = UL_DEBUG_EMPTY_MASKNAMES
;
60 #define LSNS_DEBUG_INIT (1 << 1)
61 #define LSNS_DEBUG_PROC (1 << 2)
62 #define LSNS_DEBUG_NS (1 << 3)
63 #define LSNS_DEBUG_FILTER (1 << 4)
64 #define LSNS_DEBUG_ALL 0xFFFF
66 #define LSNS_NETNS_UNUSABLE -2
68 #define DBG(m, x) __UL_DBG(lsns, LSNS_DEBUG_, m, x)
69 #define ON_DBG(m, x) __UL_DBG_CALL(lsns, LSNS_DEBUG_, m, x)
71 #define lsns_ioctl(fildes, request, ...) __extension__ ({ \
72 int ret = ioctl(fildes, request, ##__VA_ARGS__); \
73 if (ret == -1 && errno == ENOTTY) \
74 warnx("Unsupported ioctl %s", #request); \
77 #define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(lsns)
80 #define EXIT_UNSUPPORTED_IOCTL 2
82 static struct idcache
*uid_cache
= NULL
;
97 COL_PNS
, /* parent namespace */
98 COL_ONS
, /* owner namespace */
103 const char *name
; /* header */
104 double whint
; /* width hint (N < 1 is in percent of termwidth) */
105 int flags
; /* SCOLS_FL_* */
110 /* columns descriptions */
111 static const struct colinfo infos
[] = {
112 [COL_NS
] = { "NS", 10, SCOLS_FL_RIGHT
, N_("namespace identifier (inode number)"), SCOLS_JSON_NUMBER
},
113 [COL_TYPE
] = { "TYPE", 5, 0, N_("kind of namespace") },
114 [COL_PATH
] = { "PATH", 0, 0, N_("path to the namespace")},
115 [COL_NPROCS
] = { "NPROCS", 5, SCOLS_FL_RIGHT
, N_("number of processes in the namespace"), SCOLS_JSON_NUMBER
},
116 [COL_PID
] = { "PID", 5, SCOLS_FL_RIGHT
, N_("lowest PID in the namespace"), SCOLS_JSON_NUMBER
},
117 [COL_PPID
] = { "PPID", 5, SCOLS_FL_RIGHT
, N_("PPID of the PID"), SCOLS_JSON_NUMBER
},
118 [COL_COMMAND
] = { "COMMAND", 0, SCOLS_FL_TRUNC
, N_("command line of the PID")},
119 [COL_UID
] = { "UID", 0, SCOLS_FL_RIGHT
, N_("UID of the PID"), SCOLS_JSON_NUMBER
},
120 [COL_USER
] = { "USER", 0, 0, N_("username of the PID")},
121 [COL_NETNSID
] = { "NETNSID", 0, SCOLS_FL_RIGHT
, N_("namespace ID as used by network subsystem")},
122 [COL_NSFS
] = { "NSFS", 0, SCOLS_FL_WRAP
, N_("nsfs mountpoint (usually used network subsystem)")},
123 [COL_PNS
] = { "PNS", 10, SCOLS_FL_RIGHT
, N_("parent namespace identifier (inode number)"), SCOLS_JSON_NUMBER
},
124 [COL_ONS
] = { "ONS", 10, SCOLS_FL_RIGHT
, N_("owner namespace identifier (inode number)"), SCOLS_JSON_NUMBER
},
127 static int columns
[ARRAY_SIZE(infos
) * 2];
128 static size_t ncolumns
;
141 static char *ns_names
[] = {
142 [LSNS_ID_MNT
] = "mnt",
143 [LSNS_ID_NET
] = "net",
144 [LSNS_ID_PID
] = "pid",
145 [LSNS_ID_UTS
] = "uts",
146 [LSNS_ID_IPC
] = "ipc",
147 [LSNS_ID_USER
] = "user",
148 [LSNS_ID_CGROUP
] = "cgroup",
149 [LSNS_ID_TIME
] = "time"
158 struct lsns_namespace
{
160 int type
; /* LSNS_* */
163 ino_t related_id
[MAX_RELA
];
165 struct lsns_process
*proc
;
167 struct lsns_namespace
*related_ns
[MAX_RELA
];
168 struct libscols_line
*ns_outline
;
169 uid_t uid_fallback
; /* refer this member if `proc' is NULL. */
171 struct list_head namespaces
; /* lsns->processes member */
172 struct list_head processes
; /* head of lsns_process *siblings */
175 struct lsns_process
{
176 pid_t pid
; /* process PID */
177 pid_t ppid
; /* parent's PID */
178 pid_t tpid
; /* thread group */
182 ino_t ns_ids
[ARRAY_SIZE(ns_names
)];
183 ino_t ns_pids
[ARRAY_SIZE(ns_names
)];
184 ino_t ns_oids
[ARRAY_SIZE(ns_names
)];
186 struct list_head ns_siblings
[ARRAY_SIZE(ns_names
)];
188 struct list_head processes
; /* list of processes */
190 struct libscols_line
*outline
;
191 struct lsns_process
*parent
;
205 struct list_head processes
;
206 struct list_head namespaces
;
208 pid_t fltr_pid
; /* filter out by PID */
209 ino_t fltr_ns
; /* filter out by namespace */
210 int fltr_types
[ARRAY_SIZE(ns_names
)];
213 unsigned int raw
: 1,
223 struct libmnt_table
*tab
;
224 struct libscols_filter
*filter
;
227 struct netnsid_cache
{
230 struct list_head netnsids
;
233 /* "userdata" used by callback for libsmartcols filter */
236 struct lsns_namespace
*ns
;
237 struct lsns_process
*proc
;
240 static struct list_head netnsids_cache
;
242 static int netlink_fd
= -1;
244 static void lsns_init_debug(void)
246 __UL_INIT_DEBUG_FROM_ENV(lsns
, LSNS_DEBUG_
, 0, LSNS_DEBUG
);
249 static int ns_name2type(const char *name
)
253 for (i
= 0; i
< ARRAY_SIZE(ns_names
); i
++) {
254 if (strcmp(ns_names
[i
], name
) == 0)
260 static int column_name_to_id(const char *name
, size_t namesz
)
266 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
267 const char *cn
= infos
[i
].name
;
269 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
272 warnx(_("unknown column: %s"), name
);
276 static int has_column(int id
)
280 for (i
= 0; i
< ncolumns
; i
++) {
281 if (columns
[i
] == id
)
287 static inline int get_column_id(int num
)
290 assert((size_t) num
< ncolumns
);
291 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
296 static inline const struct colinfo
*get_column_info(unsigned num
)
298 return &infos
[ get_column_id(num
) ];
301 static int get_ns_ino(struct path_cxt
*pc
, const char *nsname
, ino_t
*ino
, ino_t
*pino
, ino_t
*oino
)
306 snprintf(path
, sizeof(path
), "ns/%s", nsname
);
309 if (ul_path_stat(pc
, &st
, 0, path
) != 0)
316 #ifdef USE_NS_GET_API
318 fd
= ul_path_open(pc
, 0, path
);
321 if (strcmp(nsname
, "pid") == 0 || strcmp(nsname
, "user") == 0) {
322 if ((pfd
= lsns_ioctl(fd
, NS_GET_PARENT
)) < 0) {
324 /* On the test platforms, "build (qemu-user, s390x)" and
325 * "build (qemu-user, riscv64)", the ioctl reported ENOSYS.
332 if (fstat(pfd
, &st
) < 0) {
341 if ((ofd
= lsns_ioctl(fd
, NS_GET_USERNS
)) < 0) {
343 /* On the test platforms, "build (qemu-user, s390x)" and
344 * "build (qemu-user, riscv64)", the ioctl reported ENOSYS.
351 if (fstat(ofd
, &st
) < 0) {
364 static int parse_proc_stat(char *line
, pid_t
*pid
, char *state
, pid_t
*ppid
)
369 p
= strrchr(line
, ')');
371 sscanf(line
, "%d (", pid
) != 1 ||
372 sscanf(p
, ") %c %d*[^\n]", state
, ppid
) != 2) {
382 #ifdef HAVE_LINUX_NET_NAMESPACE_H
383 static int netnsid_cache_find(ino_t netino
, int *netnsid
)
387 list_for_each(p
, &netnsids_cache
) {
388 struct netnsid_cache
*e
= list_entry(p
,
389 struct netnsid_cache
,
391 if (e
->ino
== netino
) {
400 static void netnsid_cache_add(ino_t netino
, int netnsid
)
402 struct netnsid_cache
*e
;
404 e
= xcalloc(1, sizeof(*e
));
407 INIT_LIST_HEAD(&e
->netnsids
);
408 list_add(&e
->netnsids
, &netnsids_cache
);
411 static int get_netnsid_via_netlink_send_request(int target_fd
)
413 unsigned char req
[NLMSG_SPACE(sizeof(struct rtgenmsg
))
414 + RTA_SPACE(sizeof(int32_t))];
416 struct nlmsghdr
*nlh
= (struct nlmsghdr
*)req
;
417 struct rtgenmsg
*rt
= NLMSG_DATA(req
);
418 struct rtattr
*rta
= (struct rtattr
*)
419 (req
+ NLMSG_SPACE(sizeof(struct rtgenmsg
)));
420 int32_t *fd
= RTA_DATA(rta
);
422 nlh
->nlmsg_len
= sizeof(req
);
423 nlh
->nlmsg_flags
= NLM_F_REQUEST
;
424 nlh
->nlmsg_type
= RTM_GETNSID
;
425 rt
->rtgen_family
= AF_UNSPEC
;
426 rta
->rta_type
= NETNSA_FD
;
427 rta
->rta_len
= RTA_SPACE(sizeof(int32_t));
430 if (send(netlink_fd
, req
, sizeof(req
), 0) < 0)
435 static int get_netnsid_via_netlink_recv_response(int *netnsid
)
437 unsigned char res
[NLMSG_SPACE(sizeof(struct rtgenmsg
))
438 + ((RTA_SPACE(sizeof(int32_t))
439 < RTA_SPACE(sizeof(struct nlmsgerr
)))
440 ? RTA_SPACE(sizeof(struct nlmsgerr
))
441 : RTA_SPACE(sizeof(int32_t)))];
445 struct nlmsghdr
*nlh
;
448 reslen
= recv(netlink_fd
, res
, sizeof(res
), 0);
452 nlh
= (struct nlmsghdr
*)res
;
453 if (!(NLMSG_OK(nlh
, (size_t)reslen
)
454 && nlh
->nlmsg_type
== RTM_NEWNSID
))
457 rtalen
= NLMSG_PAYLOAD(nlh
, sizeof(struct rtgenmsg
));
458 rta
= (struct rtattr
*)(res
+ NLMSG_SPACE(sizeof(struct rtgenmsg
)));
459 if (!(RTA_OK(rta
, rtalen
)
460 && rta
->rta_type
== NETNSA_NSID
))
463 *netnsid
= *(int *)RTA_DATA(rta
);
468 static int get_netnsid_via_netlink(struct path_cxt
*pc
, const char *path
)
474 return LSNS_NETNS_UNUSABLE
;
476 target_fd
= ul_path_open(pc
, O_RDONLY
, path
);
478 return LSNS_NETNS_UNUSABLE
;
480 if (get_netnsid_via_netlink_send_request(target_fd
) < 0) {
481 netnsid
= LSNS_NETNS_UNUSABLE
;
485 if (get_netnsid_via_netlink_recv_response(&netnsid
) < 0) {
486 netnsid
= LSNS_NETNS_UNUSABLE
;
495 static int get_netnsid(struct path_cxt
*pc
, ino_t netino
)
499 if (!netnsid_cache_find(netino
, &netnsid
)) {
500 netnsid
= get_netnsid_via_netlink(pc
, "ns/net");
501 netnsid_cache_add(netino
, netnsid
);
507 static int get_netnsid(struct path_cxt
*pc
__attribute__((__unused__
)),
508 ino_t netino
__attribute__((__unused__
)))
510 return LSNS_NETNS_UNUSABLE
;
512 #endif /* HAVE_LINUX_NET_NAMESPACE_H */
514 static struct lsns_namespace
*add_namespace_for_nsfd(struct lsns
*ls
, int fd
, ino_t ino
);
516 static void read_open_ns_inos(struct lsns
*ls
, struct path_cxt
*pc
)
519 struct dirent
*d
= NULL
;
520 char path
[sizeof("fd/") + sizeof(stringify_value(UINT64_MAX
))];
522 while (ul_path_next_dirent(pc
, &sub
, "fd", &d
) == 0) {
526 if (ul_strtou64(d
->d_name
, &num
, 10) != 0) /* only numbers */
529 snprintf(path
, sizeof(path
), "fd/%ju", (uintmax_t) num
);
531 if (ul_path_stat(pc
, &st
, 0, path
) == 0
532 && st
.st_dev
== ls
->nsfs_dev
) {
533 int fd
= ul_path_open(pc
, O_RDONLY
, path
);
535 add_namespace_for_nsfd(ls
, fd
, st
.st_ino
);
542 static int read_process(struct lsns
*ls
, struct path_cxt
*pc
)
544 struct lsns_process
*p
= NULL
;
549 p
= xcalloc(1, sizeof(*p
));
550 p
->netnsid
= LSNS_NETNS_UNUSABLE
;
552 if (procfs_process_get_uid(pc
, &p
->uid
) == 0)
553 add_uid(uid_cache
, p
->uid
);
555 if ((rc
= procfs_process_get_stat(pc
, buf
, sizeof(buf
))) < 0) {
556 DBG(PROC
, ul_debug("failed in procfs_process_get_stat() (rc: %d)", rc
));
559 if ((rc
= parse_proc_stat(buf
, &p
->pid
, &p
->state
, &p
->ppid
)) < 0) {
560 DBG(PROC
, ul_debug("failed in parse_proc_stat() (rc: %d)", rc
));
565 for (i
= 0; i
< ARRAY_SIZE(p
->ns_ids
); i
++) {
566 INIT_LIST_HEAD(&p
->ns_siblings
[i
]);
568 if (!ls
->fltr_types
[i
])
571 rc
= get_ns_ino(pc
, ns_names
[i
], &p
->ns_ids
[i
],
572 &p
->ns_pids
[i
], &p
->ns_oids
[i
]);
573 if (rc
&& rc
!= -EACCES
&& rc
!= -ENOENT
) {
574 DBG(PROC
, ul_debug("failed in get_ns_ino (rc: %d)", rc
));
577 if (p
->ns_ids
[i
] && i
== LSNS_ID_NET
)
578 p
->netnsid
= get_netnsid(pc
, p
->ns_ids
[i
]);
582 INIT_LIST_HEAD(&p
->processes
);
584 DBG(PROC
, ul_debugobj(p
, "new pid=%d", p
->pid
));
585 list_add_tail(&p
->processes
, &ls
->processes
);
587 read_open_ns_inos(ls
, pc
);
594 static int read_processes(struct lsns
*ls
)
601 DBG(PROC
, ul_debug("opening /proc"));
603 dir
= opendir(_PATH_PROC
);
607 pc
= ul_new_path(NULL
);
609 err(EXIT_FAILURE
, _("failed to alloc procfs handler"));
611 while ((d
= xreaddir(dir
))) {
614 if (procfs_dirent_get_pid(d
, &pid
) != 0)
617 DBG(PROC
, ul_debug("reading %d", (int) pid
));
618 rc
= procfs_process_init_path(pc
, pid
);
620 DBG(PROC
, ul_debug("failed in initializing path_cxt for /proc/%d (rc: %d)", (int) pid
, rc
));
621 /* This failure is acceptable. If a process ($pid) owning
622 * a namespace is gone while running this lsns process,
623 * procfs_process_init_path(pc, $pid) may fail.
625 * We must reset this `rc' here. If this `d' is the last
626 * dentry in `dir', this read_processes() invocation
627 * returns this `rc'. In the caller context, the
628 * non-zero value returned from read_processes() makes
629 * lsns prints nothing. We should avoid the behavior. */
634 rc
= read_process(ls
, pc
);
635 if (rc
&& rc
!= -EACCES
&& rc
!= -ENOENT
) {
636 DBG(PROC
, ul_debug("failed in read_process() (pid: %d, rc: %d)", (int) pid
, rc
));
644 DBG(PROC
, ul_debug("closing /proc"));
649 static struct lsns_namespace
*get_namespace(struct lsns
*ls
, ino_t ino
)
653 list_for_each(p
, &ls
->namespaces
) {
654 struct lsns_namespace
*ns
= list_entry(p
, struct lsns_namespace
, namespaces
);
662 static int namespace_has_process(struct lsns_namespace
*ns
, pid_t pid
)
666 list_for_each(p
, &ns
->processes
) {
667 struct lsns_process
*proc
= list_entry(p
, struct lsns_process
, ns_siblings
[ns
->type
]);
669 if (proc
->pid
== pid
)
675 static struct lsns_namespace
*add_namespace(struct lsns
*ls
, int type
, ino_t ino
,
676 ino_t parent_ino
, ino_t owner_ino
)
678 struct lsns_namespace
*ns
= xcalloc(1, sizeof(*ns
));
683 DBG(NS
, ul_debugobj(ns
, "new %s[%ju]", ns_names
[type
], (uintmax_t)ino
));
685 INIT_LIST_HEAD(&ns
->processes
);
686 INIT_LIST_HEAD(&ns
->namespaces
);
690 ns
->related_id
[RELA_PARENT
] = parent_ino
;
691 ns
->related_id
[RELA_OWNER
] = owner_ino
;
693 list_add_tail(&ns
->namespaces
, &ls
->namespaces
);
697 static int add_process_to_namespace(struct lsns
*ls
, struct lsns_namespace
*ns
, struct lsns_process
*proc
)
701 DBG(NS
, ul_debugobj(ns
, "add process [%p] pid=%d to %s[%ju]",
702 proc
, proc
->pid
, ns_names
[ns
->type
], (uintmax_t)ns
->id
));
704 list_for_each(p
, &ls
->processes
) {
705 struct lsns_process
*xproc
= list_entry(p
, struct lsns_process
, processes
);
707 if (xproc
->pid
== proc
->ppid
) /* my parent */
708 proc
->parent
= xproc
;
709 else if (xproc
->ppid
== proc
->pid
) /* my child */
710 xproc
->parent
= proc
;
713 list_add_tail(&proc
->ns_siblings
[ns
->type
], &ns
->processes
);
716 if (!ns
->proc
|| ns
->proc
->pid
> proc
->pid
)
722 static int cmp_namespaces(struct list_head
*a
, struct list_head
*b
,
723 __attribute__((__unused__
)) void *data
)
725 struct lsns_namespace
*xa
= list_entry(a
, struct lsns_namespace
, namespaces
),
726 *xb
= list_entry(b
, struct lsns_namespace
, namespaces
);
728 return cmp_numbers(xa
->id
, xb
->id
);
731 static int netnsid_xasputs(char **str
, int netnsid
)
734 return xasprintf(str
, "%d", netnsid
);
735 #ifdef NETNSA_NSID_NOT_ASSIGNED
736 if (netnsid
== NETNSA_NSID_NOT_ASSIGNED
)
737 return xasprintf(str
, "%s", "unassigned");
742 #ifdef USE_NS_GET_API
743 static int clone_type_to_lsns_type(int clone_type
)
745 switch (clone_type
) {
748 case CLONE_NEWCGROUP
:
749 return LSNS_ID_CGROUP
;
769 static struct lsns_namespace
*add_namespace_for_nsfd(struct lsns
*ls
, int fd
, ino_t ino
)
771 int fd_owner
= -1, fd_parent
= -1;
772 struct stat st_owner
, st_parent
;
773 ino_t ino_owner
= 0, ino_parent
= 0;
774 struct lsns_namespace
*ns
;
775 int clone_type
, lsns_type
;
777 clone_type
= lsns_ioctl(fd
, NS_GET_NSTYPE
);
780 lsns_type
= clone_type_to_lsns_type(clone_type
);
781 if (lsns_type
< 0 || ls
->fltr_types
[lsns_type
] == 0)
784 fd_owner
= lsns_ioctl(fd
, NS_GET_USERNS
);
787 if (fstat(fd_owner
, &st_owner
) < 0)
789 ino_owner
= st_owner
.st_ino
;
792 fd_parent
= lsns_ioctl(fd
, NS_GET_PARENT
);
795 if (fstat(fd_parent
, &st_parent
) < 0)
797 ino_parent
= st_parent
.st_ino
;
800 ns
= add_namespace(ls
, lsns_type
, ino
, ino_parent
, ino_owner
);
801 lsns_ioctl(fd
, NS_GET_OWNER_UID
, &ns
->uid_fallback
);
802 add_uid(uid_cache
, ns
->uid_fallback
);
804 if ((lsns_type
== LSNS_ID_USER
|| lsns_type
== LSNS_ID_PID
)
805 && ino_parent
!= ino
&& ino_parent
!= 0) {
806 ns
->related_ns
[RELA_PARENT
] = get_namespace(ls
, ino_parent
);
807 if (!ns
->related_ns
[RELA_PARENT
]) {
808 ns
->related_ns
[RELA_PARENT
] = add_namespace_for_nsfd(ls
, fd_parent
, ino_parent
);
809 if (ino_parent
== ino_owner
)
810 ns
->related_ns
[RELA_OWNER
] = ns
->related_ns
[RELA_PARENT
];
814 if (ns
->related_ns
[RELA_OWNER
] == NULL
&& ino_owner
!= 0) {
815 ns
->related_ns
[RELA_OWNER
] = get_namespace(ls
, ino_owner
);
816 if (!ns
->related_ns
[RELA_OWNER
])
817 ns
->related_ns
[RELA_OWNER
] = add_namespace_for_nsfd(ls
, fd_owner
, ino_owner
);
828 static void interpolate_missing_namespaces(struct lsns
*ls
, struct lsns_namespace
*orphan
, int rela
)
830 const int cmd
[MAX_RELA
] = {
831 [RELA_PARENT
] = NS_GET_PARENT
,
832 [RELA_OWNER
] = NS_GET_USERNS
835 int fd_orphan
, fd_missing
;
838 orphan
->related_ns
[rela
] = get_namespace(ls
, orphan
->related_id
[rela
]);
839 if (orphan
->related_ns
[rela
])
842 snprintf(buf
, sizeof(buf
), "/proc/%d/ns/%s", orphan
->proc
->pid
, ns_names
[orphan
->type
]);
843 fd_orphan
= open(buf
, O_RDONLY
);
847 fd_missing
= lsns_ioctl(fd_orphan
, cmd
[rela
]);
852 if (fstat(fd_missing
, &st
) < 0
853 || st
.st_ino
!= orphan
->related_id
[rela
]) {
858 orphan
->related_ns
[rela
] = add_namespace_for_nsfd(ls
, fd_missing
, orphan
->related_id
[rela
]);
862 static void read_related_namespaces(struct lsns
*ls
)
865 struct lsns_namespace
*orphan
[2] = {NULL
, NULL
};
868 list_for_each(p
, &ls
->namespaces
) {
869 struct lsns_namespace
*ns
= list_entry(p
, struct lsns_namespace
, namespaces
);
870 struct list_head
*pp
;
871 list_for_each(pp
, &ls
->namespaces
) {
872 struct lsns_namespace
*pns
= list_entry(pp
, struct lsns_namespace
, namespaces
);
873 if (ns
->type
== LSNS_ID_USER
874 || ns
->type
== LSNS_ID_PID
) {
875 if (ns
->related_id
[RELA_PARENT
] == pns
->id
)
876 ns
->related_ns
[RELA_PARENT
] = pns
;
877 if (ns
->related_id
[RELA_OWNER
] == pns
->id
)
878 ns
->related_ns
[RELA_OWNER
] = pns
;
879 if (ns
->related_ns
[RELA_PARENT
] && ns
->related_ns
[RELA_OWNER
])
882 if (ns
->related_id
[RELA_OWNER
] == pns
->id
) {
883 ns
->related_ns
[RELA_OWNER
] = pns
;
889 /* lsns scans /proc/[0-9]+ for finding namespaces.
890 * So if a namespace has no process, lsns cannot
891 * find it. Here we call it a missing namespace.
893 * If the id for a related namesspce is known but
894 * namespace for the id is not found, there must
895 * be orphan namespaces. A missing namespace is an
896 * owner or a parent of the orphan namespace.
898 for (rela
= 0; rela
< MAX_RELA
; rela
++) {
899 if (ns
->related_id
[rela
] != 0
900 && ns
->related_ns
[rela
] == NULL
) {
901 ns
->related_ns
[rela
] = orphan
[rela
];
907 for (rela
= 0; rela
< MAX_RELA
; rela
++) {
908 while (orphan
[rela
]) {
909 struct lsns_namespace
*current
= orphan
[rela
];
910 orphan
[rela
] = orphan
[rela
]->related_ns
[rela
];
911 current
->related_ns
[rela
] = NULL
;
912 interpolate_missing_namespaces(ls
, current
, rela
);
917 static int read_persistent_namespaces(struct lsns
*ls
)
919 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_FORWARD
);
920 struct libmnt_fs
*fs
= NULL
;
922 while (mnt_table_next_fs(ls
->tab
, itr
, &fs
) == 0) {
924 char *p
, *end
= NULL
;
928 if (!mnt_fs_match_fstype(fs
, "nsfs"))
930 root
= mnt_fs_get_root(fs
);
931 if (!root
|| !(p
= strchr(root
, '[')))
935 ino
= strtoumax(++p
, &end
, 10);
936 if (!end
|| *end
!= ']' || errno
!= 0)
938 if (get_namespace(ls
, ino
))
941 fd
= open(mnt_fs_get_target(fs
), O_RDONLY
);
945 add_namespace_for_nsfd(ls
, fd
, ino
);
953 #endif /* USE_NS_GET_API */
955 static int read_namespaces(struct lsns
*ls
)
959 DBG(NS
, ul_debug("reading namespace"));
961 list_for_each(p
, &ls
->processes
) {
963 struct lsns_namespace
*ns
;
964 struct lsns_process
*proc
= list_entry(p
, struct lsns_process
, processes
);
966 for (i
= 0; i
< ARRAY_SIZE(proc
->ns_ids
); i
++) {
967 if (proc
->ns_ids
[i
] == 0)
969 if (!(ns
= get_namespace(ls
, proc
->ns_ids
[i
]))) {
970 ns
= add_namespace(ls
, i
, proc
->ns_ids
[i
],
971 proc
->ns_pids
[i
], proc
->ns_oids
[i
]);
975 add_process_to_namespace(ls
, ns
, proc
);
979 #ifdef USE_NS_GET_API
980 read_persistent_namespaces(ls
);
982 if (ls
->tree
== LSNS_TREE_OWNER
|| ls
->tree
== LSNS_TREE_PARENT
)
983 read_related_namespaces(ls
);
985 list_sort(&ls
->namespaces
, cmp_namespaces
, NULL
);
990 static int is_nsfs_root(struct libmnt_fs
*fs
, void *data
)
992 if (!mnt_fs_match_fstype(fs
, "nsfs") || !mnt_fs_get_root(fs
))
995 return (strcmp(mnt_fs_get_root(fs
), (char *)data
) == 0);
998 static int is_path_included(const char *path_set
, const char *elt
,
1002 size_t path_set_len
;
1006 tmp
= strstr(path_set
, elt
);
1010 elt_len
= strlen(elt
);
1011 path_set_len
= strlen(path_set
);
1013 /* path_set includes only elt or
1014 * path_set includes elt as the first element.
1017 && ((path_set_len
== elt_len
)
1018 || (path_set
[elt_len
] == sep
)))
1021 /* path_set includes elt at the middle
1022 * or as the last element.
1024 if ((*(tmp
- 1) == sep
)
1025 && ((*(tmp
+ elt_len
) == sep
)
1026 || (*(tmp
+ elt_len
) == '\0')))
1032 static int nsfs_xasputs(char **str
,
1033 struct lsns_namespace
*ns
,
1034 struct libmnt_table
*tab
,
1037 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_FORWARD
);
1038 char *expected_root
;
1039 struct libmnt_fs
*fs
= NULL
;
1041 xasprintf(&expected_root
, "%s:[%ju]", ns_names
[ns
->type
], (uintmax_t)ns
->id
);
1044 while (mnt_table_find_next_fs(tab
, itr
, is_nsfs_root
,
1045 expected_root
, &fs
) == 0) {
1047 const char *tgt
= mnt_fs_get_target(fs
);
1050 xasprintf(str
, "%s", tgt
);
1052 else if (!is_path_included(*str
, tgt
, sep
)) {
1055 xasprintf(&tmp
, "%s%c%s", *str
, sep
, tgt
);
1060 free(expected_root
);
1066 static void fill_column(struct lsns
*ls
,
1067 struct lsns_namespace
*ns
,
1068 struct lsns_process
*proc
,
1069 struct libscols_line
*line
,
1070 size_t column_index
)
1074 switch (get_column_id(column_index
)) {
1076 xasprintf(&str
, "%ju", (uintmax_t)ns
->id
);
1080 xasprintf(&str
, "%d", (int) proc
->pid
);
1084 xasprintf(&str
, "%d", (int) proc
->ppid
);
1087 xasprintf(&str
, "%s", ns_names
[ns
->type
]);
1090 xasprintf(&str
, "%d", ns
->nprocs
);
1095 str
= pid_get_cmdline(proc
->pid
);
1097 str
= pid_get_cmdname(proc
->pid
);
1102 xasprintf(&str
, "/proc/%d/ns/%s", (int) proc
->pid
, ns_names
[ns
->type
]);
1105 xasprintf(&str
, "%d", proc
? (int) proc
->uid
: (int) ns
->uid_fallback
);
1108 xasprintf(&str
, "%s", get_id(uid_cache
, proc
? proc
->uid
: ns
->uid_fallback
)->name
);
1113 if (ns
->type
== LSNS_ID_NET
)
1114 netnsid_xasputs(&str
, proc
->netnsid
);
1117 nsfs_xasputs(&str
, ns
, ls
->tab
, ls
->no_wrap
? ',' : '\n');
1120 xasprintf(&str
, "%ju", (uintmax_t)ns
->related_id
[RELA_PARENT
]);
1123 xasprintf(&str
, "%ju", (uintmax_t)ns
->related_id
[RELA_OWNER
]);
1129 if (str
&& scols_line_refer_data(line
, column_index
, str
) != 0)
1134 static int filter_filler_cb(
1135 struct libscols_filter
*filter
__attribute__((__unused__
)),
1136 struct libscols_line
*line
,
1137 size_t column_index
,
1140 struct filler_data
*fid
= (struct filler_data
*) userdata
;
1142 fill_column(fid
->ls
, fid
->ns
, fid
->proc
, line
, column_index
);
1146 static void add_scols_line(struct lsns
*ls
, struct libscols_table
*table
,
1147 struct lsns_namespace
*ns
, struct lsns_process
*proc
)
1150 struct libscols_line
*line
;
1155 line
= scols_table_new_line(table
,
1156 (ls
->tree
== LSNS_TREE_PROCESS
&& proc
) && proc
->parent
? proc
->parent
->outline
:
1157 (ls
->tree
== LSNS_TREE_PARENT
) && ns
->related_ns
[RELA_PARENT
] ? ns
->related_ns
[RELA_PARENT
]->ns_outline
:
1158 (ls
->tree
== LSNS_TREE_OWNER
) && ns
->related_ns
[RELA_OWNER
] ? ns
->related_ns
[RELA_OWNER
]->ns_outline
:
1161 warn(_("failed to add line to output"));
1167 struct filler_data fid
= {
1173 scols_filter_set_filler_cb(ls
->filter
,
1174 filter_filler_cb
, (void *) &fid
);
1176 if (scols_line_apply_filter(line
, ls
->filter
, &status
))
1177 err(EXIT_FAILURE
, _("failed to apply filter"));
1179 struct libscols_line
*x
= scols_line_get_parent(line
);
1182 scols_line_remove_child(x
, line
);
1184 scols_table_remove_line(table
, line
);
1189 for (i
= 0; i
< ncolumns
; i
++) {
1190 if (scols_line_is_filled(line
, i
))
1192 fill_column(ls
, ns
, proc
, line
, i
);
1195 if (ls
->tree
== LSNS_TREE_OWNER
|| ls
->tree
== LSNS_TREE_PARENT
)
1196 ns
->ns_outline
= line
;
1198 proc
->outline
= line
;
1201 static struct libscols_table
*init_scols_table(struct lsns
*ls
)
1203 struct libscols_table
*tab
;
1206 tab
= scols_new_table();
1208 warn(_("failed to initialize output table"));
1212 scols_table_enable_raw(tab
, ls
->raw
);
1213 scols_table_enable_json(tab
, ls
->json
);
1214 scols_table_enable_noheadings(tab
, ls
->no_headings
);
1217 scols_table_set_name(tab
, "namespaces");
1219 for (i
= 0; i
< ncolumns
; i
++) {
1220 const struct colinfo
*col
= get_column_info(i
);
1221 int flags
= col
->flags
;
1222 struct libscols_column
*cl
;
1225 flags
&= ~SCOLS_FL_TRUNC
;
1226 if (ls
->tree
== LSNS_TREE_PROCESS
&& get_column_id(i
) == COL_COMMAND
)
1227 flags
|= SCOLS_FL_TREE
;
1229 flags
&= ~SCOLS_FL_WRAP
;
1230 if ((ls
->tree
== LSNS_TREE_OWNER
|| ls
->tree
== LSNS_TREE_PARENT
)
1231 && get_column_id(i
) == COL_NS
) {
1232 flags
|= SCOLS_FL_TREE
;
1233 flags
&= ~SCOLS_FL_RIGHT
;
1236 cl
= scols_table_new_column(tab
, col
->name
, col
->whint
, flags
);
1238 warnx(_("failed to initialize output column"));
1241 if (ls
->json
|| ls
->filter
)
1242 scols_column_set_json_type(cl
, col
->json_type
);
1244 if (!ls
->no_wrap
&& get_column_id(i
) == COL_NSFS
) {
1245 scols_column_set_wrapfunc(cl
,
1246 scols_wrapnl_chunksize
,
1247 scols_wrapnl_nextchunk
,
1249 scols_column_set_safechars(cl
, "\n");
1255 scols_unref_table(tab
);
1259 static void show_namespace(struct lsns
*ls
, struct libscols_table
*tab
,
1260 struct lsns_namespace
*ns
, struct lsns_process
*proc
)
1263 * create a tree from owner->owned and/or parent->child relation
1265 if (ls
->tree
== LSNS_TREE_OWNER
1266 && ns
->related_ns
[RELA_OWNER
]
1267 && !ns
->related_ns
[RELA_OWNER
]->ns_outline
)
1268 show_namespace(ls
, tab
, ns
->related_ns
[RELA_OWNER
], ns
->related_ns
[RELA_OWNER
]->proc
);
1269 else if (ls
->tree
== LSNS_TREE_PARENT
) {
1270 if (ns
->related_ns
[RELA_PARENT
]) {
1271 if (!ns
->related_ns
[RELA_PARENT
]->ns_outline
)
1272 show_namespace(ls
, tab
, ns
->related_ns
[RELA_PARENT
], ns
->related_ns
[RELA_PARENT
]->proc
);
1274 else if (ns
->related_ns
[RELA_OWNER
] && !ns
->related_ns
[RELA_OWNER
]->ns_outline
)
1275 show_namespace(ls
, tab
, ns
->related_ns
[RELA_OWNER
], ns
->related_ns
[RELA_OWNER
]->proc
);
1278 add_scols_line(ls
, tab
, ns
, proc
);
1281 static inline void add_column(int id
)
1283 if (ncolumns
>= ARRAY_SIZE(columns
))
1284 errx(EXIT_FAILURE
, _("too many columns specified, "
1285 "the limit is %zu columns"),
1286 ARRAY_SIZE(columns
) - 1);
1287 columns
[ ncolumns
++ ] = id
;
1290 static void init_scols_filter(struct libscols_table
*tb
, struct libscols_filter
*f
)
1292 struct libscols_iter
*itr
;
1293 const char *name
= NULL
;
1296 itr
= scols_new_iter(SCOLS_ITER_FORWARD
);
1298 err(EXIT_FAILURE
, _("failed to allocate iterator"));
1300 while (scols_filter_next_holder(f
, itr
, &name
, 0) == 0) {
1301 struct libscols_column
*col
= scols_table_get_column_by_name(tb
, name
);
1302 int id
= column_name_to_id(name
, strlen(name
));
1303 const struct colinfo
*ci
= id
>= 0 ? &infos
[id
] : NULL
;
1307 continue; /* report all unknown columns */
1311 col
= scols_table_new_column(tb
, ci
->name
,
1312 ci
->whint
, SCOLS_FL_HIDDEN
);
1314 err(EXIT_FAILURE
,_("failed to allocate output column"));
1316 scols_column_set_json_type(col
, ci
->json_type
);
1319 scols_filter_assign_column(f
, itr
, name
, col
);
1322 scols_free_iter(itr
);
1327 errx(EXIT_FAILURE
, _("failed to initialize filter"));
1330 static int show_namespaces(struct lsns
*ls
)
1332 struct libscols_table
*tab
;
1333 struct list_head
*p
;
1336 tab
= init_scols_table(ls
);
1340 init_scols_filter(tab
, ls
->filter
);
1342 list_for_each(p
, &ls
->namespaces
) {
1343 struct lsns_namespace
*ns
= list_entry(p
, struct lsns_namespace
, namespaces
);
1345 if (ls
->fltr_pid
!= 0 && !namespace_has_process(ns
, ls
->fltr_pid
))
1347 if (ls
->persist
&& ns
->nprocs
!= 0)
1350 if (!ns
->ns_outline
)
1351 show_namespace(ls
, tab
, ns
, ns
->proc
);
1354 scols_print_table(tab
);
1355 scols_unref_table(tab
);
1359 static void show_process(struct lsns
*ls
, struct libscols_table
*tab
,
1360 struct lsns_process
*proc
, struct lsns_namespace
*ns
)
1363 * create a tree from parent->child relation, but only if the parent is
1364 * within the same namespace
1366 if (ls
->tree
== LSNS_TREE_PROCESS
1368 && !proc
->parent
->outline
1369 && proc
->parent
->ns_ids
[ns
->type
] == proc
->ns_ids
[ns
->type
])
1370 show_process(ls
, tab
, proc
->parent
, ns
);
1372 add_scols_line(ls
, tab
, ns
, proc
);
1376 static int show_namespace_processes(struct lsns
*ls
, struct lsns_namespace
*ns
)
1378 struct libscols_table
*tab
;
1379 struct list_head
*p
;
1381 tab
= init_scols_table(ls
);
1385 list_for_each(p
, &ns
->processes
) {
1386 struct lsns_process
*proc
= list_entry(p
, struct lsns_process
, ns_siblings
[ns
->type
]);
1389 show_process(ls
, tab
, proc
, ns
);
1393 scols_print_table(tab
);
1394 scols_unref_table(tab
);
1398 static void free_lsns_process(struct lsns_process
*lsns_p
)
1403 static void free_netnsid_caches(struct netnsid_cache
*cache
)
1408 static void free_lsns_namespace(struct lsns_namespace
*lsns_n
)
1413 static void free_all(struct lsns
*ls
)
1415 list_free(&ls
->processes
, struct lsns_process
, processes
, free_lsns_process
);
1416 list_free(&netnsids_cache
, struct netnsid_cache
, netnsids
, free_netnsid_caches
);
1417 list_free(&ls
->namespaces
, struct lsns_namespace
, namespaces
, free_lsns_namespace
);
1420 static struct libscols_filter
*new_filter(const char *query
)
1422 struct libscols_filter
*f
;
1424 f
= scols_new_filter(NULL
);
1426 err(EXIT_FAILURE
, _("failed to allocate filter"));
1427 if (query
&& scols_filter_parse_string(f
, query
) != 0)
1428 errx(EXIT_FAILURE
, _("failed to parse \"%s\": %s"), query
,
1429 scols_filter_get_errmsg(f
));
1433 static void __attribute__((__noreturn__
)) usage(void)
1437 fputs(USAGE_HEADER
, out
);
1440 _(" %s [options] [<namespace>]\n"), program_invocation_short_name
);
1442 fputs(USAGE_SEPARATOR
, out
);
1443 fputs(_("List system namespaces.\n"), out
);
1445 fputs(USAGE_OPTIONS
, out
);
1446 fputs(_(" -J, --json use JSON output format\n"), out
);
1447 fputs(_(" -l, --list use list format output\n"), out
);
1448 fputs(_(" -n, --noheadings don't print headings\n"), out
);
1449 fputs(_(" -o, --output <list> define which output columns to use\n"), out
);
1450 fputs(_(" --output-all output all columns\n"), out
);
1451 fputs(_(" -P, --persistent namespaces without processes\n"), out
);
1452 fputs(_(" -p, --task <pid> print process namespaces\n"), out
);
1453 fputs(_(" -r, --raw use the raw output format\n"), out
);
1454 fputs(_(" -u, --notruncate don't truncate text in columns\n"), out
);
1455 fputs(_(" -W, --nowrap don't use multi-line representation\n"), out
);
1456 fputs(_(" -t, --type <name> namespace type (mnt, net, ipc, user, pid, uts, cgroup, time)\n"), out
);
1457 fputs(_(" -T, --tree[=<rel>] use tree format (parent, owner, or process)\n"), out
);
1459 fputs(USAGE_SEPARATOR
, out
);
1460 fputs(_(" -H, --list-columns list the available columns\n"), out
);
1461 fprintf(out
, USAGE_HELP_OPTIONS(24));
1462 fprintf(out
, USAGE_MAN_TAIL("lsns(8)"));
1467 static void __attribute__((__noreturn__
)) list_colunms(bool raw
, bool json
)
1469 struct libscols_table
*col_tb
= xcolumn_list_table_new("lsns-columns", stdout
, raw
, json
);
1471 for (size_t i
= 0; i
< ARRAY_SIZE(infos
); i
++)
1472 xcolumn_list_table_append_line(col_tb
, infos
[i
].name
,
1473 infos
[i
].json_type
, NULL
,
1476 scols_print_table(col_tb
);
1477 scols_unref_table(col_tb
);
1482 static dev_t
read_nsfs_dev(void)
1486 if (stat("/proc/self/ns/user", &st
) < 0)
1487 err(EXIT_FAILURE
, _("failed to do stat /proc/self/ns/user"));
1492 int main(int argc
, char *argv
[])
1495 int c
, force_list
= 0;
1497 char *outarg
= NULL
;
1499 OPT_OUTPUT_ALL
= CHAR_MAX
+ 1
1501 static const struct option long_opts
[] = {
1502 { "json", no_argument
, NULL
, 'J' },
1503 { "task", required_argument
, NULL
, 'p' },
1504 { "help", no_argument
, NULL
, 'h' },
1505 { "output", required_argument
, NULL
, 'o' },
1506 { "output-all", no_argument
, NULL
, OPT_OUTPUT_ALL
},
1507 { "persistent", no_argument
, NULL
, 'P' },
1508 { "filter", required_argument
, NULL
, 'Q' },
1509 { "notruncate", no_argument
, NULL
, 'u' },
1510 { "version", no_argument
, NULL
, 'V' },
1511 { "noheadings", no_argument
, NULL
, 'n' },
1512 { "nowrap", no_argument
, NULL
, 'W' },
1513 { "list", no_argument
, NULL
, 'l' },
1514 { "raw", no_argument
, NULL
, 'r' },
1515 { "type", required_argument
, NULL
, 't' },
1516 { "tree", optional_argument
, NULL
, 'T' },
1517 { "list-columns", no_argument
, NULL
, 'H' },
1518 { NULL
, 0, NULL
, 0 }
1521 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
1527 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
1530 setlocale(LC_ALL
, "");
1531 bindtextdomain(PACKAGE
, LOCALEDIR
);
1532 textdomain(PACKAGE
);
1533 close_stdout_atexit();
1536 memset(&ls
, 0, sizeof(ls
));
1538 INIT_LIST_HEAD(&ls
.processes
);
1539 INIT_LIST_HEAD(&ls
.namespaces
);
1540 INIT_LIST_HEAD(&netnsids_cache
);
1542 while ((c
= getopt_long(argc
, argv
,
1543 "JlPp:o:nruhVt:T::WQ:H", long_opts
, NULL
)) != -1) {
1545 err_exclusive_options(c
, long_opts
, excl
, excl_st
);
1557 case OPT_OUTPUT_ALL
:
1558 for (ncolumns
= 0; ncolumns
< ARRAY_SIZE(infos
); ncolumns
++)
1559 columns
[ncolumns
] = ncolumns
;
1565 ls
.fltr_pid
= strtos32_or_err(optarg
, _("invalid PID argument"));
1571 ls
.no_wrap
= ls
.raw
= 1;
1578 int type
= ns_name2type(optarg
);
1580 errx(EXIT_FAILURE
, _("unknown namespace type: %s"), optarg
);
1581 ls
.fltr_types
[type
] = 1;
1583 if (type
== LSNS_ID_NET
)
1591 ls
.tree
= LSNS_TREE_OWNER
;
1595 if (strcmp (optarg
, "parent") == 0)
1596 ls
.tree
= LSNS_TREE_PARENT
;
1597 else if (strcmp (optarg
, "process") == 0)
1598 ls
.tree
= LSNS_TREE_PROCESS
;
1599 else if (strcmp (optarg
, "owner") != 0)
1600 errx(EXIT_FAILURE
, _("unknown tree type: %s"), optarg
);
1604 ls
.filter
= new_filter(optarg
);
1607 list_colunms(ls
.raw
, ls
.json
);
1612 print_version(EXIT_SUCCESS
);
1614 errtryhelp(EXIT_FAILURE
);
1618 if (!ls
.fltr_ntypes
) {
1621 for (i
= 0; i
< ARRAY_SIZE(ns_names
); i
++)
1622 ls
.fltr_types
[i
] = 1;
1625 if (optind
< argc
) {
1627 errx(EXIT_FAILURE
, _("--task is mutually exclusive with <namespace>"));
1628 ls
.fltr_ns
= strtou64_or_err(argv
[optind
], _("invalid namespace argument"));
1629 if (!ls
.tree
&& !force_list
)
1630 ls
.tree
= LSNS_TREE_PROCESS
;
1633 columns
[ncolumns
++] = COL_PID
;
1634 columns
[ncolumns
++] = COL_PPID
;
1635 columns
[ncolumns
++] = COL_USER
;
1636 columns
[ncolumns
++] = COL_COMMAND
;
1641 columns
[ncolumns
++] = COL_NS
;
1642 columns
[ncolumns
++] = COL_TYPE
;
1643 columns
[ncolumns
++] = COL_NPROCS
;
1644 columns
[ncolumns
++] = COL_PID
;
1645 columns
[ncolumns
++] = COL_USER
;
1647 columns
[ncolumns
++] = COL_NETNSID
;
1648 columns
[ncolumns
++] = COL_NSFS
;
1650 columns
[ncolumns
++] = COL_COMMAND
;
1652 if (!ls
.tree
&& !force_list
)
1653 ls
.tree
= LSNS_TREE_PROCESS
;
1656 #ifndef USE_NS_GET_API
1657 if (ls
.tree
&& ls
.tree
!= LSNS_TREE_PROCESS
)
1658 errx(EXIT_FAILURE
, _("--tree={parent|owner} is unsupported for your system"));
1660 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
1661 &ncolumns
, column_name_to_id
) < 0)
1662 return EXIT_FAILURE
;
1664 scols_init_debug(0);
1666 uid_cache
= new_idcache();
1668 err(EXIT_FAILURE
, _("failed to allocate UID cache"));
1670 #ifdef HAVE_LINUX_NET_NAMESPACE_H
1671 if (has_column(COL_NETNSID
))
1672 netlink_fd
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
1674 ls
.tab
= mnt_new_table_from_file(_PATH_PROC_MOUNTINFO
);
1676 err(MNT_EX_FAIL
, _("failed to parse %s"), _PATH_PROC_MOUNTINFO
);
1678 ls
.nsfs_dev
= read_nsfs_dev();
1680 r
= read_processes(&ls
);
1682 r
= read_namespaces(&ls
);
1685 struct lsns_namespace
*ns
= get_namespace(&ls
, ls
.fltr_ns
);
1688 errx(EXIT_FAILURE
, _("not found namespace: %ju"), (uintmax_t) ls
.fltr_ns
);
1689 r
= show_namespace_processes(&ls
, ns
);
1691 r
= show_namespaces(&ls
);
1694 scols_unref_filter(ls
.filter
);
1695 mnt_free_table(ls
.tab
);
1696 if (netlink_fd
>= 0)
1698 free_idcache(uid_cache
);
1703 case 0: return EXIT_SUCCESS
;
1704 case -ENOTTY
: return EXIT_UNSUPPORTED_IOCTL
;
1705 default: return EXIT_FAILURE
;