Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 28 Nov 2007 00:23:48 GMT
From:      Callum Gibson <callumgibson@optusnet.com.au>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   bin/118292: Add support to remove all msg/shm/sem ids with ipcrm
Message-ID:  <200711280023.lAS0NmNc020711@www.freebsd.org>
Resent-Message-ID: <200711280030.lAS0U1mZ037597@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         118292
>Category:       bin
>Synopsis:       Add support to remove all msg/shm/sem ids with ipcrm
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Wed Nov 28 00:30:01 UTC 2007
>Closed-Date:
>Last-Modified:
>Originator:     Callum Gibson
>Release:        6.2-STABLE
>Organization:
>Environment:
FreeBSD omma 6.2-STABLE FreeBSD 6.2-STABLE #4: Wed Aug 22 14:52:20 EST 2007     root@omma:/usr/obj/usr/src/sys/OMMA  i386
>Description:
I've observed that linux apps running under the linuxulator have a habit of leaving behind shared memory segments which are unused, but which eventually cause the system to run out of free segments and these apps will stop working. ipcrm(1) currently only allows removal of unused message queues, shared memory segments and semaphores on an individual basis, or those having a matching (non-zero) key. However it would often be convenient to just do a complete cleanup of everything, usually as root.

The attached patch allows removal of all message queues, shared memory segments or semaphores by specifying an id of -1 (ala kill(2)). The code to lookup ids was taken from ipcs.
>How-To-Repeat:

>Fix:
See attached patch.

Patch attached with submission follows:

--- usr.bin/ipcrm/Makefile.orig	2007-11-26 11:16:27.000000000 +1100
+++ usr.bin/ipcrm/Makefile	2007-11-26 12:02:24.000000000 +1100
@@ -1,5 +1,7 @@
 # $FreeBSD: src/usr.bin/ipcrm/Makefile,v 1.6 2002/02/08 22:31:40 markm Exp $
 
 PROG=	ipcrm
+DPADD=  ${LIBKVM}
+LDADD=  -lkvm
 
 .include <bsd.prog.mk>
--- usr.bin/ipcrm/ipcrm.c.orig	2002-09-05 09:29:02.000000000 +1000
+++ usr.bin/ipcrm/ipcrm.c	2007-11-26 13:32:33.000000000 +1100
@@ -32,23 +32,107 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD: src/usr.bin/ipcrm/ipcrm.c,v 1.11 2002/09/04 23:29:02 dwmalone Exp $");
 
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/cdefs.h>
+#define _KERNEL
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#undef _KERNEL
 #include <ctype.h>
 #include <err.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <sys/types.h>
-#include <sys/ipc.h>
-#include <sys/msg.h>
-#include <sys/sem.h>
-#include <sys/shm.h>
+#include <assert.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <kvm.h>
 
 #define IPC_TO_STR(x) (x == 'Q' ? "msq" : (x == 'M' ? "shm" : "sem"))
 #define IPC_TO_STRING(x) (x == 'Q' ? "message queue" : \
-	(x == 'M' ? "shared memory segment" : "semaphore"))
+			  (x == 'M' ? "shared memory segment" : "semaphore"))
 
-int signaled;
+/* Copied from ipcs/ipcs.c */
+/* SysCtlGatherStruct structure. */
+struct scgs_vector {
+	const char *sysctl;
+	off_t offset;
+	size_t size;
+};
+
+static struct semid_kernel	*sema;
+static struct seminfo	seminfo;
+static struct msginfo	msginfo;
+static struct msqid_kernel	*msqids;
+static struct shminfo	shminfo;
+static struct shmid_kernel	*shmsegs;
+
+void	kget(int idx, void *addr, size_t size);
+void	sysctlgatherstruct(void *addr, size_t size, struct scgs_vector *vec);
+
+static struct nlist symbols[] = {
+	{"sema"},
+#define X_SEMA		0
+	{"seminfo"},
+#define X_SEMINFO	1
+	{"msginfo"},
+#define X_MSGINFO	2
+	{"msqids"},
+#define X_MSQIDS	3
+	{"shminfo"},
+#define X_SHMINFO	4
+	{"shmsegs"},
+#define X_SHMSEGS	5
+	{NULL}
+};
+
+#define	SHMINFO_XVEC				\
+	X(shmmax, sizeof(int))			\
+	X(shmmin, sizeof(int))			\
+	X(shmmni, sizeof(int))			\
+	X(shmseg, sizeof(int))			\
+	X(shmall, sizeof(int))
+
+#define	SEMINFO_XVEC				\
+	X(semmap, sizeof(int))			\
+	X(semmni, sizeof(int))			\
+	X(semmns, sizeof(int))			\
+	X(semmnu, sizeof(int))			\
+	X(semmsl, sizeof(int))			\
+	X(semopm, sizeof(int))			\
+	X(semume, sizeof(int))			\
+	X(semusz, sizeof(int))			\
+	X(semvmx, sizeof(int))			\
+	X(semaem, sizeof(int))
+
+#define	MSGINFO_XVEC				\
+	X(msgmax, sizeof(int))			\
+	X(msgmni, sizeof(int))			\
+	X(msgmnb, sizeof(int))			\
+	X(msgtql, sizeof(int))			\
+	X(msgssz, sizeof(int))			\
+	X(msgseg, sizeof(int))
+
+#define	X(a, b)	{ "kern.ipc." #a, __offsetof(TYPEC, a), (b) },
+#define	TYPEC	struct shminfo
+struct scgs_vector shminfo_scgsv[] = { SHMINFO_XVEC { NULL } };
+#undef	TYPEC
+#define	TYPEC	struct seminfo
+struct scgs_vector seminfo_scgsv[] = { SEMINFO_XVEC { NULL } };
+#undef	TYPEC
+#define	TYPEC	struct msginfo
+struct scgs_vector msginfo_scgsv[] = { MSGINFO_XVEC { NULL } };
+#undef	TYPEC
+#undef	X
+
+static int errflg;
+static int signaled;
+static int use_sysctl = 1;
 
 void usage(void);
 int msgrm(key_t, int);
@@ -56,7 +140,8 @@
 int semrm(key_t, int);
 void not_configured(int);
 
-void usage(void)
+void
+usage(void)
 {
 	fprintf(stderr, "%s\n%s\n",
 		"usage: ipcrm [-q msqid] [-m shmid] [-s semid]",
@@ -64,109 +149,302 @@
 	exit(1);
 }
 
-int msgrm(key_t key, int id)
+int
+msgrm(key_t key, int id)
+{
+	if (key) {
+		id = msgget(key, 0);
+		if (id == -1)
+			return -1;
+	}
+	if (id == -1) {
+		struct msqid_kernel *kxmsqids;
+		size_t kxmsqids_len;
+		int num;
+
+		kget(X_MSGINFO, &msginfo, sizeof(msginfo));
+		kxmsqids_len = sizeof(struct msqid_kernel) * msginfo.msgmni;
+		kxmsqids = malloc(kxmsqids_len);
+		kget(X_MSQIDS, kxmsqids, kxmsqids_len);
+		num = msginfo.msgmni;
+		while (num-- && !signaled)
+			if (kxmsqids[num].u.msg_qbytes != 0) {
+				id = IXSEQ_TO_IPCID(num, kxmsqids[num].u.msg_perm);
+				if (msgctl(id, IPC_RMID, NULL) < 0) {
+					warn("msqid(%d): ", id);
+					errflg++;
+				}
+			}
+		return signaled ? -1 : 0;	/* errors maybe handled above */
+	}
+	return msgctl(id, IPC_RMID, NULL);
+}
+
+int
+shmrm(key_t key, int id)
+{
+	if (key) {
+		id = shmget(key, 0, 0);
+		if (id == -1)
+			return -1;
+	}
+	if (id == -1) {
+		struct shmid_kernel *kxshmids;
+		size_t kxshmids_len;
+		int num;
+
+		kget(X_SHMINFO, &shminfo, sizeof(shminfo));
+		kxshmids_len = sizeof(struct shmid_kernel) * shminfo.shmmni;
+		kxshmids = malloc(kxshmids_len);
+		kget(X_SHMSEGS, kxshmids, kxshmids_len);
+		num = shminfo.shmmni;
+		while (num-- && !signaled)
+			if (kxshmids[num].u.shm_perm.mode & 0x0800) {
+				id = IXSEQ_TO_IPCID(num, kxshmids[num].u.shm_perm);
+				if (shmctl(id, IPC_RMID, NULL) < 0) {
+					warn("shmid(%d): ", id);
+					errflg++;
+				}
+			}
+		return signaled ? -1 : 0;	/* errors maybe handled above */
+	}
+	return shmctl(id, IPC_RMID, NULL);
+}
+
+int
+semrm(key_t key, int id)
 {
-    if (key) {
-	id = msgget(key, 0);
-	if (id == -1)
-	    return -1;
-    }
-    return msgctl(id, IPC_RMID, NULL);
-}
-
-int shmrm(key_t key, int id)
-{
-    if (key) {
-	id = shmget(key, 0, 0);
-	if (id == -1)
-	    return -1;
-    }
-    return shmctl(id, IPC_RMID, NULL);
-}
-
-int semrm(key_t key, int id)
-{
-    union semun arg;
-
-    if (key) {
-	id = semget(key, 0, 0);
-	if (id == -1)
-	    return -1;
-    }
-    return semctl(id, 0, IPC_RMID, arg);
-}
-
-void not_configured(int signo __unused)
-{
-    signaled++;
-}
-
-int main(int argc, char *argv[])
-{
-    int c, result, errflg, target_id;
-    key_t target_key;
-
-    errflg = 0;
-    signal(SIGSYS, not_configured);
-    while ((c = getopt(argc, argv, ":q:m:s:Q:M:S:")) != -1) {
-
-	signaled = 0;
-	switch (c) {
-	case 'q':
-	case 'm':
-	case 's':
-	    target_id = atoi(optarg);
-	    if (c == 'q')
-		result = msgrm(0, target_id);
-	    else if (c == 'm')
-		result = shmrm(0, target_id);
-	    else
-		result = semrm(0, target_id);
-	    if (result < 0) {
-		errflg++;
-		if (!signaled)
-		    warn("%sid(%d): ", IPC_TO_STR(toupper(c)), target_id);
-		else
-		    warnx("%ss are not configured in the running kernel",
-			  IPC_TO_STRING(toupper(c)));
-	    }
-	    break;
-	case 'Q':
-	case 'M':
-	case 'S':
-	    target_key = atol(optarg);
-	    if (target_key == IPC_PRIVATE) {
-		warnx("can't remove private %ss", IPC_TO_STRING(c));
-		continue;
-	    }
-	    if (c == 'Q')
-		result = msgrm(target_key, 0);
-	    else if (c == 'M')
-		result = shmrm(target_key, 0);
-	    else
-		result = semrm(target_key, 0);
-	    if (result < 0) {
-		errflg++;
-		if (!signaled)
-		    warn("%ss(%ld): ", IPC_TO_STR(c), target_key);
-		else
-		    warnx("%ss are not configured in the running kernel",
-			  IPC_TO_STRING(c));
-	    }
-	    break;
-	case ':':
-	    fprintf(stderr, "option -%c requires an argument\n", optopt);
-	    usage();
-	case '?':
-	    fprintf(stderr, "unrecognized option: -%c\n", optopt);
-	    usage();
-	}
-    }
-
-    if (optind != argc) {
-	    fprintf(stderr, "unknown argument: %s\n", argv[optind]);
-	    usage();
-    }
-    exit(errflg);
+	union semun arg;
+
+	if (key) {
+		id = semget(key, 0, 0);
+		if (id == -1)
+			return -1;
+	}
+	if (id == -1) {
+		struct semid_kernel *kxsema;
+		size_t kxsema_len;
+		int num;
+
+		kget(X_SEMINFO, &seminfo, sizeof(seminfo));
+		kxsema_len = sizeof(struct semid_kernel) * seminfo.semmni;
+		kxsema = malloc(kxsema_len);
+		kget(X_SEMA, kxsema, kxsema_len);
+		num = seminfo.semmni;
+		while (num-- && !signaled)
+			if ((kxsema[num].u.sem_perm.mode & SEM_ALLOC) != 0) {
+				id = IXSEQ_TO_IPCID(num, kxsema[num].u.sem_perm);
+				if (semctl(id, IPC_RMID, NULL) < 0) {
+					warn("semid(%d): ", id);
+					errflg++;
+				}
+			}
+		return signaled ? -1 : 0;	/* errors maybe handled above */
+	}
+	return semctl(id, 0, IPC_RMID, arg);
+}
+
+void
+not_configured(int signo __unused)
+{
+	signaled++;
+}
+
+int
+main(int argc, char *argv[])
+{
+	int c, result, target_id;
+	key_t target_key;
+
+	errflg = 0;
+	signal(SIGSYS, not_configured);
+	while ((c = getopt(argc, argv, ":q:m:s:Q:M:S:y")) != -1) {
+
+		signaled = 0;
+		switch (c) {
+		case 'q':
+		case 'm':
+		case 's':
+			target_id = atoi(optarg);
+			if (c == 'q')
+				result = msgrm(0, target_id);
+			else if (c == 'm')
+				result = shmrm(0, target_id);
+			else
+				result = semrm(0, target_id);
+			if (result < 0) {
+				errflg++;
+				if (!signaled)
+					warn("%sid(%d): ", IPC_TO_STR(toupper(c)), target_id);
+				else
+					warnx("%ss are not configured in the running kernel",
+					      IPC_TO_STRING(toupper(c)));
+			}
+			break;
+		case 'Q':
+		case 'M':
+		case 'S':
+			target_key = atol(optarg);
+			if (target_key == IPC_PRIVATE) {
+				warnx("can't remove private %ss", IPC_TO_STRING(c));
+				continue;
+			}
+			if (c == 'Q')
+				result = msgrm(target_key, 0);
+			else if (c == 'M')
+				result = shmrm(target_key, 0);
+			else
+				result = semrm(target_key, 0);
+			if (result < 0) {
+				errflg++;
+				if (!signaled)
+					warn("%ss(%ld): ", IPC_TO_STR(c), target_key);
+				else
+					warnx("%ss are not configured in the running kernel",
+					      IPC_TO_STRING(c));
+			}
+			break;
+		case 'y':
+			use_sysctl = 0;
+			break;
+		case ':':
+			fprintf(stderr, "option -%c requires an argument\n", optopt);
+			usage();
+		case '?':
+			fprintf(stderr, "unrecognized option: -%c\n", optopt);
+			usage();
+		}
+	}
+
+	if (optind != argc) {
+		fprintf(stderr, "unknown argument: %s\n", argv[optind]);
+		usage();
+	}
+	exit(errflg);
+}
+
+/* The remainder is from ipcs/ipcs.c */
+void
+sysctlgatherstruct(void *addr, size_t size, struct scgs_vector *vecarr)
+{
+	struct scgs_vector *xp;
+	size_t tsiz;
+	int rv;
+
+	for (xp = vecarr; xp->sysctl != NULL; xp++) {
+		assert(xp->offset <= size);
+		tsiz = xp->size;
+		rv = sysctlbyname(xp->sysctl, (char *)addr + xp->offset,
+				  &tsiz, NULL, 0);
+		if (rv == -1)
+			err(1, "sysctlbyname: %s", xp->sysctl);
+		if (tsiz != xp->size)
+			errx(1, "%s size mismatch (expected %d, got %d)",
+			     xp->sysctl, xp->size, tsiz);
+	}
+}
+
+void
+kget(int idx, void *addr, size_t size)
+{
+	char *symn;			/* symbol name */
+	size_t tsiz;
+	int rv;
+	unsigned long kaddr;
+	const char *sym2sysctl[] = {	/* symbol to sysctl name table */
+		"kern.ipc.sema",
+		"kern.ipc.seminfo",
+		"kern.ipc.msginfo",
+		"kern.ipc.msqids",
+		"kern.ipc.shminfo",
+		"kern.ipc.shmsegs" };
+
+	assert((unsigned)idx <= sizeof(sym2sysctl) / sizeof(*sym2sysctl));
+	if (!use_sysctl) {
+		kvm_t *kd;
+		char	kvmoferr[_POSIX2_LINE_MAX];  /* Error buf for kvm_openfiles. */
+		char   *core = NULL, *namelist = NULL;
+
+		kd = kvm_openfiles(namelist, core, NULL, O_RDONLY, kvmoferr);
+		if (kd == NULL)
+			errx(1, "kvm_openfiles: %s", kvmoferr);
+		switch (kvm_nlist(kd, symbols)) {
+		case 0:
+			break;
+		case -1:
+			errx(1, "unable to read kernel symbol table");
+		default:
+#ifdef notdef		/* they'll be told more civilly later */
+			warnx("nlist failed");
+			for (i = 0; symbols[i].n_name != NULL; i++)
+				if (symbols[i].n_value == 0)
+					warnx("symbol %s not found",
+					      symbols[i].n_name);
+#endif
+			break;
+		}
+		symn = symbols[idx].n_name;
+		if (*symn == '_')
+			symn++;
+		if (symbols[idx].n_type == 0 || symbols[idx].n_value == 0)
+			errx(1, "symbol %s undefined", symn);
+		/*
+		 * For some symbols, the value we retrieve is
+		 * actually a pointer; since we want the actual value,
+		 * we have to manually dereference it.
+		 */
+		switch (idx) {
+		case X_MSQIDS:
+			tsiz = sizeof(msqids);
+			rv = kvm_read(kd, symbols[idx].n_value,
+				      &msqids, tsiz);
+			kaddr = (u_long)msqids;
+			break;
+		case X_SHMSEGS:
+			tsiz = sizeof(shmsegs);
+			rv = kvm_read(kd, symbols[idx].n_value,
+				      &shmsegs, tsiz);
+			kaddr = (u_long)shmsegs;
+			break;
+		case X_SEMA:
+			tsiz = sizeof(sema);
+			rv = kvm_read(kd, symbols[idx].n_value,
+				      &sema, tsiz);
+			kaddr = (u_long)sema;
+			break;
+		default:
+			rv = tsiz = 0;
+			kaddr = symbols[idx].n_value;
+			break;
+		}
+		if ((unsigned)rv != tsiz)
+			errx(1, "%s: %s", symn, kvm_geterr(kd));
+		if ((unsigned)kvm_read(kd, kaddr, addr, size) != size)
+			errx(1, "%s: %s", symn, kvm_geterr(kd));
+		kvm_close(kd);
+	} else {
+		switch (idx) {
+		case X_SHMINFO:
+			sysctlgatherstruct(addr, size, shminfo_scgsv);
+			break;
+		case X_SEMINFO:
+			sysctlgatherstruct(addr, size, seminfo_scgsv);
+			break;
+		case X_MSGINFO:
+			sysctlgatherstruct(addr, size, msginfo_scgsv);
+			break;
+		default:
+			tsiz = size;
+			rv = sysctlbyname(sym2sysctl[idx], addr, &tsiz,
+					  NULL, 0);
+			if (rv == -1)
+				err(1, "sysctlbyname: %s", sym2sysctl[idx]);
+			if (tsiz != size)
+				errx(1, "%s size mismatch "
+				     "(expected %d, got %d)",
+				     sym2sysctl[idx], size, tsiz);
+			break;
+		}
+	}
 }
 
--- usr.bin/ipcrm/ipcrm.1.orig	2004-07-03 08:22:25.000000000 +1000
+++ usr.bin/ipcrm/ipcrm.1	2007-11-26 13:07:19.000000000 +1100
@@ -52,16 +52,17 @@
 .It Fl q Ar msqid
 Remove the message queue associated with the id
 .Ar msqid
-from the system.
+from the system. If msqid is -1 then attempt to remove all message queues.
 .It Fl m Ar shmid
 Mark the shared memory segment associated with id
 .Ar shmid
 for removal.
 This marked segment will be destroyed after the last detach.
+If shmid is -1 then attempt to remove all shared memory segments.
 .It Fl s Ar semid
 Remove the semaphore set associated with id
 .Ar semid
-from the system.
+from the system. If semid is -1 then attempt to remove all semaphores.
 .It Fl Q Ar msgkey
 Remove the message queue associated with key
 .Ar msgkey
@@ -75,6 +76,19 @@
 Remove the semaphore set associated with key
 .Ar semkey
 from the system.
+.It Fl y
+Use the
+.Xr kvm 3
+interface instead of the
+.Xr sysctl 3
+interface to extract the required information.
+If
+.Nm
+is to operate on the running system,
+using
+.Xr kvm 3
+will require read privileges to
+.Pa /dev/kmem .
 .El
 .Pp
 The identifiers and keys associated with these System V IPC objects can be


>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200711280023.lAS0NmNc020711>