xref: /illumos-gate/usr/src/cmd/ipcs/ipcs.c (revision 42b53e0fbc5c05289c3d334bb864b784fafe5ce4)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * ipcs - IPC status
31  *
32  * Examine and print certain things about
33  * message queues, semaphores and shared memory.
34  *
35  * IPC information is obtained via msgctl64, semctl64 and shmctl64.
36  * As of SunOS 5.8, the IPC identifiers are obtained from msgids(),
37  * semids(), and shmids() rather than reading them from /dev/kmem.
38  * This ensures that the information in each msgid_ds, semid_ds or
39  * shmid_ds data structure that we obtain is complete and consistent,
40  * and allows us not to be a setgid-sys isaexec process.
41  */
42 
43 #include <sys/types.h>
44 #include <sys/ipc.h>
45 #include <sys/ipc_impl.h>
46 #include <sys/msg.h>
47 #include <sys/sem.h>
48 #include <sys/shm.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <time.h>
52 #include <grp.h>
53 #include <pwd.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <ctype.h>
57 #include <unistd.h>
58 #include <locale.h>
59 #include <langinfo.h>
60 #include <string.h>
61 #include <limits.h>
62 #include <project.h>
63 #include <zone.h>
64 
65 #define	USAGE	\
66 	"usage: ipcs [-AabciJmopqstZ] [-D mtype] [-z zone]\n"
67 
68 static char chdr[] = "T         ID      KEY        MODE        OWNER    GROUP";
69 						/* common header format */
70 static char chdr2[] = "  CREATOR   CGROUP";	/* c option header format */
71 static char chdr3[] = "         PROJECT";	/* J option header format */
72 static char opts[] = "AabciJmopqstD:z:Z";	/* getopt options */
73 
74 static long	mtype;		/* -D: user-supplied message type */
75 static zoneid_t	zoneid;		/* -z: user-supplied zone id */
76 
77 static int	bflg,		/* biggest size: */
78 				/*	segsz on m; qbytes on q; nsems on s */
79 		cflg,		/* creator's login and group names */
80 		Dflg,		/* dump contents of message queues */
81 		iflg,		/* ISM attaches */
82 		Jflg,		/* dump project name */
83 		mflg,		/* shared memory status */
84 		oflg,		/* outstanding data: */
85 				/*	nattch on m; cbytes, qnum on q */
86 		pflg,		/* process id's: lrpid, lspid on q; */
87 				/*	cpid, lpid on m */
88 		qflg,		/* message queue status */
89 		sflg,		/* semaphore status */
90 		tflg,		/* times: atime, ctime, dtime on m;	*/
91 				/*	ctime, rtime, stime on q;	*/
92 				/*	ctime, otime on s */
93 		zflg,		/* show only objects from specified zone */
94 		Zflg,		/* display zone name */
95 		err;		/* option error count */
96 
97 static void hp(char, char *, struct ipc_perm64 *, int);
98 static void jp(struct ipc_perm64 *);
99 static void tp(ipc_time_t);
100 static void dumpmsgq(int);
101 static void dumpmsg(long, char *, size_t);
102 static zoneid_t getzone(char *);
103 static void printzone(zoneid_t);
104 
105 int
106 main(int argc, char *argv[])
107 {
108 	static	int	*ids;	/* array of IPC identifiers from *ids() */
109 	static	uint_t	nids;	/* number of entries in ids */
110 
111 	int	o;	/* option flag */
112 	int	id;	/* IPC identifier */
113 	int	i;
114 	uint_t	n;	/* table size */
115 	time_t	now;	/* date */
116 	char	tbuf[BUFSIZ];
117 	char	*dfmt;  /* date format pointer */
118 	char	*endptr;	/* terminator for strtol() */
119 
120 	(void) setlocale(LC_ALL, "");
121 	(void) textdomain(TEXT_DOMAIN);
122 
123 	(void) memset(tbuf, 0, sizeof (tbuf));
124 	dfmt = nl_langinfo(_DATE_FMT);
125 
126 	zoneid = getzoneid();	/* default zone id if -z and -Z not used */
127 
128 	/* Go through the options and set flags. */
129 	while ((o = getopt(argc, argv, opts)) != EOF) {
130 		switch (o) {
131 		case 'A':
132 			bflg = cflg = iflg = oflg = pflg = tflg = Jflg = 1;
133 			break;
134 		case 'a':
135 			bflg = cflg = oflg = pflg = tflg = 1;
136 			break;
137 		case 'b':
138 			bflg = 1;
139 			break;
140 		case 'c':
141 			cflg = 1;
142 			break;
143 		case 'D':
144 			mtype = strtol(optarg, &endptr, 0);
145 			if (endptr == optarg || *endptr != '\0') {
146 				(void) fprintf(stderr,
147 				    gettext("ipcs: invalid message type: %s\n"),
148 				    optarg);
149 				err++;
150 				break;
151 			}
152 			Dflg = 1;
153 			break;
154 		case 'i':
155 			iflg = 1;
156 			break;
157 		case 'J':
158 			Jflg = 1;
159 			break;
160 		case 'm':
161 			mflg = 1;
162 			break;
163 		case 'o':
164 			oflg = 1;
165 			break;
166 		case 'p':
167 			pflg = 1;
168 			break;
169 		case 'q':
170 			qflg = 1;
171 			break;
172 		case 's':
173 			sflg = 1;
174 			break;
175 		case 't':
176 			tflg = 1;
177 			break;
178 		case 'z':
179 			zflg = 1;
180 			zoneid = getzone(optarg);
181 			break;
182 		case 'Z':
183 			Zflg = 1;
184 			break;
185 		case '?':
186 			err++;
187 			break;
188 		}
189 	}
190 	if (err || (optind < argc)) {
191 		(void) fprintf(stderr, gettext(USAGE));
192 		exit(1);
193 	}
194 
195 	if ((mflg + qflg + sflg) == 0)
196 		mflg = qflg = sflg = 1;
197 
198 	now = time(NULL);
199 	(void) strftime(tbuf, sizeof (tbuf), dfmt, localtime(&now));
200 	(void) printf(gettext("IPC status from <running system> as of %s\n"),
201 	    tbuf);
202 
203 	/*
204 	 * Print Message Queue status report.
205 	 */
206 	if (qflg) {
207 		struct msqid_ds64 qds;
208 
209 		for (;;) {
210 			if (msgids(ids, nids, &n) != 0) {
211 				perror("msgids");
212 				exit(1);
213 			}
214 			if (n <= nids)
215 				break;
216 			ids = realloc(ids, (nids = n) * sizeof (int));
217 		}
218 
219 		(void) printf("%s%s%s%s%s%s%s%s\n", chdr,
220 		    cflg ? chdr2 : "",
221 		    oflg ? " CBYTES  QNUM" : "",
222 		    bflg ? " QBYTES" : "",
223 		    pflg ? " LSPID LRPID" : "",
224 		    tflg ? "   STIME    RTIME    CTIME " : "",
225 		    Jflg ? chdr3 : "",
226 		    Zflg ? "     ZONE" : "");
227 
228 		(void) printf(gettext("Message Queues:\n"));
229 
230 		for (i = 0; i < n; i++) {
231 			id = ids[i];
232 			if (msgctl64(id, IPC_STAT64, &qds) < 0)
233 				continue;
234 			/* ignore zone if -Z was used and -z wasn't */
235 			if ((zflg || !Zflg) &&
236 			    qds.msgx_perm.ipcx_zoneid != zoneid)
237 				continue;
238 			hp('q', "SRrw-rw-rw-", &qds.msgx_perm, id);
239 			if (oflg)
240 				(void) printf(" %6llu %5llu",
241 				    qds.msgx_cbytes, qds.msgx_qnum);
242 			if (bflg)
243 				(void) printf(" %6llu", qds.msgx_qbytes);
244 			if (pflg)
245 				(void) printf(" %5d %5d",
246 				    (int)qds.msgx_lspid, (int)qds.msgx_lrpid);
247 			if (tflg) {
248 				tp(qds.msgx_stime);
249 				tp(qds.msgx_rtime);
250 				tp(qds.msgx_ctime);
251 			}
252 			if (Jflg)
253 				jp(&qds.msgx_perm);
254 			if (Zflg)
255 				printzone(qds.msgx_perm.ipcx_zoneid);
256 			(void) printf("\n");
257 			if (Dflg)
258 				dumpmsgq(id);
259 		}
260 	}
261 
262 	/*
263 	 * Print Shared Memory status report.
264 	 */
265 	if (mflg) {
266 		struct shmid_ds64 mds;
267 
268 		for (;;) {
269 			if (shmids(ids, nids, &n) != 0) {
270 				perror("shmids");
271 				exit(1);
272 			}
273 			if (n <= nids)
274 				break;
275 			ids = realloc(ids, (nids = n) * sizeof (int));
276 		}
277 
278 		if (!qflg || oflg || bflg || pflg || tflg || iflg)
279 			(void) printf("%s%s%s%s%s%s%s%s%s\n", chdr,
280 			    cflg ? chdr2 : "",
281 			    oflg ? " NATTCH" : "",
282 			    bflg ? "      SEGSZ" : "",
283 			    pflg ? "  CPID  LPID" : "",
284 			    tflg ? "   ATIME    DTIME    CTIME " : "",
285 			    iflg ? " ISMATTCH" : "",
286 			    Jflg ? chdr3 : "",
287 			    Zflg ? "     ZONE" : "");
288 
289 		(void) printf(gettext("Shared Memory:\n"));
290 
291 		for (i = 0; i < n; i++) {
292 			id = ids[i];
293 			if (shmctl64(id, IPC_STAT64, &mds) < 0)
294 				continue;
295 			/* ignore zone if -Z was used and -z wasn't */
296 			if ((zflg || !Zflg) &&
297 			    mds.shmx_perm.ipcx_zoneid != zoneid)
298 				continue;
299 			hp('m', "--rw-rw-rw-", &mds.shmx_perm, id);
300 			if (oflg)
301 				(void) printf(" %6llu", mds.shmx_nattch);
302 			if (bflg)
303 				(void) printf(" %10llu", mds.shmx_segsz);
304 			if (pflg)
305 				(void) printf(" %5d %5d",
306 				    (int)mds.shmx_cpid, (int)mds.shmx_lpid);
307 			if (tflg) {
308 				tp(mds.shmx_atime);
309 				tp(mds.shmx_dtime);
310 				tp(mds.shmx_ctime);
311 			}
312 			if (iflg)
313 				(void) printf(" %8llu", mds.shmx_cnattch);
314 			if (Jflg)
315 				jp(&mds.shmx_perm);
316 			if (Zflg)
317 				printzone(mds.shmx_perm.ipcx_zoneid);
318 			(void) printf("\n");
319 		}
320 	}
321 
322 	/*
323 	 * Print Semaphore facility status.
324 	 */
325 	if (sflg) {
326 		struct semid_ds64 sds;
327 		union semun {
328 			int val;
329 			struct semid_ds64 *buf;
330 			ushort_t *array;
331 		} semarg;
332 		semarg.buf = &sds;
333 
334 		for (;;) {
335 			if (semids(ids, nids, &n) != 0) {
336 				perror("semids");
337 				exit(1);
338 			}
339 			if (n <= nids)
340 				break;
341 			ids = realloc(ids, (nids = n) * sizeof (int));
342 		}
343 
344 		if (bflg || tflg || (!qflg && !mflg))
345 			(void) printf("%s%s%s%s%s%s\n", chdr,
346 			    cflg ? chdr2 : "",
347 			    bflg ? " NSEMS" : "",
348 			    tflg ? "   OTIME    CTIME " : "",
349 			    Jflg ? chdr3 : "",
350 			    Zflg ? "     ZONE" : "");
351 
352 		(void) printf(gettext("Semaphores:\n"));
353 
354 		for (i = 0; i < n; i++) {
355 			id = ids[i];
356 			if (semctl64(id, 0, IPC_STAT64, semarg) < 0)
357 				continue;
358 			/* ignore zone if -Z was used and -z wasn't */
359 			if ((zflg || !Zflg) &&
360 			    sds.semx_perm.ipcx_zoneid != zoneid)
361 				continue;
362 			hp('s', "--ra-ra-ra-", &sds.semx_perm, id);
363 			if (bflg)
364 				(void) printf(" %5u", sds.semx_nsems);
365 			if (tflg) {
366 				tp(sds.semx_otime);
367 				tp(sds.semx_ctime);
368 			}
369 			if (Jflg)
370 				jp(&sds.semx_perm);
371 			if (Zflg)
372 				printzone(sds.semx_perm.ipcx_zoneid);
373 			(void) printf("\n");
374 		}
375 	}
376 
377 	return (0);
378 }
379 
380 /*
381  * hp - common header print
382  */
383 static void
384 hp(char type, char *modesp, struct ipc_perm64 *permp, int slot)
385 {
386 	int		i;	/* loop control */
387 	struct group	*g;	/* ptr to group group entry */
388 	struct passwd	*u;	/* ptr to user passwd entry */
389 	char		keyfield[16];
390 
391 	(void) snprintf(keyfield, sizeof (keyfield), "  0x%x", permp->ipcx_key);
392 	(void) printf("%c %10d %-13s", type, slot, keyfield);
393 
394 	for (i = 02000; i; modesp++, i >>= 1)
395 		(void) printf("%c", (permp->ipcx_mode & i) ? *modesp : '-');
396 	if ((u = getpwuid(permp->ipcx_uid)) == NULL)
397 		(void) printf("%9d", (int)permp->ipcx_uid);
398 	else
399 		(void) printf("%9.8s", u->pw_name);
400 	if ((g = getgrgid(permp->ipcx_gid)) == NULL)
401 		(void) printf("%9d", (int)permp->ipcx_gid);
402 	else
403 		(void) printf("%9.8s", g->gr_name);
404 
405 	if (cflg) {
406 		if ((u = getpwuid(permp->ipcx_cuid)) == NULL)
407 			(void) printf("%9d", (int)permp->ipcx_cuid);
408 		else
409 			(void) printf("%9.8s", u->pw_name);
410 		if ((g = getgrgid(permp->ipcx_cgid)) == NULL)
411 			(void) printf("%9d", (int)permp->ipcx_cgid);
412 		else
413 			(void) printf("%9.8s", g->gr_name);
414 	}
415 }
416 
417 /*
418  * jp - project header print
419  */
420 static void
421 jp(struct ipc_perm64 *permp)
422 {
423 	struct project	proj;
424 	char		buf[PROJECT_BUFSZ];
425 
426 	if ((getprojbyid(permp->ipcx_projid, &proj, buf,
427 	    PROJECT_BUFSZ)) == NULL)
428 		(void) printf("%16ld", permp->ipcx_projid);
429 	else
430 		(void) printf("%16.15s", proj.pj_name);
431 }
432 
433 /*
434  * tp - time entry printer
435  */
436 void
437 tp(ipc_time_t gmt64)
438 {
439 	struct tm *t;	/* ptr to converted time */
440 	time_t gmt = (time_t)gmt64;
441 
442 	if (gmt && gmt64 <= UINT_MAX) {
443 		t = localtime(&gmt);
444 		(void) printf(" %2d:%2.2d:%2.2d",
445 		    t->tm_hour, t->tm_min, t->tm_sec);
446 	} else {
447 		(void) printf("%9s", gettext(" no-entry"));
448 	}
449 }
450 
451 /* Round up to a sizeof (size_t) boundary */
452 #define	SZROUND(x)	(((x) + sizeof (size_t) - 1) & ~(sizeof (size_t) - 1))
453 
454 /*
455  * dumpmsgq - dump all messages on a message queue
456  */
457 void
458 dumpmsgq(int msqid)
459 {
460 	static struct msgsnap_head *buf = NULL;
461 	static size_t bufsize;
462 
463 	struct msgsnap_mhead *mhead;
464 	size_t i;
465 
466 	/* allocate the minimum required buffer size on first time through */
467 	if (buf == NULL)
468 		buf = malloc(bufsize = sizeof (struct msgsnap_head));
469 
470 	/*
471 	 * Fetch all messages specified by mtype from
472 	 * the queue while leaving the queue intact.
473 	 */
474 	for (;;) {
475 		if (msgsnap(msqid, buf, bufsize, mtype) != 0) {
476 			/*
477 			 * Don't complain; either the user does not have
478 			 * read permission on msqid or msqid was deleted.
479 			 */
480 			return;
481 		}
482 		if (bufsize >= buf->msgsnap_size) {
483 			/* we collected all of the messages */
484 			break;
485 		}
486 		/* The buffer is too small; allocate a bigger buffer */
487 		buf = realloc(buf, bufsize = buf->msgsnap_size);
488 	}
489 
490 	/*
491 	 * Process each message in the queue (there may be none).
492 	 * The first message header starts just after the buffer header.
493 	 */
494 	mhead = (struct msgsnap_mhead *)(buf + 1);
495 	for (i = 0; i < buf->msgsnap_nmsg; i++) {
496 		size_t mlen = mhead->msgsnap_mlen;
497 
498 		dumpmsg(mhead->msgsnap_mtype, (char *)(mhead + 1), mlen);
499 
500 		/* advance to next message header */
501 		/* LINTED alignment */
502 		mhead = (struct msgsnap_mhead *)
503 		    ((caddr_t)(mhead + 1) + SZROUND(mlen));
504 	}
505 }
506 
507 /*
508  * dumpmsg - dump one message from a message queue.
509  */
510 void
511 dumpmsg(long type, char *msg, size_t msgsize)
512 {
513 	size_t i, j, k;
514 	int c;
515 
516 	(void) printf(gettext("  message type %ld, size %lu\n"),
517 	    type, (ulong_t)msgsize);
518 
519 	for (i = 0; i < msgsize; i += 16) {
520 		/* first in hex */
521 		(void) printf(" %5ld:  ", (ulong_t)i);
522 		for (j = 0; j < 16; j++) {
523 			if ((k = i + j) < msgsize)
524 				(void) printf("%2.2x ", msg[k] & 0xff);
525 			else
526 				(void) printf("   ");
527 		}
528 		/* then in ascii */
529 		(void) printf("   ");
530 		for (j = 0; j < 16; j++) {
531 			if ((k = i + j) >= msgsize)
532 				break;
533 			c = msg[k] & 0xff;
534 			if (isascii(c) && isprint(c))
535 				(void) printf("%c", c);
536 			else
537 				(void) printf(".");
538 		}
539 		(void) printf("\n");
540 	}
541 }
542 
543 /* convert string containing zone name or id to a numeric id */
544 static zoneid_t
545 getzone(char *arg)
546 {
547 	zoneid_t zoneid;
548 
549 	if (zone_get_id(arg, &zoneid) != 0) {
550 		(void) fprintf(stderr,
551 		    gettext("ipcs: unknown zone: %s\n"), arg);
552 		exit(1);
553 	}
554 	return (zoneid);
555 }
556 
557 static void
558 printzone(zoneid_t id)
559 {
560 	char zone_name[ZONENAME_MAX];
561 
562 	if (getzonenamebyid(id, zone_name, sizeof (zone_name)) < 0)
563 		(void) printf("%9d", (int)id);
564 	else
565 		(void) printf("%9.8s", zone_name);
566 }
567