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