/*
 * killall5.c	Kill all processes except processes that have the
 *		same session id, so that the shell that called us
 *		won't be killed. Typically used in shutdown scripts.
 *
 * pidof.c	Tries to get the pid of the process[es] named.
 *
 * Version:	2.86 30-Jul-2004 MvS
 *
 * Usage:	killall5 [-][signal]
 *		pidof [-s] [-o omitpid [-o omitpid]] program [program..]
 *
 * Authors:	Miquel van Smoorenburg, miquels@cistron.nl
 *
 *		Riku Meskanen, <mesrik@jyu.fi>
 *		- return all running pids of given program name
 *		- single shot '-s' option for backwards compatibility
 *		- omit pid '-o' option and %PPID (parent pid metavariable)
 *		- syslog() only if not a connected to controlling terminal
 *		- swapped out programs pids are caught now
 *
 *		Werner Fink
 *		- make omit dynamic
 *		- provide '-n' to skip stat(2) syscall on network based FS
 *
 *		This file is part of the sysvinit suite,
 *		Copyright (C) 1991-2004 Miquel van Smoorenburg.
 *
 *		This program is free software; you can redistribute it and/or modify
 *		it under the terms of the GNU General Public License as published by
 *		the Free Software Foundation; either version 2 of the License, or
 *		(at your option) any later version.
 *
 *		This program is distributed in the hope that it will be useful,
 *		but WITHOUT ANY WARRANTY; without even the implied warranty of
 *		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *		GNU General Public License for more details.
 *
 *		You should have received a copy of the GNU General Public License
 *		along with this program; if not, write to the Free Software
 *		Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
#include <dirent.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <mntent.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <syslog.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#ifndef PATH_MAX
#  ifdef MAXPATHLEN
#    define PATH_MAX MAXPATHLEN
#  else
#    define PATH_MAX 2048
#  endif
#endif

#define STATNAMELEN	15

/* Info about a process. */
typedef struct proc {
	char *pathname;		/* full path to executable        */
	char *argv0;		/* Name as found out from argv[0] */
	char *argv0base;	/* `basename argv[1]`		  */
	char *argv1;		/* Name as found out from argv[1] */
	char *argv1base;	/* `basename argv[1]`		  */
	char *statname;		/* the statname without braces    */
	pid_t pid;		/* Process ID.			  */
	pid_t sid;		/* Session ID.			  */
	char kernel;		/* Kernel thread or zombie.	  */
	char nfs;		/* Name found on network FS.	  */
	struct proc *next;	/* Pointer to next struct. 	  */
} PROC;

/* pid queue */

typedef struct pidq {
	PROC		*proc;
	struct pidq	*next;
} PIDQ;

typedef struct {
	PIDQ		*head;
	PIDQ		*tail;
	PIDQ		*next;
} PIDQ_HEAD;

typedef struct _s_omit {
	struct _s_omit *next;
	struct _s_omit *prev;
	pid_t pid;
} OMIT;

typedef struct _s_shadow
{
	struct _s_shadow *next;
	struct _s_shadow *prev;
	size_t nlen;
	char * name;
} SHADOW;

typedef struct _s_nfs
{
	struct _s_nfs *next;	/* Pointer to next struct. */
	struct _s_nfs *prev;	/* Pointer to previous st. */
	SHADOW *shadow;		/* Pointer to shadows      */
	size_t nlen;
	char * name;
} NFS;

/* List of processes. */
PROC *plist = NULL;

/* List of processes to omit. */
OMIT *omit = NULL;

/* List of NFS mountes partitions. */
NFS *nlist = NULL;

/* Did we stop all processes ? */
int sent_sigstop = 0;
int scripts_too = 0;

/* Should pidof try to list processes in I/O wait (D) and zombie (Z) states? */
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
int list_dz_processes = FALSE;

char *progname;	/* the name of the running program */
#ifdef __GNUC__
__attribute__ ((format (printf, 2, 3)))
#endif
void nsyslog(int pri, char *fmt, ...);

#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
# ifndef  inline
#  define inline	__inline__
# endif
# ifndef  restrict
#  define restrict	__restrict__
# endif
#endif
#define alignof(type)	((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1))

/*
 *	Malloc space, barf if out of memory.
 */
#ifdef __GNUC__
static void *xmalloc(size_t) __attribute__ ((__malloc__));
#endif
static void *xmalloc(size_t bytes)
{
	void *p;

	if ((p = malloc(bytes)) == NULL) {
		if (sent_sigstop) kill(-1, SIGCONT);
		nsyslog(LOG_ERR, "out of memory");
		exit(1);
	}
	return p;
}

#ifdef __GNUC__
static inline void xmemalign(void **, size_t, size_t) __attribute__ ((__nonnull__ (1)));
#endif
static inline void xmemalign(void **memptr, size_t alignment, size_t size)
{
	if ((posix_memalign(memptr, alignment, size)) < 0) {
		if (sent_sigstop) kill(-1, SIGCONT);
		nsyslog(LOG_ERR, "out of memory");
		exit(1);
	}
}

/*
 *	See if the proc filesystem is there. Mount if needed.
 */
int mount_proc(void)
{
	struct stat	st;
	char		*args[] = { "mount", "-t", "proc", "proc", "/proc", 0 };
	pid_t		pid, rc;
	int		wst;
	int		did_mount = 0;

	/* Stat /proc/version to see if /proc is mounted. */
	if (stat("/proc/version", &st) < 0 && errno == ENOENT) {

		/* It's not there, so mount it. */
		if ((pid = fork()) < 0) {
			nsyslog(LOG_ERR, "cannot fork");
			exit(1);
		}
		if (pid == 0) {
			/* Try a few mount binaries. */
			execv("/bin/mount", args);
			execv("/sbin/mount", args);

			/* Okay, I give up. */
			nsyslog(LOG_ERR, "cannot execute mount");
			exit(1);
		}
		/* Wait for child. */
		while ((rc = wait(&wst)) != pid)
			if (rc < 0 && errno == ECHILD)
				break;
		if (rc != pid || WEXITSTATUS(wst) != 0)
			nsyslog(LOG_ERR, "mount returned non-zero exit status");

		did_mount = 1;
	}

	/* See if mount succeeded. */
	if (stat("/proc/version", &st) < 0) {
		if (errno == ENOENT)
			nsyslog(LOG_ERR, "/proc not mounted, failed to mount.");
		else
			nsyslog(LOG_ERR, "/proc unavailable.");
		exit(1);
	}

	return did_mount;
}

static inline int isnetfs(const char * type)
{
	static const char* netfs[] = {"nfs", "nfs4", "smbfs", "cifs", "afs", "ncpfs", (char*)0};
	int n;
	for (n = 0; netfs[n]; n++) {
		if (!strcasecmp(netfs[n], type))
			return 1;
	}
	return 0;
}

/*
 *     Remember all NFS typed partitions.
 */
void init_nfs(void)
{
        struct stat st;
        struct mntent * ent;
	FILE * mnt;

	nlist = (NFS*)0;

	if (stat("/proc/version", &st) < 0)
		return;
	if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0)
		return;

	while ((ent = getmntent(mnt))) {
		if (isnetfs(ent->mnt_type)) {
			size_t nlen = strlen(ent->mnt_dir);
			NFS *restrict p;
			xmemalign((void*)&p, sizeof(void*), alignof(NFS)+(nlen+1));
			p->name = ((char*)p)+alignof(NFS);
			p->nlen = nlen;
			p->shadow = (SHADOW*)0;

			strcpy(p->name, ent->mnt_dir);
			if (nlist)
				nlist->prev = p;
			p->next = nlist;
			p->prev = (NFS*)0;
			nlist = p;
		}
	}
	endmntent(mnt);

	if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0)
		return;

	while ((ent = getmntent(mnt))) {
		NFS *p;

		for (p = nlist; p; p = p->next) {
			SHADOW * restrict s;
			size_t nlen;

			if (strcmp(ent->mnt_dir, p->name) == 0)
				continue;
			if (strncmp(ent->mnt_dir, p->name, p->nlen) != 0)
				continue;

			nlen = strlen(ent->mnt_dir);
			xmemalign((void*)&s, sizeof(void*), alignof(SHADOW)+(nlen+1));
			s->name = ((char*)s)+alignof(SHADOW);
			s->nlen = nlen;

			strcpy(s->name, ent->mnt_dir);
			if (p->shadow)
			    p->shadow->prev = s;
			s->next = p->shadow;
			s->prev = (SHADOW*)0;
			p->shadow = s;
		}
	}
	endmntent(mnt);
}

static void clear_shadow(SHADOW *restrict shadow)
{
	SHADOW *s, *n, *l;

	n = shadow;
	l = (SHADOW*)0;
	for (s = shadow; n; s = n) {
		l = s->prev;
		n = s->next;
		if (s == shadow) {
			if (n) n->prev = (SHADOW*)0;
			shadow = n;
		} else if (l) {
			if (n) n->prev = l;
			l->next = n;
		}
		free(s);
	}
}

static void clear_mnt(void)
{
	NFS *p, *n, *l;

	n = nlist;
	l = (NFS*)0;
	for (p = nlist; n; p = n) {
		l = p->prev;
		n = p->next;
		if (p == nlist) {
			if (n) n->prev = (NFS*)0;
			nlist = n;
		} else if (l) {
			if (n) n->prev = l;
			l->next = n;
		}
		if (p->shadow)
			clear_shadow(p->shadow);
		free(p);
	}
}

/*
 *     Check if path is a shadow off a NFS partition.
 */
static int shadow(SHADOW *restrict this, const char *restrict name, const size_t nlen)
{
	SHADOW *s;

	if (!this)
		goto out;
	for (s = this; s; s = s->next) {
		if (nlen < s->nlen)
			continue;
		if (name[s->nlen] != '\0' && name[s->nlen] != '/')
			continue;
		if (strncmp(name, s->name, s->nlen) == 0)
			return 1;
	}
out:
	return 0;
}

/*
 * Get the maximal number of symlinks to follow.  Use sysconf() on
 * Hurd where the hardcoded value MAXSYMLINKS is not available.
 */
static int maxsymlinks(void)
{
	int v = sysconf(_SC_SYMLOOP_MAX);
#ifdef MAXSYMLINKS
	if (v == -1)
		return MAXSYMLINKS;
#endif
	return v;
}

/*
 *     Check path is located on a network based partition.
 */
int check4nfs(const char * path, char * real)
{
	char buf[PATH_MAX+1];
	const char *curr;
	int deep = maxsymlinks();

	if (!nlist) return 0;

	curr = path;
	do {
		const char *prev;
		int len;

		if ((prev = strdupa(curr)) == NULL) {
			nsyslog(LOG_ERR, "strdupa(): %s\n", strerror(errno));
			return 0;
		}

		errno = 0;
		if ((len = readlink(curr, buf, PATH_MAX)) < 0)
			break;
		buf[len] = '\0';

		if (buf[0] != '/') {
			const char *slash;

			if ((slash = strrchr(prev, '/'))) {
				size_t off = slash - prev + 1;

				if (off + len > PATH_MAX)
					len = PATH_MAX - off;

				memmove(&buf[off], &buf[0], len + 1);
				memcpy(&buf[0], prev, off);
			}
		}
		curr = &buf[0];

		if (deep-- <= 0) return 0;

	} while (1);

	if (real)     /* real is defined elsewhere as being PATH_MAX + 1 */
        {
           memset(real, '\0', PATH_MAX + 1);
           strncpy(real, curr, PATH_MAX);
        }

	if (errno == EINVAL) {
		const size_t nlen = strlen(curr);
		NFS *p;
		for (p = nlist; p; p = p->next) {
			if (nlen < p->nlen)
				continue;
			if (curr[p->nlen] != '\0' && curr[p->nlen] != '/')
				continue;
			if (!strncmp(curr, p->name, p->nlen)) {
				if (shadow(p->shadow, curr, nlen))
					continue;
				return 1;
			}
		}
	}

	return 0;
}

int readarg(FILE *fp, char *buf, int sz)
{
	int		c = 0, f = 0;

	while (f < (sz-1) && (c = fgetc(fp)) != EOF && c)
		buf[f++] = c;
	buf[f] = 0;

	return (c == EOF && f == 0) ? c : f;
}

/*
 *	Read the proc filesystem.
 *	CWD must be /proc to avoid problems if / is affected by the killing (ie depend on fuse).
 */
int readproc()
{
	DIR		*dir;
	FILE		*fp;
	PROC		*p, *n;
	struct dirent	*d;
	char		path[PATH_MAX+1];
	char		buf[PATH_MAX+1];
	char		*s, *q;
	unsigned long	startcode, endcode;
	int		pid, f;
        char            process_status[11];

	/* Open the /proc directory. */
	if (chdir("/proc") == -1) {
		nsyslog(LOG_ERR, "chdir /proc failed");
		return -1;
	}
	if ((dir = opendir(".")) == NULL) {
		nsyslog(LOG_ERR, "cannot opendir(/proc)");
		return -1;
	}

	/* Free the already existing process list. */
	n = plist;
	for (p = plist; n; p = n) {
		n = p->next;
		if (p->argv0) free(p->argv0);
		if (p->argv1) free(p->argv1);
		if (p->statname) free(p->statname);
		if (p->pathname) free(p->pathname);
		free(p);
	}
	plist = NULL;

	/* Walk through the directory. */
	while ((d = readdir(dir)) != NULL) {

		/* See if this is a process */
		if ((pid = atoi(d->d_name)) == 0) continue;

		/* Get a PROC struct . */
		p = (PROC *)xmalloc(sizeof(PROC));
		memset(p, 0, sizeof(PROC));

		/* Open the status file. */
		snprintf(path, sizeof(path), "%s/stat", d->d_name);

		/* Read SID & statname from it. */
		if ((fp = fopen(path, "r")) != NULL) {
			size_t len;

			len = fread(buf, sizeof(char), sizeof(buf)-1, fp);
			buf[len] = '\0';

			if (buf[0] == '\0') {
				nsyslog(LOG_ERR,
					"can't read from %s\n", path);
				fclose(fp);
				free(p);
				continue;
			}

			/* See if name starts with '(' */
			s = buf;
			while (*s && *s != ' ') s++;
			if (*s) s++;
			if (*s == '(') {
				/* Read program name. */
				q = strrchr(buf, ')');
				if (q == NULL) {
					p->sid = 0;
					nsyslog(LOG_ERR,
					"can't get program name from /proc/%s\n",
						path);
					fclose(fp);
					if (p->argv0) free(p->argv0);
					if (p->argv1) free(p->argv1);
					if (p->statname) free(p->statname);
					if (p->pathname) free(p->pathname);
					free(p);
					continue;
				}
				s++;
			} else {
				q = s;
				while (*q && *q != ' ') q++;
			}
			if (*q) *q++ = 0;
			while (*q == ' ') q++;
			p->statname = (char *)xmalloc(strlen(s)+1);
			strcpy(p->statname, s);

			/* Get session, startcode, endcode. */
			startcode = endcode = 0;
			if (sscanf(q,   "%10s %*d %*d %d %*d %*d %*u %*u "
					"%*u %*u %*u %*u %*u %*d %*d "
					"%*d %*d %*d %*d %*u %*u %*d "
					"%*u %lu %lu",
					process_status, 
					&p->sid, &startcode, &endcode) != 4) {

				p->sid = 0;
				nsyslog(LOG_ERR, "can't read sid from %s\n",
					path);
				fclose(fp);
				if (p->argv0) free(p->argv0);
				if (p->argv1) free(p->argv1);
				if (p->statname) free(p->statname);
				free(p->pathname);
				free(p);
				continue;
			}
			if (startcode == 0 && endcode == 0)
				p->kernel = 1;
			fclose(fp);
                        if ( (! list_dz_processes) &&
                               (strchr(process_status, 'Z') != NULL) ) {
                           /* Ignore zombie processes */
                              if (p->argv0) free(p->argv0);
                              if (p->argv1) free(p->argv1);
                              if (p->statname) free(p->statname);
                             free(p);
                             continue;
                        }
		} else {
			/* Process disappeared.. */
			if (p->argv0) free(p->argv0);
			if (p->argv1) free(p->argv1);
			if (p->statname) free(p->statname);
			if (p->pathname) free(p->pathname);
			free(p);
			continue;
		}

		snprintf(path, sizeof(path), "%s/cmdline", d->d_name);
		if ((fp = fopen(path, "r")) != NULL) {

			/* Now read argv[0] */
			f = readarg(fp, buf, sizeof(buf));

			if (buf[0]) {
				/* Store the name into malloced memory. */
				p->argv0 = (char *)xmalloc(f + 1);
				strcpy(p->argv0, buf);

				/* Get a pointer to the basename. */
				p->argv0base = strrchr(p->argv0, '/');
				if (p->argv0base != NULL)
					p->argv0base++;
				else
					p->argv0base = p->argv0;
			}

			/* And read argv[1] */
			while ((f = readarg(fp, buf, sizeof(buf))) != EOF)
				if (buf[0] != '-') break;

			if (buf[0]) {
				/* Store the name into malloced memory. */
				p->argv1 = (char *)xmalloc(f + 1);
				strcpy(p->argv1, buf);

				/* Get a pointer to the basename. */
				p->argv1base = strrchr(p->argv1, '/');
				if (p->argv1base != NULL)
					p->argv1base++;
				else
					p->argv1base = p->argv1;
			}

			fclose(fp);

		} else {
			/* Process disappeared.. */
			if (p->argv0) free(p->argv0);
			if (p->argv1) free(p->argv1);
			if (p->statname) free(p->statname);
			if (p->pathname) free(p->pathname);
			free(p);
			continue;
		}

		/* Try to stat the executable. */
		snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name);
                p->pathname = (char *)xmalloc(PATH_MAX);
                memset(p->pathname, 0, PATH_MAX);
 		if (readlink(path, p->pathname, PATH_MAX) == -1) {
 			p->pathname = NULL;
 		}

		/* Link it into the list. */
		p->next = plist;
		plist = p;
		p->pid = pid;
	}
	closedir(dir);

	/* Done. */
	return 0;
}

PIDQ_HEAD *init_pid_q(PIDQ_HEAD *q)
{
	q->head =  q->next = q->tail = NULL;
	return q;
}

int empty_q(PIDQ_HEAD *q)
{
	return (q->head == NULL);
}

int add_pid_to_q(PIDQ_HEAD *q, PROC *p)
{
	PIDQ *tmp;

	tmp = (PIDQ *)xmalloc(sizeof(PIDQ));

	tmp->proc = p;
	tmp->next = NULL;

	if (empty_q(q)) {
		q->head = tmp;
		q->tail  = tmp;
	} else {
		q->tail->next = tmp;
		q->tail = tmp;
	}
	return 0;
}

PROC *get_next_from_pid_q(PIDQ_HEAD *q)
{
	PROC		*p;
	PIDQ		*tmp = q->head;

	if (!empty_q(q)) {
		p = q->head->proc;
		q->head = tmp->next;
		free(tmp);
		return p;
	}

	return NULL;
}

/* Try to get the process ID of a given process. */
PIDQ_HEAD *pidof(char *prog)
{
	PROC		*p;
	PIDQ_HEAD	*q;
	char		*s;
	int		nfs = 0;
	int		dostat = 0;
	int		foundone = 0;
	int		ok = 0;
	const int	root = (getuid() == 0);
	char		real_path[PATH_MAX+1];

	if (! prog)
		return NULL;

	/* Try to stat the executable. */
	memset(real_path, 0, sizeof(real_path));
	if ( (prog[0] == '/') && ( realpath(prog, real_path) ) ) {
		dostat++;
	}

	/* Get basename of program. */
	if ((s = strrchr(prog, '/')) == NULL)
		s = prog;
	else
		s++;

	if (! *s)
		return NULL;

	q = (PIDQ_HEAD *)xmalloc(sizeof(PIDQ_HEAD));
	q = init_pid_q(q);

	/* First try to find a match based on dev/ino pair. */
	if (dostat) {
		for (p = plist; p; p = p->next) {
			if (p->pathname && strcmp(real_path, p->pathname) == 0) {
				add_pid_to_q(q, p);
				foundone++;
			}
                        else if ( (p->argv0) && (! strcmp(p->argv0, prog) ) )
                        {
                            add_pid_to_q(q, p);
                            foundone++;
                        }
		}
	}

	/* Second try to find a match based on full path name on
	 * network FS located binaries */
	if (!foundone && nfs) {
		for (p = plist; p; p = p->next) {
			if (!p->pathname)
				continue;
			if (!p->nfs)
				continue;
			if (strcmp(prog, p->pathname) != 0)
				continue;
			add_pid_to_q(q, p);
			foundone++;
		}
	}

	/* If we didn't find a match based on dev/ino, try the name. */
	if (!foundone) for (p = plist; p; p = p->next) {
		if (prog[0] == '/') {
			if (!p->pathname) {
				if (root)
					continue;
				goto fallback; 
			}
			if (strcmp(prog, p->pathname)) {
				int len = strlen(prog);
				if (strncmp(prog, p->pathname, len))
				{
					if (scripts_too)
						goto fallback;
					continue;
				}
				if (strcmp(" (deleted)", p->pathname + len))
				{
					if (scripts_too)
						goto fallback;
					continue;
				}
			}
			add_pid_to_q(q, p);
			continue;
		}

	fallback:
		ok = 0;

		/*             matching        nonmatching
		 * proc name   prog name       prog name
		 * ---         -----------     ------------
		 *   b         b, p/b, q/b
		 * p/b         b, p/b          q/b
		 *
		 * Algorithm: Match if:
		 *    cmd = arg
		 * or cmd = base(arg)
		 * or base(cmd) = arg
		 *
		 * Specifically, do not match just because base(cmd) = base(arg)
		 * as was done in earlier versions of this program, since this
		 * allows /aaa/foo to match /bbb/foo .
		 */

		ok |=
			(p->argv0 && strcmp(p->argv0, prog) == 0)
			|| (p->argv0 && s != prog && strcmp(p->argv0, s) == 0)
			|| (p->argv0base && strcmp(p->argv0base, prog) == 0);

		/* For scripts, compare argv[1] as well. */
		if (
			scripts_too && p->statname && p->argv1base
			&& !strncmp(p->statname, p->argv1base, STATNAMELEN)
		) {
			ok |=
				(p->argv1 && strcmp(p->argv1, prog) == 0)
				|| (p->argv1 && s != prog && strcmp(p->argv1, s) == 0)
				|| (p->argv1base && strcmp(p->argv1base, prog) == 0);
		}

		/*
		 *	if we have a space in argv0, process probably
		 *	used setproctitle so try statname.
		 */
		if (strlen(s) <= STATNAMELEN &&
		    (p->argv0 == NULL ||
		     p->argv0[0] == 0 ||
		     strchr(p->argv0, ' '))) {
			ok |= (strcmp(p->statname, s) == 0);
		}

		/*
		 *	if we have a `-' as the first character, process
		 *	probably used as a login shell
		 */
		if (strlen(s) <= STATNAMELEN &&
		    p->argv1 == NULL &&
		    (p->argv0 != NULL &&
		     p->argv0[0] == '-')) {
			ok |= (strcmp(p->statname, s) == 0);
		}

		if (ok) add_pid_to_q(q, p);
	}

	return q;
}

/* Give usage message and exit. */
void usage(void)
{
	nsyslog(LOG_ERR, "usage: killall5 -signum [-o omitpid] [-o omitpid] ...");
	closelog();
	exit(1);
}


void pidof_usage(void)
{
   printf("pidof usage: [options] <program-name>\n\n");
   printf(" -c           Return PIDs with the same root directory\n");
   printf(" -d <sep>     Use the provided character as output separator\n");
   printf(" -h           Display this help text\n");
   printf(" -n           Avoid using stat system function on network shares\n");
   printf(" -o <pid>     Omit results with a given PID\n");
   printf(" -q           Quiet mode. Do not display output\n");
   printf(" -s           Only return one PID\n");
   printf(" -x           Return PIDs of shells running scripts with a matching name\n");
   printf(" -z           List zombie and I/O waiting processes. May cause pidof to hang.\n");
   printf("\n");
}


/* write to syslog file if not open terminal */
#ifdef __GNUC__
__attribute__ ((format (printf, 2, 3)))
#endif
void nsyslog(int pri, char *fmt, ...)
{
	va_list  args;

	va_start(args, fmt);

	if (ttyname(0) == NULL) {
		vsyslog(pri, fmt, args);
	} else {
		fprintf(stderr, "%s: ",progname);
		vfprintf(stderr, fmt, args);
		fprintf(stderr, "\n");
	}

	va_end(args);
}

#define PIDOF_SINGLE	0x01
#define PIDOF_OMIT	0x02
#define PIDOF_NETFS	0x04
#define PIDOF_QUIET     0x08

/*
 *	Pidof functionality.
 */
int main_pidof(int argc, char **argv)
{
	PIDQ_HEAD	*q;
	PROC		*p;
	char		*token, *here;
	int		f;
	int		first = 1;
	int		opt, flags = 0;
	int		chroot_check = 0;
	struct stat	st;
	char		tmp[512];
        char            sep = ' ';

	omit = (OMIT*)0;
	nlist = (NFS*)0;
	opterr = 0;

	if ((token = getenv("PIDOF_NETFS")) && (strcmp(token,"no") != 0))
		flags |= PIDOF_NETFS;

	while ((opt = getopt(argc,argv,"qhco:d:sxzn")) != EOF) switch (opt) {
		case '?':
			nsyslog(LOG_ERR,"invalid options on command line!\n");
			closelog();
			exit(1);
		case 'c':
			if (geteuid() == 0) chroot_check = 1;
			break;
                case 'h':
                        pidof_usage();
                        exit(0);
                case 'd':
                        sep = optarg[0];
                        break;
		case 'o':
			here = optarg;
			while ((token = strsep(&here, ",;:"))) {
				OMIT *restrict optr;
				pid_t opid;

				if (strcmp("%PPID", token) == 0)
					opid = getppid();
				else
					opid = (pid_t)atoi(token);

				if (opid < 1) {
					nsyslog(LOG_ERR,
						"illegal omit pid value "
						"(%s)!\n", token);
					continue;
				}
				xmemalign((void*)&optr, sizeof(void*), alignof(OMIT));
				optr->next = omit;
				optr->prev = (OMIT*)0;
				optr->pid  = opid;
				omit = optr;
			}
			flags |= PIDOF_OMIT;
			break;
                case 'q':
                        flags |= PIDOF_QUIET;
                        break; 
		case 's':
			flags |= PIDOF_SINGLE;
			break;
		case 'x':
			scripts_too++;
			break;
                case 'z':
                        list_dz_processes = TRUE;
                        break;
		case 'n':
			flags |= PIDOF_NETFS;
			break;
		default:
			/* Nothing */
			break;
	}
	argc -= optind;
	argv += optind;

	/* Check if we are in a chroot */
	if (chroot_check) {
		snprintf(tmp, 512, "/proc/%d/root", getpid());
		if (stat(tmp, &st) < 0) {
			nsyslog(LOG_ERR, "stat failed for %s!\n", tmp);
			closelog();
			exit(1);
		}
	}

	if (flags & PIDOF_NETFS)
		init_nfs();		/* Which network based FS are online? */

	/* Print out process-ID's one by one. */
	readproc();

	for(f = 0; f < argc; f++) {
		if ((q = pidof(argv[f])) != NULL) {
			pid_t spid = 0;
			while ((p = get_next_from_pid_q(q))) {
				if ((flags & PIDOF_OMIT) && omit) {
					OMIT * optr;
					for (optr = omit; optr; optr = optr->next) {
						if (optr->pid == p->pid)
							break;
					}

					/*
					 *	On a match, continue with
					 *	the for loop above.
					 */
					if (optr)
						continue;
				}
				if (flags & PIDOF_SINGLE) {
					if (spid)
						continue;
					else
						spid = 1;
				}
				if (chroot_check) {
					struct stat st2;
					snprintf(tmp, 512, "/proc/%d/root",
						 p->pid);
					if (stat(tmp, &st2) < 0 ||
					    st.st_dev != st2.st_dev ||
					    st.st_ino != st2.st_ino) {
						continue;
					}
				}

				if ( ~flags & PIDOF_QUIET ) {
					if (! first)
						printf("%c", sep);
					printf("%d", p->pid);
				}
				first = 0;
			}
		}
	}
	if (!first)
        {
            if ( ~flags & PIDOF_QUIET ) 
		printf("\n");
        }

	clear_mnt();

	closelog();
	return(first ? 1 : 0);
}

/* Main for either killall or pidof. */
int main(int argc, char **argv)
{
	PROC		*p;
	int		pid, sid = -1;
	int		sig = SIGKILL;
	int		c;

	/* return non-zero if no process was killed */
	int		retval = 2;

	/* Get program name. */
	if ((progname = strrchr(argv[0], '/')) == NULL)
		progname = argv[0];
	else
		progname++;

	/* Now connect to syslog. */
	openlog(progname, LOG_CONS|LOG_PID, LOG_DAEMON);

	/* Were we called as 'pidof' ? */
	if (strcmp(progname, "pidof") == 0)
		return main_pidof(argc, argv);

	/* Right, so we are "killall". */
	omit = (OMIT*)0;

	if (argc > 1) {
		for (c = 1; c < argc; c++) {
			if (argv[c][0] == '-') (argv[c])++;
			if (argv[c][0] == 'o') {
				char * token, * here;

				if (++c >= argc)
					usage();

				here = argv[c];
				while ((token = strsep(&here, ",;:"))) {
					OMIT *restrict optr;
					pid_t opid = (pid_t)atoi(token);

					if (opid < 1) {
						nsyslog(LOG_ERR,
							"illegal omit pid value "
							"(%s)!\n", token);
						continue;
					}
					xmemalign((void*)&optr, sizeof(void*), alignof(OMIT));
					optr->next = omit;
					optr->prev = (OMIT*)0;
					optr->pid  = opid;
					omit = optr;
				}
			}
			else if ((sig = atoi(argv[1])) <= 0 || sig > 31)
				usage();
		}
	}

	/* First get the /proc filesystem online. */
	mount_proc();

	/*
	 *	Ignoring SIGKILL and SIGSTOP do not make sense, but
	 *	someday kill(-1, sig) might kill ourself if we don't
	 *	do this. This certainly is a valid concern for SIGTERM-
	 *	Linux 2.1 might send the calling process the signal too.
	 */
	signal(SIGTERM, SIG_IGN);
	signal(SIGSTOP, SIG_IGN);
	signal(SIGKILL, SIG_IGN);

	/* lock us into memory */
	mlockall(MCL_CURRENT | MCL_FUTURE);

        /* Get our session ID and PID to make sure we do not kill ourselves or our session. */
	sid = (int)getsid(0);
	pid = (int)getpid();

	/* Now stop all processes, unless there are some we should omit. */
        if (! omit)
        {
	    kill(-1, SIGSTOP);
	    sent_sigstop = 1;
        }

	/* Read /proc filesystem */
	if (readproc() < 0) {
             if (sent_sigstop)
		kill(-1, SIGCONT);
  	     return(1);
	}

	/* Now kill all processes except init (pid 1) and our session. */
	for (p = plist; p; p = p->next) {
		if (p->pid == 1 || p->pid == pid || p->sid == sid || p->kernel)
			continue;

		if (omit) {
			OMIT * optr;
			for (optr = omit; optr; optr = optr->next) {
				if (optr->pid == p->pid)
					break;
			}

			/* On a match, continue with the for loop above. */
			if (optr)
				continue;
		}      /* end of skipping PIDs to omit */

		kill(p->pid, sig);
		retval = 0;
	}

	/* And let them continue. */
        if (sent_sigstop)
	    kill(-1, SIGCONT);

	/* Done. */
	closelog();

	/* Force the kernel to run the scheduler */
	usleep(1);

	return retval;
}
