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