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: stable/10/usr.bin/ipcs/ipcs.c 344044 2019-02-12 07:03:37Z avos $");
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	}
227
228	kget(X_SHMINFO, &shminfo, sizeof(shminfo));
229	if (display & (SHMINFO | SHMTOTAL)) {
230
231		if (display & SHMTOTAL)
232			print_kshmtotal(shminfo);
233
234		if (display & SHMINFO) {
235			struct shmid_kernel *kxshmids;
236			size_t kxshmids_len;
237
238			kxshmids_len =
239			    sizeof(struct shmid_kernel) * shminfo.shmmni;
240			kxshmids = malloc(kxshmids_len);
241			kget(X_SHMSEGS, kxshmids, kxshmids_len);
242
243			print_kshmheader(option);
244
245			for (i = 0; i < shminfo.shmmni; i += 1) {
246				if (kxshmids[i].u.shm_perm.mode & 0x0800) {
247					if (user &&
248					    uid != kxshmids[i].u.shm_perm.uid)
249						continue;
250
251					print_kshmptr(i, option, &kxshmids[i]);
252				}
253			}
254			printf("\n");
255		}
256	}
257
258	kget(X_SEMINFO, &seminfo, sizeof(seminfo));
259	if (display & (SEMINFO | SEMTOTAL)) {
260		struct semid_kernel *kxsema;
261		size_t kxsema_len;
262
263		if (display & SEMTOTAL)
264			print_ksemtotal(seminfo);
265
266		if (display & SEMINFO) {
267			kxsema_len =
268			    sizeof(struct semid_kernel) * seminfo.semmni;
269			kxsema = malloc(kxsema_len);
270			kget(X_SEMA, kxsema, kxsema_len);
271
272			print_ksemheader(option);
273
274			for (i = 0; i < seminfo.semmni; i += 1) {
275				if ((kxsema[i].u.sem_perm.mode & SEM_ALLOC)
276				    != 0) {
277					if (user &&
278					    uid != kxsema[i].u.sem_perm.uid)
279						continue;
280
281					print_ksemptr(i, option, &kxsema[i]);
282
283				}
284			}
285
286			printf("\n");
287		}
288	}
289
290	if (!use_sysctl)
291		kvm_close(kd);
292
293	exit(0);
294}
295
296void
297print_kmsqtotal(struct msginfo msginfo)
298{
299
300	printf("msginfo:\n");
301	printf("\tmsgmax: %12d\t(max characters in a message)\n",
302	    msginfo.msgmax);
303	printf("\tmsgmni: %12d\t(# of message queues)\n",
304	    msginfo.msgmni);
305	printf("\tmsgmnb: %12d\t(max characters in a message queue)\n",
306	    msginfo.msgmnb);
307	printf("\tmsgtql: %12d\t(max # of messages in system)\n",
308	    msginfo.msgtql);
309	printf("\tmsgssz: %12d\t(size of a message segment)\n",
310	    msginfo.msgssz);
311	printf("\tmsgseg: %12d\t(# of message segments in system)\n\n",
312	    msginfo.msgseg);
313}
314
315void print_kmsqheader(int option)
316{
317
318	printf("Message Queues:\n");
319	printf("T %12s %12s %-11s %-8s %-8s",
320	    "ID", "KEY", "MODE", "OWNER", "GROUP");
321	if (option & CREATOR)
322		printf(" %-8s %-8s", "CREATOR", "CGROUP");
323	if (option & OUTSTANDING)
324		printf(" %20s %20s", "CBYTES", "QNUM");
325	if (option & BIGGEST)
326		printf(" %20s", "QBYTES");
327	if (option & PID)
328		printf(" %12s %12s", "LSPID", "LRPID");
329	if (option & TIME)
330		printf(" %-8s %-8s %-8s", "STIME", "RTIME", "CTIME");
331	printf("\n");
332}
333
334void
335print_kmsqptr(int i, int option, struct msqid_kernel *kmsqptr)
336{
337	char    stime_buf[100], rtime_buf[100], ctime_buf[100];
338
339	cvt_time(kmsqptr->u.msg_stime, stime_buf);
340	cvt_time(kmsqptr->u.msg_rtime, rtime_buf);
341	cvt_time(kmsqptr->u.msg_ctime, ctime_buf);
342
343	printf("q %12d %12d %s %-8s %-8s",
344	    IXSEQ_TO_IPCID(i, kmsqptr->u.msg_perm),
345	    (int)kmsqptr->u.msg_perm.key,
346	    fmt_perm(kmsqptr->u.msg_perm.mode),
347	    user_from_uid(kmsqptr->u.msg_perm.uid, 0),
348	    group_from_gid(kmsqptr->u.msg_perm.gid, 0));
349
350	if (option & CREATOR)
351		printf(" %-8s %-8s",
352		    user_from_uid(kmsqptr->u.msg_perm.cuid, 0),
353		    group_from_gid(kmsqptr->u.msg_perm.cgid, 0));
354
355	if (option & OUTSTANDING)
356		printf(" %12lu %12lu",
357		    kmsqptr->u.msg_cbytes,
358		    kmsqptr->u.msg_qnum);
359
360	if (option & BIGGEST)
361		printf(" %20lu", kmsqptr->u.msg_qbytes);
362
363	if (option & PID)
364		printf(" %12d %12d",
365		    kmsqptr->u.msg_lspid,
366		    kmsqptr->u.msg_lrpid);
367
368	if (option & TIME)
369		printf(" %s %s %s",
370		    stime_buf,
371		    rtime_buf,
372		    ctime_buf);
373
374	printf("\n");
375}
376
377void
378print_kshmtotal(struct shminfo shminfo)
379{
380
381	printf("shminfo:\n");
382	printf("\tshmmax: %12lu\t(max shared memory segment size)\n",
383	    shminfo.shmmax);
384	printf("\tshmmin: %12lu\t(min shared memory segment size)\n",
385	    shminfo.shmmin);
386	printf("\tshmmni: %12lu\t(max number of shared memory identifiers)\n",
387	    shminfo.shmmni);
388	printf("\tshmseg: %12lu\t(max shared memory segments per process)\n",
389	    shminfo.shmseg);
390	printf("\tshmall: %12lu\t(max amount of shared memory in pages)\n\n",
391	    shminfo.shmall);
392}
393
394void
395print_kshmheader(int option)
396{
397
398	printf("Shared Memory:\n");
399	printf("T %12s %12s %-11s %-8s %-8s",
400	    "ID", "KEY", "MODE", "OWNER", "GROUP");
401	if (option & CREATOR)
402		printf(" %-8s %-8s", "CREATOR", "CGROUP");
403	if (option & OUTSTANDING)
404		printf(" %12s", "NATTCH");
405	if (option & BIGGEST)
406		printf(" %12s", "SEGSZ");
407	if (option & PID)
408		printf(" %12s %12s", "CPID", "LPID");
409	if (option & TIME)
410		printf(" %-8s %-8s %-8s", "ATIME", "DTIME", "CTIME");
411	printf("\n");
412}
413
414void
415print_kshmptr(int i, int option, struct shmid_kernel *kshmptr)
416{
417	char    atime_buf[100], dtime_buf[100], ctime_buf[100];
418
419	cvt_time(kshmptr->u.shm_atime, atime_buf);
420	cvt_time(kshmptr->u.shm_dtime, dtime_buf);
421	cvt_time(kshmptr->u.shm_ctime, ctime_buf);
422
423	printf("m %12d %12d %s %-8s %-8s",
424	    IXSEQ_TO_IPCID(i, kshmptr->u.shm_perm),
425	    (int)kshmptr->u.shm_perm.key,
426	    fmt_perm(kshmptr->u.shm_perm.mode),
427	    user_from_uid(kshmptr->u.shm_perm.uid, 0),
428	    group_from_gid(kshmptr->u.shm_perm.gid, 0));
429
430	if (option & CREATOR)
431		printf(" %-8s %-8s",
432		    user_from_uid(kshmptr->u.shm_perm.cuid, 0),
433		    group_from_gid(kshmptr->u.shm_perm.cgid, 0));
434
435	if (option & OUTSTANDING)
436		printf(" %12d",
437		    kshmptr->u.shm_nattch);
438
439	if (option & BIGGEST)
440		printf(" %12zu",
441		    kshmptr->u.shm_segsz);
442
443	if (option & PID)
444		printf(" %12d %12d",
445		    kshmptr->u.shm_cpid,
446		    kshmptr->u.shm_lpid);
447
448	if (option & TIME)
449		printf(" %s %s %s",
450		    atime_buf,
451		    dtime_buf,
452		    ctime_buf);
453
454	printf("\n");
455}
456
457void
458print_ksemtotal(struct seminfo seminfo)
459{
460
461	printf("seminfo:\n");
462	printf("\tsemmni: %12d\t(# of semaphore identifiers)\n",
463	    seminfo.semmni);
464	printf("\tsemmns: %12d\t(# of semaphores in system)\n",
465	    seminfo.semmns);
466	printf("\tsemmnu: %12d\t(# of undo structures in system)\n",
467	    seminfo.semmnu);
468	printf("\tsemmsl: %12d\t(max # of semaphores per id)\n",
469	    seminfo.semmsl);
470	printf("\tsemopm: %12d\t(max # of operations per semop call)\n",
471	    seminfo.semopm);
472	printf("\tsemume: %12d\t(max # of undo entries per process)\n",
473	    seminfo.semume);
474	printf("\tsemusz: %12d\t(size in bytes of undo structure)\n",
475	    seminfo.semusz);
476	printf("\tsemvmx: %12d\t(semaphore maximum value)\n",
477	    seminfo.semvmx);
478	printf("\tsemaem: %12d\t(adjust on exit max value)\n\n",
479	    seminfo.semaem);
480}
481
482void
483print_ksemheader(int option)
484{
485
486	printf("Semaphores:\n");
487	printf("T %12s %12s %-11s %-8s %-8s",
488	    "ID", "KEY", "MODE", "OWNER", "GROUP");
489	if (option & CREATOR)
490		printf(" %-8s %-8s", "CREATOR", "CGROUP");
491	if (option & BIGGEST)
492		printf(" %12s", "NSEMS");
493	if (option & TIME)
494		printf(" %-8s %-8s", "OTIME", "CTIME");
495	printf("\n");
496}
497
498void
499print_ksemptr(int i, int option, struct semid_kernel *ksemaptr)
500{
501	char    ctime_buf[100], otime_buf[100];
502
503	cvt_time(ksemaptr->u.sem_otime, otime_buf);
504	cvt_time(ksemaptr->u.sem_ctime, ctime_buf);
505
506	printf("s %12d %12d %s %-8s %-8s",
507	    IXSEQ_TO_IPCID(i, ksemaptr->u.sem_perm),
508	    (int)ksemaptr->u.sem_perm.key,
509	    fmt_perm(ksemaptr->u.sem_perm.mode),
510	    user_from_uid(ksemaptr->u.sem_perm.uid, 0),
511	    group_from_gid(ksemaptr->u.sem_perm.gid, 0));
512
513	if (option & CREATOR)
514		printf(" %-8s %-8s",
515		    user_from_uid(ksemaptr->u.sem_perm.cuid, 0),
516		    group_from_gid(ksemaptr->u.sem_perm.cgid, 0));
517
518	if (option & BIGGEST)
519		printf(" %12d",
520		    ksemaptr->u.sem_nsems);
521
522	if (option & TIME)
523		printf(" %s %s",
524		    otime_buf,
525		    ctime_buf);
526
527	printf("\n");
528}
529
530uid_t
531user2uid(char *username)
532{
533	struct passwd *pwd;
534	uid_t uid;
535	char *r;
536
537	uid = strtoul(username, &r, 0);
538	if (!*r && r != username)
539		return (uid);
540	if ((pwd = getpwnam(username)) == NULL)
541		errx(1, "getpwnam failed: No such user");
542	endpwent();
543	return (pwd->pw_uid);
544}
545
546void
547usage(void)
548{
549
550	fprintf(stderr,
551	    "usage: "
552	    "ipcs [-abcmopqstyMQST] [-C corefile] [-N namelist] [-u user]\n");
553	exit(1);
554}
555