2 * lsipc - List information about IPC instances employed in the system
4 * Copyright (C) 2015 Ondrej Oprala <ooprala@redhat.com>
5 * Copyright (C) 2015 Karel Zak <ooprala@redhat.com>
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.
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.
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.
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.
32 #include <libsmartcols.h>
36 #include "closestream.h"
40 #include "procutils.h"
42 #include "timeutils.h"
59 COLDESC_IDX_GEN_FIRST
= 0,
60 COL_KEY
= COLDESC_IDX_GEN_FIRST
,
73 COLDESC_IDX_GEN_LAST
= COL_CTIME
,
76 COLDESC_IDX_MSG_FIRST
,
77 COL_USEDBYTES
= COLDESC_IDX_MSG_FIRST
,
83 COLDESC_IDX_MSG_LAST
= COL_LRPID
,
86 COLDESC_IDX_SHM_FIRST
,
87 COL_SIZE
= COLDESC_IDX_SHM_FIRST
,
95 COLDESC_IDX_SHM_LAST
= COL_LPID
,
98 COLDESC_IDX_SEM_FIRST
,
99 COL_NSEMS
= COLDESC_IDX_SEM_FIRST
,
101 COLDESC_IDX_SEM_LAST
= COL_OTIME
,
103 /* summary (--global) */
104 COLDESC_IDX_SUM_FIRST
,
105 COL_RESOURCE
= COLDESC_IDX_SUM_FIRST
,
110 COLDESC_IDX_SUM_LAST
= COL_USEPERC
113 /* not all columns apply to all options, so we specify a legal range for each */
114 static size_t LOWER
, UPPER
;
128 struct lsipc_control
{
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 */
137 struct lsipc_coldesc
{
140 const char *pretty_name
;
142 double whint
; /* width hint */
146 static const struct lsipc_coldesc coldescs
[] =
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
},
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
},
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
},
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
},
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
},
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
;
201 static inline size_t err_columns_index(size_t arysz
, size_t idx
)
204 errx(EXIT_FAILURE
, _("too many columns specified, "
205 "the limit is %zu columns"),
210 #define add_column(ary, n, id) \
211 ((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id))
213 static int column_name_to_id(const char *name
, size_t namesz
)
217 for (i
= 0; i
< ARRAY_SIZE(coldescs
); i
++) {
218 const char *cn
= coldescs
[i
].name
;
220 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
)) {
222 if (i
>= LOWER
&& i
<= UPPER
)
225 warnx(_("column %s does not apply to the specified IPC"), name
);
232 warnx(_("unknown column: %s"), name
);
236 static int get_column_id(int num
)
239 assert((size_t) num
< ncolumns
);
240 assert((size_t) columns
[num
] < ARRAY_SIZE(coldescs
));
244 static const struct lsipc_coldesc
*get_column_desc(int num
)
246 return &coldescs
[ get_column_id(num
) ];
249 static char *get_username(struct passwd
**pw
, uid_t id
)
251 if (!*pw
|| (*pw
)->pw_uid
!= id
)
254 return *pw
? xstrdup((*pw
)->pw_name
) : NULL
;
257 static char *get_groupname(struct group
**gr
, gid_t id
)
259 if (!*gr
|| (*gr
)->gr_gid
!= id
)
262 return *gr
? xstrdup((*gr
)->gr_name
) : NULL
;
265 static int parse_time_mode(const char *s
)
267 struct lsipc_timefmt
{
271 static const struct lsipc_timefmt timefmts
[] = {
274 {"short", TIME_SHORT
},
278 for (i
= 0; i
< ARRAY_SIZE(timefmts
); i
++) {
279 if (strcmp(timefmts
[i
].name
, s
) == 0)
280 return timefmts
[i
].val
;
282 errx(EXIT_FAILURE
, _("unknown time format: %s"), s
);
285 static void __attribute__((__noreturn__
)) usage(void)
290 fputs(USAGE_HEADER
, out
);
291 fprintf(out
, _(" %s [options]\n"), program_invocation_short_name
);
293 fputs(USAGE_SEPARATOR
, out
);
294 fputs(_("Show information on IPC facilities.\n"), out
);
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
);
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
);
319 fputs(USAGE_SEPARATOR
, out
);
320 printf(USAGE_HELP_OPTIONS(26));
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
));
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
));
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
));
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
));
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
));
342 printf(USAGE_MAN_TAIL("lsipc(1)"));
346 static struct libscols_table
*new_table(struct lsipc_control
*ctl
)
348 struct libscols_table
*table
= scols_new_table();
351 err(EXIT_FAILURE
, _("failed to allocate output table"));
354 scols_table_enable_noheadings(table
, 1);
356 switch(ctl
->outmode
) {
358 scols_table_set_column_separator(table
, "\n");
361 scols_table_enable_export(table
, 1);
364 scols_table_enable_raw(table
, 1);
367 scols_table_enable_noheadings(table
, 1);
370 scols_table_enable_json(table
, 1);
378 static struct libscols_table
*setup_table(struct lsipc_control
*ctl
)
380 struct libscols_table
*table
= new_table(ctl
);
383 for (n
= 0; n
< ncolumns
; n
++) {
384 const struct lsipc_coldesc
*desc
= get_column_desc(n
);
385 int flags
= desc
->flag
;
388 flags
&= ~SCOLS_FL_TRUNC
;
389 if (!scols_table_new_column(table
, desc
->name
, desc
->whint
, flags
))
394 scols_unref_table(table
);
398 static int print_pretty(struct libscols_table
*table
)
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
;
407 ln
= scols_table_get_line(table
, 0);
408 while (!scols_table_next_column(table
, itr
, &col
)) {
410 data
= scols_line_get_cell(ln
, n
);
412 hstr
= N_(get_column_desc(n
)->pretty_name
);
413 dstr
= scols_cell_get_data(data
);
416 printf("%s:%*c%-36s\n", hstr
, 35 - (int)strlen(hstr
), ' ', dstr
);
420 /* this is used to pretty-print detailed info about a semaphore array */
422 struct libscols_table
*subtab
= scols_line_get_userdata(ln
);
424 printf(_("Elements:\n\n"));
425 scols_print_table(subtab
);
429 scols_free_iter(itr
);
434 static int print_table(struct lsipc_control
*ctl
, struct libscols_table
*tb
)
436 if (ctl
->outmode
== OUT_PRETTY
)
439 scols_print_table(tb
);
442 static struct timeval now
;
444 static char *make_time(int mode
, time_t time
)
454 localtime_r(&time
, &tm
);
456 if (*(s
= buf
+ strlen(buf
) - 1) == '\n')
461 strtime_short(&time
, &now
, 0, buf
, sizeof(buf
));
464 strtime_iso(&time
, ISO_TIMESTAMP_T
, buf
, sizeof(buf
));
467 errx(EXIT_FAILURE
, _("unsupported time type"));
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
)
475 struct libscols_line
*ln
;
478 ln
= scols_table_new_line(tb
, NULL
);
480 err(EXIT_FAILURE
, _("failed to allocate output line"));
482 for (n
= 0; n
< ncolumns
; n
++) {
486 switch (get_column_id(n
)) {
488 rc
= scols_line_set_data(ln
, n
, resource
);
491 rc
= scols_line_set_data(ln
, n
, desc
);
495 xasprintf(&arg
, "%ju", used
);
496 rc
= scols_line_refer_data(ln
, n
, arg
);
498 rc
= scols_line_set_data(ln
, n
, "-");
502 xasprintf(&arg
, "%2.2f%%", (double) used
/ limit
* 100);
503 rc
= scols_line_refer_data(ln
, n
, arg
);
505 rc
= scols_line_set_data(ln
, n
, "-");
508 xasprintf(&arg
, "%ju", limit
);
509 rc
= scols_line_refer_data(ln
, n
, arg
);
514 err(EXIT_FAILURE
, _("failed to add output data"));
518 static void setup_sem_elements_columns(struct libscols_table
*tb
)
520 scols_table_set_name(tb
, "elements");
521 if (!scols_table_new_column(tb
, "SEMNUM", 0, SCOLS_FL_RIGHT
))
523 if (!scols_table_new_column(tb
, "VALUE", 0, SCOLS_FL_RIGHT
))
525 if (!scols_table_new_column(tb
, "NCOUNT", 0, SCOLS_FL_RIGHT
))
527 if (!scols_table_new_column(tb
, "ZCOUNT", 0, SCOLS_FL_RIGHT
))
529 if (!scols_table_new_column(tb
, "PID", 0, SCOLS_FL_RIGHT
))
531 if (!scols_table_new_column(tb
, "COMMAND", 0, SCOLS_FL_RIGHT
))
535 static void do_sem(int id
, struct lsipc_control
*ctl
, struct libscols_table
*tb
)
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
;
543 scols_table_set_name(tb
, "semaphores");
545 if (ipc_sem_get_info(id
, &semds
) < 1) {
547 warnx(_("id %d not found"), id
);
550 for (semdsp
= semds
; semdsp
->next
!= NULL
|| id
> -1; semdsp
= semdsp
->next
) {
553 ln
= scols_table_new_line(tb
, NULL
);
555 err(EXIT_FAILURE
, _("failed to allocate output line"));
557 for (n
= 0; n
< ncolumns
; n
++) {
559 switch (get_column_id(n
)) {
561 xasprintf(&arg
, "0x%08x",semdsp
->sem_perm
.key
);
562 rc
= scols_line_refer_data(ln
, n
, arg
);
565 xasprintf(&arg
, "%d",semdsp
->sem_perm
.id
);
566 rc
= scols_line_refer_data(ln
, n
, arg
);
569 arg
= get_username(&pw
, semdsp
->sem_perm
.uid
);
571 xasprintf(&arg
, "%u", semdsp
->sem_perm
.uid
);
572 rc
= scols_line_refer_data(ln
, n
, arg
);
576 xasprintf(&arg
, "%#o", semdsp
->sem_perm
.mode
& 0777);
579 xstrmode(semdsp
->sem_perm
.mode
& 0777, arg
);
581 rc
= scols_line_refer_data(ln
, n
, arg
);
584 xasprintf(&arg
, "%u", semdsp
->sem_perm
.cuid
);
585 rc
= scols_line_refer_data(ln
, n
, arg
);
588 arg
= get_username(&cpw
, semdsp
->sem_perm
.cuid
);
590 rc
= scols_line_refer_data(ln
, n
, arg
);
593 xasprintf(&arg
, "%u", semdsp
->sem_perm
.cgid
);
594 rc
= scols_line_refer_data(ln
, n
, arg
);
597 arg
= get_groupname(&cgr
, semdsp
->sem_perm
.cgid
);
599 rc
= scols_line_refer_data(ln
, n
, arg
);
602 xasprintf(&arg
, "%u", semdsp
->sem_perm
.uid
);
603 rc
= scols_line_refer_data(ln
, n
, arg
);
606 arg
= get_username(&pw
, semdsp
->sem_perm
.uid
);
608 rc
= scols_line_refer_data(ln
, n
, arg
);
611 xasprintf(&arg
, "%u", semdsp
->sem_perm
.gid
);
612 rc
= scols_line_refer_data(ln
, n
, arg
);
615 arg
= get_groupname(&gr
, semdsp
->sem_perm
.gid
);
617 rc
= scols_line_refer_data(ln
, n
, arg
);
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
));
627 xasprintf(&arg
, "%ju", semdsp
->sem_nsems
);
628 rc
= scols_line_refer_data(ln
, n
, arg
);
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
));
639 err(EXIT_FAILURE
, _("failed to add output data"));
643 if (id
> -1 && semds
->sem_nsems
) {
644 /* Create extra table with ID specific semaphore elements */
645 struct libscols_table
*sub
= new_table(ctl
);
649 scols_table_enable_noheadings(sub
, 0);
650 setup_sem_elements_columns(sub
);
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
);
657 err(EXIT_FAILURE
, _("failed to allocate output line"));
660 xasprintf(&arg
, "%zu", i
);
661 rc
= scols_line_refer_data(sln
, 0, arg
);
666 xasprintf(&arg
, "%d", e
->semval
);
667 rc
= scols_line_refer_data(sln
, 1, arg
);
672 xasprintf(&arg
, "%d", e
->ncount
);
673 rc
= scols_line_refer_data(sln
, 2, arg
);
678 xasprintf(&arg
, "%d", e
->zcount
);
679 rc
= scols_line_refer_data(sln
, 3, arg
);
684 xasprintf(&arg
, "%d", e
->pid
);
685 rc
= scols_line_refer_data(sln
, 4, arg
);
690 arg
= proc_get_command(e
->pid
);
691 rc
= scols_line_refer_data(sln
, 5, arg
);
697 err(EXIT_FAILURE
, _("failed to set data"));
699 scols_line_set_userdata(ln
, (void *)sub
);
703 ipc_sem_free_info(semds
);
706 static void do_sem_global(struct libscols_table
*tb
)
708 struct sem_data
*semds
, *semdsp
;
709 struct ipc_limits lim
;
710 int nsems
= 0, nsets
= 0;
712 ipc_sem_get_limits(&lim
);
714 if (ipc_sem_get_info(-1, &semds
) > 0) {
715 for (semdsp
= semds
; semdsp
->next
!= NULL
; semdsp
= semdsp
->next
) {
717 nsems
+= semds
->sem_nsems
;
719 ipc_sem_free_info(semds
);
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);
729 static void do_msg(int id
, struct lsipc_control
*ctl
, struct libscols_table
*tb
)
731 struct libscols_line
*ln
;
732 struct passwd
*pw
= NULL
;
733 struct group
*gr
= NULL
;
734 struct msg_data
*msgds
, *msgdsp
;
737 if (ipc_msg_get_info(id
, &msgds
) < 1) {
739 warnx(_("id %d not found"), id
);
742 scols_table_set_name(tb
, "messages");
744 for (msgdsp
= msgds
; msgdsp
->next
!= NULL
|| id
> -1 ; msgdsp
= msgdsp
->next
) {
746 ln
= scols_table_new_line(tb
, NULL
);
749 err(EXIT_FAILURE
, _("failed to allocate output line"));
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
);
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
);
759 for (n
= 0; n
< ncolumns
; n
++) {
762 switch (get_column_id(n
)) {
764 xasprintf(&arg
, "0x%08x",msgdsp
->msg_perm
.key
);
765 rc
= scols_line_refer_data(ln
, n
, arg
);
768 xasprintf(&arg
, "%d",msgdsp
->msg_perm
.id
);
769 rc
= scols_line_refer_data(ln
, n
, arg
);
772 arg
= get_username(&pw
, msgdsp
->msg_perm
.uid
);
774 xasprintf(&arg
, "%u", msgdsp
->msg_perm
.uid
);
775 rc
= scols_line_refer_data(ln
, n
, arg
);
779 xasprintf(&arg
, "%#o", msgdsp
->msg_perm
.mode
& 0777);
782 xstrmode(msgdsp
->msg_perm
.mode
& 0777, arg
);
783 rc
= scols_line_refer_data(ln
, n
, arg
);
787 xasprintf(&arg
, "%u", msgdsp
->msg_perm
.cuid
);
788 rc
= scols_line_refer_data(ln
, n
, arg
);
791 arg
= get_username(&pw
, msgdsp
->msg_perm
.cuid
);
793 rc
= scols_line_refer_data(ln
, n
, arg
);
796 xasprintf(&arg
, "%u", msgdsp
->msg_perm
.cuid
);
797 rc
= scols_line_refer_data(ln
, n
, arg
);
800 arg
= get_groupname(&gr
, msgdsp
->msg_perm
.cgid
);
802 rc
= scols_line_refer_data(ln
, n
, arg
);
805 xasprintf(&arg
, "%u", msgdsp
->msg_perm
.uid
);
806 rc
= scols_line_refer_data(ln
, n
, arg
);
809 arg
= get_username(&pw
, msgdsp
->msg_perm
.uid
);
811 rc
= scols_line_refer_data(ln
, n
, arg
);
814 xasprintf(&arg
, "%u", msgdsp
->msg_perm
.gid
);
815 rc
= scols_line_refer_data(ln
, n
, arg
);
818 arg
= get_groupname(&gr
,msgdsp
->msg_perm
.gid
);
820 rc
= scols_line_refer_data(ln
, n
, arg
);
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
));
829 xasprintf(&arg
, "%ju", msgdsp
->q_cbytes
);
830 rc
= scols_line_refer_data(ln
, n
, arg
);
833 xasprintf(&arg
, "%ju", msgdsp
->q_qnum
);
834 rc
= scols_line_refer_data(ln
, n
, arg
);
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
));
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
));
849 xasprintf(&arg
, "%u", msgdsp
->q_lspid
);
850 rc
= scols_line_refer_data(ln
, n
, arg
);
853 xasprintf(&arg
, "%u", msgdsp
->q_lrpid
);
854 rc
= scols_line_refer_data(ln
, n
, arg
);
858 err(EXIT_FAILURE
, _("failed to set data"));
864 ipc_msg_free_info(msgds
);
868 static void do_msg_global(struct libscols_table
*tb
)
870 struct msg_data
*msgds
, *msgdsp
;
871 struct ipc_limits lim
;
874 ipc_msg_get_limits(&lim
);
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
)
880 ipc_msg_free_info(msgds
);
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);
889 static void do_shm(int id
, struct lsipc_control
*ctl
, struct libscols_table
*tb
)
891 struct libscols_line
*ln
;
892 struct passwd
*pw
= NULL
;
893 struct group
*gr
= NULL
;
894 struct shm_data
*shmds
, *shmdsp
;
897 if (ipc_shm_get_info(id
, &shmds
) < 1) {
899 warnx(_("id %d not found"), id
);
903 scols_table_set_name(tb
, "sharedmemory");
905 for (shmdsp
= shmds
; shmdsp
->next
!= NULL
|| id
> -1 ; shmdsp
= shmdsp
->next
) {
907 ln
= scols_table_new_line(tb
, NULL
);
910 err(EXIT_FAILURE
, _("failed to allocate output line"));
912 for (n
= 0; n
< ncolumns
; n
++) {
915 switch (get_column_id(n
)) {
917 xasprintf(&arg
, "0x%08x",shmdsp
->shm_perm
.key
);
918 rc
= scols_line_refer_data(ln
, n
, arg
);
921 xasprintf(&arg
, "%d",shmdsp
->shm_perm
.id
);
922 rc
= scols_line_refer_data(ln
, n
, arg
);
925 arg
= get_username(&pw
, shmdsp
->shm_perm
.uid
);
927 xasprintf(&arg
, "%u", shmdsp
->shm_perm
.uid
);
928 rc
= scols_line_refer_data(ln
, n
, arg
);
932 xasprintf(&arg
, "%#o", shmdsp
->shm_perm
.mode
& 0777);
935 xstrmode(shmdsp
->shm_perm
.mode
& 0777, arg
);
937 rc
= scols_line_refer_data(ln
, n
, arg
);
940 xasprintf(&arg
, "%u", shmdsp
->shm_perm
.cuid
);
941 rc
= scols_line_refer_data(ln
, n
, arg
);
944 arg
= get_username(&pw
, shmdsp
->shm_perm
.cuid
);
946 rc
= scols_line_refer_data(ln
, n
, arg
);
949 xasprintf(&arg
, "%u", shmdsp
->shm_perm
.cuid
);
950 rc
= scols_line_refer_data(ln
, n
, arg
);
953 arg
= get_groupname(&gr
, shmdsp
->shm_perm
.cgid
);
955 rc
= scols_line_refer_data(ln
, n
, arg
);
958 xasprintf(&arg
, "%u", shmdsp
->shm_perm
.uid
);
959 rc
= scols_line_refer_data(ln
, n
, arg
);
962 arg
= get_username(&pw
, shmdsp
->shm_perm
.uid
);
964 rc
= scols_line_refer_data(ln
, n
, arg
);
967 xasprintf(&arg
, "%u", shmdsp
->shm_perm
.gid
);
968 rc
= scols_line_refer_data(ln
, n
, arg
);
971 arg
= get_groupname(&gr
, shmdsp
->shm_perm
.gid
);
973 rc
= scols_line_refer_data(ln
, n
, arg
);
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
));
983 xasprintf(&arg
, "%ju", shmdsp
->shm_segsz
);
985 arg
= size_to_human_string(SIZE_SUFFIX_1LETTER
, shmdsp
->shm_segsz
);
986 rc
= scols_line_refer_data(ln
, n
, arg
);
989 xasprintf(&arg
, "%ju", shmdsp
->shm_nattch
);
990 rc
= scols_line_refer_data(ln
, n
, arg
);
997 arg
= xcalloc(1, sizeof(char) * strlen(_("dest"))
998 + strlen(_("locked"))
999 + strlen(_("hugetlb"))
1000 + strlen(_("noreserve")) + 4);
1002 if (shmdsp
->shm_perm
.mode
& SHM_DEST
) {
1003 offt
+= sprintf(arg
, "%s", _("dest"));
1008 if (shmdsp
->shm_perm
.mode
& SHM_LOCKED
) {
1011 offt
+= sprintf(arg
+ offt
, "%s", _("locked"));
1015 if (shmdsp
->shm_perm
.mode
& SHM_HUGETLB
) {
1018 offt
+= sprintf(arg
+ offt
, "%s", _("hugetlb"));
1021 #ifdef SHM_NORESERVE
1022 if (shmdsp
->shm_perm
.mode
& SHM_NORESERVE
) {
1025 sprintf(arg
+ offt
, "%s", _("noreserve"));
1028 rc
= scols_line_refer_data(ln
, n
, arg
);
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
));
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
));
1044 xasprintf(&arg
, "%u", shmdsp
->shm_cprid
);
1045 rc
= scols_line_refer_data(ln
, n
, arg
);
1048 xasprintf(&arg
, "%u", shmdsp
->shm_lprid
);
1049 rc
= scols_line_refer_data(ln
, n
, arg
);
1052 arg
= proc_get_command(shmdsp
->shm_cprid
);
1053 rc
= scols_line_refer_data(ln
, n
, arg
);
1057 err(EXIT_FAILURE
, _("failed to set data"));
1063 ipc_shm_free_info(shmds
);
1066 static void do_shm_global(struct libscols_table
*tb
)
1068 struct shm_data
*shmds
, *shmdsp
;
1069 uint64_t nsegs
= 0, sum_segsz
= 0;
1070 struct ipc_limits lim
;
1072 ipc_shm_get_limits(&lim
);
1074 if (ipc_shm_get_info(-1, &shmds
) > 0) {
1075 for (shmdsp
= shmds
; shmdsp
->next
!= NULL
; shmdsp
= shmdsp
->next
) {
1077 sum_segsz
+= shmdsp
->shm_segsz
;
1079 ipc_shm_free_info(shmds
);
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);
1088 int main(int argc
, char *argv
[])
1090 int opt
, msg
= 0, sem
= 0, shm
= 0, id
= -1;
1091 int show_time
= 0, show_creat
= 0, global
= 0;
1093 struct lsipc_control
*ctl
= xcalloc(1, sizeof(struct lsipc_control
));
1094 static struct libscols_table
*tb
;
1095 char *outarg
= NULL
;
1097 /* long only options. */
1099 OPT_NOTRUNC
= CHAR_MAX
+ 1,
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' },
1128 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
1129 { 'J', 'e', 'l', 'n', 'r' },
1135 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
1137 setlocale(LC_ALL
, "");
1138 bindtextdomain(PACKAGE
, LOCALEDIR
);
1139 textdomain(PACKAGE
);
1140 atexit(close_stdout
);
1144 scols_init_debug(0);
1146 while ((opt
= getopt_long(argc
, argv
, "bceghi:Jlmno:PqrstV", longopts
, NULL
)) != -1) {
1148 err_exclusive_options(opt
, longopts
, excl
, excl_st
);
1155 id
= strtos32_or_err(optarg
, _("failed to parse IPC identifier"));
1158 ctl
->outmode
= OUT_EXPORT
;
1161 ctl
->outmode
= OUT_RAW
;
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
;
1183 ctl
->outmode
= OUT_LIST
;
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
;
1202 ctl
->outmode
= OUT_NEWLINE
;
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
;
1221 ctl
->noheadings
= 1;
1224 ctl
->time_mode
= parse_time_mode(optarg
);
1227 ctl
->outmode
= OUT_JSON
;
1238 printf(UTIL_LINUX_VERSION
);
1239 return EXIT_SUCCESS
;
1241 errtryhelp(EXIT_FAILURE
);
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"));
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
;
1261 /* default to pretty-print if --id specified */
1262 if (id
!= -1 && !ctl
->outmode
)
1263 ctl
->outmode
= OUT_PRETTY
;
1265 if (!ctl
->time_mode
)
1266 ctl
->time_mode
= ctl
->outmode
== OUT_PRETTY
? TIME_FULL
: TIME_SHORT
;
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
;
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
);
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
);
1284 if (shm
&& show_time
) {
1285 /* keep "COMMAND" as last column */
1286 size_t cmd
= columns
[ncolumns
- 1] == COL_COMMAND
;
1290 add_column(columns
, ncolumns
++, COL_ATTACH
);
1291 add_column(columns
, ncolumns
++, COL_DETACH
);
1293 add_column(columns
, ncolumns
++, COL_COMMAND
);
1295 if (sem
&& show_time
) {
1296 add_column(columns
, ncolumns
++, COL_OTIME
);
1297 add_column(columns
, ncolumns
++, COL_CTIME
);
1301 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
1302 &ncolumns
, column_name_to_id
) < 0)
1303 return EXIT_FAILURE
;
1305 tb
= setup_table(ctl
);
1307 return EXIT_FAILURE
;
1310 scols_table_set_name(tb
, "ipclimits");
1316 do_msg(id
, ctl
, tb
);
1322 do_shm(id
, ctl
, tb
);
1328 do_sem(id
, ctl
, tb
);
1331 print_table(ctl
, tb
);
1333 scols_unref_table(tb
);
1336 return EXIT_SUCCESS
;