#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>			/* for killing processes */
#include <sgtty.h>			/* for finding process groups */
#include <utmp.h>			/* for finding users */
#include <stdio.h>

#define UTMP	"/etc/utmp"

#define TIME	30			/* 5 minutes between meals */

/*
 * library function declarations.
 */
extern	long	lseek();
extern	char	*strncpy();

/*
 * internal declarations.
 */
static	int	annoy();
static	int	ask();
static	int	affirmative();
static	void	grab_user();

static	char	*user;			/* current user to annoy */
static	FILE	*tfp = 0;		/* pointer to his tty */
static	int	mypg;

main(argc, argv)
char *argv[];
{
	register int pid;
	register int len;

	/*
	 * run in the background.
	 */
	switch (pid = fork()) {
	case -1:			/* error */
		perror("fork");
		exit(1);
	default:			/* parent */
		printf("%d\n", pid);
		exit(0);
	case 0:
		break;
	}

	(void) setpgrp(0, 3);
	mypg = getpgrp(0);

	/*
	 * run in /dev so we can open ttys.
	 */
	(void) chdir("/dev");

	if (argc == 1) {
		/*
		 * grab a random user, and change argv[0] for ps.
		 */
		grab_user((char *) 0);

		len = strlen(argv[0]);
		(void) strncpy(argv[0], "cm ", len);
		(void) strncpy(argv[0] + 3, user, len - 3);
	} else {
		grab_user(argv[1]);
	}

	/*
	 * hold off keyboard-generated signals so they can't just hit ^C.
	 */
	(void) signal(SIGINT, SIG_IGN);
	(void) signal(SIGQUIT, SIG_IGN);
	(void) signal(SIGTSTP, SIG_IGN);

	while (annoy()) {
		sleep(TIME);
	}
	exit(0);
}

static int
annoy()
{
	static int state = 0;
	static int cookies = 0;

	switch (state) {
	case 0:
		if (ask("\rCan I have a cookie? ")) {
			cookies += 1;
			state = 2;
		} else {
			state = 1;
		}
		break;
	case 1:
		if (ask("\rPlease can I have a cookie? ")) {
			cookies += 1;
			state = 2;
		} else {
			state = 1;
		}
		break;
	case 2:
		if (ask("\rCan I have another cookie? ")) {
			fputs("Thank you.\n", tfp);
			cookies += 1;
			return(0);		/* finished */
		} else {
			state = 1;
		}
		break;
	}
	return(1);
}

/*
 * ask the given question, and read the reply from the tty.
 */
static int
ask(question)
char *question;
{
	static char reply[80];		/* plenty big enough for "yes" */
	int pgrp, newpgrp;		/* process group of their terminal */

	/*
	 * stop any processes, so we can read from the tty.
	 */
	(void) ioctl(fileno(tfp), TIOCGPGRP, &pgrp);
	(void) killpg(pgrp, SIGTSTP);

	/*
	 * see if the shell has awakened; if so, kill it ...
	 */
	sleep(1);
	(void) killpg(pgrp, SIGSTOP);
	(void) ioctl(fileno(tfp), TIOCGPGRP, &newpgrp);
	if (newpgrp != pgrp) {
		(void) killpg(newpgrp, SIGSTOP);
	}

	/*
	 * set the tty pgrp to ourselves so we can read input.
	 */
	(void) ioctl(fileno(tfp), TIOCSPGRP, &mypg);

	/*
	 * keep asking until they type something in.
	 */
	do {
		(void) fputs(question, tfp);
	} while (fgets(reply, sizeof(reply), tfp) != NULL && reply[0] == '\n');

	/*
	 * re-start their jobs, or just their shell
	 * if they were doing something else.
	 */
	(void) ioctl(fileno(tfp), TIOCSPGRP, &newpgrp);
	(void) killpg(newpgrp, SIGCONT);
	(void) killpg(pgrp, SIGCONT);

	return(affirmative(reply));
}

/*
 * grab a random user.
 */
static void
grab_user(name)
char *name;
{
	static struct utmp ubuf;
	struct stat stbuf;
	register int ufd;
	register int n, pos;
	long time();

	if (stat(UTMP, &stbuf) == -1) {
		perror(UTMP);
		exit(1);
	}
	n = stbuf.st_size / sizeof(ubuf);	/* n is no. entries in utmp */
	ufd = open(UTMP, 0);
	if (ufd == -1) {
		perror(UTMP);
		exit(1);
	}

	if (name != (char *) 0) {
		while (read(ufd, (char *) &ubuf, sizeof(ubuf)) > 0) {
			if (strcmp(name, ubuf.ut_name) == 0) {
				user = name;
				tfp = fopen(ubuf.ut_line, "w+");
				if (tfp == (FILE *) 0)
					continue;
				(void) setbuf(tfp, NULL);
				(void) close(ufd);
				return;
			}
		}
		exit(0);
	}

	(void) srand((int) time((int *) 0) ^ getpid());

	/*
	 * grab a random user, and open their tty.
	 */
again:
	pos = rand() % n;
	if (lseek(ufd, (long) (pos * sizeof(ubuf)), 0) == -1) {
		perror(UTMP);
		exit(1);
	}
	if (read(ufd, (char *) &ubuf, sizeof(ubuf)) < sizeof(ubuf)) {
		perror(UTMP);
		exit(1);
	}

	if (ubuf.ut_name[0] == '\0')
		goto again;
	if (strcmp(ubuf.ut_name, "opr") == 0)
		goto again;

	user = ubuf.ut_name;
	tfp = fopen(ubuf.ut_line, "w+");
	if (tfp == (FILE *) 0)
		goto again;
	(void) setbuf(tfp, NULL);

	(void) close(ufd);
}

/*
 * returns true if the given string is "yes" or something like it.
 */
static int
affirmative(string)
register char *string;
{
	switch (string[0]) {
	case 'y':			/* anything beginning with 'y' */
	case 'Y':
		return(1);
	case 'o':			/* match for "ok" */
	case 'O':
		if (string[1] == 'k' || string[1] == 'K')
			return(1);
		/* FALL THROUGH */
	default:			/* must be negative */
		return(0);
	}
}
