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