]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/lsns.c
Make the ways of using output stream consistent in usage()
[thirdparty/util-linux.git] / sys-utils / lsns.c
CommitLineData
8a204562
KZ
1/*
2 * lsns(8) - list system namespaces
3 *
4 * Copyright (C) 2015 Karel Zak <kzak@redhat.com>
5 *
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.
10 *
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.
15 *
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
19 */
20#include <stdio.h>
21#include <string.h>
22#include <getopt.h>
23#include <stdlib.h>
24#include <assert.h>
25#include <dirent.h>
26#include <unistd.h>
27#include <sys/stat.h>
28#include <sys/types.h>
a52852d3 29#include <wchar.h>
8a204562 30#include <libsmartcols.h>
74d2056a 31#include <libmount.h>
8a204562 32
4195756e 33#ifdef HAVE_LINUX_NET_NAMESPACE_H
e9de8d3b
KZ
34# include <stdbool.h>
35# include <sys/socket.h>
36# include <linux/netlink.h>
37# include <linux/rtnetlink.h>
38# include <linux/net_namespace.h>
4195756e
MY
39#endif
40
d652d4c6 41#ifdef HAVE_LINUX_NSFS_H
e9de8d3b 42# include <linux/nsfs.h>
fc686823
KZ
43# if defined(NS_GET_NSTYPE) && defined(NS_GET_OWNER_UID)
44# define USE_NS_GET_API 1
45# endif
d652d4c6
MY
46#endif
47
8a204562
KZ
48#include "pathnames.h"
49#include "nls.h"
50#include "xalloc.h"
51#include "c.h"
52#include "list.h"
53#include "closestream.h"
54#include "optutils.h"
f7f6cc63 55#include "procfs.h"
8a204562
KZ
56#include "strutils.h"
57#include "namespace.h"
a52852d3 58#include "idcache.h"
f7f6cc63 59#include "fileutils.h"
8a204562
KZ
60
61#include "debug.h"
62
2ba641e5 63static UL_DEBUG_DEFINE_MASK(lsns);
8a204562
KZ
64UL_DEBUG_DEFINE_MASKNAMES(lsns) = UL_DEBUG_EMPTY_MASKNAMES;
65
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
70
4195756e
MY
71#define LSNS_NETNS_UNUSABLE -2
72
8a204562
KZ
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)
75
249ba69c
TW
76#define lsns_ioctl(fildes, request, ...) __extension__ ({ \
77 int ret = ioctl(fildes, request, ##__VA_ARGS__); \
78 if (ret == -1 && errno == ENOTTY) \
79 warnx("Unsupported ioctl %s", #request); \
80 ret; })
81
6d00cfb2
KZ
82#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(lsns)
83#include "debugobj.h"
84
249ba69c
TW
85#define EXIT_UNSUPPORTED_IOCTL 2
86
2ba641e5 87static struct idcache *uid_cache = NULL;
a52852d3 88
8a204562
KZ
89/* column IDs */
90enum {
91 COL_NS = 0,
92 COL_TYPE,
93 COL_PATH,
94 COL_NPROCS,
95 COL_PID,
969d3d15 96 COL_PPID,
a52852d3
KZ
97 COL_COMMAND,
98 COL_UID,
4195756e 99 COL_USER,
74d2056a
MY
100 COL_NETNSID,
101 COL_NSFS,
d652d4c6
MY
102 COL_PNS, /* parent namespace */
103 COL_ONS, /* owner namespace */
8a204562
KZ
104};
105
106/* column names */
107struct colinfo {
108 const char *name; /* header */
109 double whint; /* width hint (N < 1 is in percent of termwidth) */
110 int flags; /* SCOLS_FL_* */
111 const char *help;
31b25347 112 int json_type;
8a204562
KZ
113};
114
115/* columns descriptions */
969d3d15 116static const struct colinfo infos[] = {
31b25347 117 [COL_NS] = { "NS", 10, SCOLS_FL_RIGHT, N_("namespace identifier (inode number)"), SCOLS_JSON_NUMBER },
8a204562
KZ
118 [COL_TYPE] = { "TYPE", 5, 0, N_("kind of namespace") },
119 [COL_PATH] = { "PATH", 0, 0, N_("path to the namespace")},
31b25347
KZ
120 [COL_NPROCS] = { "NPROCS", 5, SCOLS_FL_RIGHT, N_("number of processes in the namespace"), SCOLS_JSON_NUMBER },
121 [COL_PID] = { "PID", 5, SCOLS_FL_RIGHT, N_("lowest PID in the namespace"), SCOLS_JSON_NUMBER },
122 [COL_PPID] = { "PPID", 5, SCOLS_FL_RIGHT, N_("PPID of the PID"), SCOLS_JSON_NUMBER },
a52852d3 123 [COL_COMMAND] = { "COMMAND", 0, SCOLS_FL_TRUNC, N_("command line of the PID")},
31b25347 124 [COL_UID] = { "UID", 0, SCOLS_FL_RIGHT, N_("UID of the PID"), SCOLS_JSON_NUMBER},
4195756e 125 [COL_USER] = { "USER", 0, 0, N_("username of the PID")},
f593e279 126 [COL_NETNSID] = { "NETNSID", 0, SCOLS_FL_RIGHT, N_("namespace ID as used by network subsystem")},
d652d4c6
MY
127 [COL_NSFS] = { "NSFS", 0, SCOLS_FL_WRAP, N_("nsfs mountpoint (usually used network subsystem)")},
128 [COL_PNS] = { "PNS", 10, SCOLS_FL_RIGHT, N_("parent namespace identifier (inode number)"), SCOLS_JSON_NUMBER },
129 [COL_ONS] = { "ONS", 10, SCOLS_FL_RIGHT, N_("owner namespace identifier (inode number)"), SCOLS_JSON_NUMBER },
8a204562
KZ
130};
131
132static int columns[ARRAY_SIZE(infos) * 2];
133static size_t ncolumns;
134
135enum {
136 LSNS_ID_MNT = 0,
137 LSNS_ID_NET,
138 LSNS_ID_PID,
139 LSNS_ID_UTS,
140 LSNS_ID_IPC,
2b8889c4 141 LSNS_ID_USER,
96dc4f80
AR
142 LSNS_ID_CGROUP,
143 LSNS_ID_TIME
8a204562
KZ
144};
145
146static char *ns_names[] = {
147 [LSNS_ID_MNT] = "mnt",
148 [LSNS_ID_NET] = "net",
149 [LSNS_ID_PID] = "pid",
150 [LSNS_ID_UTS] = "uts",
151 [LSNS_ID_IPC] = "ipc",
2b8889c4 152 [LSNS_ID_USER] = "user",
96dc4f80
AR
153 [LSNS_ID_CGROUP] = "cgroup",
154 [LSNS_ID_TIME] = "time"
8a204562
KZ
155};
156
c67b83c1
MY
157enum {
158 RELA_PARENT,
159 RELA_OWNER,
160 MAX_RELA
161};
162
8a204562
KZ
163struct lsns_namespace {
164 ino_t id;
165 int type; /* LSNS_* */
166 int nprocs;
4195756e 167 int netnsid;
c67b83c1 168 ino_t related_id[MAX_RELA];
a52852d3
KZ
169
170 struct lsns_process *proc;
8a204562 171
c67b83c1 172 struct lsns_namespace *related_ns[MAX_RELA];
e8c56ae2 173 struct libscols_line *ns_outline;
f91ffe44 174 uid_t uid_fallback; /* refer this member if `proc' is NULL. */
e8c56ae2 175
8a204562
KZ
176 struct list_head namespaces; /* lsns->processes member */
177 struct list_head processes; /* head of lsns_process *siblings */
178};
179
180struct lsns_process {
181 pid_t pid; /* process PID */
182 pid_t ppid; /* parent's PID */
183 pid_t tpid; /* thread group */
184 char state;
a52852d3 185 uid_t uid;
8a204562
KZ
186
187 ino_t ns_ids[ARRAY_SIZE(ns_names)];
d652d4c6
MY
188 ino_t ns_pids[ARRAY_SIZE(ns_names)];
189 ino_t ns_oids[ARRAY_SIZE(ns_names)];
190
8a204562
KZ
191 struct list_head ns_siblings[ARRAY_SIZE(ns_names)];
192
8a204562 193 struct list_head processes; /* list of processes */
969d3d15
KZ
194
195 struct libscols_line *outline;
196 struct lsns_process *parent;
4195756e
MY
197
198 int netnsid;
8a204562
KZ
199};
200
8d27b605 201
e8c56ae2 202enum {
3387ad72 203 LSNS_TREE_NONE,
8d27b605
MY
204 LSNS_TREE_PROCESS,
205 LSNS_TREE_OWNER,
206 LSNS_TREE_PARENT,
e8c56ae2
MY
207};
208
8a204562
KZ
209struct lsns {
210 struct list_head processes;
211 struct list_head namespaces;
212
304fbe8b
KZ
213 pid_t fltr_pid; /* filter out by PID */
214 ino_t fltr_ns; /* filter out by namespace */
215 int fltr_types[ARRAY_SIZE(ns_names)];
216 int fltr_ntypes;
8a204562
KZ
217
218 unsigned int raw : 1,
219 json : 1,
8d27b605 220 tree : 2,
eabbd8b7 221 persist : 1,
deb3f518 222 no_trunc : 1,
0a32d39a 223 no_headings: 1,
8d27b605 224 no_wrap : 1;
e8c56ae2 225
74d2056a
MY
226
227 struct libmnt_table *tab;
8a204562
KZ
228};
229
4195756e
MY
230struct netnsid_cache {
231 ino_t ino;
232 int id;
233 struct list_head netnsids;
234};
235
236static struct list_head netnsids_cache;
237
238static int netlink_fd = -1;
239
8a204562
KZ
240static void lsns_init_debug(void)
241{
a15dca2f 242 __UL_INIT_DEBUG_FROM_ENV(lsns, LSNS_DEBUG_, 0, LSNS_DEBUG);
8a204562
KZ
243}
244
304fbe8b
KZ
245static int ns_name2type(const char *name)
246{
247 size_t i;
248
249 for (i = 0; i < ARRAY_SIZE(ns_names); i++) {
250 if (strcmp(ns_names[i], name) == 0)
251 return i;
252 }
253 return -1;
254}
255
8a204562
KZ
256static int column_name_to_id(const char *name, size_t namesz)
257{
258 size_t i;
259
260 assert(name);
261
262 for (i = 0; i < ARRAY_SIZE(infos); i++) {
263 const char *cn = infos[i].name;
264
265 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
266 return i;
267 }
268 warnx(_("unknown column: %s"), name);
269 return -1;
270}
271
f593e279
KZ
272static int has_column(int id)
273{
274 size_t i;
275
276 for (i = 0; i < ncolumns; i++) {
277 if (columns[i] == id)
278 return 1;
279 }
280 return 0;
281}
282
8a204562
KZ
283static inline int get_column_id(int num)
284{
285 assert(num >= 0);
286 assert((size_t) num < ncolumns);
287 assert(columns[num] < (int) ARRAY_SIZE(infos));
288
289 return columns[num];
290}
291
969d3d15 292static inline const struct colinfo *get_column_info(unsigned num)
8a204562
KZ
293{
294 return &infos[ get_column_id(num) ];
295}
296
d652d4c6 297static int get_ns_ino(int dir, const char *nsname, ino_t *ino, ino_t *pino, ino_t *oino)
8a204562
KZ
298{
299 struct stat st;
300 char path[16];
301
302 snprintf(path, sizeof(path), "ns/%s", nsname);
303
304 if (fstatat(dir, path, &st, 0) != 0)
305 return -errno;
306 *ino = st.st_ino;
d652d4c6
MY
307
308 *pino = 0;
309 *oino = 0;
310
e9de8d3b 311#ifdef USE_NS_GET_API
d652d4c6
MY
312 int fd, pfd, ofd;
313 fd = openat(dir, path, 0);
314 if (fd < 0)
315 return -errno;
316 if (strcmp(nsname, "pid") == 0 || strcmp(nsname, "user") == 0) {
249ba69c 317 if ((pfd = lsns_ioctl(fd, NS_GET_PARENT)) < 0) {
d652d4c6
MY
318 if (errno == EPERM)
319 goto user;
320 close(fd);
321 return -errno;
322 }
323 if (fstat(pfd, &st) < 0) {
324 close(pfd);
325 close(fd);
326 return -errno;
327 }
328 *pino = st.st_ino;
329 close(pfd);
330 }
331 user:
249ba69c 332 if ((ofd = lsns_ioctl(fd, NS_GET_USERNS)) < 0) {
d652d4c6
MY
333 if (errno == EPERM)
334 goto out;
335 close(fd);
336 return -errno;
337 }
338 if (fstat(ofd, &st) < 0) {
339 close(ofd);
340 close(fd);
341 return -errno;
342 }
343 *oino = st.st_ino;
344 close(ofd);
345 out:
346 close(fd);
347#endif
8a204562
KZ
348 return 0;
349}
350
3fcbd797
OH
351static int parse_proc_stat(FILE *fp, pid_t *pid, char *state, pid_t *ppid)
352{
353 char *line = NULL, *p;
354 size_t len = 0;
355 int rc;
356
357 if (getline(&line, &len, fp) < 0) {
358 rc = -errno;
359 goto error;
360 }
361
362 p = strrchr(line, ')');
363 if (p == NULL ||
364 sscanf(line, "%d (", pid) != 1 ||
365 sscanf(p, ") %c %d*[^\n]", state, ppid) != 2) {
366 rc = -EINVAL;
367 goto error;
368 }
369 rc = 0;
370
371error:
372 free(line);
373 return rc;
374}
304fbe8b 375
4195756e 376#ifdef HAVE_LINUX_NET_NAMESPACE_H
7eda2400 377static int netnsid_cache_find(ino_t netino, int *netnsid)
4195756e
MY
378{
379 struct list_head *p;
380
381 list_for_each(p, &netnsids_cache) {
382 struct netnsid_cache *e = list_entry(p,
383 struct netnsid_cache,
384 netnsids);
385 if (e->ino == netino) {
386 *netnsid = e->id;
7eda2400 387 return 1;
4195756e
MY
388 }
389 }
390
7eda2400 391 return 0;
4195756e
MY
392}
393
394static void netnsid_cache_add(ino_t netino, int netnsid)
395{
396 struct netnsid_cache *e;
397
398 e = xcalloc(1, sizeof(*e));
399 e->ino = netino;
400 e->id = netnsid;
401 INIT_LIST_HEAD(&e->netnsids);
402 list_add(&e->netnsids, &netnsids_cache);
403}
404
405static int get_netnsid_via_netlink_send_request(int target_fd)
406{
407 unsigned char req[NLMSG_SPACE(sizeof(struct rtgenmsg))
408 + RTA_SPACE(sizeof(int32_t))];
409
410 struct nlmsghdr *nlh = (struct nlmsghdr *)req;
411 struct rtgenmsg *rt = NLMSG_DATA(req);
412 struct rtattr *rta = (struct rtattr *)
413 (req + NLMSG_SPACE(sizeof(struct rtgenmsg)));
414 int32_t *fd = RTA_DATA(rta);
415
416 nlh->nlmsg_len = sizeof(req);
417 nlh->nlmsg_flags = NLM_F_REQUEST;
418 nlh->nlmsg_type = RTM_GETNSID;
419 rt->rtgen_family = AF_UNSPEC;
420 rta->rta_type = NETNSA_FD;
421 rta->rta_len = RTA_SPACE(sizeof(int32_t));
422 *fd = target_fd;
423
424 if (send(netlink_fd, req, sizeof(req), 0) < 0)
425 return -1;
426 return 0;
427}
428
429static int get_netnsid_via_netlink_recv_response(int *netnsid)
430{
431 unsigned char res[NLMSG_SPACE(sizeof(struct rtgenmsg))
432 + ((RTA_SPACE(sizeof(int32_t))
433 < RTA_SPACE(sizeof(struct nlmsgerr)))
434 ? RTA_SPACE(sizeof(struct nlmsgerr))
435 : RTA_SPACE(sizeof(int32_t)))];
58b29eed
RM
436 int rtalen;
437 ssize_t reslen;
4195756e
MY
438
439 struct nlmsghdr *nlh;
440 struct rtattr *rta;
441
442 reslen = recv(netlink_fd, res, sizeof(res), 0);
443 if (reslen < 0)
444 return -1;
445
446 nlh = (struct nlmsghdr *)res;
58b29eed 447 if (!(NLMSG_OK(nlh, (size_t)reslen)
4195756e
MY
448 && nlh->nlmsg_type == RTM_NEWNSID))
449 return -1;
450
451 rtalen = NLMSG_PAYLOAD(nlh, sizeof(struct rtgenmsg));
452 rta = (struct rtattr *)(res + NLMSG_SPACE(sizeof(struct rtgenmsg)));
453 if (!(RTA_OK(rta, rtalen)
454 && rta->rta_type == NETNSA_NSID))
455 return -1;
456
457 *netnsid = *(int *)RTA_DATA(rta);
458
459 return 0;
460}
461
462static int get_netnsid_via_netlink(int dir, const char *path)
463{
464 int netnsid;
465 int target_fd;
466
467 if (netlink_fd < 0)
468 return LSNS_NETNS_UNUSABLE;
469
470 target_fd = openat(dir, path, O_RDONLY);
471 if (target_fd < 0)
472 return LSNS_NETNS_UNUSABLE;
473
474 if (get_netnsid_via_netlink_send_request(target_fd) < 0) {
475 netnsid = LSNS_NETNS_UNUSABLE;
476 goto out;
477 }
478
479 if (get_netnsid_via_netlink_recv_response(&netnsid) < 0) {
480 netnsid = LSNS_NETNS_UNUSABLE;
481 goto out;
482 }
483
484 out:
485 close(target_fd);
486 return netnsid;
487}
488
489static int get_netnsid(int dir, ino_t netino)
490{
491 int netnsid;
492
493 if (!netnsid_cache_find(netino, &netnsid)) {
494 netnsid = get_netnsid_via_netlink(dir, "ns/net");
495 netnsid_cache_add(netino, netnsid);
496 }
497
498 return netnsid;
499}
500#else
501static int get_netnsid(int dir __attribute__((__unused__)),
502 ino_t netino __attribute__((__unused__)))
503{
504 return LSNS_NETNS_UNUSABLE;
505}
506#endif /* HAVE_LINUX_NET_NAMESPACE_H */
507
8a204562
KZ
508static int read_process(struct lsns *ls, pid_t pid)
509{
510 struct lsns_process *p = NULL;
511 char buf[BUFSIZ];
512 DIR *dir;
513 int rc = 0, fd;
514 FILE *f = NULL;
515 size_t i;
a52852d3 516 struct stat st;
8a204562
KZ
517
518 DBG(PROC, ul_debug("reading %d", (int) pid));
519
520 snprintf(buf, sizeof(buf), "/proc/%d", pid);
521 dir = opendir(buf);
522 if (!dir)
523 return -errno;
524
9d3d66df 525 p = xcalloc(1, sizeof(*p));
4195756e 526 p->netnsid = LSNS_NETNS_UNUSABLE;
8a204562 527
a52852d3
KZ
528 if (fstat(dirfd(dir), &st) == 0) {
529 p->uid = st.st_uid;
530 add_uid(uid_cache, st.st_uid);
531 }
532
8a204562
KZ
533 fd = openat(dirfd(dir), "stat", O_RDONLY);
534 if (fd < 0) {
535 rc = -errno;
536 goto done;
537 }
538 if (!(f = fdopen(fd, "r"))) {
539 rc = -errno;
540 goto done;
541 }
3fcbd797
OH
542 rc = parse_proc_stat(f, &p->pid, &p->state, &p->ppid);
543 if (rc < 0)
8a204562 544 goto done;
8a204562
KZ
545 rc = 0;
546
547 for (i = 0; i < ARRAY_SIZE(p->ns_ids); i++) {
548 INIT_LIST_HEAD(&p->ns_siblings[i]);
549
304fbe8b
KZ
550 if (!ls->fltr_types[i])
551 continue;
552
d652d4c6
MY
553 rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i],
554 &p->ns_pids[i], &p->ns_oids[i]);
3082f851 555 if (rc && rc != -EACCES && rc != -ENOENT)
8a204562 556 goto done;
4195756e
MY
557 if (i == LSNS_ID_NET)
558 p->netnsid = get_netnsid(dirfd(dir), p->ns_ids[i]);
8a204562
KZ
559 rc = 0;
560 }
561
562 INIT_LIST_HEAD(&p->processes);
8a204562 563
9dfd6019 564 DBG(PROC, ul_debugobj(p, "new pid=%d", p->pid));
8a204562
KZ
565 list_add_tail(&p->processes, &ls->processes);
566done:
567 if (f)
568 fclose(f);
569 closedir(dir);
570 if (rc)
571 free(p);
572 return rc;
573}
574
575static int read_processes(struct lsns *ls)
576{
f7f6cc63
KZ
577 DIR *dir;
578 struct dirent *d;
8a204562
KZ
579 int rc = 0;
580
8a204562
KZ
581 DBG(PROC, ul_debug("opening /proc"));
582
f7f6cc63
KZ
583 dir = opendir(_PATH_PROC);
584 if (!dir)
585 return -errno;
8a204562 586
f7f6cc63
KZ
587 while ((d = xreaddir(dir))) {
588 pid_t pid = 0;
589
590 if (procfs_dirent_get_pid(d, &pid) != 0)
591 continue;
592
593 /* TODO: use ul_new_procfs_path(pid, NULL) to read files from /proc/pid/
594 */
8a204562 595 rc = read_process(ls, pid);
5dc625b6 596 if (rc && rc != -EACCES && rc != -ENOENT)
8a204562
KZ
597 break;
598 rc = 0;
599 }
f7f6cc63 600
8a204562 601 DBG(PROC, ul_debug("closing /proc"));
f7f6cc63 602 closedir(dir);
8a204562
KZ
603 return rc;
604}
605
969d3d15 606static struct lsns_namespace *get_namespace(struct lsns *ls, ino_t ino)
8a204562
KZ
607{
608 struct list_head *p;
609
610 list_for_each(p, &ls->namespaces) {
611 struct lsns_namespace *ns = list_entry(p, struct lsns_namespace, namespaces);
612
969d3d15 613 if (ns->id == ino)
8a204562
KZ
614 return ns;
615 }
616 return NULL;
617}
618
9dfd6019
KZ
619static int namespace_has_process(struct lsns_namespace *ns, pid_t pid)
620{
621 struct list_head *p;
622
623 list_for_each(p, &ns->processes) {
624 struct lsns_process *proc = list_entry(p, struct lsns_process, ns_siblings[ns->type]);
625
626 if (proc->pid == pid)
627 return 1;
628 }
629 return 0;
630}
631
d652d4c6
MY
632static struct lsns_namespace *add_namespace(struct lsns *ls, int type, ino_t ino,
633 ino_t parent_ino, ino_t owner_ino)
8a204562 634{
9d3d66df 635 struct lsns_namespace *ns = xcalloc(1, sizeof(*ns));
8a204562
KZ
636
637 if (!ns)
638 return NULL;
639
e1a15f8f 640 DBG(NS, ul_debugobj(ns, "new %s[%ju]", ns_names[type], (uintmax_t)ino));
8a204562
KZ
641
642 INIT_LIST_HEAD(&ns->processes);
643 INIT_LIST_HEAD(&ns->namespaces);
644
645 ns->type = type;
646 ns->id = ino;
c67b83c1
MY
647 ns->related_id[RELA_PARENT] = parent_ino;
648 ns->related_id[RELA_OWNER] = owner_ino;
8a204562
KZ
649
650 list_add_tail(&ns->namespaces, &ls->namespaces);
651 return ns;
652}
653
969d3d15 654static int add_process_to_namespace(struct lsns *ls, struct lsns_namespace *ns, struct lsns_process *proc)
8a204562 655{
969d3d15
KZ
656 struct list_head *p;
657
e1a15f8f
RM
658 DBG(NS, ul_debugobj(ns, "add process [%p] pid=%d to %s[%ju]",
659 proc, proc->pid, ns_names[ns->type], (uintmax_t)ns->id));
8a204562 660
969d3d15
KZ
661 list_for_each(p, &ls->processes) {
662 struct lsns_process *xproc = list_entry(p, struct lsns_process, processes);
663
664 if (xproc->pid == proc->ppid) /* my parent */
665 proc->parent = xproc;
666 else if (xproc->ppid == proc->pid) /* my child */
667 xproc->parent = proc;
668 }
669
8a204562
KZ
670 list_add_tail(&proc->ns_siblings[ns->type], &ns->processes);
671 ns->nprocs++;
672
a52852d3
KZ
673 if (!ns->proc || ns->proc->pid > proc->pid)
674 ns->proc = proc;
969d3d15 675
8a204562
KZ
676 return 0;
677}
678
1880a578
KZ
679static int cmp_namespaces(struct list_head *a, struct list_head *b,
680 __attribute__((__unused__)) void *data)
681{
682 struct lsns_namespace *xa = list_entry(a, struct lsns_namespace, namespaces),
683 *xb = list_entry(b, struct lsns_namespace, namespaces);
684
685 return cmp_numbers(xa->id, xb->id);
686}
687
4195756e
MY
688static int netnsid_xasputs(char **str, int netnsid)
689{
690 if (netnsid >= 0)
691 return xasprintf(str, "%d", netnsid);
692#ifdef NETNSA_NSID_NOT_ASSIGNED
042f62df 693 if (netnsid == NETNSA_NSID_NOT_ASSIGNED)
4195756e
MY
694 return xasprintf(str, "%s", "unassigned");
695#endif
042f62df 696 return 0;
4195756e
MY
697}
698
e9de8d3b 699#ifdef USE_NS_GET_API
de72df79
MY
700static int clone_type_to_lsns_type(int clone_type)
701{
702 switch (clone_type) {
703 case CLONE_NEWNS:
704 return LSNS_ID_MNT;
705 case CLONE_NEWCGROUP:
706 return LSNS_ID_CGROUP;
707 case CLONE_NEWUTS:
708 return LSNS_ID_UTS;
709 case CLONE_NEWIPC:
710 return LSNS_ID_IPC;
711 case CLONE_NEWUSER:
712 return LSNS_ID_USER;
713 case CLONE_NEWPID:
714 return LSNS_ID_PID;
715 case CLONE_NEWNET:
716 return LSNS_ID_NET;
86e64cbf
MY
717#ifdef CLONE_NEWTIME
718 case CLONE_NEWTIME:
719 return LSNS_ID_TIME;
720#endif
de72df79
MY
721 default:
722 return -1;
723 }
724}
725
726static struct lsns_namespace *add_namespace_for_nsfd(struct lsns *ls, int fd, ino_t ino)
727{
728 int fd_owner = -1, fd_parent = -1;
729 struct stat st_owner, st_parent;
730 ino_t ino_owner = 0, ino_parent = 0;
731 struct lsns_namespace *ns;
732 int clone_type, lsns_type;
733
249ba69c 734 clone_type = lsns_ioctl(fd, NS_GET_NSTYPE);
de72df79
MY
735 if (clone_type < 0)
736 return NULL;
737 lsns_type = clone_type_to_lsns_type(clone_type);
eabbd8b7 738 if (lsns_type < 0 || ls->fltr_types[lsns_type] == 0)
de72df79
MY
739 return NULL;
740
249ba69c 741 fd_owner = lsns_ioctl(fd, NS_GET_USERNS);
de72df79
MY
742 if (fd_owner < 0)
743 goto parent;
744 if (fstat(fd_owner, &st_owner) < 0)
745 goto parent;
746 ino_owner = st_owner.st_ino;
747
748 parent:
249ba69c 749 fd_parent = lsns_ioctl(fd, NS_GET_PARENT);
de72df79
MY
750 if (fd_parent < 0)
751 goto add_ns;
752 if (fstat(fd_parent, &st_parent) < 0)
753 goto add_ns;
754 ino_parent = st_parent.st_ino;
755
756 add_ns:
757 ns = add_namespace(ls, lsns_type, ino, ino_parent, ino_owner);
249ba69c 758 lsns_ioctl(fd, NS_GET_OWNER_UID, &ns->uid_fallback);
f91ffe44 759 add_uid(uid_cache, ns->uid_fallback);
de72df79
MY
760
761 if ((lsns_type == LSNS_ID_USER || lsns_type == LSNS_ID_PID)
762 && ino_parent != ino && ino_parent != 0) {
763 ns->related_ns[RELA_PARENT] = get_namespace(ls, ino_parent);
764 if (!ns->related_ns[RELA_PARENT]) {
765 ns->related_ns[RELA_PARENT] = add_namespace_for_nsfd(ls, fd_parent, ino_parent);
766 if (ino_parent == ino_owner)
767 ns->related_ns[RELA_OWNER] = ns->related_ns[RELA_PARENT];
768 }
769 }
770
771 if (ns->related_ns[RELA_OWNER] == NULL && ino_owner != 0) {
772 ns->related_ns[RELA_OWNER] = get_namespace(ls, ino_owner);
773 if (!ns->related_ns[RELA_OWNER])
774 ns->related_ns[RELA_OWNER] = add_namespace_for_nsfd(ls, fd_owner, ino_owner);
775 }
776
777 if (fd_owner >= 0)
778 close(fd_owner);
779 if (fd_parent >= 0)
780 close(fd_parent);
781
782 return ns;
783}
784
09710a22 785static void interpolate_missing_namespaces(struct lsns *ls, struct lsns_namespace *orphan, int rela)
de72df79
MY
786{
787 const int cmd[MAX_RELA] = {
788 [RELA_PARENT] = NS_GET_PARENT,
789 [RELA_OWNER] = NS_GET_USERNS
790 };
791 char buf[BUFSIZ];
792 int fd_orphan, fd_missing;
793 struct stat st;
794
795 orphan->related_ns[rela] = get_namespace(ls, orphan->related_id[rela]);
796 if (orphan->related_ns[rela])
797 return;
798
09710a22 799 snprintf(buf, sizeof(buf), "/proc/%d/ns/%s", orphan->proc->pid, ns_names[orphan->type]);
de72df79
MY
800 fd_orphan = open(buf, O_RDONLY);
801 if (fd_orphan < 0)
802 return;
803
249ba69c 804 fd_missing = lsns_ioctl(fd_orphan, cmd[rela]);
09710a22 805 close(fd_orphan);
de72df79
MY
806 if (fd_missing < 0)
807 return;
808
809 if (fstat(fd_missing, &st) < 0
810 || st.st_ino != orphan->related_id[rela]) {
09710a22 811 close(fd_missing);
de72df79
MY
812 return;
813 }
814
815 orphan->related_ns[rela] = add_namespace_for_nsfd(ls, fd_missing, orphan->related_id[rela]);
09710a22 816 close(fd_missing);
de72df79
MY
817}
818
e9de8d3b 819static void read_related_namespaces(struct lsns *ls)
8a204562
KZ
820{
821 struct list_head *p;
de72df79
MY
822 struct lsns_namespace *orphan[2] = {NULL, NULL};
823 int rela;
8a204562 824
e9de8d3b
KZ
825 list_for_each(p, &ls->namespaces) {
826 struct lsns_namespace *ns = list_entry(p, struct lsns_namespace, namespaces);
827 struct list_head *pp;
828 list_for_each(pp, &ls->namespaces) {
829 struct lsns_namespace *pns = list_entry(pp, struct lsns_namespace, namespaces);
830 if (ns->type == LSNS_ID_USER
831 || ns->type == LSNS_ID_PID) {
832 if (ns->related_id[RELA_PARENT] == pns->id)
833 ns->related_ns[RELA_PARENT] = pns;
834 if (ns->related_id[RELA_OWNER] == pns->id)
835 ns->related_ns[RELA_OWNER] = pns;
836 if (ns->related_ns[RELA_PARENT] && ns->related_ns[RELA_OWNER])
837 break;
838 } else {
839 if (ns->related_id[RELA_OWNER] == pns->id) {
840 ns->related_ns[RELA_OWNER] = pns;
841 break;
842 }
843 }
844 }
845
846 /* lsns scans /proc/[0-9]+ for finding namespaces.
847 * So if a namespace has no process, lsns cannot
848 * find it. Here we call it a missing namespace.
849 *
850 * If the id for a related namesspce is known but
851 * namespace for the id is not found, there must
852 * be orphan namespaces. A missing namespace is an
853 * owner or a parent of the orphan namespace.
854 */
855 for (rela = 0; rela < MAX_RELA; rela++) {
856 if (ns->related_id[rela] != 0
857 && ns->related_ns[rela] == NULL) {
858 ns->related_ns[rela] = orphan[rela];
859 orphan[rela] = ns;
860 }
861 }
862 }
863
864 for (rela = 0; rela < MAX_RELA; rela++) {
865 while (orphan[rela]) {
866 struct lsns_namespace *current = orphan[rela];
867 orphan[rela] = orphan[rela]->related_ns[rela];
868 current->related_ns[rela] = NULL;
869 interpolate_missing_namespaces(ls, current, rela);
870 }
871 }
872}
873
eabbd8b7
KZ
874static int read_persistent_namespaces(struct lsns *ls)
875{
876 struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD);
877 struct libmnt_fs *fs = NULL;
878
879 while (mnt_table_next_fs(ls->tab, itr, &fs) == 0) {
880 const char *root;
881 char *p, *end = NULL;
882 ino_t ino;
883 int fd;
884
885 if (!mnt_fs_match_fstype(fs, "nsfs"))
886 continue;
887 root = mnt_fs_get_root(fs);
888 if (!root || !(p = strchr(root, '[')))
889 continue;
890
891 errno = 0;
892 ino = strtoumax(++p, &end, 10);
893 if (!end || *end != ']' || errno != 0)
894 continue;
895 if (get_namespace(ls, ino))
896 continue;
897
898 fd = open(mnt_fs_get_target(fs), O_RDONLY);
899 if (fd < 0)
900 continue;
901
902 add_namespace_for_nsfd(ls, fd, ino);
903 close(fd);
904 }
905
906 mnt_free_iter(itr);
907 return 0;
908}
909
e9de8d3b
KZ
910#endif /* USE_NS_GET_API */
911
912static int read_namespaces(struct lsns *ls)
913{
914 struct list_head *p;
915
8a204562
KZ
916 DBG(NS, ul_debug("reading namespace"));
917
918 list_for_each(p, &ls->processes) {
919 size_t i;
920 struct lsns_namespace *ns;
921 struct lsns_process *proc = list_entry(p, struct lsns_process, processes);
922
923 for (i = 0; i < ARRAY_SIZE(proc->ns_ids); i++) {
924 if (proc->ns_ids[i] == 0)
925 continue;
969d3d15 926 if (!(ns = get_namespace(ls, proc->ns_ids[i]))) {
d652d4c6
MY
927 ns = add_namespace(ls, i, proc->ns_ids[i],
928 proc->ns_pids[i], proc->ns_oids[i]);
8a204562
KZ
929 if (!ns)
930 return -ENOMEM;
931 }
969d3d15 932 add_process_to_namespace(ls, ns, proc);
8a204562
KZ
933 }
934 }
935
e9de8d3b 936#ifdef USE_NS_GET_API
eabbd8b7
KZ
937 read_persistent_namespaces(ls);
938
e9de8d3b
KZ
939 if (ls->tree == LSNS_TREE_OWNER || ls->tree == LSNS_TREE_PARENT)
940 read_related_namespaces(ls);
941#endif
1880a578
KZ
942 list_sort(&ls->namespaces, cmp_namespaces, NULL);
943
8a204562
KZ
944 return 0;
945}
946
7eda2400 947static int is_nsfs_root(struct libmnt_fs *fs, void *data)
74d2056a 948{
7eda2400
KZ
949 if (!mnt_fs_match_fstype(fs, "nsfs") || !mnt_fs_get_root(fs))
950 return 0;
951
952 return (strcmp(mnt_fs_get_root(fs), (char *)data) == 0);
74d2056a
MY
953}
954
7eda2400 955static int is_path_included(const char *path_set, const char *elt,
74d2056a
MY
956 const char sep)
957{
958 size_t elt_len;
959 size_t path_set_len;
960 char *tmp;
961
962
963 tmp = strstr(path_set, elt);
964 if (!tmp)
7eda2400 965 return 0;
74d2056a
MY
966
967 elt_len = strlen(elt);
968 path_set_len = strlen(path_set);
969
970 /* path_set includes only elt or
971 * path_set includes elt as the first element.
972 */
973 if (tmp == path_set
974 && ((path_set_len == elt_len)
975 || (path_set[elt_len] == sep)))
7eda2400
KZ
976 return 1;
977
74d2056a
MY
978 /* path_set includes elt at the middle
979 * or as the last element.
980 */
981 if ((*(tmp - 1) == sep)
982 && ((*(tmp + elt_len) == sep)
983 || (*(tmp + elt_len) == '\0')))
7eda2400 984 return 1;
74d2056a 985
7eda2400 986 return 0;
74d2056a
MY
987}
988
989static int nsfs_xasputs(char **str,
990 struct lsns_namespace *ns,
991 struct libmnt_table *tab,
992 char sep)
993{
994 struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD);
995 char *expected_root;
7eda2400 996 struct libmnt_fs *fs = NULL;
74d2056a 997
845dac5f 998 xasprintf(&expected_root, "%s:[%ju]", ns_names[ns->type], (uintmax_t)ns->id);
7eda2400 999 *str = NULL;
74d2056a 1000
7eda2400
KZ
1001 while (mnt_table_find_next_fs(tab, itr, is_nsfs_root,
1002 expected_root, &fs) == 0) {
74d2056a 1003
7eda2400
KZ
1004 const char *tgt = mnt_fs_get_target(fs);
1005
1006 if (!*str)
1007 xasprintf(str, "%s", tgt);
1008
1009 else if (!is_path_included(*str, tgt, sep)) {
1010 char *tmp = NULL;
1011
1012 xasprintf(&tmp, "%s%c%s", *str, sep, tgt);
1013 free(*str);
1014 *str = tmp;
74d2056a
MY
1015 }
1016 }
1017 free(expected_root);
1018 mnt_free_iter(itr);
1019
1020 return 1;
1021}
969d3d15
KZ
1022static void add_scols_line(struct lsns *ls, struct libscols_table *table,
1023 struct lsns_namespace *ns, struct lsns_process *proc)
8a204562
KZ
1024{
1025 size_t i;
1026 struct libscols_line *line;
1027
1028 assert(ns);
1029 assert(table);
1030
969d3d15 1031 line = scols_table_new_line(table,
e749d51a 1032 (ls->tree == LSNS_TREE_PROCESS && proc) && proc->parent ? proc->parent->outline:
c67b83c1
MY
1033 (ls->tree == LSNS_TREE_PARENT) && ns->related_ns[RELA_PARENT] ? ns->related_ns[RELA_PARENT]->ns_outline:
1034 (ls->tree == LSNS_TREE_OWNER) && ns->related_ns[RELA_OWNER] ? ns->related_ns[RELA_OWNER]->ns_outline:
e8c56ae2 1035 NULL);
8a204562
KZ
1036 if (!line) {
1037 warn(_("failed to add line to output"));
1038 return;
1039 }
1040
1041 for (i = 0; i < ncolumns; i++) {
1042 char *str = NULL;
1043
1044 switch (get_column_id(i)) {
1045 case COL_NS:
e1a15f8f 1046 xasprintf(&str, "%ju", (uintmax_t)ns->id);
8a204562
KZ
1047 break;
1048 case COL_PID:
e749d51a
MY
1049 if (proc)
1050 xasprintf(&str, "%d", (int) proc->pid);
969d3d15
KZ
1051 break;
1052 case COL_PPID:
e749d51a
MY
1053 if (proc)
1054 xasprintf(&str, "%d", (int) proc->ppid);
8a204562
KZ
1055 break;
1056 case COL_TYPE:
1057 xasprintf(&str, "%s", ns_names[ns->type]);
1058 break;
1059 case COL_NPROCS:
1060 xasprintf(&str, "%d", ns->nprocs);
1061 break;
1062 case COL_COMMAND:
e749d51a
MY
1063 if (!proc)
1064 break;
f7f6cc63 1065 str = pid_get_cmdline(proc->pid);
8a204562 1066 if (!str)
f7f6cc63 1067 str = pid_get_cmdname(proc->pid);
8a204562
KZ
1068 break;
1069 case COL_PATH:
e749d51a
MY
1070 if (!proc)
1071 break;
969d3d15 1072 xasprintf(&str, "/proc/%d/ns/%s", (int) proc->pid, ns_names[ns->type]);
a52852d3
KZ
1073 break;
1074 case COL_UID:
f91ffe44 1075 xasprintf(&str, "%d", proc? (int) proc->uid: (int) ns->uid_fallback);
a52852d3
KZ
1076 break;
1077 case COL_USER:
f91ffe44 1078 xasprintf(&str, "%s", get_id(uid_cache, proc? proc->uid: ns->uid_fallback)->name);
8a204562 1079 break;
4195756e 1080 case COL_NETNSID:
e749d51a
MY
1081 if (!proc)
1082 break;
4195756e
MY
1083 if (ns->type == LSNS_ID_NET)
1084 netnsid_xasputs(&str, proc->netnsid);
1085 break;
74d2056a 1086 case COL_NSFS:
7eda2400 1087 nsfs_xasputs(&str, ns, ls->tab, ls->no_wrap ? ',' : '\n');
74d2056a 1088 break;
d652d4c6 1089 case COL_PNS:
c67b83c1 1090 xasprintf(&str, "%ju", (uintmax_t)ns->related_id[RELA_PARENT]);
d652d4c6
MY
1091 break;
1092 case COL_ONS:
c67b83c1 1093 xasprintf(&str, "%ju", (uintmax_t)ns->related_id[RELA_OWNER]);
d652d4c6 1094 break;
8a204562
KZ
1095 default:
1096 break;
1097 }
1098
3e11eaa8 1099 if (str && scols_line_refer_data(line, i, str) != 0)
99ae5a49 1100 err_oom();
8a204562 1101 }
969d3d15 1102
8d27b605 1103 if (ls->tree == LSNS_TREE_OWNER || ls->tree == LSNS_TREE_PARENT)
e8c56ae2 1104 ns->ns_outline = line;
e749d51a 1105 else if (proc)
e8c56ae2 1106 proc->outline = line;
8a204562
KZ
1107}
1108
969d3d15 1109static struct libscols_table *init_scols_table(struct lsns *ls)
8a204562
KZ
1110{
1111 struct libscols_table *tab;
8a204562 1112 size_t i;
8a204562
KZ
1113
1114 tab = scols_new_table();
1115 if (!tab) {
1116 warn(_("failed to initialize output table"));
969d3d15 1117 return NULL;
8a204562
KZ
1118 }
1119
1120 scols_table_enable_raw(tab, ls->raw);
1121 scols_table_enable_json(tab, ls->json);
1122 scols_table_enable_noheadings(tab, ls->no_headings);
1123
1124 if (ls->json)
1125 scols_table_set_name(tab, "namespaces");
1126
1127 for (i = 0; i < ncolumns; i++) {
969d3d15
KZ
1128 const struct colinfo *col = get_column_info(i);
1129 int flags = col->flags;
74d2056a 1130 struct libscols_column *cl;
969d3d15 1131
deb3f518 1132 if (ls->no_trunc)
969d3d15 1133 flags &= ~SCOLS_FL_TRUNC;
8d27b605 1134 if (ls->tree == LSNS_TREE_PROCESS && get_column_id(i) == COL_COMMAND)
969d3d15 1135 flags |= SCOLS_FL_TREE;
0a32d39a
MY
1136 if (ls->no_wrap)
1137 flags &= ~SCOLS_FL_WRAP;
8d27b605
MY
1138 if ((ls->tree == LSNS_TREE_OWNER || ls->tree == LSNS_TREE_PARENT)
1139 && get_column_id(i) == COL_NS) {
e8c56ae2
MY
1140 flags |= SCOLS_FL_TREE;
1141 flags &= ~SCOLS_FL_RIGHT;
1142 }
8a204562 1143
74d2056a
MY
1144 cl = scols_table_new_column(tab, col->name, col->whint, flags);
1145 if (cl == NULL) {
8a204562 1146 warnx(_("failed to initialize output column"));
969d3d15 1147 goto err;
8a204562 1148 }
31b25347
KZ
1149 if (ls->json)
1150 scols_column_set_json_type(cl, col->json_type);
1151
7eda2400 1152 if (!ls->no_wrap && get_column_id(i) == COL_NSFS) {
74d2056a
MY
1153 scols_column_set_wrapfunc(cl,
1154 scols_wrapnl_chunksize,
1155 scols_wrapnl_nextchunk,
1156 NULL);
1157 scols_column_set_safechars(cl, "\n");
1158 }
8a204562
KZ
1159 }
1160
969d3d15
KZ
1161 return tab;
1162err:
1163 scols_unref_table(tab);
1164 return NULL;
1165}
1166
e8c56ae2
MY
1167static void show_namespace(struct lsns *ls, struct libscols_table *tab,
1168 struct lsns_namespace *ns, struct lsns_process *proc)
1169{
1170 /*
1171 * create a tree from owner->owned and/or parent->child relation
1172 */
8d27b605 1173 if (ls->tree == LSNS_TREE_OWNER
c67b83c1
MY
1174 && ns->related_ns[RELA_OWNER]
1175 && !ns->related_ns[RELA_OWNER]->ns_outline)
1176 show_namespace(ls, tab, ns->related_ns[RELA_OWNER], ns->related_ns[RELA_OWNER]->proc);
8d27b605 1177 else if (ls->tree == LSNS_TREE_PARENT) {
c67b83c1
MY
1178 if (ns->related_ns[RELA_PARENT]) {
1179 if (!ns->related_ns[RELA_PARENT]->ns_outline)
1180 show_namespace(ls, tab, ns->related_ns[RELA_PARENT], ns->related_ns[RELA_PARENT]->proc);
e8c56ae2 1181 }
c67b83c1
MY
1182 else if (ns->related_ns[RELA_OWNER] && !ns->related_ns[RELA_OWNER]->ns_outline)
1183 show_namespace(ls, tab, ns->related_ns[RELA_OWNER], ns->related_ns[RELA_OWNER]->proc);
e8c56ae2
MY
1184 }
1185
1186 add_scols_line(ls, tab, ns, proc);
1187}
1188
969d3d15
KZ
1189static int show_namespaces(struct lsns *ls)
1190{
1191 struct libscols_table *tab;
1192 struct list_head *p;
1193 int rc = 0;
1194
1195 tab = init_scols_table(ls);
1196 if (!tab)
1197 return -ENOMEM;
1198
8a204562
KZ
1199 list_for_each(p, &ls->namespaces) {
1200 struct lsns_namespace *ns = list_entry(p, struct lsns_namespace, namespaces);
9dfd6019 1201
304fbe8b 1202 if (ls->fltr_pid != 0 && !namespace_has_process(ns, ls->fltr_pid))
9dfd6019 1203 continue;
eabbd8b7
KZ
1204 if (ls->persist && ns->nprocs != 0)
1205 continue;
9dfd6019 1206
e8c56ae2
MY
1207 if (!ns->ns_outline)
1208 show_namespace(ls, tab, ns, ns->proc);
8a204562
KZ
1209 }
1210
1211 scols_print_table(tab);
8a204562
KZ
1212 scols_unref_table(tab);
1213 return rc;
1214}
1215
969d3d15
KZ
1216static void show_process(struct lsns *ls, struct libscols_table *tab,
1217 struct lsns_process *proc, struct lsns_namespace *ns)
1218{
1219 /*
1220 * create a tree from parent->child relation, but only if the parent is
1221 * within the same namespace
1222 */
8d27b605 1223 if (ls->tree == LSNS_TREE_PROCESS
969d3d15
KZ
1224 && proc->parent
1225 && !proc->parent->outline
1226 && proc->parent->ns_ids[ns->type] == proc->ns_ids[ns->type])
1227 show_process(ls, tab, proc->parent, ns);
1228
1229 add_scols_line(ls, tab, ns, proc);
1230}
1231
1232
1233static int show_namespace_processes(struct lsns *ls, struct lsns_namespace *ns)
1234{
1235 struct libscols_table *tab;
1236 struct list_head *p;
1237
1238 tab = init_scols_table(ls);
1239 if (!tab)
1240 return -ENOMEM;
1241
1242 list_for_each(p, &ns->processes) {
1243 struct lsns_process *proc = list_entry(p, struct lsns_process, ns_siblings[ns->type]);
1880a578
KZ
1244
1245 if (!proc->outline)
1246 show_process(ls, tab, proc, ns);
969d3d15
KZ
1247 }
1248
1249
1250 scols_print_table(tab);
1251 scols_unref_table(tab);
1252 return 0;
1253}
1254
395f3bae 1255static void free_lsns_process(struct lsns_process *lsns_p)
1256{
1257 free(lsns_p);
1258}
1259
1260static void free_netnsid_caches(struct netnsid_cache *cache)
1261{
1262 free(cache);
1263}
1264
1265static void free_lsns_namespace(struct lsns_namespace *lsns_n)
1266{
1267 free(lsns_n);
1268}
1269
1270static void free_all(struct lsns *ls)
1271{
1272 list_free(&ls->processes, struct lsns_process, processes, free_lsns_process);
1273 list_free(&netnsids_cache, struct netnsid_cache, netnsids, free_netnsid_caches);
1274 list_free(&ls->namespaces, struct lsns_namespace, namespaces, free_lsns_namespace);
1275}
1276
86be6a32 1277static void __attribute__((__noreturn__)) usage(void)
8a204562 1278{
86be6a32 1279 FILE *out = stdout;
8a204562
KZ
1280 size_t i;
1281
1282 fputs(USAGE_HEADER, out);
1283
1284 fprintf(out,
969d3d15 1285 _(" %s [options] [<namespace>]\n"), program_invocation_short_name);
8a204562
KZ
1286
1287 fputs(USAGE_SEPARATOR, out);
969d3d15 1288 fputs(_("List system namespaces.\n"), out);
8a204562
KZ
1289
1290 fputs(USAGE_OPTIONS, out);
1291 fputs(_(" -J, --json use JSON output format\n"), out);
969d3d15 1292 fputs(_(" -l, --list use list format output\n"), out);
8a204562
KZ
1293 fputs(_(" -n, --noheadings don't print headings\n"), out);
1294 fputs(_(" -o, --output <list> define which output columns to use\n"), out);
1f7b62e0 1295 fputs(_(" --output-all output all columns\n"), out);
eabbd8b7 1296 fputs(_(" -P, --persistent namespaces without processes\n"), out);
969d3d15 1297 fputs(_(" -p, --task <pid> print process namespaces\n"), out);
8a204562
KZ
1298 fputs(_(" -r, --raw use the raw output format\n"), out);
1299 fputs(_(" -u, --notruncate don't truncate text in columns\n"), out);
0a32d39a 1300 fputs(_(" -W, --nowrap don't use multi-line representation\n"), out);
96dc4f80 1301 fputs(_(" -t, --type <name> namespace type (mnt, net, ipc, user, pid, uts, cgroup, time)\n"), out);
8d27b605 1302 fputs(_(" -T, --tree <rel> use tree format (parent, owner, or process)\n"), out);
8a204562
KZ
1303
1304 fputs(USAGE_SEPARATOR, out);
bad4c729 1305 fprintf(out, USAGE_HELP_OPTIONS(24));
8a204562 1306
c3a4cfc5 1307 fputs(USAGE_COLUMNS, out);
8a204562
KZ
1308 for (i = 0; i < ARRAY_SIZE(infos); i++)
1309 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
1310
bad4c729 1311 fprintf(out, USAGE_MAN_TAIL("lsns(8)"));
8a204562 1312
86be6a32 1313 exit(EXIT_SUCCESS);
8a204562
KZ
1314}
1315
304fbe8b 1316
8a204562
KZ
1317int main(int argc, char *argv[])
1318{
1319 struct lsns ls;
3387ad72 1320 int c, force_list = 0;
8a204562
KZ
1321 int r = 0;
1322 char *outarg = NULL;
1f7b62e0
SK
1323 enum {
1324 OPT_OUTPUT_ALL = CHAR_MAX + 1
1325 };
8a204562
KZ
1326 static const struct option long_opts[] = {
1327 { "json", no_argument, NULL, 'J' },
1328 { "task", required_argument, NULL, 'p' },
1329 { "help", no_argument, NULL, 'h' },
1330 { "output", required_argument, NULL, 'o' },
1f7b62e0 1331 { "output-all", no_argument, NULL, OPT_OUTPUT_ALL },
eabbd8b7 1332 { "persistent", no_argument, NULL, 'P' },
8a204562
KZ
1333 { "notruncate", no_argument, NULL, 'u' },
1334 { "version", no_argument, NULL, 'V' },
1335 { "noheadings", no_argument, NULL, 'n' },
0a32d39a 1336 { "nowrap", no_argument, NULL, 'W' },
969d3d15 1337 { "list", no_argument, NULL, 'l' },
8a204562 1338 { "raw", no_argument, NULL, 'r' },
304fbe8b 1339 { "type", required_argument, NULL, 't' },
8d27b605 1340 { "tree", optional_argument, NULL, 'T' },
8a204562
KZ
1341 { NULL, 0, NULL, 0 }
1342 };
1343
1344 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
1345 { 'J','r' },
eabbd8b7 1346 { 'P','p' },
3387ad72 1347 { 'l','T' },
8a204562
KZ
1348 { 0 }
1349 };
1350 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
f593e279 1351 int is_net = 0;
8a204562
KZ
1352
1353 setlocale(LC_ALL, "");
1354 bindtextdomain(PACKAGE, LOCALEDIR);
1355 textdomain(PACKAGE);
2c308875 1356 close_stdout_atexit();
8a204562
KZ
1357
1358 lsns_init_debug();
8a204562 1359 memset(&ls, 0, sizeof(ls));
304fbe8b 1360
8a204562
KZ
1361 INIT_LIST_HEAD(&ls.processes);
1362 INIT_LIST_HEAD(&ls.namespaces);
4195756e 1363 INIT_LIST_HEAD(&netnsids_cache);
8a204562
KZ
1364
1365 while ((c = getopt_long(argc, argv,
eabbd8b7 1366 "JlPp:o:nruhVt:T::W", long_opts, NULL)) != -1) {
8a204562
KZ
1367
1368 err_exclusive_options(c, long_opts, excl, excl_st);
1369
1370 switch(c) {
1371 case 'J':
1372 ls.json = 1;
1373 break;
969d3d15 1374 case 'l':
3387ad72 1375 force_list = 1;
969d3d15 1376 break;
8a204562
KZ
1377 case 'o':
1378 outarg = optarg;
1379 break;
1f7b62e0
SK
1380 case OPT_OUTPUT_ALL:
1381 for (ncolumns = 0; ncolumns < ARRAY_SIZE(infos); ncolumns++)
1382 columns[ncolumns] = ncolumns;
1383 break;
eabbd8b7
KZ
1384 case 'P':
1385 ls.persist = 1;
1386 break;
8a204562 1387 case 'p':
304fbe8b 1388 ls.fltr_pid = strtos32_or_err(optarg, _("invalid PID argument"));
8a204562 1389 break;
8a204562
KZ
1390 case 'n':
1391 ls.no_headings = 1;
1392 break;
1393 case 'r':
7eda2400 1394 ls.no_wrap = ls.raw = 1;
8a204562
KZ
1395 break;
1396 case 'u':
deb3f518 1397 ls.no_trunc = 1;
8a204562 1398 break;
304fbe8b
KZ
1399 case 't':
1400 {
1401 int type = ns_name2type(optarg);
1402 if (type < 0)
1403 errx(EXIT_FAILURE, _("unknown namespace type: %s"), optarg);
1404 ls.fltr_types[type] = 1;
1405 ls.fltr_ntypes++;
7a360346 1406 if (type == LSNS_ID_NET)
f593e279 1407 is_net = 1;
304fbe8b
KZ
1408 break;
1409 }
0a32d39a
MY
1410 case 'W':
1411 ls.no_wrap = 1;
1412 break;
e8c56ae2 1413 case 'T':
8d27b605 1414 ls.tree = LSNS_TREE_OWNER;
e8c56ae2 1415 if (optarg) {
8d27b605
MY
1416 if (*optarg == '=')
1417 optarg++;
e8c56ae2 1418 if (strcmp (optarg, "parent") == 0)
8d27b605
MY
1419 ls.tree = LSNS_TREE_PARENT;
1420 else if (strcmp (optarg, "process") == 0)
1421 ls.tree = LSNS_TREE_PROCESS;
e8c56ae2 1422 else if (strcmp (optarg, "owner") != 0)
0f344e24 1423 errx(EXIT_FAILURE, _("unknown tree type: %s"), optarg);
e8c56ae2
MY
1424 }
1425 break;
2c308875
KZ
1426
1427 case 'h':
1428 usage();
1429 case 'V':
1430 print_version(EXIT_SUCCESS);
8a204562 1431 default:
677ec86c 1432 errtryhelp(EXIT_FAILURE);
8a204562
KZ
1433 }
1434 }
1435
304fbe8b
KZ
1436 if (!ls.fltr_ntypes) {
1437 size_t i;
7a360346 1438
304fbe8b
KZ
1439 for (i = 0; i < ARRAY_SIZE(ns_names); i++)
1440 ls.fltr_types[i] = 1;
82a437c7 1441 }
304fbe8b 1442
969d3d15 1443 if (optind < argc) {
304fbe8b 1444 if (ls.fltr_pid)
969d3d15 1445 errx(EXIT_FAILURE, _("--task is mutually exclusive with <namespace>"));
304fbe8b 1446 ls.fltr_ns = strtou64_or_err(argv[optind], _("invalid namespace argument"));
3387ad72
KZ
1447 if (!ls.tree && !force_list)
1448 ls.tree = LSNS_TREE_PROCESS;
969d3d15
KZ
1449
1450 if (!ncolumns) {
1451 columns[ncolumns++] = COL_PID;
1452 columns[ncolumns++] = COL_PPID;
1453 columns[ncolumns++] = COL_USER;
1454 columns[ncolumns++] = COL_COMMAND;
1455 }
1456 }
1457
8a204562 1458 if (!ncolumns) {
8a204562
KZ
1459 columns[ncolumns++] = COL_NS;
1460 columns[ncolumns++] = COL_TYPE;
1461 columns[ncolumns++] = COL_NPROCS;
1462 columns[ncolumns++] = COL_PID;
a52852d3 1463 columns[ncolumns++] = COL_USER;
f593e279 1464 if (is_net) {
7a360346 1465 columns[ncolumns++] = COL_NETNSID;
74d2056a
MY
1466 columns[ncolumns++] = COL_NSFS;
1467 }
8a204562 1468 columns[ncolumns++] = COL_COMMAND;
3387ad72
KZ
1469
1470 if (!ls.tree && !force_list)
1471 ls.tree = LSNS_TREE_PROCESS;
8a204562
KZ
1472 }
1473
e9de8d3b
KZ
1474#ifndef USE_NS_GET_API
1475 if (ls.tree && ls.tree != LSNS_TREE_PROCESS)
1476 errx(EXIT_FAILURE, _("--tree={parent|owner} is unsupported for your system"));
1477#endif
f593e279
KZ
1478 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
1479 &ncolumns, column_name_to_id) < 0)
1480 return EXIT_FAILURE;
7eda2400 1481
8a204562
KZ
1482 scols_init_debug(0);
1483
a52852d3
KZ
1484 uid_cache = new_idcache();
1485 if (!uid_cache)
1486 err(EXIT_FAILURE, _("failed to allocate UID cache"));
1487
4195756e 1488#ifdef HAVE_LINUX_NET_NAMESPACE_H
f593e279 1489 if (has_column(COL_NETNSID))
7a360346 1490 netlink_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
4195756e 1491#endif
eabbd8b7
KZ
1492 ls.tab = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
1493 if (!ls.tab)
1494 err(MNT_EX_FAIL, _("failed to parse %s"), _PATH_PROC_MOUNTINFO);
74d2056a 1495
8a204562
KZ
1496 r = read_processes(&ls);
1497 if (!r)
1498 r = read_namespaces(&ls);
969d3d15 1499 if (!r) {
304fbe8b
KZ
1500 if (ls.fltr_ns) {
1501 struct lsns_namespace *ns = get_namespace(&ls, ls.fltr_ns);
969d3d15
KZ
1502
1503 if (!ns)
82a437c7 1504 errx(EXIT_FAILURE, _("not found namespace: %ju"), (uintmax_t) ls.fltr_ns);
969d3d15
KZ
1505 r = show_namespace_processes(&ls, ns);
1506 } else
1507 r = show_namespaces(&ls);
1508 }
8a204562 1509
74d2056a 1510 mnt_free_table(ls.tab);
4195756e
MY
1511 if (netlink_fd >= 0)
1512 close(netlink_fd);
a52852d3 1513 free_idcache(uid_cache);
395f3bae 1514
1515 free_all(&ls);
1516
249ba69c
TW
1517 switch (r) {
1518 case 0: return EXIT_SUCCESS;
1519 case -ENOTTY: return EXIT_UNSUPPORTED_IOCTL;
1520 default: return EXIT_FAILURE;
1521 }
8a204562 1522}