From 3c3bbe6a30bdd4abe0926690394e53cf4fd01b14 Mon Sep 17 00:00:00 2001 From: basebuilder_pel7ppc64bebuilder0 Date: Thu, 17 May 2018 19:13:30 +0200 Subject: [PATCH] util linux patches Signed-off-by: basebuilder_pel7ppc64bebuilder0 --- SOURCES/0078-lsipc-backport-new-command.patch | 1999 +++++++++++++++++ 1 file changed, 1999 insertions(+) create mode 100644 SOURCES/0078-lsipc-backport-new-command.patch diff --git a/SOURCES/0078-lsipc-backport-new-command.patch b/SOURCES/0078-lsipc-backport-new-command.patch new file mode 100644 index 00000000..e424d6ac --- /dev/null +++ b/SOURCES/0078-lsipc-backport-new-command.patch @@ -0,0 +1,1999 @@ +From 9f643efe377d2a39929f19cc09e8890afc74d9a4 Mon Sep 17 00:00:00 2001 +From: Karel Zak +Date: Fri, 24 Jun 2016 12:57:13 +0200 +Subject: [PATCH 78/84] lsipc: backport new command + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1153770 +Signed-off-by: Karel Zak +--- + bash-completion/lsipc | 64 +++ + configure.ac | 6 + + include/xalloc.h | 7 + + sys-utils/Makemodule.am | 10 + + sys-utils/ipcs.c | 2 +- + sys-utils/ipcutils.c | 116 ++--- + sys-utils/ipcutils.h | 13 +- + sys-utils/lsipc.1 | 133 +++++ + sys-utils/lsipc.c | 1316 +++++++++++++++++++++++++++++++++++++++++++++++ + tests/functions.sh | 16 +- + tests/ts/ipcs/limits2 | 9 +- + 11 files changed, 1613 insertions(+), 79 deletions(-) + create mode 100644 bash-completion/lsipc + create mode 100644 sys-utils/lsipc.1 + create mode 100644 sys-utils/lsipc.c + +diff --git a/bash-completion/lsipc b/bash-completion/lsipc +new file mode 100644 +index 0000000..6a87393 +--- /dev/null ++++ b/bash-completion/lsipc +@@ -0,0 +1,64 @@ ++_lsipc_module() ++{ ++ local cur prev OPTS ARG ++ COMPREPLY=() ++ cur="${COMP_WORDS[COMP_CWORD]}" ++ prev="${COMP_WORDS[COMP_CWORD-1]}" ++ case $prev in ++ '-i'|'--id') ++ COMPREPLY=( $(compgen -W "id" -- $cur) ) ++ return 0 ++ ;; ++ '-h'|'--help'|'-V'|'--version') ++ return 0 ++ ;; ++ '-o'|'--output') ++ local prefix realcur OUTPUT_ALL OUTPUT ++ realcur="${cur##*,}" ++ prefix="${cur%$realcur}" ++ OUTPUT_ALL="GENERAL KEY ID OWNER PERMS CUID ++ CGID UID GID CHANGE MESSAGE USEDBYTES ++ MSGS SEND RECV LSPID LRPID SHARED BYTES ++ NATTCH STATUS ATTACH DETACH CPID LPID NSEMS ++ LASTOP" ++ for WORD in $OUTPUT_ALL; do ++ if ! [[ $prefix == *"$WORD"* ]]; then ++ OUTPUT="$WORD $OUTPUT" ++ fi ++ done ++ compopt -o nospace ++ COMPREPLY=( $(compgen -P "$prefix" -W "$OUTPUT" -S ',' -- $realcur) ) ++ return 0 ++ ;; ++ esac ++ case $cur in ++ -*) ++ OPTS="--id ++ --help ++ --version ++ --shmems ++ --queues ++ --semaphores ++ --colon-separate ++ --creator ++ --export ++ --global ++ --newline ++ --noheadings ++ --notruncate ++ --output ++ --pid ++ --print0 ++ --raw ++ --time ++ --time-format" ++ COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) ++ return 0 ++ ;; ++ esac ++ local IFS=$'\n' ++ compopt -o filenames ++ COMPREPLY=( $(compgen -f -- $cur) ) ++ return 0 ++} ++complete -F _lsipc_module lsipc +diff --git a/configure.ac b/configure.ac +index 5d9ea39..fe0a011 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1038,6 +1038,12 @@ UL_REQUIRES_BUILD([lsns], [libsmartcols]) + AM_CONDITIONAL([BUILD_LSNS], [test "x$build_lsns" = xyes]) + + ++UL_BUILD_INIT([lsipc], [check]) ++UL_REQUIRES_LINUX([lsipc]) ++UL_REQUIRES_BUILD([lsipc], [libsmartcols]) ++AM_CONDITIONAL([BUILD_LSIPC], [test "x$build_lsipc" = xyes]) ++ ++ + UL_BUILD_INIT([chcpu], [check]) + UL_REQUIRES_LINUX([chcpu]) + UL_REQUIRES_HAVE([chcpu], [cpu_set_t], [cpu_set_t type]) +diff --git a/include/xalloc.h b/include/xalloc.h +index 6342793..1a1799a 100644 +--- a/include/xalloc.h ++++ b/include/xalloc.h +@@ -19,6 +19,13 @@ + # define XALLOC_EXIT_CODE EXIT_FAILURE + #endif + ++static inline void __err_oom(const char *file, unsigned int line) ++{ ++ err(XALLOC_EXIT_CODE, "%s: %u: cannot allocate memory", file, line); ++} ++ ++#define err_oom() __err_oom(__FILE__, __LINE__) ++ + static inline __ul_alloc_size(1) + void *xmalloc(const size_t size) + { +diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am +index 9baf5a3..6badd17 100644 +--- a/sys-utils/Makemodule.am ++++ b/sys-utils/Makemodule.am +@@ -22,6 +22,16 @@ ipcs_SOURCES = sys-utils/ipcs.c \ + ipcs_LDADD = $(LDADD) libcommon.la + + ++if BUILD_LSIPC ++usrbin_exec_PROGRAMS += lsipc ++dist_man_MANS += sys-utils/lsipc.1 ++lsipc_SOURCES = sys-utils/lsipc.c \ ++ sys-utils/ipcutils.c \ ++ sys-utils/ipcutils.h ++lsipc_LDADD = $(LDADD) libcommon.la libsmartcols.la ++lsipc_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) ++endif ++ + usrbin_exec_PROGRAMS += renice + dist_man_MANS += sys-utils/renice.1 + renice_SOURCES = sys-utils/renice.c +diff --git a/sys-utils/ipcs.c b/sys-utils/ipcs.c +index 14f5f0b..1843cd5 100644 +--- a/sys-utils/ipcs.c ++++ b/sys-utils/ipcs.c +@@ -201,7 +201,7 @@ static void do_shm (char format, int unit) + _("max seg size"), lim.shmmax, "\n", 0); + ipc_print_size(unit == IPC_UNIT_DEFAULT ? IPC_UNIT_KB : unit, + _("max total shared memory"), +- lim.shmall * getpagesize(), "\n", 0); ++ (uint64_t) lim.shmall * getpagesize(), "\n", 0); + ipc_print_size(unit == IPC_UNIT_DEFAULT ? IPC_UNIT_BYTES : unit, + _("min seg size"), lim.shmmin, "\n", 0); + return; +diff --git a/sys-utils/ipcutils.c b/sys-utils/ipcutils.c +index 62d7428..51fce7b 100644 +--- a/sys-utils/ipcutils.c ++++ b/sys-utils/ipcutils.c +@@ -1,4 +1,3 @@ +- + #include + + #include "c.h" +@@ -54,8 +53,8 @@ int ipc_sem_get_limits(struct ipc_limits *lim) + + } + +- if (rc == 4) { +- struct seminfo seminfo; ++ if (rc != 4) { ++ struct seminfo seminfo = { .semmni = 0 }; + union semun arg = { .array = (ushort *) &seminfo }; + + if (semctl(0, 0, IPC_INFO, arg) < 0) +@@ -82,12 +81,15 @@ int ipc_shm_get_limits(struct ipc_limits *lim) + lim->shmmni = path_read_u64(_PATH_PROC_IPC_SHMMNI); + + } else { +- struct shminfo shminfo; ++ struct shminfo *shminfo; ++ struct shmid_ds shmbuf; + +- if (shmctl(0, IPC_INFO, (struct shmid_ds *) &shminfo) < 0) ++ if (shmctl(0, IPC_INFO, &shmbuf) < 0) + return 1; +- lim->shmmni = shminfo.shmmni; +- lim->shmall = shminfo.shmall; ++ shminfo = (struct shminfo *) &shmbuf; ++ lim->shmmni = shminfo->shmmni; ++ lim->shmall = shminfo->shmall; ++ lim->shmmax = shminfo->shmmax; + } + + return 0; +@@ -97,20 +99,24 @@ int ipc_shm_get_info(int id, struct shm_data **shmds) + { + FILE *f; + int i = 0, maxid; ++ char buf[BUFSIZ]; + struct shm_data *p; +- struct shm_info dummy; ++ struct shmid_ds dummy; + + p = *shmds = xcalloc(1, sizeof(struct shm_data)); + p->next = NULL; + + f = path_fopen("r", 0, _PATH_PROC_SYSV_SHM); + if (!f) +- goto fallback; ++ goto shm_fallback; + + while (fgetc(f) != '\n'); /* skip header */ + +- while (feof(f) == 0) { +- if (fscanf(f, ++ while (fgets(buf, sizeof(buf), f) != NULL) { ++ /* scan for the first 14-16 columns (e.g. Linux 2.6.32 has 14) */ ++ p->shm_rss = 0xdead; ++ p->shm_swp = 0xdead; ++ if (sscanf(buf, + "%d %d %o %"SCNu64 " %u %u " + "%"SCNu64 " %u %u %u %u %"SCNi64 " %"SCNi64 " %"SCNi64 + " %"SCNu64 " %"SCNu64 "\n", +@@ -129,8 +135,8 @@ int ipc_shm_get_info(int id, struct shm_data **shmds) + &p->shm_dtim, + &p->shm_ctim, + &p->shm_rss, +- &p->shm_swp) != 16) +- continue; ++ &p->shm_swp) < 14) ++ continue; /* invalid line, skipped */ + + if (id > -1) { + /* ID specified */ +@@ -153,28 +159,20 @@ int ipc_shm_get_info(int id, struct shm_data **shmds) + return i; + + /* Fallback; /proc or /sys file(s) missing. */ +-fallback: +- i = id < 0 ? 0 : id; +- +- maxid = shmctl(0, SHM_INFO, (struct shmid_ds *) &dummy); +- if (maxid < 0) +- return 0; ++shm_fallback: ++ maxid = shmctl(0, SHM_INFO, &dummy); + +- while (i <= maxid) { ++ for (int j = 0; j <= maxid; j++) { + int shmid; + struct shmid_ds shmseg; + struct ipc_perm *ipcp = &shmseg.shm_perm; + +- shmid = shmctl(i, SHM_STAT, &shmseg); +- if (shmid < 0) { +- if (-1 < id) { +- free(*shmds); +- return 0; +- } +- i++; ++ shmid = shmctl(j, SHM_STAT, &shmseg); ++ if (shmid < 0 || (id > -1 && shmid != id)) { + continue; + } + ++ i++; + p->shm_perm.key = ipcp->KEY; + p->shm_perm.id = shmid; + p->shm_perm.mode = ipcp->mode; +@@ -196,11 +194,12 @@ fallback: + p->next = xcalloc(1, sizeof(struct shm_data)); + p = p->next; + p->next = NULL; +- i++; + } else +- return 1; ++ break; + } + ++ if (i == 0) ++ free(*shmds); + return i; + } + +@@ -299,30 +298,22 @@ int ipc_sem_get_info(int id, struct sem_data **semds) + return i; + + /* Fallback; /proc or /sys file(s) missing. */ +- sem_fallback: +- i = id < 0 ? 0 : id; +- ++sem_fallback: + arg.array = (ushort *) (void *)&dummy; + maxid = semctl(0, 0, SEM_INFO, arg); +- if (maxid < 0) +- return 0; + +- while (i <= maxid) { ++ for (int j = 0; j <= maxid; j++) { + int semid; + struct semid_ds semseg; + struct ipc_perm *ipcp = &semseg.sem_perm; + arg.buf = (struct semid_ds *)&semseg; + +- semid = semctl(i, 0, SEM_STAT, arg); +- if (semid < 0) { +- if (-1 < id) { +- free(*semds); +- return 0; +- } +- i++; ++ semid = semctl(j, 0, SEM_STAT, arg); ++ if (semid < 0 || (id > -1 && semid != id)) { + continue; + } + ++ i++; + p->sem_perm.key = ipcp->KEY; + p->sem_perm.id = semid; + p->sem_perm.mode = ipcp->mode; +@@ -341,10 +332,12 @@ int ipc_sem_get_info(int id, struct sem_data **semds) + i++; + } else { + get_sem_elements(p); +- return 1; ++ break; + } + } + ++ if (i == 0) ++ free(*semds); + return i; + } + +@@ -398,10 +391,6 @@ int ipc_msg_get_info(int id, struct msg_data **msgds) + if (id > -1) { + /* ID specified */ + if (id == p->msg_perm.id) { +- /* +- * FIXME: q_qbytes are not in /proc +- * +- */ + if (msgctl(id, IPC_STAT, &msgseg) != -1) + p->q_qbytes = msgseg.msg_qbytes; + i = 1; +@@ -422,27 +411,19 @@ int ipc_msg_get_info(int id, struct msg_data **msgds) + return i; + + /* Fallback; /proc or /sys file(s) missing. */ +- msg_fallback: +- i = id < 0 ? 0 : id; +- +- maxid = msgctl(id, MSG_STAT, &dummy); +- if (maxid < 0) +- return 0; ++msg_fallback: ++ maxid = msgctl(0, MSG_INFO, &dummy); + +- while (i <= maxid) { ++ for (int j = 0; j <= maxid; j++) { + int msgid; + struct ipc_perm *ipcp = &msgseg.msg_perm; + +- msgid = msgctl(i, MSG_STAT, &msgseg); +- if (msgid < 0) { +- if (-1 < id) { +- free(*msgds); +- return 0; +- } +- i++; ++ msgid = msgctl(j, MSG_STAT, &msgseg); ++ if (msgid < 0 || (id > -1 && msgid != id)) { + continue; + } + ++ i++; + p->msg_perm.key = ipcp->KEY; + p->msg_perm.id = msgid; + p->msg_perm.mode = ipcp->mode; +@@ -463,11 +444,12 @@ int ipc_msg_get_info(int id, struct msg_data **msgds) + p->next = xcalloc(1, sizeof(struct msg_data)); + p = p->next; + p->next = NULL; +- i++; + } else +- return 1; ++ break; + } + ++ if (i == 0) ++ free(*msgds); + return i; + } + +@@ -508,10 +490,10 @@ void ipc_print_perms(FILE *f, struct ipc_stat *is) + fprintf(f, " %-10u\n", is->gid); + } + +-void ipc_print_size(int unit, char *msg, size_t size, const char *end, ++void ipc_print_size(int unit, char *msg, uint64_t size, const char *end, + int width) + { +- char format[16]; ++ char format[32]; + + if (!msg) + /* NULL */ ; +@@ -527,11 +509,11 @@ void ipc_print_size(int unit, char *msg, size_t size, const char *end, + switch (unit) { + case IPC_UNIT_DEFAULT: + case IPC_UNIT_BYTES: +- sprintf(format, "%%%dzu", width); ++ sprintf(format, "%%%dju", width); + printf(format, size); + break; + case IPC_UNIT_KB: +- sprintf(format, "%%%dzu", width); ++ sprintf(format, "%%%dju", width); + printf(format, size / 1024); + break; + case IPC_UNIT_HUMAN: +diff --git a/sys-utils/ipcutils.h b/sys-utils/ipcutils.h +index d2e5972..444065a 100644 +--- a/sys-utils/ipcutils.h ++++ b/sys-utils/ipcutils.h +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + /* + * SHM_DEST and SHM_LOCKED are defined in kernel headers, but inside +@@ -34,11 +35,11 @@ + # define SHM_INFO 14 + struct shm_info { + int used_ids; +- ulong shm_tot; /* total allocated shm */ +- ulong shm_rss; /* total resident shm */ +- ulong shm_swp; /* total swapped shm */ +- ulong swap_attempts; +- ulong swap_successes; ++ unsigned long shm_tot; /* total allocated shm */ ++ unsigned long shm_rss; /* total resident shm */ ++ unsigned long shm_swp; /* total swapped shm */ ++ unsigned long swap_attempts; ++ unsigned long swap_successes; + }; + #endif + +@@ -118,7 +119,7 @@ struct ipc_stat { + }; + + extern void ipc_print_perms(FILE *f, struct ipc_stat *is); +-extern void ipc_print_size(int unit, char *msg, size_t size, const char *end, int width); ++extern void ipc_print_size(int unit, char *msg, uint64_t size, const char *end, int width); + + /* See 'struct shmid_kernel' in kernel sources + */ +diff --git a/sys-utils/lsipc.1 b/sys-utils/lsipc.1 +new file mode 100644 +index 0000000..98449cb +--- /dev/null ++++ b/sys-utils/lsipc.1 +@@ -0,0 +1,133 @@ ++.\" Copyright 2015 Ondrej Oprala(ooprala@redhat.com) ++.\" May be distributed under the GNU General Public License ++.TH LSIPC "1" "November 2015" "util-linux" "User Commands" ++.SH NAME ++lsipc \- show information on IPC facilities currently employed in the system ++.SH SYNOPSIS ++.B lsipc ++[options] ++.SH DESCRIPTION ++.B lsipc ++shows information on the inter-process communication facilities ++for which the calling process has read access. ++.SH OPTIONS ++.TP ++\fB\-i\fR, \fB\-\-id\fR \fIid\fR ++Show full details on just the one resource element identified by ++.IR id . ++This option needs to be combined with one of the three resource options: ++.BR \-m , ++.BR \-q " or" ++.BR \-s . ++It is possible to override the default output format for this option with the ++\fB\-\-list\fR, \fB\-\-raw\fR, \fB\-\-json\fR or \fB\-\-export\fR option. ++.TP ++\fB\-g\fR, \fB\-\-global\fR ++Show system-wide usage and limits of IPC resources. ++This option may be combined with one of the three resource options: ++.BR \-m , ++.BR \-q " or" ++.BR \-s . ++The default is to show information about all resources. ++.TP ++\fB\-h\fR, \fB\-\-help\fR ++Display help text and exit. ++.TP ++\fB\-V\fR, \fB\-\-version\fR ++Display version information and exit. ++.SS "Resource options" ++.TP ++\fB\-m\fR, \fB\-\-shmems\fR ++Write information about active shared memory segments. ++.TP ++\fB\-q\fR, \fB\-\-queues\fR ++Write information about active message queues. ++.TP ++\fB\-s\fR, \fB\-\-semaphores\fR ++Write information about active semaphore sets. ++.SS "Output formatting" ++.TP ++\fB\-c\fR, \fB\-\-creator\fR ++Show creator and owner. ++.TP ++\fB\-e\fR, \fB\-\-export\fR ++Output data in the format of NAME=VALUE. ++.TP ++\fB\-l\fR, \fB\-\-list\fR ++Use the list output format. This is the default, except when \fB\-\-id\fR ++is used. ++.TP ++\fB\-n\fR, \fB\-\-newline\fR ++Display each piece of information on a separate line. ++.TP ++\fB\-\-noheadings\fR ++Do not print a header line. ++.TP ++\fB\-\-notruncate\fR ++Don't truncate output. ++.TP ++\fB\-o\fR, \fB\-\-output \fIlist\fP ++Specify which output columns to print. Use ++.B \-\-help ++to get a list of all supported columns. ++.TP ++\fB\-p\fR, \fB\-\-pid\fR ++Show PIDs of creator and last operator. ++.TP ++\fB\-r\fR, \fB\-\-raw\fR ++Raw output (no columnation). ++.TP ++\fB\-t\fR, \fB\-\-time\fR ++Write time information. The time of the last control operation that changed ++the access permissions for all facilities, the time of the last ++.I msgsnd() ++and ++.I msgrcv() ++operations on message queues, the time of the last ++.I shmat() ++and ++.I shmdt() ++operations on shared memory, and the time of the last ++.I semop() ++operation on semaphores. ++.TP ++\fB\-\-time\-format\fR \fItype\fP ++Display dates in short, full or iso format. The default is short, this time ++format is designed to be space efficient and human readable. ++ ++.SH EXIT STATUS ++.TP ++0 ++if OK, ++.TP ++1 ++if incorrect arguments specified, ++.TP ++2 ++if a serious error occurs. ++.SH SEE ALSO ++.BR ipcrm (1), ++.BR ipcmk (1), ++.BR msgrcv (2), ++.BR msgsnd (2), ++.BR semget (2), ++.BR semop (2), ++.BR shmat (2), ++.BR shmdt (2), ++.BR shmget (2) ++.SH HISTORY ++The \fBlsipc\fP utility is inspired by the \fBipcs\fP utility. ++.SH AUTHORS ++.MT ooprala@redhat.com ++Ondrej Oprala ++.ME ++.br ++.MT kzak@redhat.com ++Karel Zak ++.ME ++ ++.SH AVAILABILITY ++The lsipc command is part of the util-linux package and is available from ++.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ ++Linux Kernel Archive ++.UE . +diff --git a/sys-utils/lsipc.c b/sys-utils/lsipc.c +new file mode 100644 +index 0000000..0be9d91 +--- /dev/null ++++ b/sys-utils/lsipc.c +@@ -0,0 +1,1316 @@ ++/* ++ * 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 "c.h" ++#include "nls.h" ++#include "closestream.h" ++#include "strutils.h" ++#include "optutils.h" ++#include "xalloc.h" ++#include "procutils.h" ++#include "ipcutils.h" ++ ++/* ++ * time modes ++ * */ ++enum { ++ TIME_INVALID = 0, ++ TIME_SHORT, ++ TIME_FULL, ++ TIME_ISO ++}; ++ ++/* ++ * IDs ++ */ ++enum { ++ /* generic */ ++ COLDESC_IDX_GEN_FIRST = 0, ++ COL_KEY = COLDESC_IDX_GEN_FIRST, ++ COL_ID, ++ COL_OWNER, ++ COL_PERMS, ++ COL_CUID, ++ COL_CUSER, ++ COL_CGID, ++ COL_CGROUP, ++ COL_UID, ++ COL_USER, ++ COL_GID, ++ COL_GROUP, ++ COL_CTIME, ++ COLDESC_IDX_GEN_LAST = COL_CTIME, ++ ++ /* msgq-specific */ ++ COLDESC_IDX_MSG_FIRST, ++ COL_USEDBYTES = COLDESC_IDX_MSG_FIRST, ++ COL_MSGS, ++ COL_SEND, ++ COL_RECV, ++ COL_LSPID, ++ COL_LRPID, ++ COLDESC_IDX_MSG_LAST = COL_LRPID, ++ ++ /* shm-specific */ ++ COLDESC_IDX_SHM_FIRST, ++ COL_SIZE = COLDESC_IDX_SHM_FIRST, ++ COL_NATTCH, ++ COL_STATUS, ++ COL_ATTACH, ++ COL_DETACH, ++ COL_COMMAND, ++ COL_CPID, ++ COL_LPID, ++ COLDESC_IDX_SHM_LAST = COL_LPID, ++ ++ /* sem-specific */ ++ COLDESC_IDX_SEM_FIRST, ++ COL_NSEMS = COLDESC_IDX_SEM_FIRST, ++ COL_OTIME, ++ COLDESC_IDX_SEM_LAST = COL_OTIME, ++ ++ /* summary (--global) */ ++ COLDESC_IDX_SUM_FIRST, ++ COL_RESOURCE = COLDESC_IDX_SUM_FIRST, ++ COL_DESC, ++ COL_LIMIT, ++ COL_USED, ++ COL_USEPERC, ++ COLDESC_IDX_SUM_LAST = COL_USEPERC ++}; ++ ++/* not all columns apply to all options, so we specify a legal range for each */ ++static size_t LOWER, UPPER; ++ ++/* ++ * output modes ++ */ ++enum { ++ OUT_EXPORT = 1, ++ OUT_NEWLINE, ++ OUT_RAW, ++ OUT_PRETTY, ++ OUT_LIST ++}; ++ ++struct lsipc_control { ++ int outmode; ++ unsigned int noheadings : 1, /* don't print header line */ ++ notrunc : 1, /* don't truncate columns */ ++ 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's username or UID"), N_("Owner"), 1, SCOLS_FL_RIGHT}, ++ [COL_PERMS] = { "PERMS", N_("Permissions"), N_("Permissions"), 1, SCOLS_FL_RIGHT}, ++ [COL_CUID] = { "CUID", N_("Creator UID"), N_("Creator UID"), 1, SCOLS_FL_RIGHT}, ++ [COL_CUSER] = { "CUSER", N_("Creator user"), N_("Creator user"), 1 }, ++ [COL_CGID] = { "CGID", N_("Creator GID"), N_("Creator GID"), 1, SCOLS_FL_RIGHT}, ++ [COL_CGROUP] = { "CGROUP", N_("Creator group"), N_("Creator group"), 1 }, ++ [COL_UID] = { "UID", N_("User ID"), N_("UID"), 1, SCOLS_FL_RIGHT}, ++ [COL_USER] = { "USER", N_("User name"), N_("User name"), 1}, ++ [COL_GID] = { "GID", N_("Group ID"), N_("GID"), 1, SCOLS_FL_RIGHT}, ++ [COL_GROUP] = { "GROUP", N_("Group name"), N_("Group name"), 1}, ++ [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_COMMAND] = { "COMMAND", N_("Creator command line"), N_("Creator command"), 0, SCOLS_FL_TRUNC}, ++ [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, SCOLS_FL_RIGHT }, ++ [COL_USEPERC] = { "USE%", N_("Currently use percentage"), N_("Use"), 1, SCOLS_FL_RIGHT }, ++ [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 size_t 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 char *get_username(struct passwd **pw, uid_t id) ++{ ++ if (!*pw || (*pw)->pw_uid != id) ++ *pw = getpwuid(id); ++ ++ return *pw ? xstrdup((*pw)->pw_name) : NULL; ++} ++ ++static char *get_groupname(struct group **gr, gid_t id) ++{ ++ if (!*gr || (*gr)->gr_gid != id) ++ *gr = getgrgid(id); ++ ++ return *gr ? xstrdup((*gr)->gr_name) : NULL; ++} ++ ++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); ++ fprintf(out, _(" %s [options]\n"), program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Show information on IPC facilities.\n"), 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(_(" -g, --global info about system-wide usage (may be used with -m, -q and -s)\n"), out); ++ fputs(_(" -i, --id print details on resource identified by \n"), out); ++ ++ fputs(USAGE_OPTIONS, 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(_(" -n, --newline display each piece of information on a new line\n"), out); ++ fputs(_(" -l, --list force list output format (for example with --id)\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(USAGE_SEPARATOR, out); ++ fputs(USAGE_HELP, out); ++ fputs(USAGE_VERSION, out); ++ ++ fprintf(out, _("\nGeneric columns:\n")); ++ for (i = COLDESC_IDX_GEN_FIRST; i <= COLDESC_IDX_GEN_LAST; i++) ++ fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); ++ ++ fprintf(out, _("\nShared-memory columns (--shmems):\n")); ++ for (i = COLDESC_IDX_SHM_FIRST; i <= COLDESC_IDX_SHM_LAST; i++) ++ fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); ++ ++ fprintf(out, _("\nMessage-queue columns (--queues):\n")); ++ for (i = COLDESC_IDX_MSG_FIRST; i <= COLDESC_IDX_MSG_LAST; i++) ++ fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); ++ ++ fprintf(out, _("\nSemaphore columns (--semaphores):\n")); ++ for (i = COLDESC_IDX_SEM_FIRST; i <= COLDESC_IDX_SEM_LAST; i++) ++ fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); ++ ++ fprintf(out, _("\nSummary columns (--global):\n")); ++ for (i = COLDESC_IDX_SUM_FIRST; i <= COLDESC_IDX_SUM_LAST; i++) ++ 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 *new_table(struct lsipc_control *ctl) ++{ ++ struct libscols_table *table = scols_new_table(); ++ ++ if (!table) ++ errx(EXIT_FAILURE, _("failed to initialize output table")); ++ if (ctl->noheadings) ++ scols_table_enable_noheadings(table, 1); ++ ++ switch(ctl->outmode) { ++ case OUT_NEWLINE: ++ scols_table_set_column_separator(table, "\n"); ++ /* fallthrough */ ++ case OUT_EXPORT: ++ scols_table_enable_export(table, 1); ++ break; ++ case OUT_RAW: ++ scols_table_enable_raw(table, 1); ++ break; ++ case OUT_PRETTY: ++ scols_table_enable_noheadings(table, 1); ++ break; ++ default: ++ break; ++ } ++ return table; ++} ++ ++static struct libscols_table *setup_table(struct lsipc_control *ctl) ++{ ++ struct libscols_table *table = new_table(ctl); ++ size_t n; ++ ++ for (n = 0; n < ncolumns; n++) { ++ 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; ++ } ++ 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; ++ 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) { ++ struct libscols_table *subtab = scols_line_get_userdata(ln); ++ if (subtab) { ++ printf(_("Elements:\n\n")); ++ scols_print_table(subtab); ++ } ++ } ++ ++ scols_free_iter(itr); ++ return 0; ++ ++} ++ ++static int print_table(struct lsipc_control *ctl, struct libscols_table *tb) ++{ ++ if (ctl->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 global_set_data(struct libscols_table *tb, const char *resource, ++ const char *desc, uintmax_t used, uintmax_t limit, int usage) ++{ ++ struct libscols_line *ln; ++ size_t n; ++ ++ ln = scols_table_new_line(tb, NULL); ++ if (!ln) ++ err_oom(); ++ ++ for (n = 0; n < ncolumns; n++) { ++ int rc = 0; ++ char *arg = NULL; ++ ++ switch (columns[n]) { ++ case COL_RESOURCE: ++ rc = scols_line_set_data(ln, n, resource); ++ break; ++ case COL_DESC: ++ rc = scols_line_set_data(ln, n, desc); ++ break; ++ case COL_USED: ++ if (usage) { ++ xasprintf(&arg, "%ju", used); ++ rc = scols_line_refer_data(ln, n, arg); ++ } else ++ rc = scols_line_set_data(ln, n, "-"); ++ break; ++ case COL_USEPERC: ++ if (usage) { ++ xasprintf(&arg, "%2.2f%%", (double) used / limit * 100); ++ rc = scols_line_refer_data(ln, n, arg); ++ } else ++ rc = scols_line_set_data(ln, n, "-"); ++ break; ++ case COL_LIMIT: ++ xasprintf(&arg, "%ju", limit); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ } ++ ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ } ++} ++ ++static void setup_sem_elements_columns(struct libscols_table *tb) ++{ ++ if (!scols_table_new_column(tb, "SEMNUM", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++ if (!scols_table_new_column(tb, "VALUE", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++ if (!scols_table_new_column(tb, "NCOUNT", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++ if (!scols_table_new_column(tb, "ZCOUNT", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++ if (!scols_table_new_column(tb, "PID", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++ if (!scols_table_new_column(tb, "COMMAND", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++} ++ ++static void do_sem(int id, struct lsipc_control *ctl, struct libscols_table *tb) ++{ ++ struct libscols_line *ln; ++ struct passwd *pw = NULL, *cpw = NULL; ++ struct group *gr = NULL, *cgr = NULL; ++ struct sem_data *semds, *semdsp; ++ char *arg = NULL; ++ ++ 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) { ++ size_t n; ++ ln = scols_table_new_line(tb, NULL); ++ ++ for (n = 0; n < ncolumns; n++) { ++ int rc = 0; ++ switch (columns[n]) { ++ case COL_KEY: ++ xasprintf(&arg, "0x%08x",semdsp->sem_perm.key); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_ID: ++ xasprintf(&arg, "%d",semdsp->sem_perm.id); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_OWNER: ++ arg = get_username(&pw, semdsp->sem_perm.uid); ++ if (!arg) ++ xasprintf(&arg, "%u", semdsp->sem_perm.uid); ++ rc = scols_line_refer_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_refer_data(ln, n, arg); ++ break; ++ case COL_CUID: ++ xasprintf(&arg, "%u", semdsp->sem_perm.cuid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CUSER: ++ arg = get_username(&cpw, semdsp->sem_perm.cuid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGID: ++ xasprintf(&arg, "%u", semdsp->sem_perm.cgid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGROUP: ++ arg = get_groupname(&cgr, semdsp->sem_perm.cgid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_UID: ++ xasprintf(&arg, "%u", semdsp->sem_perm.uid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_USER: ++ arg = get_username(&pw, semdsp->sem_perm.uid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GID: ++ xasprintf(&arg, "%u", semdsp->sem_perm.gid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GROUP: ++ arg = get_groupname(&gr, semdsp->sem_perm.gid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CTIME: ++ if (semdsp->sem_ctime != 0) { ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)semdsp->sem_ctime)); ++ } ++ break; ++ case COL_NSEMS: ++ xasprintf(&arg, "%ju", semdsp->sem_nsems); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_OTIME: ++ if (semdsp->sem_otime != 0) { ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)semdsp->sem_otime)); ++ } ++ break; ++ } ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ arg = NULL; ++ } ++ ++ if (id > -1 && semds->sem_nsems) { ++ /* Create extra table with ID specific semaphore elements */ ++ struct libscols_table *sub = new_table(ctl); ++ size_t i; ++ int rc = 0; ++ ++ scols_table_enable_noheadings(sub, 0); ++ setup_sem_elements_columns(sub); ++ ++ for (i = 0; i < semds->sem_nsems; i++) { ++ struct sem_elem *e = &semds->elements[i]; ++ struct libscols_line *sln = scols_table_new_line(sub, NULL); ++ ++ /* SEMNUM */ ++ xasprintf(&arg, "%zu", i); ++ rc = scols_line_refer_data(sln, 0, arg); ++ if (rc) ++ break; ++ ++ /* VALUE */ ++ xasprintf(&arg, "%d", e->semval); ++ rc = scols_line_refer_data(sln, 1, arg); ++ if (rc) ++ break; ++ ++ /* NCOUNT */ ++ xasprintf(&arg, "%d", e->ncount); ++ rc = scols_line_refer_data(sln, 2, arg); ++ if (rc) ++ break; ++ ++ /* ZCOUNT */ ++ xasprintf(&arg, "%d", e->zcount); ++ rc = scols_line_refer_data(sln, 3, arg); ++ if (rc) ++ break; ++ ++ /* PID */ ++ xasprintf(&arg, "%d", e->pid); ++ rc = scols_line_refer_data(sln, 4, arg); ++ if (rc) ++ break; ++ ++ /* COMMAND */ ++ arg = proc_get_command(e->pid); ++ rc = scols_line_refer_data(sln, 5, arg); ++ if (rc) ++ break; ++ } ++ ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ ++ scols_line_set_userdata(ln, (void *)sub); ++ 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 nsems = 0, nsets = 0; ++ ++ ipc_sem_get_limits(&lim); ++ ++ if (ipc_sem_get_info(-1, &semds) > 0) { ++ for (semdsp = semds; semdsp->next != NULL; semdsp = semdsp->next) { ++ ++nsets; ++ nsems += semds->sem_nsems; ++ } ++ ipc_sem_free_info(semds); ++ } ++ ++ global_set_data(tb, "SEMMNI", _("Number of semaphore identifiers"), nsets, lim.semmni, 1); ++ global_set_data(tb, "SEMMNS", _("Total number of semaphores"), nsems, lim.semmns, 1); ++ global_set_data(tb, "SEMMSL", _("Max semaphores per semaphore set."), 0, lim.semmsl, 0); ++ global_set_data(tb, "SEMOPM", _("Max number of operations per semop(2)"), 0, lim.semopm, 0); ++ global_set_data(tb, "SEMVMX", _("Semaphore max value"), 0, lim.semvmx, 0); ++} ++ ++static void do_msg(int id, struct lsipc_control *ctl, struct libscols_table *tb) ++{ ++ struct libscols_line *ln; ++ struct passwd *pw = NULL; ++ struct group *gr = NULL; ++ struct msg_data *msgds, *msgdsp; ++ char *arg = NULL; ++ ++ 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) { ++ size_t n; ++ 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); ++ ++ for (n = 0; n < ncolumns; n++) { ++ int rc = 0; ++ ++ switch (columns[n]) { ++ case COL_KEY: ++ xasprintf(&arg, "0x%08x",msgdsp->msg_perm.key); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_ID: ++ xasprintf(&arg, "%d",msgdsp->msg_perm.id); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_OWNER: ++ arg = get_username(&pw, msgdsp->msg_perm.uid); ++ if (!arg) ++ xasprintf(&arg, "%u", msgdsp->msg_perm.uid); ++ rc = scols_line_refer_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_refer_data(ln, n, arg); ++ } ++ break; ++ case COL_CUID: ++ xasprintf(&arg, "%u", msgdsp->msg_perm.cuid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CUSER: ++ arg = get_username(&pw, msgdsp->msg_perm.cuid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGID: ++ xasprintf(&arg, "%u", msgdsp->msg_perm.cuid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGROUP: ++ arg = get_groupname(&gr, msgdsp->msg_perm.cgid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_UID: ++ xasprintf(&arg, "%u", msgdsp->msg_perm.uid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_USER: ++ arg = get_username(&pw, msgdsp->msg_perm.uid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GID: ++ xasprintf(&arg, "%u", msgdsp->msg_perm.gid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GROUP: ++ arg = get_groupname(&gr,msgdsp->msg_perm.gid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CTIME: ++ if (msgdsp->q_ctime != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)msgdsp->q_ctime)); ++ break; ++ case COL_USEDBYTES: ++ xasprintf(&arg, "%ju", msgdsp->q_cbytes); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_MSGS: ++ xasprintf(&arg, "%ju", msgdsp->q_qnum); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_SEND: ++ if (msgdsp->q_stime != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)msgdsp->q_stime)); ++ break; ++ case COL_RECV: ++ if (msgdsp->q_rtime != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)msgdsp->q_rtime)); ++ break; ++ case COL_LSPID: ++ xasprintf(&arg, "%u", msgdsp->q_lspid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_LRPID: ++ xasprintf(&arg, "%u", msgdsp->q_lrpid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ } ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ 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; ++ int msgqs = 0; ++ ++ ipc_msg_get_limits(&lim); ++ ++ /* count number of used queues */ ++ if (ipc_msg_get_info(-1, &msgds) > 0) { ++ for (msgdsp = msgds; msgdsp->next != NULL; msgdsp = msgdsp->next) ++ ++msgqs; ++ ipc_msg_free_info(msgds); ++ } ++ ++ global_set_data(tb, "MSGMNI", _("Number of message queues"), msgqs, lim.msgmni, 1); ++ global_set_data(tb, "MSGMAX", _("Max size of message (bytes)"), 0, lim.msgmax, 0); ++ global_set_data(tb, "MSGMNB", _("Default max size of queue (bytes)"), 0, lim.msgmnb, 0); ++} ++ ++ ++static void do_shm(int id, struct lsipc_control *ctl, struct libscols_table *tb) ++{ ++ struct libscols_line *ln; ++ struct passwd *pw = NULL; ++ struct group *gr = NULL; ++ struct shm_data *shmds, *shmdsp; ++ char *arg = NULL; ++ ++ 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) { ++ size_t n; ++ ln = scols_table_new_line(tb, NULL); ++ if (!ln) ++ err_oom(); ++ ++ for (n = 0; n < ncolumns; n++) { ++ int rc = 0; ++ ++ switch (columns[n]) { ++ case COL_KEY: ++ xasprintf(&arg, "0x%08x",shmdsp->shm_perm.key); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_ID: ++ xasprintf(&arg, "%d",shmdsp->shm_perm.id); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_OWNER: ++ arg = get_username(&pw, shmdsp->shm_perm.uid); ++ if (!arg) ++ xasprintf(&arg, "%u", shmdsp->shm_perm.uid); ++ rc = scols_line_refer_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_refer_data(ln, n, arg); ++ break; ++ case COL_CUID: ++ xasprintf(&arg, "%u", shmdsp->shm_perm.cuid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CUSER: ++ arg = get_username(&pw, shmdsp->shm_perm.cuid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGID: ++ xasprintf(&arg, "%u", shmdsp->shm_perm.cuid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGROUP: ++ arg = get_groupname(&gr, shmdsp->shm_perm.cgid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_UID: ++ xasprintf(&arg, "%u", shmdsp->shm_perm.uid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_USER: ++ arg = get_username(&pw, shmdsp->shm_perm.uid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GID: ++ xasprintf(&arg, "%u", shmdsp->shm_perm.gid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GROUP: ++ arg = get_groupname(&gr, shmdsp->shm_perm.gid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CTIME: ++ if (shmdsp->shm_ctim != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)shmdsp->shm_ctim)); ++ 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_refer_data(ln, n, arg); ++ break; ++ case COL_NATTCH: ++ xasprintf(&arg, "%ju", shmdsp->shm_nattch); ++ rc = scols_line_refer_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); ++#ifdef SHM_DEST ++ if (shmdsp->shm_perm.mode & SHM_DEST) { ++ offt += sprintf(arg, "%s", _("dest")); ++ comma++; ++ } ++#endif ++#ifdef SHM_LOCKED ++ if (shmdsp->shm_perm.mode & SHM_LOCKED) { ++ if (comma) ++ arg[offt++] = ','; ++ offt += sprintf(arg + offt, "%s", _("locked")); ++ } ++#endif ++#ifdef SHM_HUGETLB ++ if (shmdsp->shm_perm.mode & SHM_HUGETLB) { ++ if (comma) ++ arg[offt++] = ','; ++ offt += sprintf(arg + offt, "%s", _("hugetlb")); ++ } ++#endif ++#ifdef SHM_NORESERVE ++ if (shmdsp->shm_perm.mode & SHM_NORESERVE) { ++ if (comma) ++ arg[offt++] = ','; ++ sprintf(arg + offt, "%s", _("noreserve")); ++ } ++#endif ++ rc = scols_line_refer_data(ln, n, arg); ++ } ++ break; ++ case COL_ATTACH: ++ if (shmdsp->shm_atim != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)shmdsp->shm_atim)); ++ break; ++ case COL_DETACH: ++ if (shmdsp->shm_dtim != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)shmdsp->shm_dtim)); ++ break; ++ case COL_CPID: ++ xasprintf(&arg, "%u", shmdsp->shm_cprid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_LPID: ++ xasprintf(&arg, "%u", shmdsp->shm_lprid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_COMMAND: ++ arg = proc_get_command(shmdsp->shm_cprid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ } ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ 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; ++ uint64_t nsegs = 0, sum_segsz = 0; ++ struct ipc_limits lim; ++ ++ ipc_shm_get_limits(&lim); ++ ++ if (ipc_shm_get_info(-1, &shmds) > 0) { ++ for (shmdsp = shmds; shmdsp->next != NULL; shmdsp = shmdsp->next) { ++ ++nsegs; ++ sum_segsz += shmdsp->shm_segsz; ++ } ++ ipc_shm_free_info(shmds); ++ } ++ ++ global_set_data(tb, "SHMMNI", _("Shared memory segments"), nsegs, lim.shmmni, 1); ++ global_set_data(tb, "SHMALL", _("Shared memory pages"), sum_segsz / getpagesize(), lim.shmall, 1); ++ global_set_data(tb, "SHMMAX", _("Max size of shared memory segment (bytes)"), 0, lim.shmmax, 0); ++ global_set_data(tb, "SHMMIN", _("Min size of shared memory segment (bytes)"), 0, lim.shmmin, 0); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int opt, msg = 0, sem = 0, shm = 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 *outarg = NULL; ++ ++ /* long only options. */ ++ enum { ++ OPT_NOTRUNC = CHAR_MAX + 1, ++ OPT_NOHEAD, ++ OPT_TIME_FMT ++ }; ++ ++ static const struct option longopts[] = { ++ { "bytes", no_argument, 0, 'b' }, ++ { "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' }, ++ { "list", no_argument, 0, 'l' }, ++ { "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' }, ++ { "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', 'l', 'n', 'r' }, ++ { 'g', 'i' }, ++ { 'c', '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 = 0; ++ ++ scols_init_debug(0); ++ ++ while ((opt = getopt_long(argc, argv, "bceghi:lmno:PqrstuV", longopts, NULL)) != -1) { ++ ++ err_exclusive_options(opt, longopts, excl, excl_st); ++ ++ switch (opt) { ++ case 'b': ++ ctl->bytes = 1; ++ break; ++ case 'i': ++ id = strtos32_or_err(optarg, _("failed to parse IPC identifier")); ++ break; ++ case 'e': ++ ctl->outmode = OUT_EXPORT; ++ break; ++ case 'r': ++ ctl->outmode = OUT_RAW; ++ break; ++ case 'o': ++ outarg = optarg; ++ break; ++ case 'g': ++ global = 1; ++ break; ++ case 'q': ++ msg = 1; ++ add_column(columns, ncolumns++, COL_KEY); ++ add_column(columns, ncolumns++, COL_ID); ++ add_column(columns, ncolumns++, COL_PERMS); ++ add_column(columns, ncolumns++, COL_OWNER); ++ add_column(columns, ncolumns++, COL_USEDBYTES); ++ add_column(columns, ncolumns++, COL_MSGS); ++ add_column(columns, ncolumns++, COL_LSPID); ++ add_column(columns, ncolumns++, COL_LRPID); ++ LOWER = COLDESC_IDX_MSG_FIRST; ++ UPPER = COLDESC_IDX_MSG_LAST; ++ break; ++ case 'l': ++ ctl->outmode = OUT_LIST; ++ break; ++ case 'm': ++ shm = 1; ++ add_column(columns, ncolumns++, COL_KEY); ++ add_column(columns, ncolumns++, COL_ID); ++ add_column(columns, ncolumns++, COL_PERMS); ++ add_column(columns, ncolumns++, COL_OWNER); ++ add_column(columns, ncolumns++, COL_SIZE); ++ add_column(columns, ncolumns++, COL_NATTCH); ++ add_column(columns, ncolumns++, COL_STATUS); ++ add_column(columns, ncolumns++, COL_CTIME); ++ add_column(columns, ncolumns++, COL_CPID); ++ add_column(columns, ncolumns++, COL_LPID); ++ add_column(columns, ncolumns++, COL_COMMAND); ++ LOWER = COLDESC_IDX_SHM_FIRST; ++ UPPER = COLDESC_IDX_SHM_LAST; ++ break; ++ case 'n': ++ ctl->outmode = OUT_NEWLINE; ++ break; ++ case 'P': ++ ctl->numperms = 1; ++ break; ++ case 's': ++ sem = 1; ++ add_column(columns, ncolumns++, COL_KEY); ++ add_column(columns, ncolumns++, COL_ID); ++ add_column(columns, ncolumns++, COL_PERMS); ++ add_column(columns, ncolumns++, COL_OWNER); ++ add_column(columns, ncolumns++, COL_NSEMS); ++ LOWER = COLDESC_IDX_SEM_FIRST; ++ UPPER = COLDESC_IDX_SEM_LAST; ++ 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 't': ++ show_time = 1; ++ break; ++ case 'c': ++ show_creat = 1; ++ break; ++ case 'h': ++ usage(stdout); ++ case 'V': ++ printf(UTIL_LINUX_VERSION); ++ return EXIT_SUCCESS; ++ default: ++ usage(stderr); ++ } ++ } ++ ++ /* default is global */ ++ if (msg + shm + sem == 0) { ++ msg = shm = sem = global = 1; ++ if (show_time || show_creat || id != -1) ++ errx(EXIT_FAILURE, _("--global is mutually exclusive with --creator, --id and --time")); ++ } ++ if (global) { ++ add_column(columns, ncolumns++, COL_RESOURCE); ++ add_column(columns, ncolumns++, COL_DESC); ++ add_column(columns, ncolumns++, COL_LIMIT); ++ add_column(columns, ncolumns++, COL_USED); ++ add_column(columns, ncolumns++, COL_USEPERC); ++ LOWER = COLDESC_IDX_SUM_FIRST; ++ UPPER = COLDESC_IDX_SUM_LAST; ++ } ++ ++ /* default to pretty-print if --id specified */ ++ if (id != -1 && !ctl->outmode) ++ ctl->outmode = OUT_PRETTY; ++ ++ if (!ctl->time_mode) ++ ctl->time_mode = ctl->outmode == OUT_PRETTY ? TIME_FULL : TIME_SHORT; ++ ++ if (ctl->outmode == OUT_PRETTY && !(optarg || show_creat || show_time)) { ++ /* all columns for lsipc -- --id */ ++ for (ncolumns = 0, i = 0; i < ARRAY_SIZE(coldescs); i++) ++ columns[ncolumns++] = i; ++ } else { ++ 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 && show_time) { ++ add_column(columns, ncolumns++, COL_SEND); ++ add_column(columns, ncolumns++, COL_RECV); ++ add_column(columns, ncolumns++, COL_CTIME); ++ } ++ if (shm && show_time) { ++ /* keep "COMMAND" as last column */ ++ size_t cmd = columns[ncolumns - 1] == COL_COMMAND; ++ ++ if (cmd) ++ ncolumns--; ++ add_column(columns, ncolumns++, COL_ATTACH); ++ add_column(columns, ncolumns++, COL_DETACH); ++ if (cmd) ++ add_column(columns, ncolumns++, COL_COMMAND); ++ } ++ if (sem && show_time) { ++ add_column(columns, ncolumns++, COL_OTIME); ++ add_column(columns, ncolumns++, COL_CTIME); ++ } ++ } ++ ++ if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), ++ (int *) &ncolumns, column_name_to_id) < 0) ++ return EXIT_FAILURE; ++ ++ tb = setup_table(ctl); ++ if (!tb) ++ return EXIT_FAILURE; ++ ++ if (msg) { ++ if (global) ++ do_msg_global(tb); ++ else ++ do_msg(id, ctl, tb); ++ } ++ if (shm) { ++ if (global) ++ do_shm_global(tb); ++ else ++ do_shm(id, ctl, tb); ++ } ++ if (sem) { ++ if (global) ++ do_sem_global(tb); ++ else ++ do_sem(id, ctl, tb); ++ } ++ ++ print_table(ctl, tb); ++ ++ scols_unref_table(tb); ++ free(ctl); ++ ++ return EXIT_SUCCESS; ++} ++ +diff --git a/tests/functions.sh b/tests/functions.sh +index 123f6c3..b2d493c 100644 +--- a/tests/functions.sh ++++ b/tests/functions.sh +@@ -50,16 +50,25 @@ function ts_skip_nonroot { + } + + function ts_failed_subtest { ++ local msg="FAILED" ++ local ret=1 ++ if [ "$TS_KNOWN_FAIL" = "yes" ]; then ++ msg="KNOWN FAILED" ++ ret=0 ++ fi ++ + if [ x"$1" == x"" ]; then +- echo " FAILED ($TS_NS)" ++ echo " $msg ($TS_NS)" + else +- echo " FAILED ($1)" ++ echo " $msg ($1)" + fi ++ ++ return $ret + } + + function ts_failed { + ts_failed_subtest "$1" +- exit 1 ++ exit $? + } + + function ts_ok_subtest { +@@ -150,6 +159,7 @@ function ts_init_env { + TS_SUBDIR=$(dirname $TS_SCRIPT) + TS_TESTNAME=$(basename $TS_SCRIPT) + TS_COMPONENT=$(basename $TS_SUBDIR) ++ TS_KNOWN_FAIL="no" + + TS_NSUBTESTS=0 + TS_NSUBFAILED=0 +diff --git a/tests/ts/ipcs/limits2 b/tests/ts/ipcs/limits2 +index f99a354..63f834d 100755 +--- a/tests/ts/ipcs/limits2 ++++ b/tests/ts/ipcs/limits2 +@@ -16,15 +16,20 @@ + # GNU General Public License for more details. + # + +-TS_TOPDIR="$(dirname $0)/../.." ++TS_TOPDIR="${0%/*}/../.." + TS_DESC="basic limits" + + . $TS_TOPDIR/functions.sh + ts_init "$*" +-type bc >/dev/null 2>&1 || ts_skip "cannot find bc command" + + . $TS_SELF/functions.sh + ++# TODO https://github.com/karelzak/util-linux/issues/51 ++SHMALL=$(> $TS_OUTPUT + +-- +2.7.4