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