/*
 * DF-2769 film rig — VICTIM actor (runs as root).
 *
 * Models the verified scenario: a root admin session living on a unix98
 * pty inside a "terminal" (xterm / `ssh -tt` / script(1)).  The session
 * leader is a root shell with nohup semantics (SIGHUP ignored, EOF
 * tolerant) — exactly the class of session that survives its terminal
 * dying: crashed xterm, dropped ssh connection, killed script/tmux
 * server with a nohup'd leader on the pty.
 *
 *   parent = the terminal: relays master<->stdout for the LIVE phase,
 *            then "crashes" (close(master), exit) at a fixed beat.
 *   child  = the root session leader on the slave: prompt + system()
 *            loop, SIGHUP ignored; goes silent (EOF loop) when the
 *            terminal dies; executes whatever arrives on the tty.
 *
 * Output transcript goes to stdout (the film driver's pipe) prefixed
 * with [terminal]; the session's own terminal bytes are relayed raw.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#ifndef TIOCSCTTY
#define TIOCSCTTY _IO('t', 132)
#endif

static int
drain_master(int fd, int usec)
{
	char buf[1024];
	ssize_t l;
	struct timeval tv;
	fd_set rfds;

	for (;;) {
		tv.tv_sec = 0;
		tv.tv_usec = usec;
		FD_ZERO(&rfds);
		FD_SET(fd, &rfds);
		if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0)
			break;
		l = read(fd, buf, sizeof(buf) - 1);
		if (l <= 0)
			break;
		buf[l] = 0;
		printf("%s", buf);	/* raw relay: this is the terminal's screen */
		fflush(stdout);
	}
	return 0;
}

int
main(void)
{
	int mfd, sfd;
	char *slavepath;
	pid_t pid;
	FILE *info;
	const char *demo[] = { "whoami", "hostname", "id", "echo ROOT-SESSION-ALIVE" };
	int i;

	setvbuf(stdout, NULL, _IOLBF, 0);

	mfd = posix_openpt(O_RDWR | O_NOCTTY);
	if (mfd < 0) { perror("victim: posix_openpt"); exit(1); }
	if (grantpt(mfd) != 0) { perror("victim: grantpt"); exit(1); }
	if (unlockpt(mfd) != 0) { perror("victim: unlockpt"); exit(1); }
	slavepath = ptsname(mfd);
	if (slavepath == NULL) { perror("victim: ptsname"); exit(1); }

	pid = fork();
	if (pid < 0) { perror("fork"); exit(1); }
	if (pid == 0) {
		/* the root session leader on the slave tty */
		char line[512];

		close(mfd);
		setsid();
		sfd = open(slavepath, O_RDWR);
		if (sfd < 0) _exit(1);
		ioctl(sfd, TIOCSCTTY, (void *)0);
		dup2(sfd, 0); dup2(sfd, 1); dup2(sfd, 2);
		if (sfd > 2) close(sfd);

		signal(SIGHUP, SIG_IGN);	/* nohup semantics: survives HUP */

		for (;;) {
			printf("root@dfbsd:~ # ");
			fflush(stdout);
			errno = 0;
			if (!fgets(line, sizeof(line), stdin)) {
				/* terminal died: ZOMBIE tty reads EOF forever;
				 * stay alive and wait (silent, no prompt spam) */
				clearerr(stdin);
				usleep(150000);
				continue;
			}
			line[strcspn(line, "\r\n")] = 0;
			if (line[0] == 0)
				continue;
			if (!strcmp(line, "exit") || !strcmp(line, "logout"))
				break;
			system(line);		/* executed as root */
		}
		_exit(0);
	}

	/* parent: the terminal emulator around this session */
	info = fopen("/tmp/film/victim.info", "w");
	if (info) {
		fprintf(info, "UNIT=%s\nPID=%d\n", slavepath, (int)pid);
		fclose(info);
	}
	printf("[terminal] root admin session opened: pty %s, session pid %d\n",
	    slavepath, (int)pid);
	printf("[terminal] terminal fd (pty master) = %d\n", mfd);
	fflush(stdout);

	usleep(500000);
	drain_master(mfd, 300000);	/* first prompt appears */

	/* ---- LIVE phase: prove the root session is ALIVE + INTERACTIVE ---- */
	for (i = 0; i < 4; i++) {
		if (write(mfd, demo[i], strlen(demo[i])) < 0)
			perror("terminal write");
		write(mfd, "\n", 1);
		usleep(350000);
		drain_master(mfd, 600000);
	}
	usleep(300000);

	/* ---- CRASH phase: the terminal dies, the session stays ---- */
	printf("\n[terminal] *** TERMINAL CRASH *** (xterm killed / ssh connection dropped)\n");
	printf("[terminal] close(master=%d) -> kernel ptcclose(): gates disarmed,\n", mfd);
	printf("[terminal]   /dev/ptm/N left registered 0666 root:root, tty goes ZOMBIE\n");
	printf("[terminal]   SIGHUP -> ignored by session leader -> session SURVIVES\n");
	close(mfd);			/* == terminal death */
	printf("[terminal] terminal process exiting; root session left behind on %s\n",
	    slavepath);
	fflush(stdout);
	return 0;
}
