]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * lsipc - List information about IPC instances employed in the system | |
3 | * | |
4 | * Copyright (C) 2015 Ondrej Oprala <ooprala@redhat.com> | |
5 | * Copyright (C) 2015 Karel Zak <ooprala@redhat.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it would be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along | |
18 | * with this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
20 | * | |
21 | * | |
22 | * lsipc is inspired by the ipcs utility. The aim is to create | |
23 | * a utility unencumbered by a standard to provide more flexible | |
24 | * means of controlling the output. | |
25 | */ | |
26 | ||
27 | #include <errno.h> | |
28 | #include <getopt.h> | |
29 | #include <sys/time.h> | |
30 | #include <unistd.h> | |
31 | ||
32 | #include <libsmartcols.h> | |
33 | ||
34 | #include "c.h" | |
35 | #include "nls.h" | |
36 | #include "closestream.h" | |
37 | #include "strutils.h" | |
38 | #include "optutils.h" | |
39 | #include "xalloc.h" | |
40 | #include "procutils.h" | |
41 | #include "ipcutils.h" | |
42 | #include "timeutils.h" | |
43 | ||
44 | /* | |
45 | * time modes | |
46 | * */ | |
47 | enum { | |
48 | TIME_INVALID = 0, | |
49 | TIME_SHORT, | |
50 | TIME_FULL, | |
51 | TIME_ISO | |
52 | }; | |
53 | ||
54 | /* | |
55 | * IDs | |
56 | */ | |
57 | enum { | |
58 | /* generic */ | |
59 | COLDESC_IDX_GEN_FIRST = 0, | |
60 | COL_KEY = COLDESC_IDX_GEN_FIRST, | |
61 | COL_ID, | |
62 | COL_OWNER, | |
63 | COL_PERMS, | |
64 | COL_CUID, | |
65 | COL_CUSER, | |
66 | COL_CGID, | |
67 | COL_CGROUP, | |
68 | COL_UID, | |
69 | COL_USER, | |
70 | COL_GID, | |
71 | COL_GROUP, | |
72 | COL_CTIME, | |
73 | COLDESC_IDX_GEN_LAST = COL_CTIME, | |
74 | ||
75 | /* msgq-specific */ | |
76 | COLDESC_IDX_MSG_FIRST, | |
77 | COL_USEDBYTES = COLDESC_IDX_MSG_FIRST, | |
78 | COL_MSGS, | |
79 | COL_SEND, | |
80 | COL_RECV, | |
81 | COL_LSPID, | |
82 | COL_LRPID, | |
83 | COLDESC_IDX_MSG_LAST = COL_LRPID, | |
84 | ||
85 | /* shm-specific */ | |
86 | COLDESC_IDX_SHM_FIRST, | |
87 | COL_SIZE = COLDESC_IDX_SHM_FIRST, | |
88 | COL_NATTCH, | |
89 | COL_STATUS, | |
90 | COL_ATTACH, | |
91 | COL_DETACH, | |
92 | COL_COMMAND, | |
93 | COL_CPID, | |
94 | COL_LPID, | |
95 | COLDESC_IDX_SHM_LAST = COL_LPID, | |
96 | ||
97 | /* sem-specific */ | |
98 | COLDESC_IDX_SEM_FIRST, | |
99 | COL_NSEMS = COLDESC_IDX_SEM_FIRST, | |
100 | COL_OTIME, | |
101 | COLDESC_IDX_SEM_LAST = COL_OTIME, | |
102 | ||
103 | /* summary (--global) */ | |
104 | COLDESC_IDX_SUM_FIRST, | |
105 | COL_RESOURCE = COLDESC_IDX_SUM_FIRST, | |
106 | COL_DESC, | |
107 | COL_LIMIT, | |
108 | COL_USED, | |
109 | COL_USEPERC, | |
110 | COLDESC_IDX_SUM_LAST = COL_USEPERC | |
111 | }; | |
112 | ||
113 | /* not all columns apply to all options, so we specify a legal range for each */ | |
114 | static size_t LOWER, UPPER; | |
115 | ||
116 | /* | |
117 | * output modes | |
118 | */ | |
119 | enum { | |
120 | OUT_EXPORT = 1, | |
121 | OUT_NEWLINE, | |
122 | OUT_RAW, | |
123 | OUT_JSON, | |
124 | OUT_PRETTY, | |
125 | OUT_LIST | |
126 | }; | |
127 | ||
128 | struct lsipc_control { | |
129 | int outmode; | |
130 | unsigned int noheadings : 1, /* don't print header line */ | |
131 | notrunc : 1, /* don't truncate columns */ | |
132 | bytes : 1, /* SIZE in bytes */ | |
133 | numperms : 1, /* numeric permissions */ | |
134 | time_mode : 2; | |
135 | }; | |
136 | ||
137 | struct lsipc_coldesc { | |
138 | const char *name; | |
139 | const char *help; | |
140 | const char *pretty_name; | |
141 | ||
142 | double whint; /* width hint */ | |
143 | long flag; | |
144 | }; | |
145 | ||
146 | static const struct lsipc_coldesc coldescs[] = | |
147 | { | |
148 | /* common */ | |
149 | [COL_KEY] = { "KEY", N_("Resource key"), N_("Key"), 1}, | |
150 | [COL_ID] = { "ID", N_("Resource ID"), N_("ID"), 1}, | |
151 | [COL_OWNER] = { "OWNER", N_("Owner's username or UID"), N_("Owner"), 1, SCOLS_FL_RIGHT}, | |
152 | [COL_PERMS] = { "PERMS", N_("Permissions"), N_("Permissions"), 1, SCOLS_FL_RIGHT}, | |
153 | [COL_CUID] = { "CUID", N_("Creator UID"), N_("Creator UID"), 1, SCOLS_FL_RIGHT}, | |
154 | [COL_CUSER] = { "CUSER", N_("Creator user"), N_("Creator user"), 1 }, | |
155 | [COL_CGID] = { "CGID", N_("Creator GID"), N_("Creator GID"), 1, SCOLS_FL_RIGHT}, | |
156 | [COL_CGROUP] = { "CGROUP", N_("Creator group"), N_("Creator group"), 1 }, | |
157 | [COL_UID] = { "UID", N_("User ID"), N_("UID"), 1, SCOLS_FL_RIGHT}, | |
158 | [COL_USER] = { "USER", N_("User name"), N_("User name"), 1}, | |
159 | [COL_GID] = { "GID", N_("Group ID"), N_("GID"), 1, SCOLS_FL_RIGHT}, | |
160 | [COL_GROUP] = { "GROUP", N_("Group name"), N_("Group name"), 1}, | |
161 | [COL_CTIME] = { "CTIME", N_("Time of the last change"), N_("Last change"), 1, SCOLS_FL_RIGHT}, | |
162 | ||
163 | /* msgq-specific */ | |
164 | [COL_USEDBYTES] = { "USEDBYTES",N_("Bytes used"), N_("Bytes used"), 1, SCOLS_FL_RIGHT}, | |
165 | [COL_MSGS] = { "MSGS", N_("Number of messages"), N_("Messages"), 1}, | |
166 | [COL_SEND] = { "SEND", N_("Time of last msg sent"), N_("Msg sent"), 1, SCOLS_FL_RIGHT}, | |
167 | [COL_RECV] = { "RECV", N_("Time of last msg received"), N_("Msg received"), 1, SCOLS_FL_RIGHT}, | |
168 | [COL_LSPID] = { "LSPID", N_("PID of the last msg sender"), N_("Msg sender"), 1, SCOLS_FL_RIGHT}, | |
169 | [COL_LRPID] = { "LRPID", N_("PID of the last msg receiver"), N_("Msg receiver"), 1, SCOLS_FL_RIGHT}, | |
170 | ||
171 | /* shm-specific */ | |
172 | [COL_SIZE] = { "SIZE", N_("Segment size"), N_("Segment size"), 1, SCOLS_FL_RIGHT}, | |
173 | [COL_NATTCH] = { "NATTCH", N_("Number of attached processes"), N_("Attached processes"), 1, SCOLS_FL_RIGHT}, | |
174 | [COL_STATUS] = { "STATUS", N_("Status"), N_("Status"), 1, SCOLS_FL_NOEXTREMES}, | |
175 | [COL_ATTACH] = { "ATTACH", N_("Attach time"), N_("Attach time"), 1, SCOLS_FL_RIGHT}, | |
176 | [COL_DETACH] = { "DETACH", N_("Detach time"), N_("Detach time"), 1, SCOLS_FL_RIGHT}, | |
177 | [COL_COMMAND] = { "COMMAND", N_("Creator command line"), N_("Creator command"), 0, SCOLS_FL_TRUNC}, | |
178 | [COL_CPID] = { "CPID", N_("PID of the creator"), N_("Creator PID"), 1, SCOLS_FL_RIGHT}, | |
179 | [COL_LPID] = { "LPID", N_("PID of last user"), N_("Last user PID"), 1, SCOLS_FL_RIGHT}, | |
180 | ||
181 | /* sem-specific */ | |
182 | [COL_NSEMS] = { "NSEMS", N_("Number of semaphores"), N_("Semaphores"), 1, SCOLS_FL_RIGHT}, | |
183 | [COL_OTIME] = { "OTIME", N_("Time of the last operation"), N_("Last operation"), 1, SCOLS_FL_RIGHT}, | |
184 | ||
185 | /* cols for summarized information */ | |
186 | [COL_RESOURCE] = { "RESOURCE", N_("Resource name"), N_("Resource"), 1 }, | |
187 | [COL_DESC] = { "DESCRIPTION",N_("Resource description"), N_("Description"), 1 }, | |
188 | [COL_USED] = { "USED", N_("Currently used"), N_("Used"), 1, SCOLS_FL_RIGHT }, | |
189 | [COL_USEPERC] = { "USE%", N_("Currently use percentage"), N_("Use"), 1, SCOLS_FL_RIGHT }, | |
190 | [COL_LIMIT] = { "LIMIT", N_("System-wide limit"), N_("Limit"), 1, SCOLS_FL_RIGHT }, | |
191 | }; | |
192 | ||
193 | ||
194 | /* columns[] array specifies all currently wanted output column. The columns | |
195 | * are defined by coldescs[] array and you can specify (on command line) each | |
196 | * column twice. That's enough, dynamically allocated array of the columns is | |
197 | * unnecessary overkill and over-engineering in this case */ | |
198 | static int columns[ARRAY_SIZE(coldescs) * 2]; | |
199 | static size_t ncolumns; | |
200 | ||
201 | static inline size_t err_columns_index(size_t arysz, size_t idx) | |
202 | { | |
203 | if (idx >= arysz) | |
204 | errx(EXIT_FAILURE, _("too many columns specified, " | |
205 | "the limit is %zu columns"), | |
206 | arysz - 1); | |
207 | return idx; | |
208 | } | |
209 | ||
210 | #define add_column(ary, n, id) \ | |
211 | ((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id)) | |
212 | ||
213 | static int column_name_to_id(const char *name, size_t namesz) | |
214 | { | |
215 | size_t i; | |
216 | ||
217 | for (i = 0; i < ARRAY_SIZE(coldescs); i++) { | |
218 | const char *cn = coldescs[i].name; | |
219 | ||
220 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) { | |
221 | if (i > COL_CTIME) { | |
222 | if (i >= LOWER && i <= UPPER) | |
223 | return i; | |
224 | ||
225 | warnx(_("column %s does not apply to the specified IPC"), name); | |
226 | return -1; | |
227 | } | |
228 | ||
229 | return i; | |
230 | } | |
231 | } | |
232 | warnx(_("unknown column: %s"), name); | |
233 | return -1; | |
234 | } | |
235 | ||
236 | static int get_column_id(int num) | |
237 | { | |
238 | assert(num >= 0); | |
239 | assert((size_t) num < ncolumns); | |
240 | assert((size_t) columns[num] < ARRAY_SIZE(coldescs)); | |
241 | return columns[num]; | |
242 | } | |
243 | ||
244 | static const struct lsipc_coldesc *get_column_desc(int num) | |
245 | { | |
246 | return &coldescs[ get_column_id(num) ]; | |
247 | } | |
248 | ||
249 | static char *get_username(struct passwd **pw, uid_t id) | |
250 | { | |
251 | if (!*pw || (*pw)->pw_uid != id) | |
252 | *pw = getpwuid(id); | |
253 | ||
254 | return *pw ? xstrdup((*pw)->pw_name) : NULL; | |
255 | } | |
256 | ||
257 | static char *get_groupname(struct group **gr, gid_t id) | |
258 | { | |
259 | if (!*gr || (*gr)->gr_gid != id) | |
260 | *gr = getgrgid(id); | |
261 | ||
262 | return *gr ? xstrdup((*gr)->gr_name) : NULL; | |
263 | } | |
264 | ||
265 | static int parse_time_mode(const char *s) | |
266 | { | |
267 | struct lsipc_timefmt { | |
268 | const char *name; | |
269 | const int val; | |
270 | }; | |
271 | static const struct lsipc_timefmt timefmts[] = { | |
272 | {"iso", TIME_ISO}, | |
273 | {"full", TIME_FULL}, | |
274 | {"short", TIME_SHORT}, | |
275 | }; | |
276 | size_t i; | |
277 | ||
278 | for (i = 0; i < ARRAY_SIZE(timefmts); i++) { | |
279 | if (strcmp(timefmts[i].name, s) == 0) | |
280 | return timefmts[i].val; | |
281 | } | |
282 | errx(EXIT_FAILURE, _("unknown time format: %s"), s); | |
283 | } | |
284 | ||
285 | static void __attribute__((__noreturn__)) usage(void) | |
286 | { | |
287 | FILE *out = stdout; | |
288 | size_t i; | |
289 | ||
290 | fputs(USAGE_HEADER, out); | |
291 | fprintf(out, _(" %s [options]\n"), program_invocation_short_name); | |
292 | ||
293 | fputs(USAGE_SEPARATOR, out); | |
294 | fputs(_("Show information on IPC facilities.\n"), out); | |
295 | ||
296 | fputs(USAGE_SEPARATOR, out); | |
297 | fputs(_("Resource options:\n"), out); | |
298 | fputs(_(" -m, --shmems shared memory segments\n"), out); | |
299 | fputs(_(" -q, --queues message queues\n"), out); | |
300 | fputs(_(" -s, --semaphores semaphores\n"), out); | |
301 | fputs(_(" -g, --global info about system-wide usage (may be used with -m, -q and -s)\n"), out); | |
302 | fputs(_(" -i, --id <id> print details on resource identified by <id>\n"), out); | |
303 | ||
304 | fputs(USAGE_OPTIONS, out); | |
305 | fputs(_(" --noheadings don't print headings\n"), out); | |
306 | fputs(_(" --notruncate don't truncate output\n"), out); | |
307 | fputs(_(" --time-format=<type> display dates in short, full or iso format\n"), out); | |
308 | fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out); | |
309 | fputs(_(" -c, --creator show creator and owner\n"), out); | |
310 | fputs(_(" -e, --export display in an export-able output format\n"), out); | |
311 | fputs(_(" -J, --json use the JSON output format\n"), out); | |
312 | fputs(_(" -n, --newline display each piece of information on a new line\n"), out); | |
313 | fputs(_(" -l, --list force list output format (for example with --id)\n"), out); | |
314 | fputs(_(" -o, --output[=<list>] define the columns to output\n"), out); | |
315 | fputs(_(" -P, --numeric-perms print numeric permissions (PERMS column)\n"), out); | |
316 | fputs(_(" -r, --raw display in raw mode\n"), out); | |
317 | fputs(_(" -t, --time show attach, detach and change times\n"), out); | |
318 | ||
319 | fputs(USAGE_SEPARATOR, out); | |
320 | printf(USAGE_HELP_OPTIONS(26)); | |
321 | ||
322 | fprintf(out, _("\nGeneric columns:\n")); | |
323 | for (i = COLDESC_IDX_GEN_FIRST; i <= COLDESC_IDX_GEN_LAST; i++) | |
324 | fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); | |
325 | ||
326 | fprintf(out, _("\nShared-memory columns (--shmems):\n")); | |
327 | for (i = COLDESC_IDX_SHM_FIRST; i <= COLDESC_IDX_SHM_LAST; i++) | |
328 | fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); | |
329 | ||
330 | fprintf(out, _("\nMessage-queue columns (--queues):\n")); | |
331 | for (i = COLDESC_IDX_MSG_FIRST; i <= COLDESC_IDX_MSG_LAST; i++) | |
332 | fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); | |
333 | ||
334 | fprintf(out, _("\nSemaphore columns (--semaphores):\n")); | |
335 | for (i = COLDESC_IDX_SEM_FIRST; i <= COLDESC_IDX_SEM_LAST; i++) | |
336 | fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); | |
337 | ||
338 | fprintf(out, _("\nSummary columns (--global):\n")); | |
339 | for (i = COLDESC_IDX_SUM_FIRST; i <= COLDESC_IDX_SUM_LAST; i++) | |
340 | fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); | |
341 | ||
342 | printf(USAGE_MAN_TAIL("lsipc(1)")); | |
343 | exit(EXIT_SUCCESS); | |
344 | } | |
345 | ||
346 | static struct libscols_table *new_table(struct lsipc_control *ctl) | |
347 | { | |
348 | struct libscols_table *table = scols_new_table(); | |
349 | ||
350 | if (!table) | |
351 | err(EXIT_FAILURE, _("failed to allocate output table")); | |
352 | ||
353 | if (ctl->noheadings) | |
354 | scols_table_enable_noheadings(table, 1); | |
355 | ||
356 | switch(ctl->outmode) { | |
357 | case OUT_NEWLINE: | |
358 | scols_table_set_column_separator(table, "\n"); | |
359 | /* fallthrough */ | |
360 | case OUT_EXPORT: | |
361 | scols_table_enable_export(table, 1); | |
362 | break; | |
363 | case OUT_RAW: | |
364 | scols_table_enable_raw(table, 1); | |
365 | break; | |
366 | case OUT_PRETTY: | |
367 | scols_table_enable_noheadings(table, 1); | |
368 | break; | |
369 | case OUT_JSON: | |
370 | scols_table_enable_json(table, 1); | |
371 | break; | |
372 | default: | |
373 | break; | |
374 | } | |
375 | return table; | |
376 | } | |
377 | ||
378 | static struct libscols_table *setup_table(struct lsipc_control *ctl) | |
379 | { | |
380 | struct libscols_table *table = new_table(ctl); | |
381 | size_t n; | |
382 | ||
383 | for (n = 0; n < ncolumns; n++) { | |
384 | const struct lsipc_coldesc *desc = get_column_desc(n); | |
385 | int flags = desc->flag; | |
386 | ||
387 | if (ctl->notrunc) | |
388 | flags &= ~SCOLS_FL_TRUNC; | |
389 | if (!scols_table_new_column(table, desc->name, desc->whint, flags)) | |
390 | goto fail; | |
391 | } | |
392 | return table; | |
393 | fail: | |
394 | scols_unref_table(table); | |
395 | return NULL; | |
396 | } | |
397 | ||
398 | static int print_pretty(struct libscols_table *table) | |
399 | { | |
400 | struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); | |
401 | struct libscols_column *col; | |
402 | struct libscols_cell *data; | |
403 | struct libscols_line *ln; | |
404 | const char *hstr, *dstr; | |
405 | int n = 0; | |
406 | ||
407 | ln = scols_table_get_line(table, 0); | |
408 | while (!scols_table_next_column(table, itr, &col)) { | |
409 | ||
410 | data = scols_line_get_cell(ln, n); | |
411 | ||
412 | hstr = N_(get_column_desc(n)->pretty_name); | |
413 | dstr = scols_cell_get_data(data); | |
414 | ||
415 | if (dstr) | |
416 | printf("%s:%*c%-36s\n", hstr, 35 - (int)strlen(hstr), ' ', dstr); | |
417 | ++n; | |
418 | } | |
419 | ||
420 | /* this is used to pretty-print detailed info about a semaphore array */ | |
421 | if (ln) { | |
422 | struct libscols_table *subtab = scols_line_get_userdata(ln); | |
423 | if (subtab) { | |
424 | printf(_("Elements:\n\n")); | |
425 | scols_print_table(subtab); | |
426 | } | |
427 | } | |
428 | ||
429 | scols_free_iter(itr); | |
430 | return 0; | |
431 | ||
432 | } | |
433 | ||
434 | static int print_table(struct lsipc_control *ctl, struct libscols_table *tb) | |
435 | { | |
436 | if (ctl->outmode == OUT_PRETTY) | |
437 | print_pretty(tb); | |
438 | else | |
439 | scols_print_table(tb); | |
440 | return 0; | |
441 | } | |
442 | static struct timeval now; | |
443 | ||
444 | static char *make_time(int mode, time_t time) | |
445 | { | |
446 | char buf[64] = {0}; | |
447 | ||
448 | switch(mode) { | |
449 | case TIME_FULL: | |
450 | { | |
451 | struct tm tm; | |
452 | char *s; | |
453 | ||
454 | localtime_r(&time, &tm); | |
455 | asctime_r(&tm, buf); | |
456 | if (*(s = buf + strlen(buf) - 1) == '\n') | |
457 | *s = '\0'; | |
458 | break; | |
459 | } | |
460 | case TIME_SHORT: | |
461 | strtime_short(&time, &now, 0, buf, sizeof(buf)); | |
462 | break; | |
463 | case TIME_ISO: | |
464 | strtime_iso(&time, ISO_TIMESTAMP_T, buf, sizeof(buf)); | |
465 | break; | |
466 | default: | |
467 | errx(EXIT_FAILURE, _("unsupported time type")); | |
468 | } | |
469 | return xstrdup(buf); | |
470 | } | |
471 | ||
472 | static void global_set_data(struct libscols_table *tb, const char *resource, | |
473 | const char *desc, uintmax_t used, uintmax_t limit, int usage) | |
474 | { | |
475 | struct libscols_line *ln; | |
476 | size_t n; | |
477 | ||
478 | ln = scols_table_new_line(tb, NULL); | |
479 | if (!ln) | |
480 | err(EXIT_FAILURE, _("failed to allocate output line")); | |
481 | ||
482 | for (n = 0; n < ncolumns; n++) { | |
483 | int rc = 0; | |
484 | char *arg = NULL; | |
485 | ||
486 | switch (get_column_id(n)) { | |
487 | case COL_RESOURCE: | |
488 | rc = scols_line_set_data(ln, n, resource); | |
489 | break; | |
490 | case COL_DESC: | |
491 | rc = scols_line_set_data(ln, n, desc); | |
492 | break; | |
493 | case COL_USED: | |
494 | if (usage) { | |
495 | xasprintf(&arg, "%ju", used); | |
496 | rc = scols_line_refer_data(ln, n, arg); | |
497 | } else | |
498 | rc = scols_line_set_data(ln, n, "-"); | |
499 | break; | |
500 | case COL_USEPERC: | |
501 | if (usage) { | |
502 | xasprintf(&arg, "%2.2f%%", (double) used / limit * 100); | |
503 | rc = scols_line_refer_data(ln, n, arg); | |
504 | } else | |
505 | rc = scols_line_set_data(ln, n, "-"); | |
506 | break; | |
507 | case COL_LIMIT: | |
508 | xasprintf(&arg, "%ju", limit); | |
509 | rc = scols_line_refer_data(ln, n, arg); | |
510 | break; | |
511 | } | |
512 | ||
513 | if (rc != 0) | |
514 | err(EXIT_FAILURE, _("failed to add output data")); | |
515 | } | |
516 | } | |
517 | ||
518 | static void setup_sem_elements_columns(struct libscols_table *tb) | |
519 | { | |
520 | scols_table_set_name(tb, "elements"); | |
521 | if (!scols_table_new_column(tb, "SEMNUM", 0, SCOLS_FL_RIGHT)) | |
522 | err_oom(); | |
523 | if (!scols_table_new_column(tb, "VALUE", 0, SCOLS_FL_RIGHT)) | |
524 | err_oom(); | |
525 | if (!scols_table_new_column(tb, "NCOUNT", 0, SCOLS_FL_RIGHT)) | |
526 | err_oom(); | |
527 | if (!scols_table_new_column(tb, "ZCOUNT", 0, SCOLS_FL_RIGHT)) | |
528 | err_oom(); | |
529 | if (!scols_table_new_column(tb, "PID", 0, SCOLS_FL_RIGHT)) | |
530 | err_oom(); | |
531 | if (!scols_table_new_column(tb, "COMMAND", 0, SCOLS_FL_RIGHT)) | |
532 | err_oom(); | |
533 | } | |
534 | ||
535 | static void do_sem(int id, struct lsipc_control *ctl, struct libscols_table *tb) | |
536 | { | |
537 | struct libscols_line *ln; | |
538 | struct passwd *pw = NULL, *cpw = NULL; | |
539 | struct group *gr = NULL, *cgr = NULL; | |
540 | struct sem_data *semds, *semdsp; | |
541 | char *arg = NULL; | |
542 | ||
543 | scols_table_set_name(tb, "semaphores"); | |
544 | ||
545 | if (ipc_sem_get_info(id, &semds) < 1) { | |
546 | if (id > -1) | |
547 | warnx(_("id %d not found"), id); | |
548 | return; | |
549 | } | |
550 | for (semdsp = semds; semdsp->next != NULL || id > -1; semdsp = semdsp->next) { | |
551 | size_t n; | |
552 | ||
553 | ln = scols_table_new_line(tb, NULL); | |
554 | if (!ln) | |
555 | err(EXIT_FAILURE, _("failed to allocate output line")); | |
556 | ||
557 | for (n = 0; n < ncolumns; n++) { | |
558 | int rc = 0; | |
559 | switch (get_column_id(n)) { | |
560 | case COL_KEY: | |
561 | xasprintf(&arg, "0x%08x",semdsp->sem_perm.key); | |
562 | rc = scols_line_refer_data(ln, n, arg); | |
563 | break; | |
564 | case COL_ID: | |
565 | xasprintf(&arg, "%d",semdsp->sem_perm.id); | |
566 | rc = scols_line_refer_data(ln, n, arg); | |
567 | break; | |
568 | case COL_OWNER: | |
569 | arg = get_username(&pw, semdsp->sem_perm.uid); | |
570 | if (!arg) | |
571 | xasprintf(&arg, "%u", semdsp->sem_perm.uid); | |
572 | rc = scols_line_refer_data(ln, n, arg); | |
573 | break; | |
574 | case COL_PERMS: | |
575 | if (ctl->numperms) | |
576 | xasprintf(&arg, "%#o", semdsp->sem_perm.mode & 0777); | |
577 | else { | |
578 | arg = xmalloc(11); | |
579 | xstrmode(semdsp->sem_perm.mode & 0777, arg); | |
580 | } | |
581 | rc = scols_line_refer_data(ln, n, arg); | |
582 | break; | |
583 | case COL_CUID: | |
584 | xasprintf(&arg, "%u", semdsp->sem_perm.cuid); | |
585 | rc = scols_line_refer_data(ln, n, arg); | |
586 | break; | |
587 | case COL_CUSER: | |
588 | arg = get_username(&cpw, semdsp->sem_perm.cuid); | |
589 | if (arg) | |
590 | rc = scols_line_refer_data(ln, n, arg); | |
591 | break; | |
592 | case COL_CGID: | |
593 | xasprintf(&arg, "%u", semdsp->sem_perm.cgid); | |
594 | rc = scols_line_refer_data(ln, n, arg); | |
595 | break; | |
596 | case COL_CGROUP: | |
597 | arg = get_groupname(&cgr, semdsp->sem_perm.cgid); | |
598 | if (arg) | |
599 | rc = scols_line_refer_data(ln, n, arg); | |
600 | break; | |
601 | case COL_UID: | |
602 | xasprintf(&arg, "%u", semdsp->sem_perm.uid); | |
603 | rc = scols_line_refer_data(ln, n, arg); | |
604 | break; | |
605 | case COL_USER: | |
606 | arg = get_username(&pw, semdsp->sem_perm.uid); | |
607 | if (arg) | |
608 | rc = scols_line_refer_data(ln, n, arg); | |
609 | break; | |
610 | case COL_GID: | |
611 | xasprintf(&arg, "%u", semdsp->sem_perm.gid); | |
612 | rc = scols_line_refer_data(ln, n, arg); | |
613 | break; | |
614 | case COL_GROUP: | |
615 | arg = get_groupname(&gr, semdsp->sem_perm.gid); | |
616 | if (arg) | |
617 | rc = scols_line_refer_data(ln, n, arg); | |
618 | break; | |
619 | case COL_CTIME: | |
620 | if (semdsp->sem_ctime != 0) { | |
621 | rc = scols_line_refer_data(ln, n, | |
622 | make_time(ctl->time_mode, | |
623 | (time_t)semdsp->sem_ctime)); | |
624 | } | |
625 | break; | |
626 | case COL_NSEMS: | |
627 | xasprintf(&arg, "%ju", semdsp->sem_nsems); | |
628 | rc = scols_line_refer_data(ln, n, arg); | |
629 | break; | |
630 | case COL_OTIME: | |
631 | if (semdsp->sem_otime != 0) { | |
632 | rc = scols_line_refer_data(ln, n, | |
633 | make_time(ctl->time_mode, | |
634 | (time_t)semdsp->sem_otime)); | |
635 | } | |
636 | break; | |
637 | } | |
638 | if (rc != 0) | |
639 | err(EXIT_FAILURE, _("failed to add output data")); | |
640 | arg = NULL; | |
641 | } | |
642 | ||
643 | if (id > -1 && semds->sem_nsems) { | |
644 | /* Create extra table with ID specific semaphore elements */ | |
645 | struct libscols_table *sub = new_table(ctl); | |
646 | size_t i; | |
647 | int rc = 0; | |
648 | ||
649 | scols_table_enable_noheadings(sub, 0); | |
650 | setup_sem_elements_columns(sub); | |
651 | ||
652 | for (i = 0; i < semds->sem_nsems; i++) { | |
653 | struct sem_elem *e = &semds->elements[i]; | |
654 | struct libscols_line *sln = scols_table_new_line(sub, NULL); | |
655 | ||
656 | if (!sln) | |
657 | err(EXIT_FAILURE, _("failed to allocate output line")); | |
658 | ||
659 | /* SEMNUM */ | |
660 | xasprintf(&arg, "%zu", i); | |
661 | rc = scols_line_refer_data(sln, 0, arg); | |
662 | if (rc) | |
663 | break; | |
664 | ||
665 | /* VALUE */ | |
666 | xasprintf(&arg, "%d", e->semval); | |
667 | rc = scols_line_refer_data(sln, 1, arg); | |
668 | if (rc) | |
669 | break; | |
670 | ||
671 | /* NCOUNT */ | |
672 | xasprintf(&arg, "%d", e->ncount); | |
673 | rc = scols_line_refer_data(sln, 2, arg); | |
674 | if (rc) | |
675 | break; | |
676 | ||
677 | /* ZCOUNT */ | |
678 | xasprintf(&arg, "%d", e->zcount); | |
679 | rc = scols_line_refer_data(sln, 3, arg); | |
680 | if (rc) | |
681 | break; | |
682 | ||
683 | /* PID */ | |
684 | xasprintf(&arg, "%d", e->pid); | |
685 | rc = scols_line_refer_data(sln, 4, arg); | |
686 | if (rc) | |
687 | break; | |
688 | ||
689 | /* COMMAND */ | |
690 | arg = proc_get_command(e->pid); | |
691 | rc = scols_line_refer_data(sln, 5, arg); | |
692 | if (rc) | |
693 | break; | |
694 | } | |
695 | ||
696 | if (rc != 0) | |
697 | err(EXIT_FAILURE, _("failed to set data")); | |
698 | ||
699 | scols_line_set_userdata(ln, (void *)sub); | |
700 | break; | |
701 | } | |
702 | } | |
703 | ipc_sem_free_info(semds); | |
704 | } | |
705 | ||
706 | static void do_sem_global(struct libscols_table *tb) | |
707 | { | |
708 | struct sem_data *semds, *semdsp; | |
709 | struct ipc_limits lim; | |
710 | int nsems = 0, nsets = 0; | |
711 | ||
712 | ipc_sem_get_limits(&lim); | |
713 | ||
714 | if (ipc_sem_get_info(-1, &semds) > 0) { | |
715 | for (semdsp = semds; semdsp->next != NULL; semdsp = semdsp->next) { | |
716 | ++nsets; | |
717 | nsems += semds->sem_nsems; | |
718 | } | |
719 | ipc_sem_free_info(semds); | |
720 | } | |
721 | ||
722 | global_set_data(tb, "SEMMNI", _("Number of semaphore identifiers"), nsets, lim.semmni, 1); | |
723 | global_set_data(tb, "SEMMNS", _("Total number of semaphores"), nsems, lim.semmns, 1); | |
724 | global_set_data(tb, "SEMMSL", _("Max semaphores per semaphore set."), 0, lim.semmsl, 0); | |
725 | global_set_data(tb, "SEMOPM", _("Max number of operations per semop(2)"), 0, lim.semopm, 0); | |
726 | global_set_data(tb, "SEMVMX", _("Semaphore max value"), 0, lim.semvmx, 0); | |
727 | } | |
728 | ||
729 | static void do_msg(int id, struct lsipc_control *ctl, struct libscols_table *tb) | |
730 | { | |
731 | struct libscols_line *ln; | |
732 | struct passwd *pw = NULL; | |
733 | struct group *gr = NULL; | |
734 | struct msg_data *msgds, *msgdsp; | |
735 | char *arg = NULL; | |
736 | ||
737 | if (ipc_msg_get_info(id, &msgds) < 1) { | |
738 | if (id > -1) | |
739 | warnx(_("id %d not found"), id); | |
740 | return; | |
741 | } | |
742 | scols_table_set_name(tb, "messages"); | |
743 | ||
744 | for (msgdsp = msgds; msgdsp->next != NULL || id > -1 ; msgdsp = msgdsp->next) { | |
745 | size_t n; | |
746 | ln = scols_table_new_line(tb, NULL); | |
747 | ||
748 | if (!ln) | |
749 | err(EXIT_FAILURE, _("failed to allocate output line")); | |
750 | ||
751 | /* no need to call getpwuid() for the same user */ | |
752 | if (!(pw && pw->pw_uid == msgdsp->msg_perm.uid)) | |
753 | pw = getpwuid(msgdsp->msg_perm.uid); | |
754 | ||
755 | /* no need to call getgrgid() for the same user */ | |
756 | if (!(gr && gr->gr_gid == msgdsp->msg_perm.gid)) | |
757 | gr = getgrgid(msgdsp->msg_perm.gid); | |
758 | ||
759 | for (n = 0; n < ncolumns; n++) { | |
760 | int rc = 0; | |
761 | ||
762 | switch (get_column_id(n)) { | |
763 | case COL_KEY: | |
764 | xasprintf(&arg, "0x%08x",msgdsp->msg_perm.key); | |
765 | rc = scols_line_refer_data(ln, n, arg); | |
766 | break; | |
767 | case COL_ID: | |
768 | xasprintf(&arg, "%d",msgdsp->msg_perm.id); | |
769 | rc = scols_line_refer_data(ln, n, arg); | |
770 | break; | |
771 | case COL_OWNER: | |
772 | arg = get_username(&pw, msgdsp->msg_perm.uid); | |
773 | if (!arg) | |
774 | xasprintf(&arg, "%u", msgdsp->msg_perm.uid); | |
775 | rc = scols_line_refer_data(ln, n, arg); | |
776 | break; | |
777 | case COL_PERMS: | |
778 | if (ctl->numperms) | |
779 | xasprintf(&arg, "%#o", msgdsp->msg_perm.mode & 0777); | |
780 | else { | |
781 | arg = xmalloc(11); | |
782 | xstrmode(msgdsp->msg_perm.mode & 0777, arg); | |
783 | rc = scols_line_refer_data(ln, n, arg); | |
784 | } | |
785 | break; | |
786 | case COL_CUID: | |
787 | xasprintf(&arg, "%u", msgdsp->msg_perm.cuid); | |
788 | rc = scols_line_refer_data(ln, n, arg); | |
789 | break; | |
790 | case COL_CUSER: | |
791 | arg = get_username(&pw, msgdsp->msg_perm.cuid); | |
792 | if (arg) | |
793 | rc = scols_line_refer_data(ln, n, arg); | |
794 | break; | |
795 | case COL_CGID: | |
796 | xasprintf(&arg, "%u", msgdsp->msg_perm.cuid); | |
797 | rc = scols_line_refer_data(ln, n, arg); | |
798 | break; | |
799 | case COL_CGROUP: | |
800 | arg = get_groupname(&gr, msgdsp->msg_perm.cgid); | |
801 | if (arg) | |
802 | rc = scols_line_refer_data(ln, n, arg); | |
803 | break; | |
804 | case COL_UID: | |
805 | xasprintf(&arg, "%u", msgdsp->msg_perm.uid); | |
806 | rc = scols_line_refer_data(ln, n, arg); | |
807 | break; | |
808 | case COL_USER: | |
809 | arg = get_username(&pw, msgdsp->msg_perm.uid); | |
810 | if (arg) | |
811 | rc = scols_line_refer_data(ln, n, arg); | |
812 | break; | |
813 | case COL_GID: | |
814 | xasprintf(&arg, "%u", msgdsp->msg_perm.gid); | |
815 | rc = scols_line_refer_data(ln, n, arg); | |
816 | break; | |
817 | case COL_GROUP: | |
818 | arg = get_groupname(&gr,msgdsp->msg_perm.gid); | |
819 | if (arg) | |
820 | rc = scols_line_refer_data(ln, n, arg); | |
821 | break; | |
822 | case COL_CTIME: | |
823 | if (msgdsp->q_ctime != 0) | |
824 | rc = scols_line_refer_data(ln, n, | |
825 | make_time(ctl->time_mode, | |
826 | (time_t)msgdsp->q_ctime)); | |
827 | break; | |
828 | case COL_USEDBYTES: | |
829 | xasprintf(&arg, "%ju", msgdsp->q_cbytes); | |
830 | rc = scols_line_refer_data(ln, n, arg); | |
831 | break; | |
832 | case COL_MSGS: | |
833 | xasprintf(&arg, "%ju", msgdsp->q_qnum); | |
834 | rc = scols_line_refer_data(ln, n, arg); | |
835 | break; | |
836 | case COL_SEND: | |
837 | if (msgdsp->q_stime != 0) | |
838 | rc = scols_line_refer_data(ln, n, | |
839 | make_time(ctl->time_mode, | |
840 | (time_t)msgdsp->q_stime)); | |
841 | break; | |
842 | case COL_RECV: | |
843 | if (msgdsp->q_rtime != 0) | |
844 | rc = scols_line_refer_data(ln, n, | |
845 | make_time(ctl->time_mode, | |
846 | (time_t)msgdsp->q_rtime)); | |
847 | break; | |
848 | case COL_LSPID: | |
849 | xasprintf(&arg, "%u", msgdsp->q_lspid); | |
850 | rc = scols_line_refer_data(ln, n, arg); | |
851 | break; | |
852 | case COL_LRPID: | |
853 | xasprintf(&arg, "%u", msgdsp->q_lrpid); | |
854 | rc = scols_line_refer_data(ln, n, arg); | |
855 | break; | |
856 | } | |
857 | if (rc != 0) | |
858 | err(EXIT_FAILURE, _("failed to set data")); | |
859 | arg = NULL; | |
860 | } | |
861 | if (id > -1) | |
862 | break; | |
863 | } | |
864 | ipc_msg_free_info(msgds); | |
865 | } | |
866 | ||
867 | ||
868 | static void do_msg_global(struct libscols_table *tb) | |
869 | { | |
870 | struct msg_data *msgds, *msgdsp; | |
871 | struct ipc_limits lim; | |
872 | int msgqs = 0; | |
873 | ||
874 | ipc_msg_get_limits(&lim); | |
875 | ||
876 | /* count number of used queues */ | |
877 | if (ipc_msg_get_info(-1, &msgds) > 0) { | |
878 | for (msgdsp = msgds; msgdsp->next != NULL; msgdsp = msgdsp->next) | |
879 | ++msgqs; | |
880 | ipc_msg_free_info(msgds); | |
881 | } | |
882 | ||
883 | global_set_data(tb, "MSGMNI", _("Number of message queues"), msgqs, lim.msgmni, 1); | |
884 | global_set_data(tb, "MSGMAX", _("Max size of message (bytes)"), 0, lim.msgmax, 0); | |
885 | global_set_data(tb, "MSGMNB", _("Default max size of queue (bytes)"), 0, lim.msgmnb, 0); | |
886 | } | |
887 | ||
888 | ||
889 | static void do_shm(int id, struct lsipc_control *ctl, struct libscols_table *tb) | |
890 | { | |
891 | struct libscols_line *ln; | |
892 | struct passwd *pw = NULL; | |
893 | struct group *gr = NULL; | |
894 | struct shm_data *shmds, *shmdsp; | |
895 | char *arg = NULL; | |
896 | ||
897 | if (ipc_shm_get_info(id, &shmds) < 1) { | |
898 | if (id > -1) | |
899 | warnx(_("id %d not found"), id); | |
900 | return; | |
901 | } | |
902 | ||
903 | scols_table_set_name(tb, "sharedmemory"); | |
904 | ||
905 | for (shmdsp = shmds; shmdsp->next != NULL || id > -1 ; shmdsp = shmdsp->next) { | |
906 | size_t n; | |
907 | ln = scols_table_new_line(tb, NULL); | |
908 | ||
909 | if (!ln) | |
910 | err(EXIT_FAILURE, _("failed to allocate output line")); | |
911 | ||
912 | for (n = 0; n < ncolumns; n++) { | |
913 | int rc = 0; | |
914 | ||
915 | switch (get_column_id(n)) { | |
916 | case COL_KEY: | |
917 | xasprintf(&arg, "0x%08x",shmdsp->shm_perm.key); | |
918 | rc = scols_line_refer_data(ln, n, arg); | |
919 | break; | |
920 | case COL_ID: | |
921 | xasprintf(&arg, "%d",shmdsp->shm_perm.id); | |
922 | rc = scols_line_refer_data(ln, n, arg); | |
923 | break; | |
924 | case COL_OWNER: | |
925 | arg = get_username(&pw, shmdsp->shm_perm.uid); | |
926 | if (!arg) | |
927 | xasprintf(&arg, "%u", shmdsp->shm_perm.uid); | |
928 | rc = scols_line_refer_data(ln, n, arg); | |
929 | break; | |
930 | case COL_PERMS: | |
931 | if (ctl->numperms) | |
932 | xasprintf(&arg, "%#o", shmdsp->shm_perm.mode & 0777); | |
933 | else { | |
934 | arg = xmalloc(11); | |
935 | xstrmode(shmdsp->shm_perm.mode & 0777, arg); | |
936 | } | |
937 | rc = scols_line_refer_data(ln, n, arg); | |
938 | break; | |
939 | case COL_CUID: | |
940 | xasprintf(&arg, "%u", shmdsp->shm_perm.cuid); | |
941 | rc = scols_line_refer_data(ln, n, arg); | |
942 | break; | |
943 | case COL_CUSER: | |
944 | arg = get_username(&pw, shmdsp->shm_perm.cuid); | |
945 | if (arg) | |
946 | rc = scols_line_refer_data(ln, n, arg); | |
947 | break; | |
948 | case COL_CGID: | |
949 | xasprintf(&arg, "%u", shmdsp->shm_perm.cuid); | |
950 | rc = scols_line_refer_data(ln, n, arg); | |
951 | break; | |
952 | case COL_CGROUP: | |
953 | arg = get_groupname(&gr, shmdsp->shm_perm.cgid); | |
954 | if (arg) | |
955 | rc = scols_line_refer_data(ln, n, arg); | |
956 | break; | |
957 | case COL_UID: | |
958 | xasprintf(&arg, "%u", shmdsp->shm_perm.uid); | |
959 | rc = scols_line_refer_data(ln, n, arg); | |
960 | break; | |
961 | case COL_USER: | |
962 | arg = get_username(&pw, shmdsp->shm_perm.uid); | |
963 | if (arg) | |
964 | rc = scols_line_refer_data(ln, n, arg); | |
965 | break; | |
966 | case COL_GID: | |
967 | xasprintf(&arg, "%u", shmdsp->shm_perm.gid); | |
968 | rc = scols_line_refer_data(ln, n, arg); | |
969 | break; | |
970 | case COL_GROUP: | |
971 | arg = get_groupname(&gr, shmdsp->shm_perm.gid); | |
972 | if (arg) | |
973 | rc = scols_line_refer_data(ln, n, arg); | |
974 | break; | |
975 | case COL_CTIME: | |
976 | if (shmdsp->shm_ctim != 0) | |
977 | rc = scols_line_refer_data(ln, n, | |
978 | make_time(ctl->time_mode, | |
979 | (time_t)shmdsp->shm_ctim)); | |
980 | break; | |
981 | case COL_SIZE: | |
982 | if (ctl->bytes) | |
983 | xasprintf(&arg, "%ju", shmdsp->shm_segsz); | |
984 | else | |
985 | arg = size_to_human_string(SIZE_SUFFIX_1LETTER, shmdsp->shm_segsz); | |
986 | rc = scols_line_refer_data(ln, n, arg); | |
987 | break; | |
988 | case COL_NATTCH: | |
989 | xasprintf(&arg, "%ju", shmdsp->shm_nattch); | |
990 | rc = scols_line_refer_data(ln, n, arg); | |
991 | break; | |
992 | case COL_STATUS: { | |
993 | int comma = 0; | |
994 | size_t offt = 0; | |
995 | ||
996 | free(arg); | |
997 | arg = xcalloc(1, sizeof(char) * strlen(_("dest")) | |
998 | + strlen(_("locked")) | |
999 | + strlen(_("hugetlb")) | |
1000 | + strlen(_("noreserve")) + 4); | |
1001 | #ifdef SHM_DEST | |
1002 | if (shmdsp->shm_perm.mode & SHM_DEST) { | |
1003 | offt += sprintf(arg, "%s", _("dest")); | |
1004 | comma++; | |
1005 | } | |
1006 | #endif | |
1007 | #ifdef SHM_LOCKED | |
1008 | if (shmdsp->shm_perm.mode & SHM_LOCKED) { | |
1009 | if (comma) | |
1010 | arg[offt++] = ','; | |
1011 | offt += sprintf(arg + offt, "%s", _("locked")); | |
1012 | } | |
1013 | #endif | |
1014 | #ifdef SHM_HUGETLB | |
1015 | if (shmdsp->shm_perm.mode & SHM_HUGETLB) { | |
1016 | if (comma) | |
1017 | arg[offt++] = ','; | |
1018 | offt += sprintf(arg + offt, "%s", _("hugetlb")); | |
1019 | } | |
1020 | #endif | |
1021 | #ifdef SHM_NORESERVE | |
1022 | if (shmdsp->shm_perm.mode & SHM_NORESERVE) { | |
1023 | if (comma) | |
1024 | arg[offt++] = ','; | |
1025 | sprintf(arg + offt, "%s", _("noreserve")); | |
1026 | } | |
1027 | #endif | |
1028 | rc = scols_line_refer_data(ln, n, arg); | |
1029 | } | |
1030 | break; | |
1031 | case COL_ATTACH: | |
1032 | if (shmdsp->shm_atim != 0) | |
1033 | rc = scols_line_refer_data(ln, n, | |
1034 | make_time(ctl->time_mode, | |
1035 | (time_t)shmdsp->shm_atim)); | |
1036 | break; | |
1037 | case COL_DETACH: | |
1038 | if (shmdsp->shm_dtim != 0) | |
1039 | rc = scols_line_refer_data(ln, n, | |
1040 | make_time(ctl->time_mode, | |
1041 | (time_t)shmdsp->shm_dtim)); | |
1042 | break; | |
1043 | case COL_CPID: | |
1044 | xasprintf(&arg, "%u", shmdsp->shm_cprid); | |
1045 | rc = scols_line_refer_data(ln, n, arg); | |
1046 | break; | |
1047 | case COL_LPID: | |
1048 | xasprintf(&arg, "%u", shmdsp->shm_lprid); | |
1049 | rc = scols_line_refer_data(ln, n, arg); | |
1050 | break; | |
1051 | case COL_COMMAND: | |
1052 | arg = proc_get_command(shmdsp->shm_cprid); | |
1053 | rc = scols_line_refer_data(ln, n, arg); | |
1054 | break; | |
1055 | } | |
1056 | if (rc != 0) | |
1057 | err(EXIT_FAILURE, _("failed to set data")); | |
1058 | arg = NULL; | |
1059 | } | |
1060 | if (id > -1) | |
1061 | break; | |
1062 | } | |
1063 | ipc_shm_free_info(shmds); | |
1064 | } | |
1065 | ||
1066 | static void do_shm_global(struct libscols_table *tb) | |
1067 | { | |
1068 | struct shm_data *shmds, *shmdsp; | |
1069 | uint64_t nsegs = 0, sum_segsz = 0; | |
1070 | struct ipc_limits lim; | |
1071 | ||
1072 | ipc_shm_get_limits(&lim); | |
1073 | ||
1074 | if (ipc_shm_get_info(-1, &shmds) > 0) { | |
1075 | for (shmdsp = shmds; shmdsp->next != NULL; shmdsp = shmdsp->next) { | |
1076 | ++nsegs; | |
1077 | sum_segsz += shmdsp->shm_segsz; | |
1078 | } | |
1079 | ipc_shm_free_info(shmds); | |
1080 | } | |
1081 | ||
1082 | global_set_data(tb, "SHMMNI", _("Shared memory segments"), nsegs, lim.shmmni, 1); | |
1083 | global_set_data(tb, "SHMALL", _("Shared memory pages"), sum_segsz / getpagesize(), lim.shmall, 1); | |
1084 | global_set_data(tb, "SHMMAX", _("Max size of shared memory segment (bytes)"), 0, lim.shmmax, 0); | |
1085 | global_set_data(tb, "SHMMIN", _("Min size of shared memory segment (bytes)"), 0, lim.shmmin, 0); | |
1086 | } | |
1087 | ||
1088 | int main(int argc, char *argv[]) | |
1089 | { | |
1090 | int opt, msg = 0, sem = 0, shm = 0, id = -1; | |
1091 | int show_time = 0, show_creat = 0, global = 0; | |
1092 | size_t i; | |
1093 | struct lsipc_control *ctl = xcalloc(1, sizeof(struct lsipc_control)); | |
1094 | static struct libscols_table *tb; | |
1095 | char *outarg = NULL; | |
1096 | ||
1097 | /* long only options. */ | |
1098 | enum { | |
1099 | OPT_NOTRUNC = CHAR_MAX + 1, | |
1100 | OPT_NOHEAD, | |
1101 | OPT_TIME_FMT | |
1102 | }; | |
1103 | ||
1104 | static const struct option longopts[] = { | |
1105 | { "bytes", no_argument, NULL, 'b' }, | |
1106 | { "creator", no_argument, NULL, 'c' }, | |
1107 | { "export", no_argument, NULL, 'e' }, | |
1108 | { "global", no_argument, NULL, 'g' }, | |
1109 | { "help", no_argument, NULL, 'h' }, | |
1110 | { "id", required_argument, NULL, 'i' }, | |
1111 | { "json", no_argument, NULL, 'J' }, | |
1112 | { "list", no_argument, NULL, 'l' }, | |
1113 | { "newline", no_argument, NULL, 'n' }, | |
1114 | { "noheadings", no_argument, NULL, OPT_NOHEAD }, | |
1115 | { "notruncate", no_argument, NULL, OPT_NOTRUNC }, | |
1116 | { "numeric-perms", no_argument, NULL, 'P' }, | |
1117 | { "output", required_argument, NULL, 'o' }, | |
1118 | { "queues", no_argument, NULL, 'q' }, | |
1119 | { "raw", no_argument, NULL, 'r' }, | |
1120 | { "semaphores", no_argument, NULL, 's' }, | |
1121 | { "shmems", no_argument, NULL, 'm' }, | |
1122 | { "time", no_argument, NULL, 't' }, | |
1123 | { "time-format", required_argument, NULL, OPT_TIME_FMT }, | |
1124 | { "version", no_argument, NULL, 'V' }, | |
1125 | {NULL, 0, NULL, 0} | |
1126 | }; | |
1127 | ||
1128 | static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ | |
1129 | { 'J', 'e', 'l', 'n', 'r' }, | |
1130 | { 'g', 'i' }, | |
1131 | { 'c', 'o', 't' }, | |
1132 | { 'm', 'q', 's' }, | |
1133 | { 0 } | |
1134 | }; | |
1135 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
1136 | ||
1137 | setlocale(LC_ALL, ""); | |
1138 | bindtextdomain(PACKAGE, LOCALEDIR); | |
1139 | textdomain(PACKAGE); | |
1140 | close_stdout_atexit(); | |
1141 | ||
1142 | ctl->time_mode = 0; | |
1143 | ||
1144 | scols_init_debug(0); | |
1145 | ||
1146 | while ((opt = getopt_long(argc, argv, "bceghi:Jlmno:PqrstV", longopts, NULL)) != -1) { | |
1147 | ||
1148 | err_exclusive_options(opt, longopts, excl, excl_st); | |
1149 | ||
1150 | switch (opt) { | |
1151 | case 'b': | |
1152 | ctl->bytes = 1; | |
1153 | break; | |
1154 | case 'i': | |
1155 | id = strtos32_or_err(optarg, _("failed to parse IPC identifier")); | |
1156 | break; | |
1157 | case 'e': | |
1158 | ctl->outmode = OUT_EXPORT; | |
1159 | break; | |
1160 | case 'r': | |
1161 | ctl->outmode = OUT_RAW; | |
1162 | break; | |
1163 | case 'o': | |
1164 | outarg = optarg; | |
1165 | break; | |
1166 | case 'g': | |
1167 | global = 1; | |
1168 | break; | |
1169 | case 'q': | |
1170 | msg = 1; | |
1171 | add_column(columns, ncolumns++, COL_KEY); | |
1172 | add_column(columns, ncolumns++, COL_ID); | |
1173 | add_column(columns, ncolumns++, COL_PERMS); | |
1174 | add_column(columns, ncolumns++, COL_OWNER); | |
1175 | add_column(columns, ncolumns++, COL_USEDBYTES); | |
1176 | add_column(columns, ncolumns++, COL_MSGS); | |
1177 | add_column(columns, ncolumns++, COL_LSPID); | |
1178 | add_column(columns, ncolumns++, COL_LRPID); | |
1179 | LOWER = COLDESC_IDX_MSG_FIRST; | |
1180 | UPPER = COLDESC_IDX_MSG_LAST; | |
1181 | break; | |
1182 | case 'l': | |
1183 | ctl->outmode = OUT_LIST; | |
1184 | break; | |
1185 | case 'm': | |
1186 | shm = 1; | |
1187 | add_column(columns, ncolumns++, COL_KEY); | |
1188 | add_column(columns, ncolumns++, COL_ID); | |
1189 | add_column(columns, ncolumns++, COL_PERMS); | |
1190 | add_column(columns, ncolumns++, COL_OWNER); | |
1191 | add_column(columns, ncolumns++, COL_SIZE); | |
1192 | add_column(columns, ncolumns++, COL_NATTCH); | |
1193 | add_column(columns, ncolumns++, COL_STATUS); | |
1194 | add_column(columns, ncolumns++, COL_CTIME); | |
1195 | add_column(columns, ncolumns++, COL_CPID); | |
1196 | add_column(columns, ncolumns++, COL_LPID); | |
1197 | add_column(columns, ncolumns++, COL_COMMAND); | |
1198 | LOWER = COLDESC_IDX_SHM_FIRST; | |
1199 | UPPER = COLDESC_IDX_SHM_LAST; | |
1200 | break; | |
1201 | case 'n': | |
1202 | ctl->outmode = OUT_NEWLINE; | |
1203 | break; | |
1204 | case 'P': | |
1205 | ctl->numperms = 1; | |
1206 | break; | |
1207 | case 's': | |
1208 | sem = 1; | |
1209 | add_column(columns, ncolumns++, COL_KEY); | |
1210 | add_column(columns, ncolumns++, COL_ID); | |
1211 | add_column(columns, ncolumns++, COL_PERMS); | |
1212 | add_column(columns, ncolumns++, COL_OWNER); | |
1213 | add_column(columns, ncolumns++, COL_NSEMS); | |
1214 | LOWER = COLDESC_IDX_SEM_FIRST; | |
1215 | UPPER = COLDESC_IDX_SEM_LAST; | |
1216 | break; | |
1217 | case OPT_NOTRUNC: | |
1218 | ctl->notrunc = 1; | |
1219 | break; | |
1220 | case OPT_NOHEAD: | |
1221 | ctl->noheadings = 1; | |
1222 | break; | |
1223 | case OPT_TIME_FMT: | |
1224 | ctl->time_mode = parse_time_mode(optarg); | |
1225 | break; | |
1226 | case 'J': | |
1227 | ctl->outmode = OUT_JSON; | |
1228 | break; | |
1229 | case 't': | |
1230 | show_time = 1; | |
1231 | break; | |
1232 | case 'c': | |
1233 | show_creat = 1; | |
1234 | break; | |
1235 | ||
1236 | case 'h': | |
1237 | usage(); | |
1238 | case 'V': | |
1239 | print_version(EXIT_SUCCESS); | |
1240 | default: | |
1241 | errtryhelp(EXIT_FAILURE); | |
1242 | } | |
1243 | } | |
1244 | ||
1245 | /* default is global */ | |
1246 | if (msg + shm + sem == 0) { | |
1247 | msg = shm = sem = global = 1; | |
1248 | if (show_time || show_creat || id != -1) | |
1249 | errx(EXIT_FAILURE, _("--global is mutually exclusive with --creator, --id and --time")); | |
1250 | } | |
1251 | if (global) { | |
1252 | add_column(columns, ncolumns++, COL_RESOURCE); | |
1253 | add_column(columns, ncolumns++, COL_DESC); | |
1254 | add_column(columns, ncolumns++, COL_LIMIT); | |
1255 | add_column(columns, ncolumns++, COL_USED); | |
1256 | add_column(columns, ncolumns++, COL_USEPERC); | |
1257 | LOWER = COLDESC_IDX_SUM_FIRST; | |
1258 | UPPER = COLDESC_IDX_SUM_LAST; | |
1259 | } | |
1260 | ||
1261 | /* default to pretty-print if --id specified */ | |
1262 | if (id != -1 && !ctl->outmode) | |
1263 | ctl->outmode = OUT_PRETTY; | |
1264 | ||
1265 | if (!ctl->time_mode) | |
1266 | ctl->time_mode = ctl->outmode == OUT_PRETTY ? TIME_FULL : TIME_SHORT; | |
1267 | ||
1268 | if (ctl->outmode == OUT_PRETTY && !(optarg || show_creat || show_time)) { | |
1269 | /* all columns for lsipc --<RESOURCE> --id <ID> */ | |
1270 | for (ncolumns = 0, i = 0; i < ARRAY_SIZE(coldescs); i++) | |
1271 | columns[ncolumns++] = i; | |
1272 | } else { | |
1273 | if (show_creat) { | |
1274 | add_column(columns, ncolumns++, COL_CUID); | |
1275 | add_column(columns, ncolumns++, COL_CGID); | |
1276 | add_column(columns, ncolumns++, COL_UID); | |
1277 | add_column(columns, ncolumns++, COL_GID); | |
1278 | } | |
1279 | if (msg && show_time) { | |
1280 | add_column(columns, ncolumns++, COL_SEND); | |
1281 | add_column(columns, ncolumns++, COL_RECV); | |
1282 | add_column(columns, ncolumns++, COL_CTIME); | |
1283 | } | |
1284 | if (shm && show_time) { | |
1285 | /* keep "COMMAND" as last column */ | |
1286 | size_t cmd = columns[ncolumns - 1] == COL_COMMAND; | |
1287 | ||
1288 | if (cmd) | |
1289 | ncolumns--; | |
1290 | add_column(columns, ncolumns++, COL_ATTACH); | |
1291 | add_column(columns, ncolumns++, COL_DETACH); | |
1292 | if (cmd) | |
1293 | add_column(columns, ncolumns++, COL_COMMAND); | |
1294 | } | |
1295 | if (sem && show_time) { | |
1296 | add_column(columns, ncolumns++, COL_OTIME); | |
1297 | add_column(columns, ncolumns++, COL_CTIME); | |
1298 | } | |
1299 | } | |
1300 | ||
1301 | if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), | |
1302 | &ncolumns, column_name_to_id) < 0) | |
1303 | return EXIT_FAILURE; | |
1304 | ||
1305 | tb = setup_table(ctl); | |
1306 | if (!tb) | |
1307 | return EXIT_FAILURE; | |
1308 | ||
1309 | if (global) | |
1310 | scols_table_set_name(tb, "ipclimits"); | |
1311 | ||
1312 | if (msg) { | |
1313 | if (global) | |
1314 | do_msg_global(tb); | |
1315 | else | |
1316 | do_msg(id, ctl, tb); | |
1317 | } | |
1318 | if (shm) { | |
1319 | if (global) | |
1320 | do_shm_global(tb); | |
1321 | else | |
1322 | do_shm(id, ctl, tb); | |
1323 | } | |
1324 | if (sem) { | |
1325 | if (global) | |
1326 | do_sem_global(tb); | |
1327 | else | |
1328 | do_sem(id, ctl, tb); | |
1329 | } | |
1330 | ||
1331 | print_table(ctl, tb); | |
1332 | ||
1333 | scols_unref_table(tb); | |
1334 | free(ctl); | |
1335 | ||
1336 | return EXIT_SUCCESS; | |
1337 | } | |
1338 |