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