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>
33 #ifdef HAVE_LINUX_NET_NAMESPACE_H
35 # include <sys/socket.h>
36 # include <linux/netlink.h>
37 # include <linux/rtnetlink.h>
38 # include <linux/net_namespace.h>
41 #ifdef HAVE_LINUX_NSFS_H
42 # include <linux/nsfs.h>
43 # if defined(NS_GET_NSTYPE) && defined(NS_GET_OWNER_UID)
44 # define USE_NS_GET_API 1
48 #include "pathnames.h"
53 #include "closestream.h"
57 #include "namespace.h"
59 #include "fileutils.h"
63 static UL_DEBUG_DEFINE_MASK(lsns
);
64 UL_DEBUG_DEFINE_MASKNAMES(lsns
) = UL_DEBUG_EMPTY_MASKNAMES
;
66 #define LSNS_DEBUG_INIT (1 << 1)
67 #define LSNS_DEBUG_PROC (1 << 2)
68 #define LSNS_DEBUG_NS (1 << 3)
69 #define LSNS_DEBUG_ALL 0xFFFF
71 #define LSNS_NETNS_UNUSABLE -2
73 #define DBG(m, x) __UL_DBG(lsns, LSNS_DEBUG_, m, x)
74 #define ON_DBG(m, x) __UL_DBG_CALL(lsns, LSNS_DEBUG_, m, x)
76 #define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(lsns)
79 static struct idcache
*uid_cache
= NULL
;
94 COL_PNS
, /* parent namespace */
95 COL_ONS
, /* owner namespace */
100 const char *name
; /* header */
101 double whint
; /* width hint (N < 1 is in percent of termwidth) */
102 int flags
; /* SCOLS_FL_* */
107 /* columns descriptions */
108 static const struct colinfo infos
[] = {
109 [COL_NS
] = { "NS", 10, SCOLS_FL_RIGHT
, N_("namespace identifier (inode number)"), SCOLS_JSON_NUMBER
},
110 [COL_TYPE
] = { "TYPE", 5, 0, N_("kind of namespace") },
111 [COL_PATH
] = { "PATH", 0, 0, N_("path to the namespace")},
112 [COL_NPROCS
] = { "NPROCS", 5, SCOLS_FL_RIGHT
, N_("number of processes in the namespace"), SCOLS_JSON_NUMBER
},
113 [COL_PID
] = { "PID", 5, SCOLS_FL_RIGHT
, N_("lowest PID in the namespace"), SCOLS_JSON_NUMBER
},
114 [COL_PPID
] = { "PPID", 5, SCOLS_FL_RIGHT
, N_("PPID of the PID"), SCOLS_JSON_NUMBER
},
115 [COL_COMMAND
] = { "COMMAND", 0, SCOLS_FL_TRUNC
, N_("command line of the PID")},
116 [COL_UID
] = { "UID", 0, SCOLS_FL_RIGHT
, N_("UID of the PID"), SCOLS_JSON_NUMBER
},
117 [COL_USER
] = { "USER", 0, 0, N_("username of the PID")},
118 [COL_NETNSID
] = { "NETNSID", 0, SCOLS_FL_RIGHT
, N_("namespace ID as used by network subsystem")},
119 [COL_NSFS
] = { "NSFS", 0, SCOLS_FL_WRAP
, N_("nsfs mountpoint (usually used network subsystem)")},
120 [COL_PNS
] = { "PNS", 10, SCOLS_FL_RIGHT
, N_("parent namespace identifier (inode number)"), SCOLS_JSON_NUMBER
},
121 [COL_ONS
] = { "ONS", 10, SCOLS_FL_RIGHT
, N_("owner namespace identifier (inode number)"), SCOLS_JSON_NUMBER
},
124 static int columns
[ARRAY_SIZE(infos
) * 2];
125 static size_t ncolumns
;
138 static char *ns_names
[] = {
139 [LSNS_ID_MNT
] = "mnt",
140 [LSNS_ID_NET
] = "net",
141 [LSNS_ID_PID
] = "pid",
142 [LSNS_ID_UTS
] = "uts",
143 [LSNS_ID_IPC
] = "ipc",
144 [LSNS_ID_USER
] = "user",
145 [LSNS_ID_CGROUP
] = "cgroup",
146 [LSNS_ID_TIME
] = "time"
155 struct lsns_namespace
{
157 int type
; /* LSNS_* */
160 ino_t related_id
[MAX_RELA
];
162 struct lsns_process
*proc
;
164 struct lsns_namespace
*related_ns
[MAX_RELA
];
165 struct libscols_line
*ns_outline
;
166 uid_t uid_fallback
; /* refer this member if `proc' is NULL. */
168 struct list_head namespaces
; /* lsns->processes member */
169 struct list_head processes
; /* head of lsns_process *siblings */
172 struct lsns_process
{
173 pid_t pid
; /* process PID */
174 pid_t ppid
; /* parent's PID */
175 pid_t tpid
; /* thread group */
179 ino_t ns_ids
[ARRAY_SIZE(ns_names
)];
180 ino_t ns_pids
[ARRAY_SIZE(ns_names
)];
181 ino_t ns_oids
[ARRAY_SIZE(ns_names
)];
183 struct list_head ns_siblings
[ARRAY_SIZE(ns_names
)];
185 struct list_head processes
; /* list of processes */
187 struct libscols_line
*outline
;
188 struct lsns_process
*parent
;
202 struct list_head processes
;
203 struct list_head namespaces
;
205 pid_t fltr_pid
; /* filter out by PID */
206 ino_t fltr_ns
; /* filter out by namespace */
207 int fltr_types
[ARRAY_SIZE(ns_names
)];
210 unsigned int raw
: 1,
218 struct libmnt_table
*tab
;
221 struct netnsid_cache
{
224 struct list_head netnsids
;
227 static struct list_head netnsids_cache
;
229 static int netlink_fd
= -1;
231 static void lsns_init_debug(void)
233 __UL_INIT_DEBUG_FROM_ENV(lsns
, LSNS_DEBUG_
, 0, LSNS_DEBUG
);
236 static int ns_name2type(const char *name
)
240 for (i
= 0; i
< ARRAY_SIZE(ns_names
); i
++) {
241 if (strcmp(ns_names
[i
], name
) == 0)
247 static int column_name_to_id(const char *name
, size_t namesz
)
253 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
254 const char *cn
= infos
[i
].name
;
256 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
259 warnx(_("unknown column: %s"), name
);
263 static int has_column(int id
)
267 for (i
= 0; i
< ncolumns
; i
++) {
268 if (columns
[i
] == id
)
274 static inline int get_column_id(int num
)
277 assert((size_t) num
< ncolumns
);
278 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
283 static inline const struct colinfo
*get_column_info(unsigned num
)
285 return &infos
[ get_column_id(num
) ];
288 static int get_ns_ino(int dir
, const char *nsname
, ino_t
*ino
, ino_t
*pino
, ino_t
*oino
)
293 snprintf(path
, sizeof(path
), "ns/%s", nsname
);
295 if (fstatat(dir
, path
, &st
, 0) != 0)
302 #ifdef USE_NS_GET_API
304 fd
= openat(dir
, path
, 0);
307 if (strcmp(nsname
, "pid") == 0 || strcmp(nsname
, "user") == 0) {
308 if ((pfd
= ioctl(fd
, NS_GET_PARENT
)) < 0) {
314 if (fstat(pfd
, &st
) < 0) {
323 if ((ofd
= ioctl(fd
, NS_GET_USERNS
)) < 0) {
329 if (fstat(ofd
, &st
) < 0) {
342 static int parse_proc_stat(FILE *fp
, pid_t
*pid
, char *state
, pid_t
*ppid
)
344 char *line
= NULL
, *p
;
348 if (getline(&line
, &len
, fp
) < 0) {
353 p
= strrchr(line
, ')');
355 sscanf(line
, "%d (", pid
) != 1 ||
356 sscanf(p
, ") %c %d*[^\n]", state
, ppid
) != 2) {
367 #ifdef HAVE_LINUX_NET_NAMESPACE_H
368 static int netnsid_cache_find(ino_t netino
, int *netnsid
)
372 list_for_each(p
, &netnsids_cache
) {
373 struct netnsid_cache
*e
= list_entry(p
,
374 struct netnsid_cache
,
376 if (e
->ino
== netino
) {
385 static void netnsid_cache_add(ino_t netino
, int netnsid
)
387 struct netnsid_cache
*e
;
389 e
= xcalloc(1, sizeof(*e
));
392 INIT_LIST_HEAD(&e
->netnsids
);
393 list_add(&e
->netnsids
, &netnsids_cache
);
396 static int get_netnsid_via_netlink_send_request(int target_fd
)
398 unsigned char req
[NLMSG_SPACE(sizeof(struct rtgenmsg
))
399 + RTA_SPACE(sizeof(int32_t))];
401 struct nlmsghdr
*nlh
= (struct nlmsghdr
*)req
;
402 struct rtgenmsg
*rt
= NLMSG_DATA(req
);
403 struct rtattr
*rta
= (struct rtattr
*)
404 (req
+ NLMSG_SPACE(sizeof(struct rtgenmsg
)));
405 int32_t *fd
= RTA_DATA(rta
);
407 nlh
->nlmsg_len
= sizeof(req
);
408 nlh
->nlmsg_flags
= NLM_F_REQUEST
;
409 nlh
->nlmsg_type
= RTM_GETNSID
;
410 rt
->rtgen_family
= AF_UNSPEC
;
411 rta
->rta_type
= NETNSA_FD
;
412 rta
->rta_len
= RTA_SPACE(sizeof(int32_t));
415 if (send(netlink_fd
, req
, sizeof(req
), 0) < 0)
420 static int get_netnsid_via_netlink_recv_response(int *netnsid
)
422 unsigned char res
[NLMSG_SPACE(sizeof(struct rtgenmsg
))
423 + ((RTA_SPACE(sizeof(int32_t))
424 < RTA_SPACE(sizeof(struct nlmsgerr
)))
425 ? RTA_SPACE(sizeof(struct nlmsgerr
))
426 : RTA_SPACE(sizeof(int32_t)))];
430 struct nlmsghdr
*nlh
;
433 reslen
= recv(netlink_fd
, res
, sizeof(res
), 0);
437 nlh
= (struct nlmsghdr
*)res
;
438 if (!(NLMSG_OK(nlh
, (size_t)reslen
)
439 && nlh
->nlmsg_type
== RTM_NEWNSID
))
442 rtalen
= NLMSG_PAYLOAD(nlh
, sizeof(struct rtgenmsg
));
443 rta
= (struct rtattr
*)(res
+ NLMSG_SPACE(sizeof(struct rtgenmsg
)));
444 if (!(RTA_OK(rta
, rtalen
)
445 && rta
->rta_type
== NETNSA_NSID
))
448 *netnsid
= *(int *)RTA_DATA(rta
);
453 static int get_netnsid_via_netlink(int dir
, const char *path
)
459 return LSNS_NETNS_UNUSABLE
;
461 target_fd
= openat(dir
, path
, O_RDONLY
);
463 return LSNS_NETNS_UNUSABLE
;
465 if (get_netnsid_via_netlink_send_request(target_fd
) < 0) {
466 netnsid
= LSNS_NETNS_UNUSABLE
;
470 if (get_netnsid_via_netlink_recv_response(&netnsid
) < 0) {
471 netnsid
= LSNS_NETNS_UNUSABLE
;
480 static int get_netnsid(int dir
, ino_t netino
)
484 if (!netnsid_cache_find(netino
, &netnsid
)) {
485 netnsid
= get_netnsid_via_netlink(dir
, "ns/net");
486 netnsid_cache_add(netino
, netnsid
);
492 static int get_netnsid(int dir
__attribute__((__unused__
)),
493 ino_t netino
__attribute__((__unused__
)))
495 return LSNS_NETNS_UNUSABLE
;
497 #endif /* HAVE_LINUX_NET_NAMESPACE_H */
499 static int read_process(struct lsns
*ls
, pid_t pid
)
501 struct lsns_process
*p
= NULL
;
509 DBG(PROC
, ul_debug("reading %d", (int) pid
));
511 snprintf(buf
, sizeof(buf
), "/proc/%d", pid
);
516 p
= xcalloc(1, sizeof(*p
));
517 p
->netnsid
= LSNS_NETNS_UNUSABLE
;
519 if (fstat(dirfd(dir
), &st
) == 0) {
521 add_uid(uid_cache
, st
.st_uid
);
524 fd
= openat(dirfd(dir
), "stat", O_RDONLY
);
529 if (!(f
= fdopen(fd
, "r"))) {
533 rc
= parse_proc_stat(f
, &p
->pid
, &p
->state
, &p
->ppid
);
538 for (i
= 0; i
< ARRAY_SIZE(p
->ns_ids
); i
++) {
539 INIT_LIST_HEAD(&p
->ns_siblings
[i
]);
541 if (!ls
->fltr_types
[i
])
544 rc
= get_ns_ino(dirfd(dir
), ns_names
[i
], &p
->ns_ids
[i
],
545 &p
->ns_pids
[i
], &p
->ns_oids
[i
]);
546 if (rc
&& rc
!= -EACCES
&& rc
!= -ENOENT
)
548 if (i
== LSNS_ID_NET
)
549 p
->netnsid
= get_netnsid(dirfd(dir
), p
->ns_ids
[i
]);
553 INIT_LIST_HEAD(&p
->processes
);
555 DBG(PROC
, ul_debugobj(p
, "new pid=%d", p
->pid
));
556 list_add_tail(&p
->processes
, &ls
->processes
);
566 static int read_processes(struct lsns
*ls
)
572 DBG(PROC
, ul_debug("opening /proc"));
574 dir
= opendir(_PATH_PROC
);
578 while ((d
= xreaddir(dir
))) {
581 if (procfs_dirent_get_pid(d
, &pid
) != 0)
584 /* TODO: use ul_new_procfs_path(pid, NULL) to read files from /proc/pid/
586 rc
= read_process(ls
, pid
);
587 if (rc
&& rc
!= -EACCES
&& rc
!= -ENOENT
)
592 DBG(PROC
, ul_debug("closing /proc"));
597 static struct lsns_namespace
*get_namespace(struct lsns
*ls
, ino_t ino
)
601 list_for_each(p
, &ls
->namespaces
) {
602 struct lsns_namespace
*ns
= list_entry(p
, struct lsns_namespace
, namespaces
);
610 static int namespace_has_process(struct lsns_namespace
*ns
, pid_t pid
)
614 list_for_each(p
, &ns
->processes
) {
615 struct lsns_process
*proc
= list_entry(p
, struct lsns_process
, ns_siblings
[ns
->type
]);
617 if (proc
->pid
== pid
)
623 static struct lsns_namespace
*add_namespace(struct lsns
*ls
, int type
, ino_t ino
,
624 ino_t parent_ino
, ino_t owner_ino
)
626 struct lsns_namespace
*ns
= xcalloc(1, sizeof(*ns
));
631 DBG(NS
, ul_debugobj(ns
, "new %s[%ju]", ns_names
[type
], (uintmax_t)ino
));
633 INIT_LIST_HEAD(&ns
->processes
);
634 INIT_LIST_HEAD(&ns
->namespaces
);
638 ns
->related_id
[RELA_PARENT
] = parent_ino
;
639 ns
->related_id
[RELA_OWNER
] = owner_ino
;
641 list_add_tail(&ns
->namespaces
, &ls
->namespaces
);
645 static int add_process_to_namespace(struct lsns
*ls
, struct lsns_namespace
*ns
, struct lsns_process
*proc
)
649 DBG(NS
, ul_debugobj(ns
, "add process [%p] pid=%d to %s[%ju]",
650 proc
, proc
->pid
, ns_names
[ns
->type
], (uintmax_t)ns
->id
));
652 list_for_each(p
, &ls
->processes
) {
653 struct lsns_process
*xproc
= list_entry(p
, struct lsns_process
, processes
);
655 if (xproc
->pid
== proc
->ppid
) /* my parent */
656 proc
->parent
= xproc
;
657 else if (xproc
->ppid
== proc
->pid
) /* my child */
658 xproc
->parent
= proc
;
661 list_add_tail(&proc
->ns_siblings
[ns
->type
], &ns
->processes
);
664 if (!ns
->proc
|| ns
->proc
->pid
> proc
->pid
)
670 static int cmp_namespaces(struct list_head
*a
, struct list_head
*b
,
671 __attribute__((__unused__
)) void *data
)
673 struct lsns_namespace
*xa
= list_entry(a
, struct lsns_namespace
, namespaces
),
674 *xb
= list_entry(b
, struct lsns_namespace
, namespaces
);
676 return cmp_numbers(xa
->id
, xb
->id
);
679 static int netnsid_xasputs(char **str
, int netnsid
)
682 return xasprintf(str
, "%d", netnsid
);
683 #ifdef NETNSA_NSID_NOT_ASSIGNED
684 if (netnsid
== NETNSA_NSID_NOT_ASSIGNED
)
685 return xasprintf(str
, "%s", "unassigned");
690 #ifdef USE_NS_GET_API
691 static int clone_type_to_lsns_type(int clone_type
)
693 switch (clone_type
) {
696 case CLONE_NEWCGROUP
:
697 return LSNS_ID_CGROUP
;
713 static struct lsns_namespace
*add_namespace_for_nsfd(struct lsns
*ls
, int fd
, ino_t ino
)
715 int fd_owner
= -1, fd_parent
= -1;
716 struct stat st_owner
, st_parent
;
717 ino_t ino_owner
= 0, ino_parent
= 0;
718 struct lsns_namespace
*ns
;
719 int clone_type
, lsns_type
;
721 clone_type
= ioctl(fd
, NS_GET_NSTYPE
);
724 lsns_type
= clone_type_to_lsns_type(clone_type
);
728 fd_owner
= ioctl(fd
, NS_GET_USERNS
);
731 if (fstat(fd_owner
, &st_owner
) < 0)
733 ino_owner
= st_owner
.st_ino
;
736 fd_parent
= ioctl(fd
, NS_GET_PARENT
);
739 if (fstat(fd_parent
, &st_parent
) < 0)
741 ino_parent
= st_parent
.st_ino
;
744 ns
= add_namespace(ls
, lsns_type
, ino
, ino_parent
, ino_owner
);
745 ioctl(fd
, NS_GET_OWNER_UID
, &ns
->uid_fallback
);
746 add_uid(uid_cache
, ns
->uid_fallback
);
748 if ((lsns_type
== LSNS_ID_USER
|| lsns_type
== LSNS_ID_PID
)
749 && ino_parent
!= ino
&& ino_parent
!= 0) {
750 ns
->related_ns
[RELA_PARENT
] = get_namespace(ls
, ino_parent
);
751 if (!ns
->related_ns
[RELA_PARENT
]) {
752 ns
->related_ns
[RELA_PARENT
] = add_namespace_for_nsfd(ls
, fd_parent
, ino_parent
);
753 if (ino_parent
== ino_owner
)
754 ns
->related_ns
[RELA_OWNER
] = ns
->related_ns
[RELA_PARENT
];
758 if (ns
->related_ns
[RELA_OWNER
] == NULL
&& ino_owner
!= 0) {
759 ns
->related_ns
[RELA_OWNER
] = get_namespace(ls
, ino_owner
);
760 if (!ns
->related_ns
[RELA_OWNER
])
761 ns
->related_ns
[RELA_OWNER
] = add_namespace_for_nsfd(ls
, fd_owner
, ino_owner
);
772 static void interpolate_missing_namespaces(struct lsns
*ls
, struct lsns_namespace
*orphan
, int rela
)
774 const int cmd
[MAX_RELA
] = {
775 [RELA_PARENT
] = NS_GET_PARENT
,
776 [RELA_OWNER
] = NS_GET_USERNS
779 int fd_orphan
, fd_missing
;
782 orphan
->related_ns
[rela
] = get_namespace(ls
, orphan
->related_id
[rela
]);
783 if (orphan
->related_ns
[rela
])
786 snprintf(buf
, sizeof(buf
), "/proc/%d/ns/%s", orphan
->proc
->pid
, ns_names
[orphan
->type
]);
787 fd_orphan
= open(buf
, O_RDONLY
);
791 fd_missing
= ioctl(fd_orphan
, cmd
[rela
]);
796 if (fstat(fd_missing
, &st
) < 0
797 || st
.st_ino
!= orphan
->related_id
[rela
]) {
802 orphan
->related_ns
[rela
] = add_namespace_for_nsfd(ls
, fd_missing
, orphan
->related_id
[rela
]);
806 static void read_related_namespaces(struct lsns
*ls
)
809 struct lsns_namespace
*orphan
[2] = {NULL
, NULL
};
812 list_for_each(p
, &ls
->namespaces
) {
813 struct lsns_namespace
*ns
= list_entry(p
, struct lsns_namespace
, namespaces
);
814 struct list_head
*pp
;
815 list_for_each(pp
, &ls
->namespaces
) {
816 struct lsns_namespace
*pns
= list_entry(pp
, struct lsns_namespace
, namespaces
);
817 if (ns
->type
== LSNS_ID_USER
818 || ns
->type
== LSNS_ID_PID
) {
819 if (ns
->related_id
[RELA_PARENT
] == pns
->id
)
820 ns
->related_ns
[RELA_PARENT
] = pns
;
821 if (ns
->related_id
[RELA_OWNER
] == pns
->id
)
822 ns
->related_ns
[RELA_OWNER
] = pns
;
823 if (ns
->related_ns
[RELA_PARENT
] && ns
->related_ns
[RELA_OWNER
])
826 if (ns
->related_id
[RELA_OWNER
] == pns
->id
) {
827 ns
->related_ns
[RELA_OWNER
] = pns
;
833 /* lsns scans /proc/[0-9]+ for finding namespaces.
834 * So if a namespace has no process, lsns cannot
835 * find it. Here we call it a missing namespace.
837 * If the id for a related namesspce is known but
838 * namespace for the id is not found, there must
839 * be orphan namespaces. A missing namespace is an
840 * owner or a parent of the orphan namespace.
842 for (rela
= 0; rela
< MAX_RELA
; rela
++) {
843 if (ns
->related_id
[rela
] != 0
844 && ns
->related_ns
[rela
] == NULL
) {
845 ns
->related_ns
[rela
] = orphan
[rela
];
851 for (rela
= 0; rela
< MAX_RELA
; rela
++) {
852 while (orphan
[rela
]) {
853 struct lsns_namespace
*current
= orphan
[rela
];
854 orphan
[rela
] = orphan
[rela
]->related_ns
[rela
];
855 current
->related_ns
[rela
] = NULL
;
856 interpolate_missing_namespaces(ls
, current
, rela
);
861 #endif /* USE_NS_GET_API */
863 static int read_namespaces(struct lsns
*ls
)
867 DBG(NS
, ul_debug("reading namespace"));
869 list_for_each(p
, &ls
->processes
) {
871 struct lsns_namespace
*ns
;
872 struct lsns_process
*proc
= list_entry(p
, struct lsns_process
, processes
);
874 for (i
= 0; i
< ARRAY_SIZE(proc
->ns_ids
); i
++) {
875 if (proc
->ns_ids
[i
] == 0)
877 if (!(ns
= get_namespace(ls
, proc
->ns_ids
[i
]))) {
878 ns
= add_namespace(ls
, i
, proc
->ns_ids
[i
],
879 proc
->ns_pids
[i
], proc
->ns_oids
[i
]);
883 add_process_to_namespace(ls
, ns
, proc
);
887 #ifdef USE_NS_GET_API
888 if (ls
->tree
== LSNS_TREE_OWNER
|| ls
->tree
== LSNS_TREE_PARENT
)
889 read_related_namespaces(ls
);
891 list_sort(&ls
->namespaces
, cmp_namespaces
, NULL
);
896 static int is_nsfs_root(struct libmnt_fs
*fs
, void *data
)
898 if (!mnt_fs_match_fstype(fs
, "nsfs") || !mnt_fs_get_root(fs
))
901 return (strcmp(mnt_fs_get_root(fs
), (char *)data
) == 0);
904 static int is_path_included(const char *path_set
, const char *elt
,
912 tmp
= strstr(path_set
, elt
);
916 elt_len
= strlen(elt
);
917 path_set_len
= strlen(path_set
);
919 /* path_set includes only elt or
920 * path_set includes elt as the first element.
923 && ((path_set_len
== elt_len
)
924 || (path_set
[elt_len
] == sep
)))
927 /* path_set includes elt at the middle
928 * or as the last element.
930 if ((*(tmp
- 1) == sep
)
931 && ((*(tmp
+ elt_len
) == sep
)
932 || (*(tmp
+ elt_len
) == '\0')))
938 static int nsfs_xasputs(char **str
,
939 struct lsns_namespace
*ns
,
940 struct libmnt_table
*tab
,
943 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_FORWARD
);
945 struct libmnt_fs
*fs
= NULL
;
947 xasprintf(&expected_root
, "%s:[%ju]", ns_names
[ns
->type
], (uintmax_t)ns
->id
);
950 while (mnt_table_find_next_fs(tab
, itr
, is_nsfs_root
,
951 expected_root
, &fs
) == 0) {
953 const char *tgt
= mnt_fs_get_target(fs
);
956 xasprintf(str
, "%s", tgt
);
958 else if (!is_path_included(*str
, tgt
, sep
)) {
961 xasprintf(&tmp
, "%s%c%s", *str
, sep
, tgt
);
971 static void add_scols_line(struct lsns
*ls
, struct libscols_table
*table
,
972 struct lsns_namespace
*ns
, struct lsns_process
*proc
)
975 struct libscols_line
*line
;
980 line
= scols_table_new_line(table
,
981 (ls
->tree
== LSNS_TREE_PROCESS
&& proc
) && proc
->parent
? proc
->parent
->outline
:
982 (ls
->tree
== LSNS_TREE_PARENT
) && ns
->related_ns
[RELA_PARENT
] ? ns
->related_ns
[RELA_PARENT
]->ns_outline
:
983 (ls
->tree
== LSNS_TREE_OWNER
) && ns
->related_ns
[RELA_OWNER
] ? ns
->related_ns
[RELA_OWNER
]->ns_outline
:
986 warn(_("failed to add line to output"));
990 for (i
= 0; i
< ncolumns
; i
++) {
993 switch (get_column_id(i
)) {
995 xasprintf(&str
, "%ju", (uintmax_t)ns
->id
);
999 xasprintf(&str
, "%d", (int) proc
->pid
);
1003 xasprintf(&str
, "%d", (int) proc
->ppid
);
1006 xasprintf(&str
, "%s", ns_names
[ns
->type
]);
1009 xasprintf(&str
, "%d", ns
->nprocs
);
1014 str
= pid_get_cmdline(proc
->pid
);
1016 str
= pid_get_cmdname(proc
->pid
);
1021 xasprintf(&str
, "/proc/%d/ns/%s", (int) proc
->pid
, ns_names
[ns
->type
]);
1024 xasprintf(&str
, "%d", proc
? (int) proc
->uid
: (int) ns
->uid_fallback
);
1027 xasprintf(&str
, "%s", get_id(uid_cache
, proc
? proc
->uid
: ns
->uid_fallback
)->name
);
1032 if (ns
->type
== LSNS_ID_NET
)
1033 netnsid_xasputs(&str
, proc
->netnsid
);
1036 nsfs_xasputs(&str
, ns
, ls
->tab
, ls
->no_wrap
? ',' : '\n');
1039 xasprintf(&str
, "%ju", (uintmax_t)ns
->related_id
[RELA_PARENT
]);
1042 xasprintf(&str
, "%ju", (uintmax_t)ns
->related_id
[RELA_OWNER
]);
1048 if (str
&& scols_line_refer_data(line
, i
, str
) != 0)
1052 if (ls
->tree
== LSNS_TREE_OWNER
|| ls
->tree
== LSNS_TREE_PARENT
)
1053 ns
->ns_outline
= line
;
1055 proc
->outline
= line
;
1058 static struct libscols_table
*init_scols_table(struct lsns
*ls
)
1060 struct libscols_table
*tab
;
1063 tab
= scols_new_table();
1065 warn(_("failed to initialize output table"));
1069 scols_table_enable_raw(tab
, ls
->raw
);
1070 scols_table_enable_json(tab
, ls
->json
);
1071 scols_table_enable_noheadings(tab
, ls
->no_headings
);
1074 scols_table_set_name(tab
, "namespaces");
1076 for (i
= 0; i
< ncolumns
; i
++) {
1077 const struct colinfo
*col
= get_column_info(i
);
1078 int flags
= col
->flags
;
1079 struct libscols_column
*cl
;
1082 flags
&= ~SCOLS_FL_TRUNC
;
1083 if (ls
->tree
== LSNS_TREE_PROCESS
&& get_column_id(i
) == COL_COMMAND
)
1084 flags
|= SCOLS_FL_TREE
;
1086 flags
&= ~SCOLS_FL_WRAP
;
1087 if ((ls
->tree
== LSNS_TREE_OWNER
|| ls
->tree
== LSNS_TREE_PARENT
)
1088 && get_column_id(i
) == COL_NS
) {
1089 flags
|= SCOLS_FL_TREE
;
1090 flags
&= ~SCOLS_FL_RIGHT
;
1093 cl
= scols_table_new_column(tab
, col
->name
, col
->whint
, flags
);
1095 warnx(_("failed to initialize output column"));
1099 scols_column_set_json_type(cl
, col
->json_type
);
1101 if (!ls
->no_wrap
&& get_column_id(i
) == COL_NSFS
) {
1102 scols_column_set_wrapfunc(cl
,
1103 scols_wrapnl_chunksize
,
1104 scols_wrapnl_nextchunk
,
1106 scols_column_set_safechars(cl
, "\n");
1112 scols_unref_table(tab
);
1116 static void show_namespace(struct lsns
*ls
, struct libscols_table
*tab
,
1117 struct lsns_namespace
*ns
, struct lsns_process
*proc
)
1120 * create a tree from owner->owned and/or parent->child relation
1122 if (ls
->tree
== LSNS_TREE_OWNER
1123 && ns
->related_ns
[RELA_OWNER
]
1124 && !ns
->related_ns
[RELA_OWNER
]->ns_outline
)
1125 show_namespace(ls
, tab
, ns
->related_ns
[RELA_OWNER
], ns
->related_ns
[RELA_OWNER
]->proc
);
1126 else if (ls
->tree
== LSNS_TREE_PARENT
) {
1127 if (ns
->related_ns
[RELA_PARENT
]) {
1128 if (!ns
->related_ns
[RELA_PARENT
]->ns_outline
)
1129 show_namespace(ls
, tab
, ns
->related_ns
[RELA_PARENT
], ns
->related_ns
[RELA_PARENT
]->proc
);
1131 else if (ns
->related_ns
[RELA_OWNER
] && !ns
->related_ns
[RELA_OWNER
]->ns_outline
)
1132 show_namespace(ls
, tab
, ns
->related_ns
[RELA_OWNER
], ns
->related_ns
[RELA_OWNER
]->proc
);
1135 add_scols_line(ls
, tab
, ns
, proc
);
1138 static int show_namespaces(struct lsns
*ls
)
1140 struct libscols_table
*tab
;
1141 struct list_head
*p
;
1144 tab
= init_scols_table(ls
);
1148 list_for_each(p
, &ls
->namespaces
) {
1149 struct lsns_namespace
*ns
= list_entry(p
, struct lsns_namespace
, namespaces
);
1151 if (ls
->fltr_pid
!= 0 && !namespace_has_process(ns
, ls
->fltr_pid
))
1154 if (!ns
->ns_outline
)
1155 show_namespace(ls
, tab
, ns
, ns
->proc
);
1158 scols_print_table(tab
);
1159 scols_unref_table(tab
);
1163 static void show_process(struct lsns
*ls
, struct libscols_table
*tab
,
1164 struct lsns_process
*proc
, struct lsns_namespace
*ns
)
1167 * create a tree from parent->child relation, but only if the parent is
1168 * within the same namespace
1170 if (ls
->tree
== LSNS_TREE_PROCESS
1172 && !proc
->parent
->outline
1173 && proc
->parent
->ns_ids
[ns
->type
] == proc
->ns_ids
[ns
->type
])
1174 show_process(ls
, tab
, proc
->parent
, ns
);
1176 add_scols_line(ls
, tab
, ns
, proc
);
1180 static int show_namespace_processes(struct lsns
*ls
, struct lsns_namespace
*ns
)
1182 struct libscols_table
*tab
;
1183 struct list_head
*p
;
1185 tab
= init_scols_table(ls
);
1189 list_for_each(p
, &ns
->processes
) {
1190 struct lsns_process
*proc
= list_entry(p
, struct lsns_process
, ns_siblings
[ns
->type
]);
1193 show_process(ls
, tab
, proc
, ns
);
1197 scols_print_table(tab
);
1198 scols_unref_table(tab
);
1202 static void free_lsns_process(struct lsns_process
*lsns_p
)
1207 static void free_netnsid_caches(struct netnsid_cache
*cache
)
1212 static void free_lsns_namespace(struct lsns_namespace
*lsns_n
)
1217 static void free_all(struct lsns
*ls
)
1219 list_free(&ls
->processes
, struct lsns_process
, processes
, free_lsns_process
);
1220 list_free(&netnsids_cache
, struct netnsid_cache
, netnsids
, free_netnsid_caches
);
1221 list_free(&ls
->namespaces
, struct lsns_namespace
, namespaces
, free_lsns_namespace
);
1224 static void __attribute__((__noreturn__
)) usage(void)
1229 fputs(USAGE_HEADER
, out
);
1232 _(" %s [options] [<namespace>]\n"), program_invocation_short_name
);
1234 fputs(USAGE_SEPARATOR
, out
);
1235 fputs(_("List system namespaces.\n"), out
);
1237 fputs(USAGE_OPTIONS
, out
);
1238 fputs(_(" -J, --json use JSON output format\n"), out
);
1239 fputs(_(" -l, --list use list format output\n"), out
);
1240 fputs(_(" -n, --noheadings don't print headings\n"), out
);
1241 fputs(_(" -o, --output <list> define which output columns to use\n"), out
);
1242 fputs(_(" --output-all output all columns\n"), out
);
1243 fputs(_(" -p, --task <pid> print process namespaces\n"), out
);
1244 fputs(_(" -r, --raw use the raw output format\n"), out
);
1245 fputs(_(" -u, --notruncate don't truncate text in columns\n"), out
);
1246 fputs(_(" -W, --nowrap don't use multi-line representation\n"), out
);
1247 fputs(_(" -t, --type <name> namespace type (mnt, net, ipc, user, pid, uts, cgroup, time)\n"), out
);
1248 fputs(_(" -T, --tree <rel> use tree format (parent, owner, or process)\n"), out
);
1250 fputs(USAGE_SEPARATOR
, out
);
1251 printf(USAGE_HELP_OPTIONS(24));
1253 fputs(USAGE_COLUMNS
, out
);
1254 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
1255 fprintf(out
, " %11s %s\n", infos
[i
].name
, _(infos
[i
].help
));
1257 printf(USAGE_MAN_TAIL("lsns(8)"));
1263 int main(int argc
, char *argv
[])
1266 int c
, force_list
= 0;
1268 char *outarg
= NULL
;
1270 OPT_OUTPUT_ALL
= CHAR_MAX
+ 1
1272 static const struct option long_opts
[] = {
1273 { "json", no_argument
, NULL
, 'J' },
1274 { "task", required_argument
, NULL
, 'p' },
1275 { "help", no_argument
, NULL
, 'h' },
1276 { "output", required_argument
, NULL
, 'o' },
1277 { "output-all", no_argument
, NULL
, OPT_OUTPUT_ALL
},
1278 { "notruncate", no_argument
, NULL
, 'u' },
1279 { "version", no_argument
, NULL
, 'V' },
1280 { "noheadings", no_argument
, NULL
, 'n' },
1281 { "nowrap", no_argument
, NULL
, 'W' },
1282 { "list", no_argument
, NULL
, 'l' },
1283 { "raw", no_argument
, NULL
, 'r' },
1284 { "type", required_argument
, NULL
, 't' },
1285 { "tree", optional_argument
, NULL
, 'T' },
1286 { NULL
, 0, NULL
, 0 }
1289 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
1294 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
1297 setlocale(LC_ALL
, "");
1298 bindtextdomain(PACKAGE
, LOCALEDIR
);
1299 textdomain(PACKAGE
);
1300 close_stdout_atexit();
1303 memset(&ls
, 0, sizeof(ls
));
1305 INIT_LIST_HEAD(&ls
.processes
);
1306 INIT_LIST_HEAD(&ls
.namespaces
);
1307 INIT_LIST_HEAD(&netnsids_cache
);
1309 while ((c
= getopt_long(argc
, argv
,
1310 "Jlp:o:nruhVt:T::W", long_opts
, NULL
)) != -1) {
1312 err_exclusive_options(c
, long_opts
, excl
, excl_st
);
1324 case OPT_OUTPUT_ALL
:
1325 for (ncolumns
= 0; ncolumns
< ARRAY_SIZE(infos
); ncolumns
++)
1326 columns
[ncolumns
] = ncolumns
;
1329 ls
.fltr_pid
= strtos32_or_err(optarg
, _("invalid PID argument"));
1335 ls
.no_wrap
= ls
.raw
= 1;
1342 int type
= ns_name2type(optarg
);
1344 errx(EXIT_FAILURE
, _("unknown namespace type: %s"), optarg
);
1345 ls
.fltr_types
[type
] = 1;
1347 if (type
== LSNS_ID_NET
)
1355 ls
.tree
= LSNS_TREE_OWNER
;
1359 if (strcmp (optarg
, "parent") == 0)
1360 ls
.tree
= LSNS_TREE_PARENT
;
1361 else if (strcmp (optarg
, "process") == 0)
1362 ls
.tree
= LSNS_TREE_PROCESS
;
1363 else if (strcmp (optarg
, "owner") != 0)
1364 errx(EXIT_FAILURE
, _("unknown tree type: %s"), optarg
);
1371 print_version(EXIT_SUCCESS
);
1373 errtryhelp(EXIT_FAILURE
);
1377 if (!ls
.fltr_ntypes
) {
1380 for (i
= 0; i
< ARRAY_SIZE(ns_names
); i
++)
1381 ls
.fltr_types
[i
] = 1;
1384 if (optind
< argc
) {
1386 errx(EXIT_FAILURE
, _("--task is mutually exclusive with <namespace>"));
1387 ls
.fltr_ns
= strtou64_or_err(argv
[optind
], _("invalid namespace argument"));
1388 if (!ls
.tree
&& !force_list
)
1389 ls
.tree
= LSNS_TREE_PROCESS
;
1392 columns
[ncolumns
++] = COL_PID
;
1393 columns
[ncolumns
++] = COL_PPID
;
1394 columns
[ncolumns
++] = COL_USER
;
1395 columns
[ncolumns
++] = COL_COMMAND
;
1400 columns
[ncolumns
++] = COL_NS
;
1401 columns
[ncolumns
++] = COL_TYPE
;
1402 columns
[ncolumns
++] = COL_NPROCS
;
1403 columns
[ncolumns
++] = COL_PID
;
1404 columns
[ncolumns
++] = COL_USER
;
1406 columns
[ncolumns
++] = COL_NETNSID
;
1407 columns
[ncolumns
++] = COL_NSFS
;
1409 columns
[ncolumns
++] = COL_COMMAND
;
1411 if (!ls
.tree
&& !force_list
)
1412 ls
.tree
= LSNS_TREE_PROCESS
;
1415 #ifndef USE_NS_GET_API
1416 if (ls
.tree
&& ls
.tree
!= LSNS_TREE_PROCESS
)
1417 errx(EXIT_FAILURE
, _("--tree={parent|owner} is unsupported for your system"));
1419 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
1420 &ncolumns
, column_name_to_id
) < 0)
1421 return EXIT_FAILURE
;
1423 scols_init_debug(0);
1425 uid_cache
= new_idcache();
1427 err(EXIT_FAILURE
, _("failed to allocate UID cache"));
1429 #ifdef HAVE_LINUX_NET_NAMESPACE_H
1430 if (has_column(COL_NETNSID
))
1431 netlink_fd
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
1433 if (has_column(COL_NSFS
)) {
1434 ls
.tab
= mnt_new_table_from_file(_PATH_PROC_MOUNTINFO
);
1436 err(MNT_EX_FAIL
, _("failed to parse %s"), _PATH_PROC_MOUNTINFO
);
1439 r
= read_processes(&ls
);
1441 r
= read_namespaces(&ls
);
1444 struct lsns_namespace
*ns
= get_namespace(&ls
, ls
.fltr_ns
);
1447 errx(EXIT_FAILURE
, _("not found namespace: %ju"), (uintmax_t) ls
.fltr_ns
);
1448 r
= show_namespace_processes(&ls
, ns
);
1450 r
= show_namespaces(&ls
);
1453 mnt_free_table(ls
.tab
);
1454 if (netlink_fd
>= 0)
1456 free_idcache(uid_cache
);
1460 return r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;