/* Tests the behavior of poll(3) on a pty after the
 * controller is closed. As we only set `POLLIN` and `POLLOUT` for the
 * `pollfd.events` field, we should only ever see `POLLIN`, `POLLERR`,
 * `POLLHUP` or `POLLNVAL`. After closing, a `write(3)` call will error
 * out, hence we should see `POLLERR`. A `read(3)` call should be
 * successful, and return EOF as there is no in-flight data; therefore we
 * should get `POLLIN`. As the remote end is closed, we should also see
 * `POLLHUP`, but not the mutually exclusive `POLLOUT`. */

#include "suite.h"

int main(void)
{
	pid_t session = fork();
	if ( session < 0 )
		err(1, "fork");

	if ( session == 0 )
	{
		// Avoid dying on SIGHUP when the controller is closed below.
		if ( signal(SIGHUP, SIG_IGN) == SIG_ERR )
			err(1, "signal");

		int controller = posix_openpt(O_RDWR | O_NOCTTY);
		if ( controller < 0 )
			err(1, "posix_openpt");
		if ( grantpt(controller) < 0 )
			err(1, "grantpt");
		if ( unlockpt(controller) < 0 )
			err(1, "unlockpt");
		char* name = ptsname(controller);
		if ( !name )
			err(1, "ptsname");

		if ( setsid() == (pid_t) -1 )
			err(1, "setsid");

		int pty = open(name, O_RDWR);
		if ( pty == -1 )
			err(1, "open(pty)");

#ifdef TIOCSCTTY
		if ( ioctl(pty, TIOCSCTTY, 0) < 0 && errno != ENOTTY )
			err(1, "ioctl: TIOCSCTTY");
#endif
		if ( tcsetpgrp(pty, getpid()) < 0 )
			err(1, "tcsetpgrp");

		// Close the controlling terminal
		close(controller);

		struct pollfd pfd;
		pfd.fd = pty;
		pfd.events = POLLIN | POLLOUT; // POLLERR and POLLHUP are always reported.
		pfd.revents = 0;

		// We expect this to return POLLIN | POLLERR | POLLHUP:
		// - POLLIN because reads succeed with EOF;
		// - POLLERR because writes fail with an error due to the file;
		// - POLLHUP because the remote end got closed.
		int ret = poll(&pfd, 1, 0);
		if ( ret < 0 )
			err(1, "poll");

		fprintf(stderr, "0");
		if ( pfd.revents & POLLIN )
			fprintf(stderr, " | POLLIN");
		if ( pfd.revents & POLLOUT )
			fprintf(stderr, " | POLLOUT");
		if ( pfd.revents & POLLERR )
			fprintf(stderr, " | POLLERR");
		if ( pfd.revents & POLLHUP )
			fprintf(stderr, " | POLLHUP");
		if ( pfd.revents & POLLNVAL )
			fprintf(stderr, " | POLLNVAL");
		fprintf(stderr, "\n");

		return 0;
	}

	int status;
	if ( waitpid(session, &status, 0) < 0 )
		err(1, "waitpid");
	if ( WIFEXITED(status) )
		return WEXITSTATUS(status);
	else if ( WIFSIGNALED(status) )
		errx(1, "%s", strsignal(WTERMSIG(status)));
	else
		errx(1, "unknown exit: %#x", status);
	return 0;
}
