/*
 * $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 1988 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: killall.c 1.13 90/01/25 $@(#)killall:killall.c	1.15"
/*
 *  Go through the process table and kill all valid processes that
 *  are not Process 0 (vhand on 3B20, swapper on other machines) 
 *  or Process 1 (init) or that do not
 *  have the current process (or session) group leader id.
 */

#include	<stdio.h>
#include	<a.out.h>
#include	<fcntl.h>
#include	<sys/types.h>

#ifdef u3b
#include	<sys/seg.h>
#include	<sys/page.h>
#include	<sys/region.h>
#endif /* u3b */

#ifdef u3b2
#include	<sys/immu.h>
#include	<sys/psw.h>
#include	<sys/pcb.h>
#include	<sys/region.h>
#include	<sys/sbd.h>
#endif	/* u3b2 */

#ifdef i386
#include	<signal.h>
#include	<sys/timer.h>
#include	<sys/procstats.h>
#endif  /* i386 */

#include	<sys/param.h>
#include	<sys/proc.h>
#include	<sys/var.h>
#include	<sys/errno.h>

#ifdef SAVE_NETD
#include	<sys/vm.h>
#include	<sys/user.h>
#include	<string.h>
#endif  /* SAVE_NETD */

#define	ERR	(-1)
#define	PROC	nl[0]
#define	VAR	nl[1]
#define	ENTRY	sizeof(struct psdata)
#ifdef i286
#define	PROCSZ	((unsigned)(v.ve_proc - PROC.n_value) / sizeof(struct proc))
#elif i386
#define PROCSZ	(((unsigned)v.ve_proc - Proc) / sizeof(struct proc))
#else
#define	PROCSZ	(((unsigned)v.ve_proc - PROC.n_value) / sizeof(struct proc))
#endif
#ifdef u370
				/* no sched in u370 */
#define	FIRSTPROC	1
#elif i386
#define FIRSTPROC	3
#else
#define	FIRSTPROC	2
#endif

#define LEADERS		0
#define FOLLOWERS	1
#define WAITFOR		2

/**
 * file /etc/ps_data is built by ps command,
 * only use 1st part (device info) and
 * 3rd part (/unix namelist info)
 **/
struct psdata {
        char    device[12+1];	/* size of utmp ut_line field */
        dev_t   dev;   		/* major/minor of device */
} ;
struct udata {  /* only used for size of entry */
        ushort  userid;
        char    usernm[8];
} ;
/*
 * u3b warning:  nl[0] will be "proc" and nl[1] will be "_v"
 * regardless of what variables appear below.
 */
#if pdp11 || vax
#define PROC_STR	"_proc"
#define V_STR		"_v"
#endif
#if u3b || u3b5 || u370 || u3b2 || i286 || i386
#define PROC_STR	"proc"
#define V_STR		"v"
#endif

/*
 * The 3b5 has to perform a mapping to go from physical to virtual
 * memory references.  With this command, the 2 seeks (proc and v)
 * are in kernal space and if the source for the command is changed 
 * to open /dev/kmem instead of /dev/mem, no mapping need be done.
 */
#if u3b || u3b5 || u3b2 || i286 || i386
#define MEMORY	"/dev/kmem"
#else
#define MEMORY	"/dev/mem"
#endif

struct nlist nl[] = {
	{ PROC_STR },
	{ V_STR },
	{ 0 },
};
extern int	errno;
int	sig, fd, pgrp, pid, sid;
unsigned long	Proc, V;
struct proc	*p;
struct var	v;
long		lseek();
char		*malloc();
int		found = 0;
int		Debug = 0;
#ifdef SAVE_NETD
int		netd_pid = 0;
#endif /* SAVE_NETD */

main(argc, argv)
int  argc;
char *argv[];
{
	int	i;

	switch(argc) {
		case 1:
			sig = 9;
			break;
		case 3:
			Debug = atoi(argv[2]);
			/* fall through */
		case 2:
			sig = atoi(argv[1]);
			break;
		default:
			fprintf(stderr, "Usage: %s [signal]\n", argv[0]);
			exit(1);
	}

	if((fd = open(MEMORY, O_RDONLY)) == ERR) {
#ifdef DEBUG
		perror("read on mem");
#endif
		kill(sig, -1);
		exit(1);
	}

	nl[0].n_value = 0;
	nl[1].n_value = 0;

#ifdef u3b
	nl[0].n_value = sys3b(4,1); /* proc */
	nl[1].n_value = sys3b(4,4); /* v */
#else /* u3b */

        /**
         * read in device info from ps_data file if present
         **/
        if (ps_nlist())
		nlist("/unix", nl);

	if(PROC.n_value == 0) {
#ifdef DEBUG
		fprintf(stderr, "%s: can't find process table\n", argv[0]);
#endif
		kill(sig, -1);
		exit(1);
	}

	if(VAR.n_value == 0) {
#ifdef DEBUG
		fprintf(stderr, "%s: can't find variables table\n", argv[0]);
#endif
		kill(sig, -1);
		exit(1);
	}
#endif /* u3b */

#ifdef vax
	Proc = (PROC.n_value & 0x7fffffff);
	V = (VAR.n_value & 0x7fffffff);
#else
	Proc = PROC.n_value;
	V = VAR.n_value;
#endif

	if((pgrp = getpgrp()) == ERR) {
#ifdef DEBUG
		perror("getpgrp");
#endif
		kill(sig, -1);
		exit(1);
	}
	if((pid = getpid()) == ERR) {
#ifdef DEBUG
		perror("getpid");
#endif
		kill(sig, -1);
		exit(1);
	}

#ifdef	DEBUG
	if (Debug)
		printf("effective pgrp = %d\n", pgrp);
#endif
#if u370 || i386
	/* u370 and i386 stores a pointer to the process table */
	if(lseek(fd, Proc, 0) == ERR) {
#ifdef DEBUG
		perror("lseek on process table address");
#endif
		kill(sig, -1);
		exit(1);
	}
	if(read(fd,&Proc,sizeof(Proc)) == ERR) {
#ifdef DEBUG
		perror("read on process table address ");
#endif
		kill(sig, -1);
		exit(1);
	}
#endif
	killall (LEADERS);	/* kill session leaders */
	killall (FOLLOWERS);	/* kill anyone left */
	/*
	 * while there is still a lot of processes around wait a while
	 */
	for (i=0; i<5; i++) {
		killall (WAITFOR);	/* wait for everyone to die */
		if (found == 0)
			exit(0);
		sleep(1);
	} 
	exit(0);
}


killall(leaders)
int leaders;
{
	register int	i = 0;
#ifdef SAVE_NETD
	char		*ublock;
	struct user	*uarea;
	int		swap;
#endif /* SAVE_NETD */

/*
 *  Read in the variable table so that the current process
 *  table size can be used.
 */

	if (leaders != WAITFOR) {
		if(lseek(fd, (long)V, 0) == (long)ERR) {
#ifdef DEBUG
			perror("lseek on variable table");
#endif
			kill(sig, -1);
			exit(1);
		}

		if(read(fd, &v, sizeof(struct var)) == ERR) {
#ifdef DEBUG
			perror("read err on var");
#endif
			kill(sig, -1);
			exit(1);
		}

		/*
		 *  Seek to process table and on past slots 0 and 1
		 *  which are the scheduler and init.
		 */
		if(lseek(fd, (long)Proc, 0) == (long)ERR) {
#ifdef DEBUG
			perror("lseek on process table");
#endif
			kill(sig, -1);
			exit(1);
		}
#ifdef	DEBUG
		if (Debug)
			printf("procsz=%d\n",PROCSZ);
#endif

		/* allocate a process table */
		if ((p = (struct proc *)malloc(sizeof(struct proc) * PROCSZ)) == 0) {
#ifdef DEBUG
			perror("malloc on process table copy");
#endif
			kill(sig, -1);
			exit(1);
		}
		if(read(fd, p, sizeof(struct proc) * PROCSZ) == ERR) {
#ifdef DEBUG
			perror("read err on proc table");
#endif
			kill(sig, -1);
			exit(1);
		}
	} else {
		sig = 0;
		found = 0;
	}
	/* 
	 * we must first find our sid.
	 */
	if (leaders == LEADERS) {
		for(i=FIRSTPROC; i < PROCSZ; ++i) {
			if (p[i].p_pid == pid) 
				sid = p[i].p_sid;
		}
	}

	/*
	 * kill selected processes.
	 */
	for(i=FIRSTPROC; i < PROCSZ; ++i) {
		if(p[i].p_stat == 0 || p[i].p_stat == SZOMB || p[i].p_pid == 0 || p[i].p_flag & SSYS)
			continue;
		if ((leaders == LEADERS) && (p[i].p_pid != p[i].p_sid))
			continue;
		if (p[i].p_pid == pid || p[i].p_pgrp == pgrp || p[i].p_sid == sid)
			continue;
#ifdef SAVE_NETD
		/*
		 * Don't kill netd.  This process needs to stick around
		 * so remote (NFS) filesystems can be unmounted.  The
		 * search for netd is expensive, so only check until
		 * one is found.  Just hope that no other process has
		 * a name of 'netd*' and comes before the *real* netd
		 * in the proc table.
		 *
		 * If you could guarantee that netd would NOT be a
		 * session leader, the following test could be made
		 * more exclusive by adding '&& leaders != LEADERS'.
		 */
		if (netd_pid == 0) {
			register int	size;

			/*
			 * Sector align a user block in case it has to come
			 * from swap area.
			 */
			if (ublock == NULL) {
				ublock = malloc(ctob(UPAGES) + 511);
				ublock = (char *)(((int)ublock + 511) &~ 511);
				uarea = UBTOUSER(ublock);
			}

			/*
			 * Get the uarea and look at the command name
			 */
			if (p[i].p_flag & SLOAD) {	/* proc is in core */
				if (lseek(fd, (long)p[i].p_uarea, 0) < 0) {
					perror("seek on kmem failed");
					continue;
				}
				size = sizeof(struct user);
				if (read(fd, (char *) uarea, size) != size) {
					continue;
				}
			} else {		/* proc is swapped out */
				/*
				 * Only open swap device if it's needed.
				 */
				if (swap
				|| (swap = open("/dev/swap", O_RDONLY))) {
					if (swap == ERR) {
						perror("failed to open /dev/swap");
						continue;
					}
				}
				if (lseek(swap, (long)dtob(p[i].p_swaddr), 0) < 0) {
					perror("seek on swap failed");
					continue;
				}
				size = ctob(UPAGES);
				if (read(swap, ublock, size) != size) {
					continue;
				}
			}

			/*
			 * See if the name starts with "netd".
			 */
			if (strncmp(uarea->u_comm, "netd", 4) == 0) {
				netd_pid = p[i].p_pid;
				p[i].p_flag |= SSYS;	/* keep it safe */
				close(swap);
				continue;
			}
		}
#endif /* SAVE_NETD */
		found++;
#ifdef	DEBUG
		if (Debug)
			printf("slot=%4d:  kill(%6d,%2d)\tuid=%5u\tp_pgrp=%5d\n",
				i,p[i].p_pid,sig,p[i].p_uid, p[i].p_pgrp);
#endif
		/* kill session leaders */
		if ((kill(p[i].p_pid, sig) == ERR) && errno != ESRCH) {
			fprintf(stderr, "kill: pid= %d: ", p[i].p_pid);
			perror("");
		}
	}
	return;
}
#ifdef u3b
int sys3b(cmd, arg1)
{
	asm("	ost	&50");
}
#endif /* u3b */


/**
 * read in device info from ps_data file if present - quicker than nlist.
 **/
ps_nlist()
{	
	int	ndevs;
	int	psfd;
	int	size;

        if((psfd = open("/etc/ps_data", O_RDONLY)) != ERR) {
		/* first int tells how many entries follow */
		if(read(psfd, (char *)&ndevs, sizeof(ndevs)) == ERR) {
			close(psfd);
			return (1);
		}
		if(lseek(psfd, (long)(ndevs*ENTRY), 1) == ERR) {
			close(psfd);
			return (1);
		}
		/**
		 * skip past uid data in /etc/ps_data
		 **/
		if(read(psfd, (char *)&size, sizeof(size)) == ERR) {
			close(psfd);
			return (1);
		}
		if(lseek(psfd, (long)(size*sizeof(struct udata)), 1) == ERR) {
			close(psfd);
			return (1);
		}
		/**
		 * fill in the name list table from /etc/ps_data
		 **/
		if(read(psfd, (char *)nl, sizeof(nl)) == ERR) {
			return (1);
		}
		close(psfd);
		return (0);
	}
	return (1);
}
