1/*
2 * Copyright (c) 1994 SigmaSoft, Th. Lockert <tholo@sigmasoft.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 *    derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
19 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/param.h>
32#include <sys/proc.h>
33#define _KERNEL
34#include <sys/sem.h>
35#include <sys/shm.h>
36#include <sys/msg.h>
37#undef _KERNEL
38
39#include <err.h>
40#include <fcntl.h>
41#include <grp.h>
42#include <kvm.h>
43#include <limits.h>
44#include <pwd.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <unistd.h>
49
50#include "ipc.h"
51
52char   *fmt_perm(u_short);
53void	cvt_time(time_t, char *);
54void	usage(void);
55uid_t	user2uid(char *username);
56
57void	print_kmsqtotal(struct msginfo msginfo);
58void	print_kmsqheader(int option);
59void	print_kmsqptr(int i, int option, struct msqid_kernel *kmsqptr);
60void	print_kshmtotal(struct shminfo shminfo);
61void	print_kshmheader(int option);
62void	print_kshmptr(int i, int option, struct shmid_kernel *kshmptr);
63void	print_ksemtotal(struct seminfo seminfo);
64void	print_ksemheader(int option);
65void	print_ksemptr(int i, int option, struct semid_kernel *ksemaptr);
66
67char   *
68fmt_perm(u_short mode)
69{
70	static char buffer[100];
71
72	buffer[0] = '-';
73	buffer[1] = '-';
74	buffer[2] = ((mode & 0400) ? 'r' : '-');
75	buffer[3] = ((mode & 0200) ? 'w' : '-');
76	buffer[4] = ((mode & 0100) ? 'a' : '-');
77	buffer[5] = ((mode & 0040) ? 'r' : '-');
78	buffer[6] = ((mode & 0020) ? 'w' : '-');
79	buffer[7] = ((mode & 0010) ? 'a' : '-');
80	buffer[8] = ((mode & 0004) ? 'r' : '-');
81	buffer[9] = ((mode & 0002) ? 'w' : '-');
82	buffer[10] = ((mode & 0001) ? 'a' : '-');
83	buffer[11] = '\0';
84	return (&buffer[0]);
85}
86
87void
88cvt_time(time_t t, char *buf)
89{
90	struct tm *tm;
91
92	if (t == 0) {
93		strcpy(buf, "no-entry");
94	} else {
95		tm = localtime(&t);
96		sprintf(buf, "%2d:%02d:%02d",
97			tm->tm_hour, tm->tm_min, tm->tm_sec);
98	}
99}
100
101#define BIGGEST		1
102#define CREATOR		2
103#define OUTSTANDING	4
104#define PID		8
105#define TIME		16
106
107int
108main(int argc, char *argv[])
109{
110	int     display = SHMINFO | MSGINFO | SEMINFO;
111	int     option = 0;
112	char   *core = NULL, *user = NULL, *namelist = NULL;
113	char	kvmoferr[_POSIX2_LINE_MAX];  /* Error buf for kvm_openfiles. */
114	int     i;
115	uid_t   uid = 0;
116
117	while ((i = getopt(argc, argv, "MmQqSsabC:cN:optTu:y")) != -1)
118		switch (i) {
119		case 'a':
120			option |= BIGGEST | CREATOR | OUTSTANDING | PID | TIME;
121			break;
122		case 'b':
123			option |= BIGGEST;
124			break;
125		case 'C':
126			core = optarg;
127			break;
128		case 'c':
129			option |= CREATOR;
130			break;
131		case 'M':
132			display = SHMTOTAL;
133			break;
134		case 'm':
135			display = SHMINFO;
136			break;
137		case 'N':
138			namelist = optarg;
139			break;
140		case 'o':
141			option |= OUTSTANDING;
142			break;
143		case 'p':
144			option |= PID;
145			break;
146		case 'Q':
147			display = MSGTOTAL;
148			break;
149		case 'q':
150			display = MSGINFO;
151			break;
152		case 'S':
153			display = SEMTOTAL;
154			break;
155		case 's':
156			display = SEMINFO;
157			break;
158		case 'T':
159			display = SHMTOTAL | MSGTOTAL | SEMTOTAL;
160			break;
161		case 't':
162			option |= TIME;
163			break;
164		case 'u':
165			user = optarg;
166			uid = user2uid(user);
167			break;
168		case 'y':
169			use_sysctl = 0;
170			break;
171		default:
172			usage();
173		}
174
175	/*
176	 * If paths to the exec file or core file were specified, we
177	 * aren't operating on the running kernel, so we can't use
178	 * sysctl.
179	 */
180	if (namelist != NULL || core != NULL)
181		use_sysctl = 0;
182
183	if (!use_sysctl) {
184		kd = kvm_openfiles(namelist, core, NULL, O_RDONLY, kvmoferr);
185		if (kd == NULL)
186			errx(1, "kvm_openfiles: %s", kvmoferr);
187		switch (kvm_nlist(kd, symbols)) {
188		case 0:
189			break;
190		case -1:
191			errx(1, "unable to read kernel symbol table");
192		default:
193			break;
194		}
195	}
196
197	kget(X_MSGINFO, &msginfo, sizeof(msginfo));
198	if ((display & (MSGINFO | MSGTOTAL))) {
199		if (display & MSGTOTAL)
200			print_kmsqtotal(msginfo);
201
202		if (display & MSGINFO) {
203			struct msqid_kernel *kxmsqids;
204			size_t kxmsqids_len;
205
206			kxmsqids_len =
207			    sizeof(struct msqid_kernel) * msginfo.msgmni;
208			kxmsqids = malloc(kxmsqids_len);
209			kget(X_MSQIDS, kxmsqids, kxmsqids_len);
210
211			print_kmsqheader(option);
212
213			for (i = 0; i < msginfo.msgmni; i += 1) {
214				if (kxmsqids[i].u.msg_qbytes != 0) {
215					if (user &&
216					    uid != kxmsqids[i].u.msg_perm.uid)
217						continue;
218
219					print_kmsqptr(i, option, &kxmsqids[i]);
220				}
221
222			}
223
224			printf("\n");
225		}
226	} else
227		if (display & (MSGINFO | MSGTOTAL)) {
228			fprintf(stderr,
229			    "SVID messages facility "
230			    "not configured in the system\n");
231		}
232
233	kget(X_SHMINFO, &shminfo, sizeof(shminfo));
234	if ((display & (SHMINFO | SHMTOTAL))) {
235
236		if (display & SHMTOTAL)
237			print_kshmtotal(shminfo);
238
239		if (display & SHMINFO) {
240			struct shmid_kernel *kxshmids;
241			size_t kxshmids_len;
242
243			kxshmids_len =
244			    sizeof(struct shmid_kernel) * shminfo.shmmni;
245			kxshmids = malloc(kxshmids_len);
246			kget(X_SHMSEGS, kxshmids, kxshmids_len);
247
248			print_kshmheader(option);
249
250			for (i = 0; i < shminfo.shmmni; i += 1) {
251				if (kxshmids[i].u.shm_perm.mode & 0x0800) {
252					if (user &&
253					    uid != kxshmids[i].u.shm_perm.uid)
254						continue;
255
256					print_kshmptr(i, option, &kxshmids[i]);
257				}
258			}
259			printf("\n");
260		}
261	} else
262		if (display & (SHMINFO | SHMTOTAL)) {
263			fprintf(stderr,
264			    "SVID shared memory facility "
265			    "not configured in the system\n");
266		}
267
268	kget(X_SEMINFO, &seminfo, sizeof(seminfo));
269	if ((display & (SEMINFO | SEMTOTAL))) {
270		struct semid_kernel *kxsema;
271		size_t kxsema_len;
272
273		if (display & SEMTOTAL)
274			print_ksemtotal(seminfo);
275
276		if (display & SEMINFO) {
277			kxsema_len =
278			    sizeof(struct semid_kernel) * seminfo.semmni;
279			kxsema = malloc(kxsema_len);
280			kget(X_SEMA, kxsema, kxsema_len);
281
282			print_ksemheader(option);
283
284			for (i = 0; i < seminfo.semmni; i += 1) {
285				if ((kxsema[i].u.sem_perm.mode & SEM_ALLOC)
286				    != 0) {
287					if (user &&
288					    uid != kxsema[i].u.sem_perm.uid)
289						continue;
290
291					print_ksemptr(i, option, &kxsema[i]);
292
293				}
294			}
295
296			printf("\n");
297		}
298	} else
299		if (display & (SEMINFO | SEMTOTAL)) {
300			fprintf(stderr,
301			    "SVID semaphores facility "
302			    "not configured in the system\n");
303		}
304
305	if (!use_sysctl)
306		kvm_close(kd);
307
308	exit(0);
309}
310
311void
312print_kmsqtotal(struct msginfo msginfo)
313{
314
315	printf("msginfo:\n");
316	printf("\tmsgmax: %12d\t(max characters in a message)\n",
317	    msginfo.msgmax);
318	printf("\tmsgmni: %12d\t(# of message queues)\n",
319	    msginfo.msgmni);
320	printf("\tmsgmnb: %12d\t(max characters in a message queue)\n",
321	    msginfo.msgmnb);
322	printf("\tmsgtql: %12d\t(max # of messages in system)\n",
323	    msginfo.msgtql);
324	printf("\tmsgssz: %12d\t(size of a message segment)\n",
325	    msginfo.msgssz);
326	printf("\tmsgseg: %12d\t(# of message segments in system)\n\n",
327	    msginfo.msgseg);
328}
329
330void print_kmsqheader(int option)
331{
332
333	printf("Message Queues:\n");
334	printf("T %12s %12s %-11s %-8s %-8s",
335	    "ID", "KEY", "MODE", "OWNER", "GROUP");
336	if (option & CREATOR)
337		printf(" %-8s %-8s", "CREATOR", "CGROUP");
338	if (option & OUTSTANDING)
339		printf(" %20s %20s", "CBYTES", "QNUM");
340	if (option & BIGGEST)
341		printf(" %20s", "QBYTES");
342	if (option & PID)
343		printf(" %12s %12s", "LSPID", "LRPID");
344	if (option & TIME)
345		printf(" %-8s %-8s %-8s", "STIME", "RTIME", "CTIME");
346	printf("\n");
347}
348
349void
350print_kmsqptr(int i, int option, struct msqid_kernel *kmsqptr)
351{
352	char    stime_buf[100], rtime_buf[100], ctime_buf[100];
353
354	cvt_time(kmsqptr->u.msg_stime, stime_buf);
355	cvt_time(kmsqptr->u.msg_rtime, rtime_buf);
356	cvt_time(kmsqptr->u.msg_ctime, ctime_buf);
357
358	printf("q %12d %12d %s %-8s %-8s",
359	    IXSEQ_TO_IPCID(i, kmsqptr->u.msg_perm),
360	    (int)kmsqptr->u.msg_perm.key,
361	    fmt_perm(kmsqptr->u.msg_perm.mode),
362	    user_from_uid(kmsqptr->u.msg_perm.uid, 0),
363	    group_from_gid(kmsqptr->u.msg_perm.gid, 0));
364
365	if (option & CREATOR)
366		printf(" %-8s %-8s",
367		    user_from_uid(kmsqptr->u.msg_perm.cuid, 0),
368		    group_from_gid(kmsqptr->u.msg_perm.cgid, 0));
369
370	if (option & OUTSTANDING)
371		printf(" %12lu %12lu",
372		    kmsqptr->u.msg_cbytes,
373		    kmsqptr->u.msg_qnum);
374
375	if (option & BIGGEST)
376		printf(" %20lu", kmsqptr->u.msg_qbytes);
377
378	if (option & PID)
379		printf(" %12d %12d",
380		    kmsqptr->u.msg_lspid,
381		    kmsqptr->u.msg_lrpid);
382
383	if (option & TIME)
384		printf(" %s %s %s",
385		    stime_buf,
386		    rtime_buf,
387		    ctime_buf);
388
389	printf("\n");
390}
391
392void
393print_kshmtotal(struct shminfo shminfo)
394{
395
396	printf("shminfo:\n");
397	printf("\tshmmax: %12lu\t(max shared memory segment size)\n",
398	    shminfo.shmmax);
399	printf("\tshmmin: %12lu\t(min shared memory segment size)\n",
400	    shminfo.shmmin);
401	printf("\tshmmni: %12lu\t(max number of shared memory identifiers)\n",
402	    shminfo.shmmni);
403	printf("\tshmseg: %12lu\t(max shared memory segments per process)\n",
404	    shminfo.shmseg);
405	printf("\tshmall: %12lu\t(max amount of shared memory in pages)\n\n",
406	    shminfo.shmall);
407}
408
409void
410print_kshmheader(int option)
411{
412
413	printf("Shared Memory:\n");
414	printf("T %12s %12s %-11s %-8s %-8s",
415	    "ID", "KEY", "MODE", "OWNER", "GROUP");
416	if (option & CREATOR)
417		printf(" %-8s %-8s", "CREATOR", "CGROUP");
418	if (option & OUTSTANDING)
419		printf(" %12s", "NATTCH");
420	if (option & BIGGEST)
421		printf(" %12s", "SEGSZ");
422	if (option & PID)
423		printf(" %12s %12s", "CPID", "LPID");
424	if (option & TIME)
425		printf(" %-8s %-8s %-8s", "ATIME", "DTIME", "CTIME");
426	printf("\n");
427}
428
429void
430print_kshmptr(int i, int option, struct shmid_kernel *kshmptr)
431{
432	char    atime_buf[100], dtime_buf[100], ctime_buf[100];
433
434	cvt_time(kshmptr->u.shm_atime, atime_buf);
435	cvt_time(kshmptr->u.shm_dtime, dtime_buf);
436	cvt_time(kshmptr->u.shm_ctime, ctime_buf);
437
438	printf("m %12d %12d %s %-8s %-8s",
439	    IXSEQ_TO_IPCID(i, kshmptr->u.shm_perm),
440	    (int)kshmptr->u.shm_perm.key,
441	    fmt_perm(kshmptr->u.shm_perm.mode),
442	    user_from_uid(kshmptr->u.shm_perm.uid, 0),
443	    group_from_gid(kshmptr->u.shm_perm.gid, 0));
444
445	if (option & CREATOR)
446		printf(" %-8s %-8s",
447		    user_from_uid(kshmptr->u.shm_perm.cuid, 0),
448		    group_from_gid(kshmptr->u.shm_perm.cgid, 0));
449
450	if (option & OUTSTANDING)
451		printf(" %12d",
452		    kshmptr->u.shm_nattch);
453
454	if (option & BIGGEST)
455		printf(" %12zu",
456		    kshmptr->u.shm_segsz);
457
458	if (option & PID)
459		printf(" %12d %12d",
460		    kshmptr->u.shm_cpid,
461		    kshmptr->u.shm_lpid);
462
463	if (option & TIME)
464		printf(" %s %s %s",
465		    atime_buf,
466		    dtime_buf,
467		    ctime_buf);
468
469	printf("\n");
470}
471
472void
473print_ksemtotal(struct seminfo seminfo)
474{
475
476	printf("seminfo:\n");
477	printf("\tsemmni: %12d\t(# of semaphore identifiers)\n",
478	    seminfo.semmni);
479	printf("\tsemmns: %12d\t(# of semaphores in system)\n",
480	    seminfo.semmns);
481	printf("\tsemmnu: %12d\t(# of undo structures in system)\n",
482	    seminfo.semmnu);
483	printf("\tsemmsl: %12d\t(max # of semaphores per id)\n",
484	    seminfo.semmsl);
485	printf("\tsemopm: %12d\t(max # of operations per semop call)\n",
486	    seminfo.semopm);
487	printf("\tsemume: %12d\t(max # of undo entries per process)\n",
488	    seminfo.semume);
489	printf("\tsemusz: %12d\t(size in bytes of undo structure)\n",
490	    seminfo.semusz);
491	printf("\tsemvmx: %12d\t(semaphore maximum value)\n",
492	    seminfo.semvmx);
493	printf("\tsemaem: %12d\t(adjust on exit max value)\n\n",
494	    seminfo.semaem);
495}
496
497void
498print_ksemheader(int option)
499{
500
501	printf("Semaphores:\n");
502	printf("T %12s %12s %-11s %-8s %-8s",
503	    "ID", "KEY", "MODE", "OWNER", "GROUP");
504	if (option & CREATOR)
505		printf(" %-8s %-8s", "CREATOR", "CGROUP");
506	if (option & BIGGEST)
507		printf(" %12s", "NSEMS");
508	if (option & TIME)
509		printf(" %-8s %-8s", "OTIME", "CTIME");
510	printf("\n");
511}
512
513void
514print_ksemptr(int i, int option, struct semid_kernel *ksemaptr)
515{
516	char    ctime_buf[100], otime_buf[100];
517
518	cvt_time(ksemaptr->u.sem_otime, otime_buf);
519	cvt_time(ksemaptr->u.sem_ctime, ctime_buf);
520
521	printf("s %12d %12d %s %-8s %-8s",
522	    IXSEQ_TO_IPCID(i, ksemaptr->u.sem_perm),
523	    (int)ksemaptr->u.sem_perm.key,
524	    fmt_perm(ksemaptr->u.sem_perm.mode),
525	    user_from_uid(ksemaptr->u.sem_perm.uid, 0),
526	    group_from_gid(ksemaptr->u.sem_perm.gid, 0));
527
528	if (option & CREATOR)
529		printf(" %-8s %-8s",
530		    user_from_uid(ksemaptr->u.sem_perm.cuid, 0),
531		    group_from_gid(ksemaptr->u.sem_perm.cgid, 0));
532
533	if (option & BIGGEST)
534		printf(" %12d",
535		    ksemaptr->u.sem_nsems);
536
537	if (option & TIME)
538		printf(" %s %s",
539		    otime_buf,
540		    ctime_buf);
541
542	printf("\n");
543}
544
545uid_t
546user2uid(char *username)
547{
548	struct passwd *pwd;
549	uid_t uid;
550	char *r;
551
552	uid = strtoul(username, &r, 0);
553	if (!*r && r != username)
554		return (uid);
555	if ((pwd = getpwnam(username)) == NULL)
556		errx(1, "getpwnam failed: No such user");
557	endpwent();
558	return (pwd->pw_uid);
559}
560
561void
562usage(void)
563{
564
565	fprintf(stderr,
566	    "usage: "
567	    "ipcs [-abcmopqstyMQST] [-C corefile] [-N namelist] [-u user]\n");
568	exit(1);
569}
570