#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <grp.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "ptmx_fake_internal.h"

/* Return the name of the slave device */
static char *ptsname(int fd)
{
	struct stat statbuf;
	static char tty[11]="/dev/tty??";
	const char *I="pqrstuvwxyzabcde";
	const char *J="0123456789abcdef";
	unsigned int minor;

	/* Check that we have a pty master */
	if (fstat(fd, &statbuf)==-1)
	{
		perror("fstat\n");
		return NULL;
	}
	if (!S_ISCHR(statbuf.st_mode)
		|| MAJOR(statbuf.st_rdev)!=PTY_MASTER_MAJOR)
	{
		fprintf(stderr, "stdin should be a pty master\n");
		return NULL;
	}

	/* Generate name of pty slave */
	minor=MINOR(statbuf.st_rdev);
	tty[8]=I[minor/16], tty[9]=J[minor%16];

	/* Check that result really is the correct pty slave */
	if (stat(tty, &statbuf)==-1)
	{
		perror("stat");
		return NULL;
	}
	if (!S_ISCHR(statbuf.st_mode))
	{
		fprintf(stderr, "pty slave should be a character device!\n");
		return NULL;
	}
	if (MAJOR(statbuf.st_rdev)!=PTY_SLAVE_MAJOR)
	{
		fprintf(stderr, "pty slave has unexpected major no.\n");
		return NULL;
	}
	if ((unsigned int)MINOR(statbuf.st_rdev)!=minor)
	{
		fprintf(stderr, "pty slave has unexpected minor no.\n");
		return NULL;
	}
	return tty;
}

/* Check if we own the master, and change the slave permissions to
   crw------- user tty */
int main(void)
{
	char *tty;
	struct group *ttygroup;
	gid_t ttygid=0;
	struct sigaction sa;
	
	if ((ttygroup=getgrnam("tty"))) ttygid=ttygroup->gr_gid;
	if (!(tty=ptsname(0)))
	{
		fprintf(stderr, "ptsname failed\n");
		return 1;
	}
	if (chown(tty, getuid(), ttygid))
	{
		perror("chown tty");
		return 1;
	}
	if (chmod(tty, 0600))
	{
		perror("chmod tty");
		return 1;
	}
	sa.sa_handler = SIG_IGN;
	sa.sa_flags = 0;
	sigemptyset(&sa.sa_mask);
	if (sigaction(SIGHUP, &sa, NULL)==-1)
	{
		perror("sigaction");
		return 1;
	}
	/* Don't bother forking before setsid() - it can't go wrong if
           called from grantpt since that already did the fork. If the
           program is used incorrectly, the worst that can happen is
           that the user is given back an error 'Operation not permitted'. */
	if (setsid()==-1)
	{
		perror("setsid");
		return 1;
	}
	/* The pty slave become our controlling terminal */
	if (open(tty, O_RDONLY)==-1)
	{
		perror("open tty");
		return 1;
	}
	/* Close *all* filehandles on our controlling terminal. All
           processes with the tty open get a SIGHUP too - which is why
           we ignore that signal. */
	if (vhangup()==-1)
	{
		perror("vhangup");
		return 1;
	}
	return 0;
}

