/* * lsipc - List information about IPC instances employed in the system * * Copyright (C) 2015 Ondrej Oprala * Copyright (C) 2015 Karel Zak * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * * lsipc is inspired by the ipcs utility. The aim is to create * a utility unencumbered by a standard to provide more flexible * means of controlling the output. */ #include #include #include #include #include #include #include "c.h" #include "nls.h" #include "closestream.h" #include "strutils.h" #include "optutils.h" #include "xalloc.h" #include "ipcutils.h" /* * time modes * */ enum { TIME_INVALID = 0, TIME_SHORT, TIME_FULL, TIME_ISO, }; /* * IDs */ enum { /* common */ COL_KEY = 0, COL_ID, COL_OWNER, COL_PERMS, COL_CUID, COL_CGID, COL_UID, COL_GID, COL_CTIME, /* msgq-specific */ COL_USEDBYTES, COL_MSGS, COL_SEND, COL_RECV, COL_LSPID, COL_LRPID, /* shm-specific */ COL_SIZE, COL_NATTCH, COL_STATUS, COL_ATTACH, COL_DETACH, COL_CPID, COL_LPID, /* sem-specific */ COL_NSEMS, COL_OTIME, COL_RESOURCE, COL_DESC, COL_USED, COL_LIMIT, }; /* we use the value of outmode to determine * appropriate flags for the libsmartcols table * (e.g., a value of out_newline would imply a raw * table with the column separator set to '\n'). */ static int outmode; /* not all columns apply to all options, so we specify a legal range for each */ static size_t LOWER, UPPER; /* * output modes */ enum { OUT_COLON = 1, OUT_EXPORT, OUT_NEWLINE, OUT_RAW, OUT_NUL, OUT_PRETTY }; struct lsipc_control { unsigned int noheadings : 1, /* don't print header line */ notrunc : 1, /* don't truncate columns */ json : 1, /* JSON output */ bytes : 1, /* SIZE in bytes */ numperms : 1, /* numeric permissions */ time_mode : 2; }; struct lsipc_coldesc { const char *name; const char *help; const char *pretty_name; double whint; /* width hint */ long flag; }; static const struct lsipc_coldesc coldescs[] = { /* common */ [COL_KEY] = { "KEY", N_("Resource key"), N_("Key"), 1}, [COL_ID] = { "ID", N_("Resource ID"), N_("ID"), 1}, [COL_OWNER] = { "OWNER", N_("Owner"), N_("Owner"), 1, SCOLS_FL_RIGHT}, [COL_PERMS] = { "PERMS", N_("Permissions"), N_("Permissions"), 1, SCOLS_FL_RIGHT}, [COL_CUID] = { "CUID", N_("Creator UID"), N_("CUID"), 1, SCOLS_FL_RIGHT}, [COL_CGID] = { "CGID", N_("Creator GID"), N_("CGID"), 1, SCOLS_FL_RIGHT}, [COL_UID] = { "UID", N_("User ID"), N_("UID"), 1, SCOLS_FL_RIGHT}, [COL_GID] = { "GID", N_("Group ID"), N_("GID"), 1, SCOLS_FL_RIGHT}, [COL_CTIME] = { "CTIME", N_("Time of the last change"), N_("Last change"), 1, SCOLS_FL_RIGHT}, /* msgq-specific */ [COL_USEDBYTES] = { "USEDBYTES",N_("Bytes used"), N_("Bytes used"), 1, SCOLS_FL_RIGHT}, [COL_MSGS] = { "MSGS", N_("Number of messages"), N_("Messages"), 1}, [COL_SEND] = { "SEND", N_("Time of last msg sent"), N_("Msg sent"), 1, SCOLS_FL_RIGHT}, [COL_RECV] = { "RECV", N_("Time of last msg received"), N_("Msg received"), 1, SCOLS_FL_RIGHT}, [COL_LSPID] = { "LSPID", N_("PID of the last msg sender"), N_("Msg sender"), 1, SCOLS_FL_RIGHT}, [COL_LRPID] = { "LRPID", N_("PID of the last msg receiver"), N_("Msg receiver"), 1, SCOLS_FL_RIGHT}, /* shm-specific */ [COL_SIZE] = { "SIZE", N_("Segment size"), N_("Segment size"), 1, SCOLS_FL_RIGHT}, [COL_NATTCH] = { "NATTCH", N_("Number of attached processes"), N_("Attached processes"), 1, SCOLS_FL_RIGHT}, [COL_STATUS] = { "STATUS", N_("Status"), N_("Status"), 1, SCOLS_FL_NOEXTREMES}, [COL_ATTACH] = { "ATTACH", N_("Attach time"), N_("Attach time"), 1, SCOLS_FL_RIGHT}, [COL_DETACH] = { "DETACH", N_("Detach time"), N_("Detach time"), 1, SCOLS_FL_RIGHT}, [COL_CPID] = { "CPID", N_("PID of the creator"), N_("Creator PID"), 1, SCOLS_FL_RIGHT}, [COL_LPID] = { "LPID", N_("PID of last user"), N_("Last user PID"), 1, SCOLS_FL_RIGHT}, /* sem-specific */ [COL_NSEMS] = { "NSEMS", N_("Number of semaphores"), N_("Semaphores"), 1, SCOLS_FL_RIGHT}, [COL_OTIME] = { "OTIME", N_("Time of the last operation"), N_("Last operation"), 1, SCOLS_FL_RIGHT}, /* cols for summarized information */ [COL_RESOURCE] = { "RESOURCE", N_("Resource name"), N_("Resource"), 1 }, [COL_DESC] = { "DESCRIPTION",N_("Resource description"), N_("Description"), 1 }, [COL_USED] = { "USED", N_("Currently used"), N_("Used"), 1 }, [COL_LIMIT] = { "LIMIT", N_("System-wide limit"), N_("Limit"), 1, SCOLS_FL_RIGHT }, }; /* columns[] array specifies all currently wanted output column. The columns * are defined by coldescs[] array and you can specify (on command line) each * column twice. That's enough, dynamically allocated array of the columns is * unnecessary overkill and over-engineering in this case */ static int columns[ARRAY_SIZE(coldescs) * 2]; static int ncolumns; static inline size_t err_columns_index(size_t arysz, size_t idx) { if (idx >= arysz) errx(EXIT_FAILURE, _("too many columns specified, " "the limit is %zu columns"), arysz - 1); return idx; } #define add_column(ary, n, id) \ ((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id)) static int column_name_to_id(const char *name, size_t namesz) { size_t i; for (i = 0; i < ARRAY_SIZE(coldescs); i++) { const char *cn = coldescs[i].name; if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) { if (i > COL_CTIME) { if (i >= LOWER && i <= UPPER) return i; else { warnx(_("column %s does not apply to the specified IPC"), name); return -1; } } else return i; } } warnx(_("unknown column: %s"), name); return -1; } static int parse_time_mode(const char *optarg) { struct lsipc_timefmt { const char *name; const int val; }; static const struct lsipc_timefmt timefmts[] = { {"iso", TIME_ISO}, {"full", TIME_FULL}, {"short", TIME_SHORT}, }; size_t i; for (i = 0; i < ARRAY_SIZE(timefmts); i++) { if (strcmp(timefmts[i].name, optarg) == 0) return timefmts[i].val; } errx(EXIT_FAILURE, _("unknown time format: %s"), optarg); } static void __attribute__ ((__noreturn__)) usage(FILE * out) { size_t i; fputs(USAGE_HEADER, out); fputs(USAGE_HEADER, out); fprintf(out, _(" %s [options]\n"), program_invocation_short_name); fputs(USAGE_SEPARATOR, out); fputs(_("Show information on IPC facilities.\n"), out); fputs(USAGE_OPTIONS, out); fputs(USAGE_HELP, out); fputs(USAGE_VERSION, out); fputs(USAGE_SEPARATOR, out); fputs(_("Resource options:\n"), out); fputs(_(" -m, --shmems shared memory segments\n"), out); fputs(_(" -q, --queues message queues\n"), out); fputs(_(" -s, --semaphores semaphores\n"), out); fputs(_(" -i, --id print details on resource identified by \n"), out); fputs(USAGE_SEPARATOR, out); fputs(_("Output options:\n"), out); fputs(_(" --colon-separate display data in a format similar to /etc/passwd\n"), out); fputs(_(" --noheadings don't print headings\n"), out); fputs(_(" --notruncate don't truncate output\n"), out); fputs(_(" --time-format= display dates in short, full or iso format\n"), out); fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out); fputs(_(" -c, --creator show creator and owner\n"), out); fputs(_(" -e, --export display in an export-able output format\n"), out); fputs(_(" -g, --global display info about about system-wide usage\n"), out); fputs(_(" -J, --json use the JSON output format\n"), out); fputs(_(" -n, --newline display each piece of information on a new line\n"), out); fputs(_(" -o, --output[=] define the columns to output\n"), out); fputs(_(" -P, --numeric-perms print numeric permissions (PERMS column)\n"), out); fputs(_(" -r, --raw display in raw mode\n"), out); fputs(_(" -t, --time show attach, detach and change times\n"), out); fputs(_(" -z, --print0 delimit user entries with a nul character\n"), out); fprintf(out, _("\nAvailable columns:\n")); fprintf(out, _("GENERAL COLUMNS:\n")); for (i = 0; i < ARRAY_SIZE(coldescs); i++) { if (i == COL_USEDBYTES) fprintf(out, _("MESSAGE QUEUES:\n")); if (i == COL_SIZE) fprintf(out, _("SHARED MEMORY:\n")); if (i == COL_NSEMS) fprintf(out, _("SEMAPHORES:\n")); if (i == COL_RESOURCE) fprintf(out, _("SUMMARIZED INFO:\n")); fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); } fprintf(out, USAGE_MAN_TAIL("lsipc(1)")); exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); } static struct libscols_table *setup_table(struct lsipc_control *ctl) { struct libscols_table *table = scols_new_table(); int n = 0; if (!table) errx(EXIT_FAILURE, _("failed to initialize output table")); if (ctl->noheadings) scols_table_enable_noheadings(table, 1); if (ctl->json) scols_table_enable_json(table, 1); switch(outmode) { case OUT_COLON: scols_table_enable_raw(table, 1); scols_table_set_column_separator(table, ":"); break; case OUT_NEWLINE: scols_table_set_column_separator(table, "\n"); /* fallthrough */ case OUT_EXPORT: scols_table_enable_export(table, 1); break; case OUT_NUL: scols_table_set_line_separator(table, "\0"); /* fallthrough */ case OUT_RAW: scols_table_enable_raw(table, 1); break; case OUT_PRETTY: scols_table_enable_noheadings(table, 1); default: break; } while (n < ncolumns) { int flags = coldescs[columns[n]].flag; if (ctl->notrunc) flags &= ~SCOLS_FL_TRUNC; if (!scols_table_new_column(table, coldescs[columns[n]].name, coldescs[columns[n]].whint, flags)) goto fail; ++n; } return table; fail: scols_unref_table(table); return NULL; } static int print_pretty(struct libscols_table *table) { struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); struct libscols_column *col; struct libscols_cell *data; struct libscols_line *ln; const char *hstr, *dstr, *estr; int n = 0; ln = scols_table_get_line(table, 0); while (!scols_table_next_column(table, itr, &col)) { data = scols_line_get_cell(ln, n); hstr = N_(coldescs[columns[n]].pretty_name); dstr = scols_cell_get_data(data); if (dstr) printf("%s:%*c%-36s\n", hstr, 35 - (int)strlen(hstr), ' ', dstr); ++n; } /* this is used to pretty-print detailed info about a semaphore array */ if (ln) { estr = scols_line_get_userdata(ln); if (estr) { printf("%s", (const char *)estr); free((void *)estr); } } scols_free_iter(itr); return 0; } static int print_table(struct libscols_table *tb) { if (outmode == OUT_PRETTY) print_pretty(tb); else scols_print_table(tb); return 0; } static struct timeval now; static int date_is_today(time_t t) { if (now.tv_sec == 0) gettimeofday(&now, NULL); return t / (3600 * 24) == now.tv_sec / (3600 * 24); } static int date_is_thisyear(time_t t) { if (now.tv_sec == 0) gettimeofday(&now, NULL); return t / (3600 * 24 * 365) == now.tv_sec / (3600 * 24 * 365); } static char *make_time(int mode, time_t time) { char *s; struct tm tm; char buf[64] = {0}; localtime_r(&time, &tm); switch(mode) { case TIME_FULL: asctime_r(&tm, buf); if (*(s = buf + strlen(buf) - 1) == '\n') *s = '\0'; break; case TIME_SHORT: if (date_is_today(time)) strftime(buf, sizeof(buf), "%H:%M", &tm); else if (date_is_thisyear(time)) strftime(buf, sizeof(buf), "%b%d", &tm); else strftime(buf, sizeof(buf), "%Y-%b%d", &tm); break; case TIME_ISO: strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", &tm); break; default: errx(EXIT_FAILURE, _("unsupported time type")); } return xstrdup(buf); } static void do_sem(int id, struct lsipc_control *ctl, struct libscols_table *tb) { int n = 0; struct libscols_line *ln; struct passwd *pw = NULL, *cpw = NULL; struct group *gr = NULL, *cgr = NULL; struct sem_data *semds, *semdsp; char *arg = NULL, *time; if (ipc_sem_get_info(id, &semds) < 1) { if (id > -1) warnx(_("id %d not found"), id); return; } for (semdsp = semds; semdsp->next != NULL || id > -1; semdsp = semdsp->next) { ln = scols_table_new_line(tb, NULL); /* no need to call getpwuid() for the same user */ if (!(pw && pw->pw_uid == semdsp->sem_perm.uid)) pw = getpwuid(semdsp->sem_perm.uid); /* no need to call getgrgid() for the same group */ if (!(gr && gr->gr_gid == semdsp->sem_perm.gid)) gr = getgrgid(semdsp->sem_perm.gid); /* no need to call getpwuid() for the same user */ if (!(cpw && cpw->pw_uid == semdsp->sem_perm.cuid)) cpw = getpwuid(semdsp->sem_perm.cuid); /* no need to call getcgrgid() for the same cgroup */ if (!(cgr && cgr->gr_gid == semdsp->sem_perm.cgid)) cgr = getgrgid(semdsp->sem_perm.cgid); n = 0; while (n < ncolumns) { int rc = 0; switch (columns[n]) { case COL_KEY: xasprintf(&arg, "0x%08x",semdsp->sem_perm.key); rc = scols_line_set_data(ln, n, arg); break; case COL_ID: xasprintf(&arg, "%d",semdsp->sem_perm.id); rc = scols_line_set_data(ln, n, arg); break; case COL_OWNER: if (pw) xasprintf(&arg, "%s", pw->pw_name); else xasprintf(&arg, "%u", semdsp->sem_perm.uid); rc = scols_line_set_data(ln, n, arg); break; case COL_PERMS: if (ctl->numperms) xasprintf(&arg, "%#o", semdsp->sem_perm.mode & 0777); else { arg = xmalloc(11); strmode(semdsp->sem_perm.mode & 0777, arg); } rc = scols_line_set_data(ln, n, arg); break; case COL_CUID: if (cpw) xasprintf(&arg, "%s", cpw->pw_name); else xasprintf(&arg, "%u", semdsp->sem_perm.cuid); rc = scols_line_set_data(ln, n, arg); break; case COL_CGID: if (cgr) xasprintf(&arg, "%s", cgr->gr_name); else xasprintf(&arg, "%u", semdsp->sem_perm.cuid); rc = scols_line_set_data(ln, n, arg); break; case COL_UID: if (pw) xasprintf(&arg, "%s", pw->pw_name); else xasprintf(&arg, "%u", semdsp->sem_perm.uid); rc = scols_line_set_data(ln, n, arg); break; case COL_GID: if (gr) xasprintf(&arg, "%s", gr->gr_name); else xasprintf(&arg, "%u", semdsp->sem_perm.gid); rc = scols_line_set_data(ln, n, arg); break; case COL_CTIME: if (semdsp->sem_ctime != 0) { rc = scols_line_set_data(ln, n, time = make_time(ctl->time_mode, (time_t)semdsp->sem_ctime)); free(time); } break; case COL_NSEMS: xasprintf(&arg, "%ju", semdsp->sem_nsems); rc = scols_line_set_data(ln, n, arg); break; case COL_OTIME: if (semdsp->sem_otime != 0) { rc = scols_line_set_data(ln, n, time = make_time(ctl->time_mode, (time_t)semdsp->sem_otime)); free(time); } break; } if (rc != 0) err(EXIT_FAILURE, _("failed to set data")); ++n; free(arg); arg = NULL; } /* no empty element when ID was specified */ if (id > -1) { size_t i = 0, offt; /* line length * (header + nsems) + '\0' */ char *data = xcalloc(1, sizeof(char) * 55 * (semds->sem_nsems + 1) + 1); /* same as in ipcs.c */ offt = sprintf(data, "%-10s %-10s %-10s %-10s %-10s\n", _("semnum"), _("value"), _("ncount"), _("zcount"), _("pid")); for (i = 0; i < semds->sem_nsems; i++) { struct sem_elem *e = &semds->elements[i]; offt += sprintf(data + offt, "%-10zd %-10d %-10d %-10d %-10d\n", i, e->semval, e->ncount, e->zcount, e->pid); } scols_line_set_userdata(ln, (void *)data); break; } } ipc_sem_free_info(semds); } static void do_sem_global(struct libscols_table *tb) { struct sem_data *semds, *semdsp; struct ipc_limits lim; int sems = 0, sets = 0, n = 0; struct libscols_line *ln; char *result = NULL; if (ipc_sem_get_info(-1, &semds) < 1) return; ipc_sem_get_limits(&lim); for (semdsp = semds; semdsp->next != NULL; semdsp = semdsp->next) { ++sets; sems += semds->sem_nsems; } ln = scols_table_new_line(tb, NULL); scols_line_set_data(ln, n++, "SEMMNS"); scols_line_set_data(ln, n++, "Total number of semaphores"); xasprintf(&result, "%d", sems); scols_line_set_data(ln, n++, result); free(result); xasprintf(&result, "%d", lim.semmns); scols_line_set_data(ln, n, result); free(result); n = 0; ln = scols_table_new_line(tb, NULL); scols_line_set_data(ln, n++, "SEMMNI"); scols_line_set_data(ln, n++, "Number of Semaphore IDs"); xasprintf(&result, "%d", sets); scols_line_set_data(ln, n++, result); free(result); xasprintf(&result, "%d", lim.semmni); scols_line_set_data(ln, n, result); free(result); ipc_sem_free_info(semds); } static void do_msg(int id, struct lsipc_control *ctl, struct libscols_table *tb) { int n = 0; struct libscols_line *ln; struct passwd *pw = NULL; struct group *gr = NULL; struct msg_data *msgds, *msgdsp; char *arg = NULL, *time; if (ipc_msg_get_info(id, &msgds) < 1) { if (id > -1) warnx(_("id %d not found"), id); return; } for (msgdsp = msgds; msgdsp->next != NULL || id > -1 ; msgdsp = msgdsp->next) { ln = scols_table_new_line(tb, NULL); /* no need to call getpwuid() for the same user */ if (!(pw && pw->pw_uid == msgdsp->msg_perm.uid)) pw = getpwuid(msgdsp->msg_perm.uid); /* no need to call getgrgid() for the same user */ if (!(gr && gr->gr_gid == msgdsp->msg_perm.gid)) gr = getgrgid(msgdsp->msg_perm.gid); n = 0; while (n < ncolumns) { int rc = 0; switch (columns[n]) { case COL_KEY: xasprintf(&arg, "0x%08x",msgdsp->msg_perm.key); rc = scols_line_set_data(ln, n, arg); break; case COL_ID: xasprintf(&arg, "%d",msgdsp->msg_perm.id); rc = scols_line_set_data(ln, n, arg); break; case COL_OWNER: if (pw) xasprintf(&arg, "%s", pw->pw_name); else xasprintf(&arg, "%u", msgdsp->msg_perm.uid); rc = scols_line_set_data(ln, n, arg); break; case COL_PERMS: if (ctl->numperms) xasprintf(&arg, "%#o", msgdsp->msg_perm.mode & 0777); else { arg = xmalloc(11); strmode(msgdsp->msg_perm.mode & 0777, arg); rc = scols_line_set_data(ln, n, arg); } break; case COL_CUID: if (msgdsp->msg_perm.cuid == msgdsp->msg_perm.uid || (pw = getpwuid(msgdsp->msg_perm.cuid))) xasprintf(&arg, "%s", pw->pw_name); else xasprintf(&arg, "%u", msgdsp->msg_perm.cuid); rc = scols_line_set_data(ln, n, arg); break; case COL_CGID: if (msgdsp->msg_perm.cgid == msgdsp->msg_perm.gid || (gr = getgrgid(msgdsp->msg_perm.cgid))) xasprintf(&arg, "%s", gr->gr_name); else xasprintf(&arg, "%u", msgdsp->msg_perm.cuid); rc = scols_line_set_data(ln, n, arg); break; case COL_UID: if (pw) xasprintf(&arg, "%s", pw->pw_name); else xasprintf(&arg, "%u", msgdsp->msg_perm.uid); rc = scols_line_set_data(ln, n, arg); break; case COL_GID: if (gr) xasprintf(&arg, "%s", gr->gr_name); else xasprintf(&arg, "%u", msgdsp->msg_perm.gid); rc = scols_line_set_data(ln, n, arg); break; case COL_CTIME: if (msgdsp->q_ctime != 0) { rc = scols_line_set_data(ln, n, time = make_time(ctl->time_mode, (time_t)msgdsp->q_ctime)); free(time); } break; case COL_USEDBYTES: xasprintf(&arg, "%ju", msgdsp->q_cbytes); rc = scols_line_set_data(ln, n, arg); break; case COL_MSGS: xasprintf(&arg, "%ju", msgdsp->q_qnum); rc = scols_line_set_data(ln, n, arg); break; case COL_SEND: if (msgdsp->q_stime != 0) { rc = scols_line_set_data(ln, n, time = make_time(ctl->time_mode, (time_t)msgdsp->q_stime)); free(time); } break; case COL_RECV: if (msgdsp->q_rtime != 0) { rc = scols_line_set_data(ln, n, time = make_time(ctl->time_mode, (time_t)msgdsp->q_rtime)); free(time); } break; case COL_LSPID: xasprintf(&arg, "%u", msgdsp->q_lspid); rc = scols_line_set_data(ln, n, arg); break; case COL_LRPID: xasprintf(&arg, "%u", msgdsp->q_lrpid); rc = scols_line_set_data(ln, n, arg); break; } if (rc != 0) err(EXIT_FAILURE, _("failed to set data")); ++n; free(arg); arg = NULL; } if (id > -1) break; } ipc_msg_free_info(msgds); } static void do_msg_global(struct libscols_table *tb) { struct msg_data *msgds, *msgdsp; struct ipc_limits lim; struct libscols_line *ln = scols_table_new_line(tb, NULL); int msgqs = 0, n = 0; char *result; if (ipc_msg_get_info(-1, &msgds) < 1) return; ipc_msg_get_limits(&lim); for (msgdsp = msgds; msgdsp->next != NULL; msgdsp = msgdsp->next) { ++msgqs; } scols_line_set_data(ln, n++, "MSGMNI"); scols_line_set_data(ln, n++, "Number of message queues"); xasprintf(&result, "%d", msgqs); scols_line_set_data(ln, n++, result); free(result); xasprintf(&result, "%d", lim.msgmni); scols_line_set_data(ln, n, result); free(result); ipc_msg_free_info(msgds); } static void do_shm(int id, struct lsipc_control *ctl, struct libscols_table *tb) { int n = 0; struct libscols_line *ln; struct passwd *pw = NULL; struct group *gr = NULL; struct shm_data *shmds, *shmdsp; char *arg = NULL, *time; if (ipc_shm_get_info(id, &shmds) < 1) { if (id > -1) warnx(_("id %d not found"), id); return; } for (shmdsp = shmds; shmdsp->next != NULL || id > -1 ; shmdsp = shmdsp->next) { ln = scols_table_new_line(tb, NULL); /* no need to call getpwuid() for the same user */ if (!(pw && pw->pw_uid == shmdsp->shm_perm.uid)) pw = getpwuid(shmdsp->shm_perm.uid); /* no need to call getgrgid() for the same user */ if (!(gr && gr->gr_gid == shmdsp->shm_perm.gid)) gr = getgrgid(shmdsp->shm_perm.gid); n = 0; while (n < ncolumns) { int rc = 0; switch (columns[n]) { case COL_KEY: xasprintf(&arg, "0x%08x",shmdsp->shm_perm.key); rc = scols_line_set_data(ln, n, arg); break; case COL_ID: xasprintf(&arg, "%d",shmdsp->shm_perm.id); rc = scols_line_set_data(ln, n, arg); break; case COL_OWNER: if (pw) xasprintf(&arg, "%s", pw->pw_name); else xasprintf(&arg, "%u", shmdsp->shm_perm.uid); rc = scols_line_set_data(ln, n, arg); break; case COL_PERMS: if (ctl->numperms) xasprintf(&arg, "%#o", shmdsp->shm_perm.mode & 0777); else { arg = xmalloc(11); strmode(shmdsp->shm_perm.mode & 0777, arg); } rc = scols_line_set_data(ln, n, arg); break; case COL_CUID: if (shmdsp->shm_perm.cuid == shmdsp->shm_perm.uid || (pw = getpwuid(shmdsp->shm_perm.cuid))) xasprintf(&arg, "%s", pw->pw_name); else xasprintf(&arg, "%u", shmdsp->shm_perm.cuid); rc = scols_line_set_data(ln, n, arg); break; case COL_CGID: if (shmdsp->shm_perm.cgid == shmdsp->shm_perm.gid || (gr = getgrgid(shmdsp->shm_perm.cgid))) xasprintf(&arg, "%s", gr->gr_name); else xasprintf(&arg, "%u", shmdsp->shm_perm.cuid); rc = scols_line_set_data(ln, n, arg); break; case COL_UID: if (pw) xasprintf(&arg, "%s", pw->pw_name); else xasprintf(&arg, "%u", shmdsp->shm_perm.uid); rc = scols_line_set_data(ln, n, arg); break; case COL_GID: if (gr) xasprintf(&arg, "%s", gr->gr_name); else xasprintf(&arg, "%u", shmdsp->shm_perm.gid); rc = scols_line_set_data(ln, n, arg); break; case COL_CTIME: if (shmdsp->shm_ctim != 0) { rc = scols_line_set_data(ln, n, time = make_time(ctl->time_mode, (time_t)shmdsp->shm_ctim)); free(time); } break; case COL_SIZE: if (ctl->bytes) xasprintf(&arg, "%ju", shmdsp->shm_segsz); else arg = size_to_human_string(SIZE_SUFFIX_1LETTER, shmdsp->shm_segsz); rc = scols_line_set_data(ln, n, arg); break; case COL_NATTCH: xasprintf(&arg, "%ju", shmdsp->shm_nattch); rc = scols_line_set_data(ln, n, arg); break; case COL_STATUS: { int comma = 0; size_t offt = 0; free (arg); arg = xcalloc(1, sizeof(char) * strlen(_("dest")) + strlen(_("locked")) + strlen(_("hugetlb")) + strlen(_("noreserve")) + 4); if (shmdsp->shm_perm.mode & SHM_DEST) { offt += sprintf(arg, "%s", _("dest")); comma++; } if (shmdsp->shm_perm.mode & SHM_LOCKED) { if (comma) arg[offt++] = ','; offt += sprintf(arg + offt, "%s", _("locked")); } if (shmdsp->shm_perm.mode & SHM_HUGETLB) { if (comma) arg[offt++] = ','; offt += sprintf(arg + offt, "%s", _("hugetlb")); } if (shmdsp->shm_perm.mode & SHM_NORESERVE) { if (comma) arg[offt++] = ','; offt += sprintf(arg + offt, "%s", _("noreserve")); } rc = scols_line_set_data(ln, n, arg); } break; case COL_ATTACH: if (shmdsp->shm_atim != 0) { rc = scols_line_set_data(ln, n, time = make_time(ctl->time_mode, (time_t)shmdsp->shm_atim)); free(time); } break; case COL_DETACH: if (shmdsp->shm_dtim != 0) { rc = scols_line_set_data(ln, n, time = make_time(ctl->time_mode, (time_t)shmdsp->shm_dtim)); free(time); } break; case COL_CPID: xasprintf(&arg, "%u", shmdsp->shm_cprid); rc = scols_line_set_data(ln, n, arg); break; case COL_LPID: xasprintf(&arg, "%u", shmdsp->shm_lprid); rc = scols_line_set_data(ln, n, arg); break; } if (rc != 0) err(EXIT_FAILURE, _("failed to set data")); ++n; free(arg); arg = NULL; } if (id > -1) break; } ipc_shm_free_info(shmds); } static void do_shm_global(struct libscols_table *tb) { struct shm_data *shmds, *shmdsp; int n = 0; uint64_t segs = 0, segsz = 0; struct ipc_limits lim; struct libscols_line *ln; char *result = NULL; if (ipc_shm_get_info(-1, &shmds) < 1) return; ipc_shm_get_limits(&lim); for (shmdsp = shmds; shmdsp->next != NULL; shmdsp = shmdsp->next) { ++segs; segsz += shmdsp->shm_segsz; } ln = scols_table_new_line(tb, NULL); scols_line_set_data(ln, n++, "SHMMNI"); scols_line_set_data(ln, n++, "Shared memory segments"); xasprintf(&result, "%lu", segs); scols_line_set_data(ln, n++, result); free(result); xasprintf(&result, "%lu", lim.shmmni); scols_line_set_data(ln, n, result); free(result); n = 0; ln = scols_table_new_line(tb, NULL); scols_line_set_data(ln, n++, "SHMALL"); scols_line_set_data(ln, n++, "Shared memory pages"); xasprintf(&result, "%lu", segsz / getpagesize()); scols_line_set_data(ln, n++, result); free(result); xasprintf(&result, "%lu", lim.shmall); scols_line_set_data(ln, n, result); free(result); ipc_shm_free_info(shmds); } int main(int argc, char *argv[]) { int opt, msg = 0, sem = 0, shm = 0, opt_o = 0, id = -1; int show_time = 0, show_creat = 0, global = 0; size_t i; struct lsipc_control *ctl = xcalloc(1, sizeof(struct lsipc_control)); static struct libscols_table *tb; char *opts = NULL; /* long only options. */ enum { OPT_NOTRUNC = CHAR_MAX + 1, OPT_NOHEAD, OPT_TIME_FMT, OPT_COLON, }; static const struct option longopts[] = { { "bytes", no_argument, 0, 'b' }, { "colon-separate", no_argument, 0, OPT_COLON }, { "creator", no_argument, 0, 'c' }, { "export", no_argument, 0, 'e' }, { "global", no_argument, 0, 'g' }, { "help", no_argument, 0, 'h' }, { "id", required_argument, 0, 'i' }, { "json", no_argument, 0, 'J' }, { "newline", no_argument, 0, 'n' }, { "noheadings", no_argument, 0, OPT_NOHEAD }, { "notruncate", no_argument, 0, OPT_NOTRUNC }, { "numeric-perms", no_argument, 0, 'P' }, { "output", required_argument, 0, 'o' }, { "pid", no_argument, 0, 'p' }, { "print0", no_argument, 0, 'z' }, { "queues", no_argument, 0, 'q' }, { "raw", no_argument, 0, 'r' }, { "semaphores", no_argument, 0, 's' }, { "shmems", no_argument, 0, 'm' }, { "time", no_argument, 0, 't' }, { "time-format", required_argument, 0, OPT_TIME_FMT }, { "version", no_argument, 0, 'V' }, {NULL, 0, NULL, 0} }; static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ { 'J', 'e', 'n', 'r', 'z', OPT_COLON }, { 'c', 'g', 'i', 'o', 't' }, { 'm', 'q', 's' }, { 0 } }; int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); atexit(close_stdout); ctl->time_mode = TIME_SHORT; while ((opt = getopt_long(argc, argv, "bceghi:Jmno:PqrstuVz", longopts, NULL)) != -1) { err_exclusive_options(opt, longopts, excl, excl_st); switch (opt) { case 'b': ctl->bytes = 1; break; case 'i': id = atoi (optarg); outmode = OUT_PRETTY; break; case OPT_COLON: outmode = OUT_COLON; break; case 'e': outmode = OUT_EXPORT; break; case 'r': outmode = OUT_RAW; break; case 'o': if (optarg) { if (*optarg == '=') optarg++; opts = xstrdup(optarg); } opt_o = 1; break; case 'g': global = 1; break; case 'q': msg = 1; LOWER = COL_USEDBYTES; UPPER = COL_LRPID; break; case 'm': shm = 1; LOWER = COL_SIZE; UPPER = COL_LPID; break; case 'n': outmode = OUT_NEWLINE; break; case 'P': ctl->numperms = 1; break; case 's': sem = 1; LOWER = COL_NSEMS; UPPER = COL_OTIME; break; case OPT_NOTRUNC: ctl->notrunc = 1; break; case OPT_NOHEAD: ctl->noheadings = 1; break; case OPT_TIME_FMT: ctl->time_mode = parse_time_mode(optarg); break; case 'J': ctl->json = 1; break; case 't': show_time = 1; break; case 'c': show_creat = 1; break; case 'h': usage(stdout); case 'V': printf(UTIL_LINUX_VERSION); return EXIT_SUCCESS; case 'z': outmode = OUT_NUL; break; default: usage(stderr); } } if (msg + shm + sem != 1) errx (EXIT_FAILURE, _("One of --shmems, --queues or --semaphores must be specified")); if (global) { add_column(columns, ncolumns++, COL_RESOURCE); add_column(columns, ncolumns++, COL_DESC); add_column(columns, ncolumns++, COL_USED); add_column(columns, ncolumns++, COL_LIMIT); } else if (opt_o) { ncolumns = string_to_idarray(opts, columns, ARRAY_SIZE(columns), column_name_to_id); if (ncolumns < 0) return EXIT_FAILURE; } else { if (outmode == OUT_PRETTY) { /* all columns for lsipc -- --id */ for (ncolumns = 0, i = 0; i < ARRAY_SIZE(coldescs); i++) columns[ncolumns++] = i; } else { /* default columns */ add_column(columns, ncolumns++, COL_KEY); add_column(columns, ncolumns++, COL_ID); add_column(columns, ncolumns++, COL_PERMS); add_column(columns, ncolumns++, COL_OWNER); if (show_creat) { add_column(columns, ncolumns++, COL_CUID); add_column(columns, ncolumns++, COL_CGID); add_column(columns, ncolumns++, COL_UID); add_column(columns, ncolumns++, COL_GID); } if (msg) { add_column(columns, ncolumns++, COL_USEDBYTES); add_column(columns, ncolumns++, COL_MSGS); if (show_time) { add_column(columns, ncolumns++, COL_SEND); add_column(columns, ncolumns++, COL_RECV); add_column(columns, ncolumns++, COL_CTIME); } add_column(columns, ncolumns++, COL_LSPID); add_column(columns, ncolumns++, COL_LRPID); } else if (shm) { add_column(columns, ncolumns++, COL_SIZE); add_column(columns, ncolumns++, COL_NATTCH); add_column(columns, ncolumns++, COL_STATUS); if (show_time) { add_column(columns, ncolumns++, COL_ATTACH); add_column(columns, ncolumns++, COL_DETACH); } add_column(columns, ncolumns++, COL_CTIME); add_column(columns, ncolumns++, COL_CPID); add_column(columns, ncolumns++, COL_LPID); } else if (sem) { add_column(columns, ncolumns++, COL_NSEMS); if (show_time) { add_column(columns, ncolumns++, COL_OTIME); add_column(columns, ncolumns++, COL_CTIME); } } } } tb = setup_table(ctl); if (!tb) return EXIT_FAILURE; if (msg) { if (global) do_msg_global(tb); else do_msg(id, ctl, tb); } else if (shm) { if (global) do_shm_global(tb); else do_shm(id, ctl, tb); } else if (sem) { if (global) do_sem_global(tb); else do_sem(id, ctl, tb); } print_table(tb); scols_unref_table(tb); free(ctl); return EXIT_SUCCESS; }