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