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>