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