]>
Commit | Line | Data |
---|---|---|
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 MY |
33 | #ifdef HAVE_LINUX_NET_NAMESPACE_H |
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> | |
39 | #endif | |
40 | ||
8a204562 KZ |
41 | #include "pathnames.h" |
42 | #include "nls.h" | |
43 | #include "xalloc.h" | |
44 | #include "c.h" | |
45 | #include "list.h" | |
46 | #include "closestream.h" | |
47 | #include "optutils.h" | |
48 | #include "procutils.h" | |
49 | #include "strutils.h" | |
50 | #include "namespace.h" | |
a52852d3 | 51 | #include "idcache.h" |
8a204562 KZ |
52 | |
53 | #include "debug.h" | |
54 | ||
2ba641e5 | 55 | static UL_DEBUG_DEFINE_MASK(lsns); |
8a204562 KZ |
56 | UL_DEBUG_DEFINE_MASKNAMES(lsns) = UL_DEBUG_EMPTY_MASKNAMES; |
57 | ||
58 | #define LSNS_DEBUG_INIT (1 << 1) | |
59 | #define LSNS_DEBUG_PROC (1 << 2) | |
60 | #define LSNS_DEBUG_NS (1 << 3) | |
61 | #define LSNS_DEBUG_ALL 0xFFFF | |
62 | ||
4195756e MY |
63 | #define LSNS_NETNS_UNUSABLE -2 |
64 | ||
8a204562 KZ |
65 | #define DBG(m, x) __UL_DBG(lsns, LSNS_DEBUG_, m, x) |
66 | #define ON_DBG(m, x) __UL_DBG_CALL(lsns, LSNS_DEBUG_, m, x) | |
67 | ||
6d00cfb2 KZ |
68 | #define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(lsns) |
69 | #include "debugobj.h" | |
70 | ||
2ba641e5 | 71 | static struct idcache *uid_cache = NULL; |
a52852d3 | 72 | |
8a204562 KZ |
73 | /* column IDs */ |
74 | enum { | |
75 | COL_NS = 0, | |
76 | COL_TYPE, | |
77 | COL_PATH, | |
78 | COL_NPROCS, | |
79 | COL_PID, | |
969d3d15 | 80 | COL_PPID, |
a52852d3 KZ |
81 | COL_COMMAND, |
82 | COL_UID, | |
4195756e | 83 | COL_USER, |
74d2056a MY |
84 | COL_NETNSID, |
85 | COL_NSFS, | |
8a204562 KZ |
86 | }; |
87 | ||
88 | /* column names */ | |
89 | struct colinfo { | |
90 | const char *name; /* header */ | |
91 | double whint; /* width hint (N < 1 is in percent of termwidth) */ | |
92 | int flags; /* SCOLS_FL_* */ | |
93 | const char *help; | |
31b25347 | 94 | int json_type; |
8a204562 KZ |
95 | }; |
96 | ||
97 | /* columns descriptions */ | |
969d3d15 | 98 | static const struct colinfo infos[] = { |
31b25347 | 99 | [COL_NS] = { "NS", 10, SCOLS_FL_RIGHT, N_("namespace identifier (inode number)"), SCOLS_JSON_NUMBER }, |
8a204562 KZ |
100 | [COL_TYPE] = { "TYPE", 5, 0, N_("kind of namespace") }, |
101 | [COL_PATH] = { "PATH", 0, 0, N_("path to the namespace")}, | |
31b25347 KZ |
102 | [COL_NPROCS] = { "NPROCS", 5, SCOLS_FL_RIGHT, N_("number of processes in the namespace"), SCOLS_JSON_NUMBER }, |
103 | [COL_PID] = { "PID", 5, SCOLS_FL_RIGHT, N_("lowest PID in the namespace"), SCOLS_JSON_NUMBER }, | |
104 | [COL_PPID] = { "PPID", 5, SCOLS_FL_RIGHT, N_("PPID of the PID"), SCOLS_JSON_NUMBER }, | |
a52852d3 | 105 | [COL_COMMAND] = { "COMMAND", 0, SCOLS_FL_TRUNC, N_("command line of the PID")}, |
31b25347 | 106 | [COL_UID] = { "UID", 0, SCOLS_FL_RIGHT, N_("UID of the PID"), SCOLS_JSON_NUMBER}, |
4195756e | 107 | [COL_USER] = { "USER", 0, 0, N_("username of the PID")}, |
f593e279 KZ |
108 | [COL_NETNSID] = { "NETNSID", 0, SCOLS_FL_RIGHT, N_("namespace ID as used by network subsystem")}, |
109 | [COL_NSFS] = { "NSFS", 0, SCOLS_FL_WRAP, N_("nsfs mountpoint (usually used network subsystem)")} | |
8a204562 KZ |
110 | }; |
111 | ||
112 | static int columns[ARRAY_SIZE(infos) * 2]; | |
113 | static size_t ncolumns; | |
114 | ||
115 | enum { | |
116 | LSNS_ID_MNT = 0, | |
117 | LSNS_ID_NET, | |
118 | LSNS_ID_PID, | |
119 | LSNS_ID_UTS, | |
120 | LSNS_ID_IPC, | |
2b8889c4 MB |
121 | LSNS_ID_USER, |
122 | LSNS_ID_CGROUP | |
8a204562 KZ |
123 | }; |
124 | ||
125 | static char *ns_names[] = { | |
126 | [LSNS_ID_MNT] = "mnt", | |
127 | [LSNS_ID_NET] = "net", | |
128 | [LSNS_ID_PID] = "pid", | |
129 | [LSNS_ID_UTS] = "uts", | |
130 | [LSNS_ID_IPC] = "ipc", | |
2b8889c4 MB |
131 | [LSNS_ID_USER] = "user", |
132 | [LSNS_ID_CGROUP] = "cgroup" | |
8a204562 KZ |
133 | }; |
134 | ||
135 | struct lsns_namespace { | |
136 | ino_t id; | |
137 | int type; /* LSNS_* */ | |
138 | int nprocs; | |
4195756e | 139 | int netnsid; |
a52852d3 KZ |
140 | |
141 | struct lsns_process *proc; | |
8a204562 KZ |
142 | |
143 | struct list_head namespaces; /* lsns->processes member */ | |
144 | struct list_head processes; /* head of lsns_process *siblings */ | |
145 | }; | |
146 | ||
147 | struct lsns_process { | |
148 | pid_t pid; /* process PID */ | |
149 | pid_t ppid; /* parent's PID */ | |
150 | pid_t tpid; /* thread group */ | |
151 | char state; | |
a52852d3 | 152 | uid_t uid; |
8a204562 KZ |
153 | |
154 | ino_t ns_ids[ARRAY_SIZE(ns_names)]; | |
155 | struct list_head ns_siblings[ARRAY_SIZE(ns_names)]; | |
156 | ||
8a204562 | 157 | struct list_head processes; /* list of processes */ |
969d3d15 KZ |
158 | |
159 | struct libscols_line *outline; | |
160 | struct lsns_process *parent; | |
4195756e MY |
161 | |
162 | int netnsid; | |
8a204562 KZ |
163 | }; |
164 | ||
165 | struct lsns { | |
166 | struct list_head processes; | |
167 | struct list_head namespaces; | |
168 | ||
304fbe8b KZ |
169 | pid_t fltr_pid; /* filter out by PID */ |
170 | ino_t fltr_ns; /* filter out by namespace */ | |
171 | int fltr_types[ARRAY_SIZE(ns_names)]; | |
172 | int fltr_ntypes; | |
8a204562 KZ |
173 | |
174 | unsigned int raw : 1, | |
175 | json : 1, | |
969d3d15 KZ |
176 | tree : 1, |
177 | list : 1, | |
deb3f518 | 178 | no_trunc : 1, |
0a32d39a MY |
179 | no_headings: 1, |
180 | no_wrap : 1; | |
74d2056a MY |
181 | |
182 | struct libmnt_table *tab; | |
8a204562 KZ |
183 | }; |
184 | ||
4195756e MY |
185 | struct netnsid_cache { |
186 | ino_t ino; | |
187 | int id; | |
188 | struct list_head netnsids; | |
189 | }; | |
190 | ||
191 | static struct list_head netnsids_cache; | |
192 | ||
193 | static int netlink_fd = -1; | |
194 | ||
8a204562 KZ |
195 | static void lsns_init_debug(void) |
196 | { | |
a15dca2f | 197 | __UL_INIT_DEBUG_FROM_ENV(lsns, LSNS_DEBUG_, 0, LSNS_DEBUG); |
8a204562 KZ |
198 | } |
199 | ||
304fbe8b KZ |
200 | static int ns_name2type(const char *name) |
201 | { | |
202 | size_t i; | |
203 | ||
204 | for (i = 0; i < ARRAY_SIZE(ns_names); i++) { | |
205 | if (strcmp(ns_names[i], name) == 0) | |
206 | return i; | |
207 | } | |
208 | return -1; | |
209 | } | |
210 | ||
8a204562 KZ |
211 | static int column_name_to_id(const char *name, size_t namesz) |
212 | { | |
213 | size_t i; | |
214 | ||
215 | assert(name); | |
216 | ||
217 | for (i = 0; i < ARRAY_SIZE(infos); i++) { | |
218 | const char *cn = infos[i].name; | |
219 | ||
220 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) | |
221 | return i; | |
222 | } | |
223 | warnx(_("unknown column: %s"), name); | |
224 | return -1; | |
225 | } | |
226 | ||
f593e279 KZ |
227 | static int has_column(int id) |
228 | { | |
229 | size_t i; | |
230 | ||
231 | for (i = 0; i < ncolumns; i++) { | |
232 | if (columns[i] == id) | |
233 | return 1; | |
234 | } | |
235 | return 0; | |
236 | } | |
237 | ||
8a204562 KZ |
238 | static inline int get_column_id(int num) |
239 | { | |
240 | assert(num >= 0); | |
241 | assert((size_t) num < ncolumns); | |
242 | assert(columns[num] < (int) ARRAY_SIZE(infos)); | |
243 | ||
244 | return columns[num]; | |
245 | } | |
246 | ||
969d3d15 | 247 | static inline const struct colinfo *get_column_info(unsigned num) |
8a204562 KZ |
248 | { |
249 | return &infos[ get_column_id(num) ]; | |
250 | } | |
251 | ||
3082f851 | 252 | static int get_ns_ino(int dir, const char *nsname, ino_t *ino) |
8a204562 KZ |
253 | { |
254 | struct stat st; | |
255 | char path[16]; | |
256 | ||
257 | snprintf(path, sizeof(path), "ns/%s", nsname); | |
258 | ||
259 | if (fstatat(dir, path, &st, 0) != 0) | |
260 | return -errno; | |
261 | *ino = st.st_ino; | |
262 | return 0; | |
263 | } | |
264 | ||
3fcbd797 OH |
265 | static int parse_proc_stat(FILE *fp, pid_t *pid, char *state, pid_t *ppid) |
266 | { | |
267 | char *line = NULL, *p; | |
268 | size_t len = 0; | |
269 | int rc; | |
270 | ||
271 | if (getline(&line, &len, fp) < 0) { | |
272 | rc = -errno; | |
273 | goto error; | |
274 | } | |
275 | ||
276 | p = strrchr(line, ')'); | |
277 | if (p == NULL || | |
278 | sscanf(line, "%d (", pid) != 1 || | |
279 | sscanf(p, ") %c %d*[^\n]", state, ppid) != 2) { | |
280 | rc = -EINVAL; | |
281 | goto error; | |
282 | } | |
283 | rc = 0; | |
284 | ||
285 | error: | |
286 | free(line); | |
287 | return rc; | |
288 | } | |
304fbe8b | 289 | |
4195756e | 290 | #ifdef HAVE_LINUX_NET_NAMESPACE_H |
7eda2400 | 291 | static int netnsid_cache_find(ino_t netino, int *netnsid) |
4195756e MY |
292 | { |
293 | struct list_head *p; | |
294 | ||
295 | list_for_each(p, &netnsids_cache) { | |
296 | struct netnsid_cache *e = list_entry(p, | |
297 | struct netnsid_cache, | |
298 | netnsids); | |
299 | if (e->ino == netino) { | |
300 | *netnsid = e->id; | |
7eda2400 | 301 | return 1; |
4195756e MY |
302 | } |
303 | } | |
304 | ||
7eda2400 | 305 | return 0; |
4195756e MY |
306 | } |
307 | ||
308 | static void netnsid_cache_add(ino_t netino, int netnsid) | |
309 | { | |
310 | struct netnsid_cache *e; | |
311 | ||
312 | e = xcalloc(1, sizeof(*e)); | |
313 | e->ino = netino; | |
314 | e->id = netnsid; | |
315 | INIT_LIST_HEAD(&e->netnsids); | |
316 | list_add(&e->netnsids, &netnsids_cache); | |
317 | } | |
318 | ||
319 | static int get_netnsid_via_netlink_send_request(int target_fd) | |
320 | { | |
321 | unsigned char req[NLMSG_SPACE(sizeof(struct rtgenmsg)) | |
322 | + RTA_SPACE(sizeof(int32_t))]; | |
323 | ||
324 | struct nlmsghdr *nlh = (struct nlmsghdr *)req; | |
325 | struct rtgenmsg *rt = NLMSG_DATA(req); | |
326 | struct rtattr *rta = (struct rtattr *) | |
327 | (req + NLMSG_SPACE(sizeof(struct rtgenmsg))); | |
328 | int32_t *fd = RTA_DATA(rta); | |
329 | ||
330 | nlh->nlmsg_len = sizeof(req); | |
331 | nlh->nlmsg_flags = NLM_F_REQUEST; | |
332 | nlh->nlmsg_type = RTM_GETNSID; | |
333 | rt->rtgen_family = AF_UNSPEC; | |
334 | rta->rta_type = NETNSA_FD; | |
335 | rta->rta_len = RTA_SPACE(sizeof(int32_t)); | |
336 | *fd = target_fd; | |
337 | ||
338 | if (send(netlink_fd, req, sizeof(req), 0) < 0) | |
339 | return -1; | |
340 | return 0; | |
341 | } | |
342 | ||
343 | static int get_netnsid_via_netlink_recv_response(int *netnsid) | |
344 | { | |
345 | unsigned char res[NLMSG_SPACE(sizeof(struct rtgenmsg)) | |
346 | + ((RTA_SPACE(sizeof(int32_t)) | |
347 | < RTA_SPACE(sizeof(struct nlmsgerr))) | |
348 | ? RTA_SPACE(sizeof(struct nlmsgerr)) | |
349 | : RTA_SPACE(sizeof(int32_t)))]; | |
58b29eed RM |
350 | int rtalen; |
351 | ssize_t reslen; | |
4195756e MY |
352 | |
353 | struct nlmsghdr *nlh; | |
354 | struct rtattr *rta; | |
355 | ||
356 | reslen = recv(netlink_fd, res, sizeof(res), 0); | |
357 | if (reslen < 0) | |
358 | return -1; | |
359 | ||
360 | nlh = (struct nlmsghdr *)res; | |
58b29eed | 361 | if (!(NLMSG_OK(nlh, (size_t)reslen) |
4195756e MY |
362 | && nlh->nlmsg_type == RTM_NEWNSID)) |
363 | return -1; | |
364 | ||
365 | rtalen = NLMSG_PAYLOAD(nlh, sizeof(struct rtgenmsg)); | |
366 | rta = (struct rtattr *)(res + NLMSG_SPACE(sizeof(struct rtgenmsg))); | |
367 | if (!(RTA_OK(rta, rtalen) | |
368 | && rta->rta_type == NETNSA_NSID)) | |
369 | return -1; | |
370 | ||
371 | *netnsid = *(int *)RTA_DATA(rta); | |
372 | ||
373 | return 0; | |
374 | } | |
375 | ||
376 | static int get_netnsid_via_netlink(int dir, const char *path) | |
377 | { | |
378 | int netnsid; | |
379 | int target_fd; | |
380 | ||
381 | if (netlink_fd < 0) | |
382 | return LSNS_NETNS_UNUSABLE; | |
383 | ||
384 | target_fd = openat(dir, path, O_RDONLY); | |
385 | if (target_fd < 0) | |
386 | return LSNS_NETNS_UNUSABLE; | |
387 | ||
388 | if (get_netnsid_via_netlink_send_request(target_fd) < 0) { | |
389 | netnsid = LSNS_NETNS_UNUSABLE; | |
390 | goto out; | |
391 | } | |
392 | ||
393 | if (get_netnsid_via_netlink_recv_response(&netnsid) < 0) { | |
394 | netnsid = LSNS_NETNS_UNUSABLE; | |
395 | goto out; | |
396 | } | |
397 | ||
398 | out: | |
399 | close(target_fd); | |
400 | return netnsid; | |
401 | } | |
402 | ||
403 | static int get_netnsid(int dir, ino_t netino) | |
404 | { | |
405 | int netnsid; | |
406 | ||
407 | if (!netnsid_cache_find(netino, &netnsid)) { | |
408 | netnsid = get_netnsid_via_netlink(dir, "ns/net"); | |
409 | netnsid_cache_add(netino, netnsid); | |
410 | } | |
411 | ||
412 | return netnsid; | |
413 | } | |
414 | #else | |
415 | static int get_netnsid(int dir __attribute__((__unused__)), | |
416 | ino_t netino __attribute__((__unused__))) | |
417 | { | |
418 | return LSNS_NETNS_UNUSABLE; | |
419 | } | |
420 | #endif /* HAVE_LINUX_NET_NAMESPACE_H */ | |
421 | ||
8a204562 KZ |
422 | static int read_process(struct lsns *ls, pid_t pid) |
423 | { | |
424 | struct lsns_process *p = NULL; | |
425 | char buf[BUFSIZ]; | |
426 | DIR *dir; | |
427 | int rc = 0, fd; | |
428 | FILE *f = NULL; | |
429 | size_t i; | |
a52852d3 | 430 | struct stat st; |
8a204562 KZ |
431 | |
432 | DBG(PROC, ul_debug("reading %d", (int) pid)); | |
433 | ||
434 | snprintf(buf, sizeof(buf), "/proc/%d", pid); | |
435 | dir = opendir(buf); | |
436 | if (!dir) | |
437 | return -errno; | |
438 | ||
9d3d66df | 439 | p = xcalloc(1, sizeof(*p)); |
4195756e | 440 | p->netnsid = LSNS_NETNS_UNUSABLE; |
8a204562 | 441 | |
a52852d3 KZ |
442 | if (fstat(dirfd(dir), &st) == 0) { |
443 | p->uid = st.st_uid; | |
444 | add_uid(uid_cache, st.st_uid); | |
445 | } | |
446 | ||
8a204562 KZ |
447 | fd = openat(dirfd(dir), "stat", O_RDONLY); |
448 | if (fd < 0) { | |
449 | rc = -errno; | |
450 | goto done; | |
451 | } | |
452 | if (!(f = fdopen(fd, "r"))) { | |
453 | rc = -errno; | |
454 | goto done; | |
455 | } | |
3fcbd797 OH |
456 | rc = parse_proc_stat(f, &p->pid, &p->state, &p->ppid); |
457 | if (rc < 0) | |
8a204562 | 458 | goto done; |
8a204562 KZ |
459 | rc = 0; |
460 | ||
461 | for (i = 0; i < ARRAY_SIZE(p->ns_ids); i++) { | |
462 | INIT_LIST_HEAD(&p->ns_siblings[i]); | |
463 | ||
304fbe8b KZ |
464 | if (!ls->fltr_types[i]) |
465 | continue; | |
466 | ||
8a204562 | 467 | rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i]); |
3082f851 | 468 | if (rc && rc != -EACCES && rc != -ENOENT) |
8a204562 | 469 | goto done; |
4195756e MY |
470 | if (i == LSNS_ID_NET) |
471 | p->netnsid = get_netnsid(dirfd(dir), p->ns_ids[i]); | |
8a204562 KZ |
472 | rc = 0; |
473 | } | |
474 | ||
475 | INIT_LIST_HEAD(&p->processes); | |
8a204562 | 476 | |
9dfd6019 | 477 | DBG(PROC, ul_debugobj(p, "new pid=%d", p->pid)); |
8a204562 KZ |
478 | list_add_tail(&p->processes, &ls->processes); |
479 | done: | |
480 | if (f) | |
481 | fclose(f); | |
482 | closedir(dir); | |
483 | if (rc) | |
484 | free(p); | |
485 | return rc; | |
486 | } | |
487 | ||
488 | static int read_processes(struct lsns *ls) | |
489 | { | |
490 | struct proc_processes *proc = NULL; | |
491 | pid_t pid; | |
492 | int rc = 0; | |
493 | ||
8a204562 KZ |
494 | DBG(PROC, ul_debug("opening /proc")); |
495 | ||
496 | if (!(proc = proc_open_processes())) { | |
497 | rc = -errno; | |
498 | goto done; | |
499 | } | |
500 | ||
501 | while (proc_next_pid(proc, &pid) == 0) { | |
502 | rc = read_process(ls, pid); | |
5dc625b6 | 503 | if (rc && rc != -EACCES && rc != -ENOENT) |
8a204562 KZ |
504 | break; |
505 | rc = 0; | |
506 | } | |
507 | done: | |
508 | DBG(PROC, ul_debug("closing /proc")); | |
509 | proc_close_processes(proc); | |
510 | return rc; | |
511 | } | |
512 | ||
969d3d15 | 513 | static struct lsns_namespace *get_namespace(struct lsns *ls, ino_t ino) |
8a204562 KZ |
514 | { |
515 | struct list_head *p; | |
516 | ||
517 | list_for_each(p, &ls->namespaces) { | |
518 | struct lsns_namespace *ns = list_entry(p, struct lsns_namespace, namespaces); | |
519 | ||
969d3d15 | 520 | if (ns->id == ino) |
8a204562 KZ |
521 | return ns; |
522 | } | |
523 | return NULL; | |
524 | } | |
525 | ||
9dfd6019 KZ |
526 | static int namespace_has_process(struct lsns_namespace *ns, pid_t pid) |
527 | { | |
528 | struct list_head *p; | |
529 | ||
530 | list_for_each(p, &ns->processes) { | |
531 | struct lsns_process *proc = list_entry(p, struct lsns_process, ns_siblings[ns->type]); | |
532 | ||
533 | if (proc->pid == pid) | |
534 | return 1; | |
535 | } | |
536 | return 0; | |
537 | } | |
538 | ||
8a204562 KZ |
539 | static struct lsns_namespace *add_namespace(struct lsns *ls, int type, ino_t ino) |
540 | { | |
9d3d66df | 541 | struct lsns_namespace *ns = xcalloc(1, sizeof(*ns)); |
8a204562 KZ |
542 | |
543 | if (!ns) | |
544 | return NULL; | |
545 | ||
e1a15f8f | 546 | DBG(NS, ul_debugobj(ns, "new %s[%ju]", ns_names[type], (uintmax_t)ino)); |
8a204562 KZ |
547 | |
548 | INIT_LIST_HEAD(&ns->processes); | |
549 | INIT_LIST_HEAD(&ns->namespaces); | |
550 | ||
551 | ns->type = type; | |
552 | ns->id = ino; | |
553 | ||
554 | list_add_tail(&ns->namespaces, &ls->namespaces); | |
555 | return ns; | |
556 | } | |
557 | ||
969d3d15 | 558 | static int add_process_to_namespace(struct lsns *ls, struct lsns_namespace *ns, struct lsns_process *proc) |
8a204562 | 559 | { |
969d3d15 KZ |
560 | struct list_head *p; |
561 | ||
e1a15f8f RM |
562 | DBG(NS, ul_debugobj(ns, "add process [%p] pid=%d to %s[%ju]", |
563 | proc, proc->pid, ns_names[ns->type], (uintmax_t)ns->id)); | |
8a204562 | 564 | |
969d3d15 KZ |
565 | list_for_each(p, &ls->processes) { |
566 | struct lsns_process *xproc = list_entry(p, struct lsns_process, processes); | |
567 | ||
568 | if (xproc->pid == proc->ppid) /* my parent */ | |
569 | proc->parent = xproc; | |
570 | else if (xproc->ppid == proc->pid) /* my child */ | |
571 | xproc->parent = proc; | |
572 | } | |
573 | ||
8a204562 KZ |
574 | list_add_tail(&proc->ns_siblings[ns->type], &ns->processes); |
575 | ns->nprocs++; | |
576 | ||
a52852d3 KZ |
577 | if (!ns->proc || ns->proc->pid > proc->pid) |
578 | ns->proc = proc; | |
969d3d15 | 579 | |
8a204562 KZ |
580 | return 0; |
581 | } | |
582 | ||
1880a578 KZ |
583 | static int cmp_namespaces(struct list_head *a, struct list_head *b, |
584 | __attribute__((__unused__)) void *data) | |
585 | { | |
586 | struct lsns_namespace *xa = list_entry(a, struct lsns_namespace, namespaces), | |
587 | *xb = list_entry(b, struct lsns_namespace, namespaces); | |
588 | ||
589 | return cmp_numbers(xa->id, xb->id); | |
590 | } | |
591 | ||
4195756e MY |
592 | static int netnsid_xasputs(char **str, int netnsid) |
593 | { | |
594 | if (netnsid >= 0) | |
595 | return xasprintf(str, "%d", netnsid); | |
596 | #ifdef NETNSA_NSID_NOT_ASSIGNED | |
597 | else if (netnsid == NETNSA_NSID_NOT_ASSIGNED) | |
598 | return xasprintf(str, "%s", "unassigned"); | |
599 | #endif | |
600 | else | |
601 | return 0; | |
602 | } | |
603 | ||
8a204562 KZ |
604 | static int read_namespaces(struct lsns *ls) |
605 | { | |
606 | struct list_head *p; | |
607 | ||
608 | DBG(NS, ul_debug("reading namespace")); | |
609 | ||
610 | list_for_each(p, &ls->processes) { | |
611 | size_t i; | |
612 | struct lsns_namespace *ns; | |
613 | struct lsns_process *proc = list_entry(p, struct lsns_process, processes); | |
614 | ||
615 | for (i = 0; i < ARRAY_SIZE(proc->ns_ids); i++) { | |
616 | if (proc->ns_ids[i] == 0) | |
617 | continue; | |
969d3d15 | 618 | if (!(ns = get_namespace(ls, proc->ns_ids[i]))) { |
8a204562 KZ |
619 | ns = add_namespace(ls, i, proc->ns_ids[i]); |
620 | if (!ns) | |
621 | return -ENOMEM; | |
622 | } | |
969d3d15 | 623 | add_process_to_namespace(ls, ns, proc); |
8a204562 KZ |
624 | } |
625 | } | |
626 | ||
1880a578 KZ |
627 | list_sort(&ls->namespaces, cmp_namespaces, NULL); |
628 | ||
8a204562 KZ |
629 | return 0; |
630 | } | |
631 | ||
7eda2400 | 632 | static int is_nsfs_root(struct libmnt_fs *fs, void *data) |
74d2056a | 633 | { |
7eda2400 KZ |
634 | if (!mnt_fs_match_fstype(fs, "nsfs") || !mnt_fs_get_root(fs)) |
635 | return 0; | |
636 | ||
637 | return (strcmp(mnt_fs_get_root(fs), (char *)data) == 0); | |
74d2056a MY |
638 | } |
639 | ||
7eda2400 | 640 | static int is_path_included(const char *path_set, const char *elt, |
74d2056a MY |
641 | const char sep) |
642 | { | |
643 | size_t elt_len; | |
644 | size_t path_set_len; | |
645 | char *tmp; | |
646 | ||
647 | ||
648 | tmp = strstr(path_set, elt); | |
649 | if (!tmp) | |
7eda2400 | 650 | return 0; |
74d2056a MY |
651 | |
652 | elt_len = strlen(elt); | |
653 | path_set_len = strlen(path_set); | |
654 | ||
655 | /* path_set includes only elt or | |
656 | * path_set includes elt as the first element. | |
657 | */ | |
658 | if (tmp == path_set | |
659 | && ((path_set_len == elt_len) | |
660 | || (path_set[elt_len] == sep))) | |
7eda2400 KZ |
661 | return 1; |
662 | ||
74d2056a MY |
663 | /* path_set includes elt at the middle |
664 | * or as the last element. | |
665 | */ | |
666 | if ((*(tmp - 1) == sep) | |
667 | && ((*(tmp + elt_len) == sep) | |
668 | || (*(tmp + elt_len) == '\0'))) | |
7eda2400 | 669 | return 1; |
74d2056a | 670 | |
7eda2400 | 671 | return 0; |
74d2056a MY |
672 | } |
673 | ||
674 | static int nsfs_xasputs(char **str, | |
675 | struct lsns_namespace *ns, | |
676 | struct libmnt_table *tab, | |
677 | char sep) | |
678 | { | |
679 | struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD); | |
680 | char *expected_root; | |
7eda2400 | 681 | struct libmnt_fs *fs = NULL; |
74d2056a | 682 | |
845dac5f | 683 | xasprintf(&expected_root, "%s:[%ju]", ns_names[ns->type], (uintmax_t)ns->id); |
7eda2400 | 684 | *str = NULL; |
74d2056a | 685 | |
7eda2400 KZ |
686 | while (mnt_table_find_next_fs(tab, itr, is_nsfs_root, |
687 | expected_root, &fs) == 0) { | |
74d2056a | 688 | |
7eda2400 KZ |
689 | const char *tgt = mnt_fs_get_target(fs); |
690 | ||
691 | if (!*str) | |
692 | xasprintf(str, "%s", tgt); | |
693 | ||
694 | else if (!is_path_included(*str, tgt, sep)) { | |
695 | char *tmp = NULL; | |
696 | ||
697 | xasprintf(&tmp, "%s%c%s", *str, sep, tgt); | |
698 | free(*str); | |
699 | *str = tmp; | |
74d2056a MY |
700 | } |
701 | } | |
702 | free(expected_root); | |
703 | mnt_free_iter(itr); | |
704 | ||
705 | return 1; | |
706 | } | |
969d3d15 KZ |
707 | static void add_scols_line(struct lsns *ls, struct libscols_table *table, |
708 | struct lsns_namespace *ns, struct lsns_process *proc) | |
8a204562 KZ |
709 | { |
710 | size_t i; | |
711 | struct libscols_line *line; | |
712 | ||
713 | assert(ns); | |
714 | assert(table); | |
715 | ||
969d3d15 KZ |
716 | line = scols_table_new_line(table, |
717 | ls->tree && proc->parent ? proc->parent->outline : NULL); | |
8a204562 KZ |
718 | if (!line) { |
719 | warn(_("failed to add line to output")); | |
720 | return; | |
721 | } | |
722 | ||
723 | for (i = 0; i < ncolumns; i++) { | |
724 | char *str = NULL; | |
725 | ||
726 | switch (get_column_id(i)) { | |
727 | case COL_NS: | |
e1a15f8f | 728 | xasprintf(&str, "%ju", (uintmax_t)ns->id); |
8a204562 KZ |
729 | break; |
730 | case COL_PID: | |
969d3d15 KZ |
731 | xasprintf(&str, "%d", (int) proc->pid); |
732 | break; | |
733 | case COL_PPID: | |
734 | xasprintf(&str, "%d", (int) proc->ppid); | |
8a204562 KZ |
735 | break; |
736 | case COL_TYPE: | |
737 | xasprintf(&str, "%s", ns_names[ns->type]); | |
738 | break; | |
739 | case COL_NPROCS: | |
740 | xasprintf(&str, "%d", ns->nprocs); | |
741 | break; | |
742 | case COL_COMMAND: | |
969d3d15 | 743 | str = proc_get_command(proc->pid); |
8a204562 | 744 | if (!str) |
969d3d15 | 745 | str = proc_get_command_name(proc->pid); |
8a204562 KZ |
746 | break; |
747 | case COL_PATH: | |
969d3d15 | 748 | xasprintf(&str, "/proc/%d/ns/%s", (int) proc->pid, ns_names[ns->type]); |
a52852d3 KZ |
749 | break; |
750 | case COL_UID: | |
969d3d15 | 751 | xasprintf(&str, "%d", (int) proc->uid); |
a52852d3 KZ |
752 | break; |
753 | case COL_USER: | |
969d3d15 | 754 | xasprintf(&str, "%s", get_id(uid_cache, proc->uid)->name); |
8a204562 | 755 | break; |
4195756e MY |
756 | case COL_NETNSID: |
757 | if (ns->type == LSNS_ID_NET) | |
758 | netnsid_xasputs(&str, proc->netnsid); | |
759 | break; | |
74d2056a | 760 | case COL_NSFS: |
7eda2400 | 761 | nsfs_xasputs(&str, ns, ls->tab, ls->no_wrap ? ',' : '\n'); |
74d2056a | 762 | break; |
8a204562 KZ |
763 | default: |
764 | break; | |
765 | } | |
766 | ||
3e11eaa8 | 767 | if (str && scols_line_refer_data(line, i, str) != 0) |
99ae5a49 | 768 | err_oom(); |
8a204562 | 769 | } |
969d3d15 KZ |
770 | |
771 | proc->outline = line; | |
8a204562 KZ |
772 | } |
773 | ||
969d3d15 | 774 | static struct libscols_table *init_scols_table(struct lsns *ls) |
8a204562 KZ |
775 | { |
776 | struct libscols_table *tab; | |
8a204562 | 777 | size_t i; |
8a204562 KZ |
778 | |
779 | tab = scols_new_table(); | |
780 | if (!tab) { | |
781 | warn(_("failed to initialize output table")); | |
969d3d15 | 782 | return NULL; |
8a204562 KZ |
783 | } |
784 | ||
785 | scols_table_enable_raw(tab, ls->raw); | |
786 | scols_table_enable_json(tab, ls->json); | |
787 | scols_table_enable_noheadings(tab, ls->no_headings); | |
788 | ||
789 | if (ls->json) | |
790 | scols_table_set_name(tab, "namespaces"); | |
791 | ||
792 | for (i = 0; i < ncolumns; i++) { | |
969d3d15 KZ |
793 | const struct colinfo *col = get_column_info(i); |
794 | int flags = col->flags; | |
74d2056a | 795 | struct libscols_column *cl; |
969d3d15 | 796 | |
deb3f518 | 797 | if (ls->no_trunc) |
969d3d15 KZ |
798 | flags &= ~SCOLS_FL_TRUNC; |
799 | if (ls->tree && get_column_id(i) == COL_COMMAND) | |
800 | flags |= SCOLS_FL_TREE; | |
0a32d39a MY |
801 | if (ls->no_wrap) |
802 | flags &= ~SCOLS_FL_WRAP; | |
8a204562 | 803 | |
74d2056a MY |
804 | cl = scols_table_new_column(tab, col->name, col->whint, flags); |
805 | if (cl == NULL) { | |
8a204562 | 806 | warnx(_("failed to initialize output column")); |
969d3d15 | 807 | goto err; |
8a204562 | 808 | } |
31b25347 KZ |
809 | if (ls->json) |
810 | scols_column_set_json_type(cl, col->json_type); | |
811 | ||
7eda2400 | 812 | if (!ls->no_wrap && get_column_id(i) == COL_NSFS) { |
74d2056a MY |
813 | scols_column_set_wrapfunc(cl, |
814 | scols_wrapnl_chunksize, | |
815 | scols_wrapnl_nextchunk, | |
816 | NULL); | |
817 | scols_column_set_safechars(cl, "\n"); | |
818 | } | |
8a204562 KZ |
819 | } |
820 | ||
969d3d15 KZ |
821 | return tab; |
822 | err: | |
823 | scols_unref_table(tab); | |
824 | return NULL; | |
825 | } | |
826 | ||
827 | static int show_namespaces(struct lsns *ls) | |
828 | { | |
829 | struct libscols_table *tab; | |
830 | struct list_head *p; | |
831 | int rc = 0; | |
832 | ||
833 | tab = init_scols_table(ls); | |
834 | if (!tab) | |
835 | return -ENOMEM; | |
836 | ||
8a204562 KZ |
837 | list_for_each(p, &ls->namespaces) { |
838 | struct lsns_namespace *ns = list_entry(p, struct lsns_namespace, namespaces); | |
9dfd6019 | 839 | |
304fbe8b | 840 | if (ls->fltr_pid != 0 && !namespace_has_process(ns, ls->fltr_pid)) |
9dfd6019 KZ |
841 | continue; |
842 | ||
969d3d15 | 843 | add_scols_line(ls, tab, ns, ns->proc); |
8a204562 KZ |
844 | } |
845 | ||
846 | scols_print_table(tab); | |
8a204562 KZ |
847 | scols_unref_table(tab); |
848 | return rc; | |
849 | } | |
850 | ||
969d3d15 KZ |
851 | static void show_process(struct lsns *ls, struct libscols_table *tab, |
852 | struct lsns_process *proc, struct lsns_namespace *ns) | |
853 | { | |
854 | /* | |
855 | * create a tree from parent->child relation, but only if the parent is | |
856 | * within the same namespace | |
857 | */ | |
858 | if (ls->tree | |
859 | && proc->parent | |
860 | && !proc->parent->outline | |
861 | && proc->parent->ns_ids[ns->type] == proc->ns_ids[ns->type]) | |
862 | show_process(ls, tab, proc->parent, ns); | |
863 | ||
864 | add_scols_line(ls, tab, ns, proc); | |
865 | } | |
866 | ||
867 | ||
868 | static int show_namespace_processes(struct lsns *ls, struct lsns_namespace *ns) | |
869 | { | |
870 | struct libscols_table *tab; | |
871 | struct list_head *p; | |
872 | ||
873 | tab = init_scols_table(ls); | |
874 | if (!tab) | |
875 | return -ENOMEM; | |
876 | ||
877 | list_for_each(p, &ns->processes) { | |
878 | struct lsns_process *proc = list_entry(p, struct lsns_process, ns_siblings[ns->type]); | |
1880a578 KZ |
879 | |
880 | if (!proc->outline) | |
881 | show_process(ls, tab, proc, ns); | |
969d3d15 KZ |
882 | } |
883 | ||
884 | ||
885 | scols_print_table(tab); | |
886 | scols_unref_table(tab); | |
887 | return 0; | |
888 | } | |
889 | ||
86be6a32 | 890 | static void __attribute__((__noreturn__)) usage(void) |
8a204562 | 891 | { |
86be6a32 | 892 | FILE *out = stdout; |
8a204562 KZ |
893 | size_t i; |
894 | ||
895 | fputs(USAGE_HEADER, out); | |
896 | ||
897 | fprintf(out, | |
969d3d15 | 898 | _(" %s [options] [<namespace>]\n"), program_invocation_short_name); |
8a204562 KZ |
899 | |
900 | fputs(USAGE_SEPARATOR, out); | |
969d3d15 | 901 | fputs(_("List system namespaces.\n"), out); |
8a204562 KZ |
902 | |
903 | fputs(USAGE_OPTIONS, out); | |
904 | fputs(_(" -J, --json use JSON output format\n"), out); | |
969d3d15 | 905 | fputs(_(" -l, --list use list format output\n"), out); |
8a204562 KZ |
906 | fputs(_(" -n, --noheadings don't print headings\n"), out); |
907 | fputs(_(" -o, --output <list> define which output columns to use\n"), out); | |
1f7b62e0 | 908 | fputs(_(" --output-all output all columns\n"), out); |
969d3d15 | 909 | fputs(_(" -p, --task <pid> print process namespaces\n"), out); |
8a204562 KZ |
910 | fputs(_(" -r, --raw use the raw output format\n"), out); |
911 | fputs(_(" -u, --notruncate don't truncate text in columns\n"), out); | |
0a32d39a | 912 | fputs(_(" -W, --nowrap don't use multi-line representation\n"), out); |
2b8889c4 | 913 | fputs(_(" -t, --type <name> namespace type (mnt, net, ipc, user, pid, uts, cgroup)\n"), out); |
8a204562 KZ |
914 | |
915 | fputs(USAGE_SEPARATOR, out); | |
f45f3ec3 | 916 | printf(USAGE_HELP_OPTIONS(24)); |
8a204562 | 917 | |
c3a4cfc5 | 918 | fputs(USAGE_COLUMNS, out); |
8a204562 KZ |
919 | for (i = 0; i < ARRAY_SIZE(infos); i++) |
920 | fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help)); | |
921 | ||
f45f3ec3 | 922 | printf(USAGE_MAN_TAIL("lsns(8)")); |
8a204562 | 923 | |
86be6a32 | 924 | exit(EXIT_SUCCESS); |
8a204562 KZ |
925 | } |
926 | ||
304fbe8b | 927 | |
8a204562 KZ |
928 | int main(int argc, char *argv[]) |
929 | { | |
930 | struct lsns ls; | |
931 | int c; | |
932 | int r = 0; | |
933 | char *outarg = NULL; | |
1f7b62e0 SK |
934 | enum { |
935 | OPT_OUTPUT_ALL = CHAR_MAX + 1 | |
936 | }; | |
8a204562 KZ |
937 | static const struct option long_opts[] = { |
938 | { "json", no_argument, NULL, 'J' }, | |
939 | { "task", required_argument, NULL, 'p' }, | |
940 | { "help", no_argument, NULL, 'h' }, | |
941 | { "output", required_argument, NULL, 'o' }, | |
1f7b62e0 | 942 | { "output-all", no_argument, NULL, OPT_OUTPUT_ALL }, |
8a204562 KZ |
943 | { "notruncate", no_argument, NULL, 'u' }, |
944 | { "version", no_argument, NULL, 'V' }, | |
945 | { "noheadings", no_argument, NULL, 'n' }, | |
0a32d39a | 946 | { "nowrap", no_argument, NULL, 'W' }, |
969d3d15 | 947 | { "list", no_argument, NULL, 'l' }, |
8a204562 | 948 | { "raw", no_argument, NULL, 'r' }, |
304fbe8b | 949 | { "type", required_argument, NULL, 't' }, |
8a204562 KZ |
950 | { NULL, 0, NULL, 0 } |
951 | }; | |
952 | ||
953 | static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ | |
954 | { 'J','r' }, | |
955 | { 0 } | |
956 | }; | |
957 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
f593e279 | 958 | int is_net = 0; |
8a204562 KZ |
959 | |
960 | setlocale(LC_ALL, ""); | |
961 | bindtextdomain(PACKAGE, LOCALEDIR); | |
962 | textdomain(PACKAGE); | |
2c308875 | 963 | close_stdout_atexit(); |
8a204562 KZ |
964 | |
965 | lsns_init_debug(); | |
8a204562 | 966 | memset(&ls, 0, sizeof(ls)); |
304fbe8b | 967 | |
8a204562 KZ |
968 | INIT_LIST_HEAD(&ls.processes); |
969 | INIT_LIST_HEAD(&ls.namespaces); | |
4195756e | 970 | INIT_LIST_HEAD(&netnsids_cache); |
8a204562 KZ |
971 | |
972 | while ((c = getopt_long(argc, argv, | |
7eda2400 | 973 | "Jlp:o:nruhVt:W", long_opts, NULL)) != -1) { |
8a204562 KZ |
974 | |
975 | err_exclusive_options(c, long_opts, excl, excl_st); | |
976 | ||
977 | switch(c) { | |
978 | case 'J': | |
979 | ls.json = 1; | |
980 | break; | |
969d3d15 KZ |
981 | case 'l': |
982 | ls.list = 1; | |
983 | break; | |
8a204562 KZ |
984 | case 'o': |
985 | outarg = optarg; | |
986 | break; | |
1f7b62e0 SK |
987 | case OPT_OUTPUT_ALL: |
988 | for (ncolumns = 0; ncolumns < ARRAY_SIZE(infos); ncolumns++) | |
989 | columns[ncolumns] = ncolumns; | |
990 | break; | |
8a204562 | 991 | case 'p': |
304fbe8b | 992 | ls.fltr_pid = strtos32_or_err(optarg, _("invalid PID argument")); |
8a204562 | 993 | break; |
8a204562 KZ |
994 | case 'n': |
995 | ls.no_headings = 1; | |
996 | break; | |
997 | case 'r': | |
7eda2400 | 998 | ls.no_wrap = ls.raw = 1; |
8a204562 KZ |
999 | break; |
1000 | case 'u': | |
deb3f518 | 1001 | ls.no_trunc = 1; |
8a204562 | 1002 | break; |
304fbe8b KZ |
1003 | case 't': |
1004 | { | |
1005 | int type = ns_name2type(optarg); | |
1006 | if (type < 0) | |
1007 | errx(EXIT_FAILURE, _("unknown namespace type: %s"), optarg); | |
1008 | ls.fltr_types[type] = 1; | |
1009 | ls.fltr_ntypes++; | |
7a360346 | 1010 | if (type == LSNS_ID_NET) |
f593e279 | 1011 | is_net = 1; |
304fbe8b KZ |
1012 | break; |
1013 | } | |
0a32d39a MY |
1014 | case 'W': |
1015 | ls.no_wrap = 1; | |
1016 | break; | |
2c308875 KZ |
1017 | |
1018 | case 'h': | |
1019 | usage(); | |
1020 | case 'V': | |
1021 | print_version(EXIT_SUCCESS); | |
8a204562 | 1022 | default: |
677ec86c | 1023 | errtryhelp(EXIT_FAILURE); |
8a204562 KZ |
1024 | } |
1025 | } | |
1026 | ||
304fbe8b KZ |
1027 | if (!ls.fltr_ntypes) { |
1028 | size_t i; | |
7a360346 | 1029 | |
304fbe8b KZ |
1030 | for (i = 0; i < ARRAY_SIZE(ns_names); i++) |
1031 | ls.fltr_types[i] = 1; | |
82a437c7 | 1032 | } |
304fbe8b | 1033 | |
969d3d15 | 1034 | if (optind < argc) { |
304fbe8b | 1035 | if (ls.fltr_pid) |
969d3d15 | 1036 | errx(EXIT_FAILURE, _("--task is mutually exclusive with <namespace>")); |
304fbe8b | 1037 | ls.fltr_ns = strtou64_or_err(argv[optind], _("invalid namespace argument")); |
969d3d15 KZ |
1038 | ls.tree = ls.list ? 0 : 1; |
1039 | ||
1040 | if (!ncolumns) { | |
1041 | columns[ncolumns++] = COL_PID; | |
1042 | columns[ncolumns++] = COL_PPID; | |
1043 | columns[ncolumns++] = COL_USER; | |
1044 | columns[ncolumns++] = COL_COMMAND; | |
1045 | } | |
1046 | } | |
1047 | ||
8a204562 | 1048 | if (!ncolumns) { |
8a204562 KZ |
1049 | columns[ncolumns++] = COL_NS; |
1050 | columns[ncolumns++] = COL_TYPE; | |
1051 | columns[ncolumns++] = COL_NPROCS; | |
1052 | columns[ncolumns++] = COL_PID; | |
a52852d3 | 1053 | columns[ncolumns++] = COL_USER; |
f593e279 | 1054 | if (is_net) { |
7a360346 | 1055 | columns[ncolumns++] = COL_NETNSID; |
74d2056a MY |
1056 | columns[ncolumns++] = COL_NSFS; |
1057 | } | |
8a204562 KZ |
1058 | columns[ncolumns++] = COL_COMMAND; |
1059 | } | |
1060 | ||
f593e279 KZ |
1061 | if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), |
1062 | &ncolumns, column_name_to_id) < 0) | |
1063 | return EXIT_FAILURE; | |
7eda2400 | 1064 | |
8a204562 KZ |
1065 | scols_init_debug(0); |
1066 | ||
a52852d3 KZ |
1067 | uid_cache = new_idcache(); |
1068 | if (!uid_cache) | |
1069 | err(EXIT_FAILURE, _("failed to allocate UID cache")); | |
1070 | ||
4195756e | 1071 | #ifdef HAVE_LINUX_NET_NAMESPACE_H |
f593e279 | 1072 | if (has_column(COL_NETNSID)) |
7a360346 | 1073 | netlink_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
4195756e | 1074 | #endif |
f593e279 KZ |
1075 | if (has_column(COL_NSFS)) { |
1076 | ls.tab = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); | |
1077 | if (!ls.tab) | |
1078 | err(MNT_EX_FAIL, _("failed to parse %s"), _PATH_PROC_MOUNTINFO); | |
1079 | } | |
74d2056a | 1080 | |
8a204562 KZ |
1081 | r = read_processes(&ls); |
1082 | if (!r) | |
1083 | r = read_namespaces(&ls); | |
969d3d15 | 1084 | if (!r) { |
304fbe8b KZ |
1085 | if (ls.fltr_ns) { |
1086 | struct lsns_namespace *ns = get_namespace(&ls, ls.fltr_ns); | |
969d3d15 KZ |
1087 | |
1088 | if (!ns) | |
82a437c7 | 1089 | errx(EXIT_FAILURE, _("not found namespace: %ju"), (uintmax_t) ls.fltr_ns); |
969d3d15 KZ |
1090 | r = show_namespace_processes(&ls, ns); |
1091 | } else | |
1092 | r = show_namespaces(&ls); | |
1093 | } | |
8a204562 | 1094 | |
74d2056a | 1095 | mnt_free_table(ls.tab); |
4195756e MY |
1096 | if (netlink_fd >= 0) |
1097 | close(netlink_fd); | |
a52852d3 | 1098 | free_idcache(uid_cache); |
8a204562 KZ |
1099 | return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; |
1100 | } |