1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
26/*	  All Rights Reserved  	*/
27
28
29
30/*
31 * ipcrm - IPC remove
32 *
33 * Remove specified message queues,
34 * semaphore sets and shared memory ids.
35 */
36
37#include <sys/types.h>
38#include <sys/ipc.h>
39#include <sys/msg.h>
40#include <sys/sem.h>
41#include <sys/shm.h>
42#include <errno.h>
43#include <sys/ipc_impl.h>
44#include <zone.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <signal.h>
48#include <locale.h>
49
50#define	NULL_MSG	((struct msqid_ds *)NULL)
51#define	NULL_SEM	((struct semid_ds *)NULL)
52#define	NULL_SHM	((struct shmid_ds *)NULL)
53
54#define	USAGE	"usage: ipcrm [-z zone] [ [-q msqid] [-m shmid] " \
55"[-s semid]\n\t [-Q msgkey] [-M shmkey] [-S semkey] ... ]\n"
56
57#define	IPC_KEYMATCH(perm, zoneid, key) \
58	((perm).ipcx_key == (key) && (perm).ipcx_zoneid == (zoneid))
59
60static char opts[] = "z:q:m:s:Q:M:S:";	/* allowable options for getopt */
61extern char	*optarg;	/* arg pointer for getopt */
62extern int	optind;		/* option index for getopt */
63
64static zoneid_t zoneid;
65static int zflg;
66
67static int *idlist, nids;
68
69static void
70oops(char *thing, char *arg)
71{
72	char *e;
73
74	switch (errno) {
75	case ENOENT:	/* key not found */
76	case EINVAL:	/* id not found */
77		e = "not found";
78		break;
79
80	case EPERM:
81		e = "permission denied";
82		break;
83	default:
84		e = "unknown error";
85	}
86
87	(void) fprintf(stderr, gettext("ipcrm: %s(%s): %s\n"), thing, arg, e);
88}
89
90/* convert string to numeric key */
91static key_t
92getkey(char *kp)
93{
94	key_t k;
95	char *tp;	/* will point to char that terminates strtol scan */
96
97	if ((k = (key_t)strtoul(kp, &tp, 0)) == IPC_PRIVATE || *tp != '\0') {
98		(void) fprintf(stderr, gettext("ipcrm: illegal key: %s\n"),
99		    kp);
100		return (0);
101	}
102	return (k);
103}
104
105/*
106 * Gets list of all IPC ids (of a particular type) visible in the
107 * caller's zone.  Returns number of ids retrieved.  On return, idlist
108 * is set to point to an array of ids at least as large as the number
109 * retrieved.
110 */
111static uint_t
112getids(int (*idsfunc)(int *, uint_t, uint_t *))
113{
114	uint_t n;
115
116	for (;;) {
117		if (idsfunc(idlist, nids, &n) != 0)
118			goto err;	/* should never happen */
119		if (n <= nids)
120			break;
121		idlist = realloc(idlist, (nids = n) * sizeof (int));
122		if (idlist == NULL)
123			goto err;
124	}
125	return (n);
126
127err:
128	perror("ipcrm");
129	exit(1);
130	/* NOTREACHED */
131}
132
133static int
134msggetid(char *arg)
135{
136	int id = atol(arg);
137	struct msqid_ds64 qds;
138
139	if (!zflg)
140		return (id);
141
142	if (msgctl64(id, IPC_STAT64, &qds) < 0) {
143		oops("msgctl", arg);
144		return (-1);
145	}
146	if (qds.msgx_perm.ipcx_zoneid != zoneid) {
147		/*
148		 * Not in right zone, pretend the call failed.
149		 * Message should be the same as that returned if
150		 * msggetid succeeds but the subsequent IPC_RMID fails
151		 * with EINVAL.
152		 */
153		errno = EINVAL;
154		oops("msgctl", arg);
155		return (-1);
156	}
157	return (id);
158}
159
160static int
161msggetkey(char *kp)
162{
163	key_t k;
164	int id, i;
165	uint_t n;
166	struct msqid_ds64 qds;
167
168	if ((k = getkey(kp)) == 0)
169		return (-1);
170
171	if (!zflg) {
172		/* lookup in local zone is simple */
173		if ((id = msgget(k, 0)) == -1)
174			oops("msgget", kp);
175		return (id);
176	}
177
178	n = getids(msgids);
179
180	/* search for right key and zone combination */
181	for (i = 0; i < n; i++) {
182		id = idlist[i];
183		if (msgctl64(id, IPC_STAT64, &qds) < 0)
184			continue;
185		if (IPC_KEYMATCH(qds.msgx_perm, zoneid, k))
186			return (id);	/* found it, no need to look further */
187	}
188	(void) fprintf(stderr, gettext("ipcrm: unknown key: %s\n"), kp);
189	return (-1);
190}
191
192static int
193semgetid(char *arg)
194{
195	int id = atol(arg);
196	struct semid_ds64 sds;
197	union semun {
198		int val;
199		struct semid_ds64 *buf;
200		ushort_t *array;
201	} semarg;
202
203	if (!zflg)
204		return (id);
205
206	semarg.buf = &sds;
207	if (semctl64(id, 0, IPC_STAT64, semarg) < 0) {
208		oops("semctl", arg);
209		return (-1);
210	}
211	if (sds.semx_perm.ipcx_zoneid != zoneid) {
212		/*
213		 * Not in right zone, pretend the call failed.
214		 * Message should be the same as that returned if
215		 * semgetid succeeds but the subsequent IPC_RMID fails
216		 * with EINVAL.
217		 */
218		errno = EINVAL;
219		oops("semctl", arg);
220		return (-1);
221	}
222	return (id);
223}
224
225static int
226semgetkey(char *kp)
227{
228	key_t k;
229	int id, i;
230	uint_t n;
231	struct semid_ds64 sds;
232	union semun {
233		int val;
234		struct semid_ds64 *buf;
235		ushort_t *array;
236	} semarg;
237
238	if ((k = getkey(kp)) == 0)
239		return (-1);
240
241	if (!zflg) {
242		/* lookup in local zone is simple */
243		if ((id = semget(k, 0, 0)) == -1)
244			oops("semget", kp);
245		return (id);
246	}
247
248	n = getids(semids);
249
250	semarg.buf = &sds;
251	/* search for right key and zone combination */
252	for (i = 0; i < n; i++) {
253		int id;
254		id = idlist[i];
255		if (semctl64(id, 0, IPC_STAT64, semarg) < 0)
256			continue;
257		if (IPC_KEYMATCH(sds.semx_perm, zoneid, k))
258			return (id);	/* found it, no need to look further */
259	}
260
261	(void) fprintf(stderr, gettext("ipcrm: unknown key: %s\n"), kp);
262	return (-1);
263}
264
265static int
266shmgetid(char *arg)
267{
268	int id = atol(arg);
269	struct shmid_ds64 mds;
270
271	if (!zflg)
272		return (id);
273
274	if (shmctl64(id, IPC_STAT64, &mds) < 0) {
275		oops("shmctl", arg);
276		return (-1);
277	}
278	if (mds.shmx_perm.ipcx_zoneid != zoneid) {
279		/*
280		 * Not in right zone, pretend the call failed.
281		 * Message should be the same as that returned if
282		 * shmgetid succeeds but the subsequent IPC_RMID fails
283		 * with EINVAL.
284		 */
285		errno = EINVAL;
286		oops("shmctl", arg);
287		return (-1);
288	}
289	return (id);
290}
291
292static int
293shmgetkey(char *kp)
294{
295	key_t k;
296	int id, i;
297	uint_t n;
298	struct shmid_ds64 mds;
299
300	if ((k = getkey(kp)) == 0)
301		return (-1);
302
303	if (!zflg) {
304		/* lookup in local zone is simple */
305		if ((id = shmget(k, 0, 0)) == -1)
306			oops("shmget", kp);
307		return (id);
308	}
309
310	n = getids(shmids);
311
312	/* search for right key and zone combination */
313	for (i = 0; i < n; i++) {
314		int id;
315		id = idlist[i];
316		if (shmctl64(id, IPC_STAT64, &mds) < 0)
317			continue;
318		if (IPC_KEYMATCH(mds.shmx_perm, zoneid, k))
319			return (id);	/* found it, no need to look further */
320	}
321	(void) fprintf(stderr, gettext("ipcrm: unknown key: %s\n"), kp);
322	return (-1);
323}
324
325
326/* convert string containing zone name or id to a numeric id */
327static zoneid_t
328getzone(char *arg)
329{
330	zoneid_t zoneid;
331
332	if (zone_get_id(arg, &zoneid) != 0) {
333		(void) fprintf(stderr, gettext("ipcrm: unknown zone: %s\n"),
334		    arg);
335		exit(1);
336	}
337	return (zoneid);
338}
339
340int
341main(int argc, char **argv)
342{
343	int	o;		/* option flag */
344	int	err;		/* error count */
345	int	ipc_id;		/* id to remove */
346
347	(void) setlocale(LC_ALL, "");
348	(void) textdomain(TEXT_DOMAIN);
349	/*
350	 * If one or more of the IPC modules is not
351	 * included in the kernel, the corresponding
352	 * system calls will incur SIGSYS.  Ignoring
353	 * that signal makes the system call appear
354	 * to fail with errno == EINVAL, which can be
355	 * interpreted appropriately in oops().
356	 */
357
358	(void) signal(SIGSYS, SIG_IGN);
359
360	/*
361	 * If no -z argument is specified, only objects in the current
362	 * zone can be removed with keys.
363	 */
364	zoneid = getzoneid();
365
366	/*
367	 * Go through the options.  The first pass looks only for -z
368	 * since this option can affect the processing of keys.  The
369	 * second pass looks for the other options and ignores -z.
370	 */
371	err = 0;
372	while ((o = getopt(argc, argv, opts)) != EOF) {
373		switch (o) {
374		case 'z':
375			zflg++;
376			zoneid = getzone(optarg);
377			break;
378
379		case 'q':	/* skip the rest of the flags */
380		case 'm':
381		case 's':
382		case 'Q':
383		case 'M':
384		case 'S':
385			break;
386
387		case '?':	/* anything else is an error */
388		default:
389			err++;
390			break;
391		}
392	}
393
394	if (err || (optind < argc)) {
395		(void) fprintf(stderr, gettext(USAGE));
396		return (err);
397	}
398
399	if (zflg > 1) {
400		(void) fprintf(stderr,
401		    gettext("multiple -z options not allowed\n"));
402		(void) fprintf(stderr, gettext(USAGE));
403		return (1);
404	}
405
406	optind = 1;	/* rewind for pass 2 */
407	while ((o = getopt(argc, argv, opts)) != EOF) {
408		switch (o) {
409		case 'z':	/* zone identifier */
410			break;
411
412		case 'q':	/* message queue */
413			if ((ipc_id = msggetid(optarg)) < 0) {
414				err++;
415			} else if (msgctl(ipc_id, IPC_RMID, NULL_MSG) == -1) {
416				oops("msgctl", optarg);
417				err++;
418			}
419			break;
420
421		case 'm':	/* shared memory */
422			if ((ipc_id = shmgetid(optarg)) < 0) {
423				err++;
424			} else if (shmctl(ipc_id, IPC_RMID, NULL_SHM) == -1) {
425				oops("shmctl", optarg);
426				err++;
427			}
428			break;
429
430		case 's':	/* semaphores */
431			if ((ipc_id = semgetid(optarg)) < 0) {
432				err++;
433			} else if (semctl(ipc_id, 0, IPC_RMID, NULL_SEM) ==
434			    -1) {
435				oops("semctl", optarg);
436				err++;
437			}
438			break;
439
440		case 'Q':	/* message queue (by key) */
441			if ((ipc_id = msggetkey(optarg)) == -1) {
442				err++;
443				break;
444			}
445			if (msgctl(ipc_id, IPC_RMID, NULL_MSG) == -1) {
446				oops("msgctl", optarg);
447				err++;
448			}
449			break;
450
451		case 'M':	/* shared memory (by key) */
452			if ((ipc_id = shmgetkey(optarg)) == -1) {
453				err++;
454				break;
455			}
456			if (shmctl(ipc_id, IPC_RMID, NULL_SHM) == -1) {
457				oops("shmctl", optarg);
458				err++;
459			}
460			break;
461
462		case 'S':	/* semaphores (by key) */
463			if ((ipc_id = semgetkey(optarg)) == -1) {
464				err++;
465				break;
466			}
467			if (semctl(ipc_id, 0, IPC_RMID, NULL_SEM) == -1) {
468				oops("semctl", optarg);
469				err++;
470			}
471			break;
472		}
473	}
474	return (err);
475}
476