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
oops(char * thing,char * arg)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
getkey(char * kp)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
getids(int (* idsfunc)(int *,uint_t,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
msggetid(char * arg)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
msggetkey(char * kp)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
semgetid(char * arg)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
semgetkey(char * kp)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
shmgetid(char * arg)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
shmgetkey(char * kp)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
getzone(char * arg)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
main(int argc,char ** argv)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