/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: ipcs.c 1.8 90/04/30 $@(#)ipc:/ipcs.c	1.12"
/*
**	ipcs - IPC status
**	Examine and print certain things about message queues, semaphores,
**		and shared memory.
**
**	INKERNEL is defined around all ipc header files to allow
**	ipc to walk through kmem unharmed.
*/

#include	"sys/types.h"
#define		INKERNEL
#include	"sys/ipc.h"
#include	"sys/msg.h"
#include	"sys/sem.h"
#ifndef pdp11
#undef		INKERNEL
#include	"sys/vnode.h"
#include	"sys/dmap.h"
#define		INKERNEL
#include	"sys/shm.h"
#undef		INKERNEL
#endif
#ifdef pdp11
#include	"storclass.h"
#endif
#include	"a.out.h"
#include	"fcntl.h"
#include	"time.h"
#include	"grp.h"
#include	"pwd.h"
#include	"stdio.h"

#define	TIME	0
#define	MSG	1
#define	SEM	2
#define	SHM	3
#define	MSGINFO	4
#define	SEMINFO	5
#define	SHMINFO	6

struct nlist nl[] = {		/* name list entries for IPC facilities */
#if u3b || u3b15 || u3b2 || i386
	{"time", 0, 0, 0, C_EXT, 0},
	{"msgque", 0, 0, 0, C_EXT, 0},
	{"sema", 0, 0, 0, C_EXT, 0},
	{"shmem", 0, 0, 0, C_EXT, 0},
	{"msginfo", 0, 0, 0, C_EXT, 0},
	{"seminfo", 0, 0, 0, C_EXT, 0},
	{"shminfo", 0, 0, 0, C_EXT, 0},
#else
#ifdef vax
	{"_time", 0, 0, 0, C_EXT, 0},
	{"_msgque", 0, 0, 0, C_EXT, 0},
	{"_sema", 0, 0, 0, C_EXT, 0},
	{"_shmem", 0, 0, 0, C_EXT, 0},
	{"_msginfo", 0, 0, 0, C_EXT, 0},
	{"_seminfo", 0, 0, 0, C_EXT, 0},
	{"_shminfo", 0, 0, 0, C_EXT, 0},
#else
	{"_time", C_EXT, 0},
	{"_msgque", C_EXT, 0},
	{"_sema", C_EXT, 0},
	{"_shmem", C_EXT, 0},
	{"_msginfo", C_EXT, 0},
	{"_seminfo", C_EXT, 0},
	{"_shminfo", C_EXT, 0},
#endif
#endif
	{NULL}
};
char	chdr[] = "T     ID     KEY        MODE       OWNER    GROUP",
				/* common header format */
	chdr2[] = "  CREATOR   CGROUP",
				/* c option header format */
	*name = "/unix",	/* name list file */
	*mem = "/dev/kmem",	/* memory file */
	opts[] = "abcmMopqstC:N:";/* allowable options for getopt */
extern char	*optarg;	/* arg pointer for getopt */
int		bflg,		/* biggest size:
					segsz on m; qbytes on q; nsems on s */
		cflg,		/* creator's login and group names */
		mflg,		/* shared memory status */
		Mflg,		/* display status for All shared memory ids,
					even those marked for removal */
		oflg,		/* outstanding data:
					nattch on m; cbytes, qnum on q */
		pflg,		/* process id's: lrpid, lspid on q;
					cpid, lpid on m */
		qflg,		/* message queue status */
		sflg,		/* semaphore status */
		tflg,		/* times: atime, ctime, dtime on m;
					ctime, rtime, stime on q;
					ctime, otime on s */
		Cflg,		/* user supplied corefile */
		Nflg,		/* user supplied namelist */
		err;		/* option error count */
extern int	optind;		/* option index for getopt */

extern char		*ctime();
extern struct group	*getgrgid();
extern struct passwd	*getpwuid();
extern long		lseek();

main(argc, argv)
int	argc;	/* arg count */
char	**argv;	/* arg vector */
{
	register int	i,	/* loop control */
			md,	/* memory file file descriptor */
			o;	/* option flag */
	time_t		time;	/* date in memory file */
	gid_t		saved_gid;	/* saved original effective gid */
#ifndef pdp11
	struct shmid_ds	mds;	/* shared memory data structure */
	struct shminfo shminfo;	/* shared memory information structure */
#endif
	struct msqid_ds	qds;	/* message queue data structure */
	struct msginfo msginfo;	/* message information structure */
	struct semid_ds	sds;	/* semaphore data structure */
	struct seminfo seminfo;	/* semaphore information structure */
#ifdef i386
	struct msqid_ds	*qdsp;	/* message queue data structure (real data) */
	struct semid_ds	*sdsp;	/* semaphore data structure (real data) */
	struct shmid_ds	*mdsp;	/* shared memory data structure (real data) */
#endif /* i386 */

	/* Go through the options and set flags. */
	while((o = getopt(argc, argv, opts)) != EOF)
		switch(o) {
		case 'a':
			bflg = cflg = oflg = pflg = tflg = 1;
			break;
		case 'b':
			bflg = 1;
			break;
		case 'c':
			cflg = 1;
			break;
		case 'C':
			mem = optarg;
			Cflg = 1;
			break;
		case 'm':
#ifdef pdp11
			printf("Shared Memory facility not in system.\n");
#endif
			mflg = 1;
			break;
		case 'M':
#ifdef pdp11
			printf("Shared Memory facility not in system.\n");
#endif
			mflg = 1;
			Mflg = 1;
			break;
		case 'N':
			name = optarg;
			Nflg = 1;
			break;
		case 'o':
			oflg = 1;
			break;
		case 'p':
			pflg = 1;
			break;
		case 'q':
			qflg = 1;
			break;
		case 's':
			sflg = 1;
			break;
		case 't':
			tflg = 1;
			break;
		case '?':
			err++;
			break;
		}
	if(err || (optind < argc)) {
		fprintf(stderr,
			"usage:  ipcs [-abcmopqst] [-C corefile] [-N namelist]\n");
		exit(1);
	}
	/*
	   If the user supplied either the corefile or namelist then
	   reset the uid/gid to the user invoking this command.
	*/
	if (Cflg || Nflg) {
		setuid(getuid());
		setgid(getgid());
	}
	if((mflg + qflg + sflg) == 0)
		mflg = qflg = sflg = 1;

	/* Check out namelist and memory files. */
	nlist(name, nl);
	if(!nl[TIME].n_value) {
		fprintf(stderr, "ipcs:  no namelist\n");
		exit(1);
	}
	if((md = open(mem, O_RDONLY)) < 0) {
		fprintf(stderr, "ipcs:  no memory file\n");
		exit(1);
	}
	lseeke(md, (long)nl[TIME].n_value, 0);
	reade(md, &time, sizeof(time));
	printf("IPC status from %s as of %s", mem, ctime(&time));

	/* Print Message Queue status report. */
	if(qflg) {
		if(nl[MSG].n_value) {
			i = 0;
			lseeke(md, (long)nl[MSGINFO].n_value, 0);
			reade(md, &msginfo, sizeof(msginfo));
			lseeke(md, (long)nl[MSG].n_value, 0);
#ifdef i386
			reade(md, &qdsp, sizeof(qdsp));
			lseeke(md, (long)qdsp, 0);
#endif /* i386 */
			printf("%s%s%s%s%s%s\nMessage Queues:\n", chdr,
				cflg ? chdr2 : "",
				oflg ? " CBYTES  QNUM" : "",
				bflg ? " QBYTES" : "",
				pflg ? " LSPID LRPID" : "",
				tflg ? "   STIME    RTIME    CTIME " : "");
		} else {
			i = msginfo.msgmni;
			printf("Message Queue facility not in system.\n");
		}
		while(i < msginfo.msgmni) {
			reade(md, &qds, sizeof(qds));
			if(!(qds.msg_perm.mode & IPC_ALLOC)) {
				i++;
				continue;
			}
			hp('q', "SRrw-rw-rw-", &qds.msg_perm, i++, msginfo.msgmni);
			if(oflg)
				printf("%7u%6u", qds.msg_cbytes, qds.msg_qnum);
			if(bflg)
				printf("%7u", qds.msg_qbytes);
			if(pflg)
				printf("%6u%6u", qds.msg_lspid, qds.msg_lrpid);
			if(tflg) {
				tp(qds.msg_stime);
				tp(qds.msg_rtime);
				tp(qds.msg_ctime);
			}
			printf("\n");
		}
	}

	/* Print Shared Memory status report. */
#ifndef pdp11
	if(mflg) {
		if(nl[SHM].n_value) {
			i = 0;
			lseeke(md, (long)nl[SHMINFO].n_value, 0);
			reade(md, &shminfo, sizeof(shminfo));
			lseeke(md, (long)nl[SHM].n_value, 0);
#ifdef i386
			reade(md, &mdsp, sizeof(mdsp));
			lseeke(md, (long)mdsp, 0);
#endif /* i386 */
			if(oflg || bflg || tflg || !qflg || !nl[MSG].n_value)
				printf("%s%s%s%s%s%s\n", chdr,
					cflg ? chdr2 : "",
					oflg ? " NATTCH" : "",
					bflg ? "  SEGSZ" : "",
					pflg ? "  CPID  LPID" : "",
					tflg ? "   ATIME    DTIME    CTIME " : "");
			printf("Shared Memory:\n");
		} else {
			i = shminfo.shmmni;
			printf("Shared Memory facility not in system.\n");
		}
		while(i < shminfo.shmmni) {
			reade(md, &mds, sizeof(mds));
			if (!(mds.shm_perm.mode & IPC_ALLOC)) {
				i++;
				continue;
			}
			/*
			 * If the size of the segment is zero and
			 * the key is IPC_PRIVATE then this shared
			 * memory identifier has been marked for
			 * removal, but will not vanish (i.e., the
			 * mode will not be cleared) until the last
			 * process which references the shared segment
			 * detaches the segment or terminates.  So don't
			 * display it unless you really want to see it
			 * (via the Mflg option).
			 */
			if (!Mflg && (mds.shm_segsz == 0 &&
				mds.shm_perm.key == IPC_PRIVATE)) {
				i++;
				continue;
			}
			hp('m', "DCrw-rw-rw-", &mds.shm_perm, i++, shminfo.shmmni);
			if(oflg) {
#ifdef i386
				/* the ref counts are kept in the mmap
				   structure so get via shmctl */
				struct shmid_ds tmpmds;
				int mid;

				mid = (i-1) + shminfo.shmmni * mds.shm_perm.seq;
				/*
				 * Before issuing the shmctl call, let's be
				 * sure that group access checks are performed
				 * against the caller's group id, not our
				 * current effective group id since we are
				 * a setgid "sys" executable.
				 */
				saved_gid = getegid();
				setgid(getgid());
				if (shmctl(mid, IPC_STAT, &tmpmds) != 0) {
					perror("\n\t\tshmctl");
					continue;
				}
				/* 
				 * now restore our saved-set-gid from exec.
				 */
				setgid(saved_gid);

				mds.shm_nattch = tmpmds.shm_nattch;
#endif /* i386 */
				printf("%7u", mds.shm_nattch);
			}
			if(bflg)
				printf("%7d", mds.shm_segsz);
			if(pflg)
				printf("%6u%6u", mds.shm_cpid, mds.shm_lpid);
			if(tflg) {
				tp(mds.shm_atime);
				tp(mds.shm_dtime);
				tp(mds.shm_ctime);
			}
			printf("\n");
		}
	}
#endif

	/* Print Semaphore facility status. */
	if(sflg) {
		if(nl[SEM].n_value) {
			i = 0;
			lseeke(md, (long)nl[SEMINFO].n_value, 0);
			reade(md, &seminfo, sizeof(seminfo));
			lseeke(md, (long)nl[SEM].n_value, 0);
#ifdef i386
			reade(md, &sdsp, sizeof(sdsp));
			lseeke(md, (long)sdsp, 0);
#endif /* i386 */
			if(bflg || tflg || (!qflg || !nl[MSG].n_value) &&
				(!mflg || !nl[SHM].n_value))
				printf("%s%s%s%s\n", chdr,
					cflg ? chdr2 : "",
					bflg ? " NSEMS" : "",
					tflg ? "   OTIME    CTIME " : "");
			printf("Semaphores:\n");
		} else {
			i = seminfo.semmni;
			printf("Semaphore facility not in system.\n");
		}
		while(i < seminfo.semmni) {
			reade(md, &sds, sizeof(sds));
			if(!(sds.sem_perm.mode & IPC_ALLOC)) {
				i++;
				continue;
			}
			hp('s', "--ra-ra-ra-", &sds.sem_perm, i++, seminfo.semmni);
			if(bflg)
				printf("%6u", sds.sem_nsems);
			if(tflg) {
				tp(sds.sem_otime);
				tp(sds.sem_ctime);
			}
			printf("\n");
		}
	}
	exit(0);
}

/*
**	lseeke - lseek with error exit
*/

lseeke(f, o, w)
int	f,	/* fd */
	w;	/* whence */
long	o;	/* offset */
{
#ifdef vax
	o &= 0x3fffffff;
#endif
	if(lseek(f, o, w) < 0) {
		perror("ipcs:  seek error");
		exit(1);
	}
}

/*
**	reade - read with error exit
*/

reade(f, b, s)
int	f,	/* fd */
	s;	/* size */
char	*b;	/* buffer address */
{
	if(read(f, b, s) != s) {
		perror("ipcs:  read error");
		exit(1);
	}
}

/*
**	hp - common header print
*/

hp(type, modesp, permp, slot, slots)
char				type,	/* facility type */
				*modesp;/* ptr to mode replacement characters */
register struct ipc_perm	*permp;	/* ptr to permission structure */
int				slot,	/* facility slot number */
				slots;	/* # of facility slots */
{
	register int		i,	/* loop control */
				j;	/* loop control */
	register struct group	*g;	/* ptr to group group entry */
	register struct passwd	*u;	/* ptr to user passwd entry */

	printf("%c%7d%s%#8.8x ", type, slot + slots * permp->seq,
		permp->key ? " " : " 0x", permp->key);
	for(i = 02000;i;modesp++, i >>= 1)
		printf("%c", (permp->mode & i) ? *modesp : '-');
	if((u = getpwuid(permp->uid)) == NULL)
		printf("%9d", permp->uid);
	else
		printf("%9.8s", u->pw_name);
	if((g = getgrgid(permp->gid)) == NULL)
		printf("%9d", permp->gid);
	else
		printf("%9.8s", g->gr_name);
	if(cflg) {
		if((u = getpwuid(permp->cuid)) == NULL)
			printf("%9d", permp->cuid);
		else
			printf("%9.8s", u->pw_name);
		if((g = getgrgid(permp->cgid)) == NULL)
			printf("%9d", permp->cgid);
		else
			printf("%9.8s", g->gr_name);
	}
}

/*
**	tp - time entry printer
*/

tp(time)
time_t	time;	/* time to be displayed */
{
	register struct tm	*t;	/* ptr to converted time */

	if(time) {
		t = localtime(&time);
		printf(" %2d:%2.2d:%2.2d", t->tm_hour, t->tm_min, t->tm_sec);
	} else
		printf(" no-entry");
}

/* <@(#)ipc:/ipcs.c	1.12> */
