xref: /illumos-gate/usr/src/cmd/ipcrm/ipcrm.c (revision afab0816ecb604f0099a09ad8ee398f0d7b77b1c)
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 
60 static char opts[] = "z:q:m:s:Q:M:S:";	/* allowable options for getopt */
61 extern char	*optarg;	/* arg pointer for getopt */
62 extern int	optind;		/* option index for getopt */
63 
64 static zoneid_t zoneid;
65 static int zflg;
66 
67 static int *idlist, nids;
68 
69 static void
70 oops(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 */
91 static key_t
92 getkey(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  */
111 static uint_t
112 getids(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 
127 err:
128 	perror("ipcrm");
129 	exit(1);
130 	/* NOTREACHED */
131 }
132 
133 static int
134 msggetid(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 
160 static int
161 msggetkey(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 
192 static int
193 semgetid(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 
225 static int
226 semgetkey(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 
265 static int
266 shmgetid(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 
292 static int
293 shmgetkey(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 */
327 static zoneid_t
328 getzone(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 
340 int
341 main(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