/* ttysrv - serve a tty to stdin/stdout, a named pipe, or a network socket
 * vix 28may91 [written]
 */

#ifndef LINT
static char RCSid[] = "$Id: ttysrv.c,v 1.17 2001/03/24 21:14:32 vixie Exp $";
#endif

/* Copyright (c) 1996 by Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#include <sys/file.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/param.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <termios.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <netdb.h>
#include <pwd.h>
#include <unistd.h>

#if defined(DEBUG) && !defined(dprintf)
#define dprintf if (Debug) lprintf
#else
#define	dprintf (void)
#endif

#include "rtty.h"
#include "misc.h"
#include "ttyprot.h"
#ifdef WANT_TCPIP
# include "locbrok.h"
#endif

struct whoson {
	char *who, *host, *auth;
	time_t lastInput;
	enum {local, remote} type;
	enum {wlogin, wpasswd, auth} state;
	int aux;
};

#define MAX_AUTH_ATTEMPTS 3
#define USAGE_STR "{-o option} [-s LServ|-r RServ] [-l Log]\n\
	-t Tty [-b Baud] [-p Parity] [-w Wordsize] [-i Pidfile]"


#ifdef DEBUG
int			Debug = 0;
#endif

extern	int		rconnect(char *host, char *service,
				 FILE *verbose, FILE *errors,
				 int timeout);
extern	char		Version[];

static	char		*ProgName = "amnesia",
			*LServSpec = NULL,
			*RServSpec = NULL,
			*TtySpec = NULL,
			*LogSpec = NULL,
			*Parity = "none",
			ParityBuf[TP_MAXVAR],
			*PidFile = NULL;
static	int		LServ = -1,
			RServ = -1,
			Tty = -1,
			Ttyios_set = 0,
			LogDirty = FALSE,
			Baud = 9600,
			Wordsize = 8,
			highest_fd = -1,
#ifdef WANT_TCPIP
			LocBrok = -1,
#endif
			Sigpiped = 0;
#ifdef WANT_TCPIP
static	u_int		Port;
#endif
static	struct termios	Ttyios, Ttyios_orig;
static	FILE		*LogF = NULL;
static	fd_set		Clients;
static	ttyprot		T;
static	time_t		Now;
static	char		Hostname[MAXHOSTNAMELEN];

static	struct timeval	TOinput = {0, 3000};	/* 3ms: >1byte @9600baud */
static	struct timeval	TOflush = {1, 0};	/* 1 second */
static	struct whoson	**WhosOn;

static	char		*handle_option(char *);
static	void		main_loop(void),
			tty_input(int, int),
			broadcast(u_char *, int, u_int),
			sigpipe(int),
			sighup(int),
			open_log(void),
			quit(int),
			serv_input(int),
			client_input(int),
			close_client(int),
			set_parity(u_int),
			set_wordsize(u_int),
			auth_needed(int),
			auth_ok(int),
			lprintf(FILE *, const char *, ...);

static	int		set_baud(int),
			find_parity(char *),
			find_wordsize(int);

int
main(int argc, char *argv[]) {
	int i;
	char ch, *msg;

	gethostname(Hostname, sizeof Hostname);
	ProgName = argv[0];

	while ((ch = getopt(argc, argv, "o:s:r:t:l:b:p:w:x:i:")) != EOF) {
		switch (ch) {
		case 'o':
			msg = handle_option(optarg);
			if (msg) {
				USAGE((stderr, "%s: bad option (%s): %s\n",
				       ProgName, optarg, msg));
			}
		case 's':
			LServSpec = optarg;
			break;
		case 'r':
#ifdef WANT_TCPIP
			RServSpec = optarg;
#else
			USAGE((stderr, "%s: -r not supported on this system\n",
			       ProgName));
#endif
			break;
		case 't':
			TtySpec = optarg;
			break;
		case 'l':
			LogSpec = optarg;
			break;
		case 'b':
			Baud = atoi(optarg);
			break;
		case 'p':
			Parity = optarg;
			break;
		case 'w':
			Wordsize = atoi(optarg);
			break;
#ifdef DEBUG
		case 'x':
			Debug = atoi(optarg);
			break;
#endif
		case 'i':
			PidFile = optarg;
			break;
		default:
			USAGE((stderr, "%s: getopt=%c ?\n", ProgName, ch));
		}
	}

	if (!TtySpec) {
		USAGE((stderr, "%s: must specify -t ttyspec ?\n", ProgName));
	}
	if (0 > (Tty = open(TtySpec, O_NONBLOCK|O_RDWR))) {
		fprintf(stderr, "%s: can't open tty ", ProgName);
		perror(TtySpec);
		exit(2);
	}
	dprintf(stderr, "main: tty open on fd%d\n", Tty);
	tcgetattr(Tty, &Ttyios);
	Ttyios_orig = Ttyios;
	prepare_term(&Ttyios, 0);
	set_baud(Baud);

	if ((i = find_parity(Parity)) == -1) {
		USAGE((stderr, "%s: parity %s ?\n", ProgName, Parity));
	}
	set_parity(i);

	if ((i = find_wordsize(Wordsize)) == -1) {
		USAGE((stderr, "%s: wordsize %d ?\n", ProgName, Wordsize));
	}
	set_wordsize(i);

	signal(SIGINT, quit);
	signal(SIGQUIT, quit);
	install_ttyios(Tty, &Ttyios);
	Ttyios_set++;

	if (!LServSpec && !RServSpec) {
		USAGE((stderr, "%s: must specify either -s or -r\n",
		       ProgName));
	}
	if (LServSpec) {
		struct sockaddr_un n;
		struct stat statbuf;

		if (LServSpec[0] != '/') {
			USAGE((stderr, "%s: -s must specify local pathname\n",
			       ProgName));
		}

		LServ = socket(PF_UNIX, SOCK_STREAM, 0);
		ASSERT(LServ>=0, "socket");

		n.sun_family = AF_UNIX;
		(void) strcpy(n.sun_path, LServSpec);

		if (stat(LServSpec, &statbuf) >= 0) {
			fprintf(stderr, "warning: removing \"%s\"\n",
				LServSpec);
			if ((statbuf.st_mode & S_IFMT) == S_IFSOCK) {
				if (unlink(LServSpec) < 0) perror("unlink");
			}
		}

		ASSERT(0<=bind(LServ, (struct sockaddr *)&n, sizeof n),
		       n.sun_path);
	}
#ifdef WANT_TCPIP
	if (RServSpec) {
		struct sockaddr_in n;
		int nlen = sizeof n;

		RServ = socket(PF_INET, SOCK_STREAM, 0);
		ASSERT(RServ>=0, "socket");

		n.sin_family = AF_INET;
#ifndef NO_SOCKADDR_LEN
		n.sin_len = sizeof(struct sockaddr_in);
#endif
		n.sin_port = 0;			/* "any" */
		n.sin_addr.s_addr = INADDR_ANY;
		ASSERT(0<=bind(RServ, (struct sockaddr *)&n, sizeof n),
		       "bind");

		ASSERT(0<=getsockname(RServ, (struct sockaddr *)&n, &nlen),
		       "getsockname");
		Port = ntohs(n.sin_port);
		fprintf(stderr, "serving internet port %d\n", Port);

		/* register with the location broker, or die */
		{
			int len = min(LB_MAXNAMELEN, strlen(RServSpec));
			locbrok lb;

			LocBrok = rconnect("127.1", "locbrok", NULL,stderr,0);
			ASSERT(LocBrok>0, "rconnect locbrok");
			lb.lb_port = htons(Port);
			lb.lb_nlen = htons(len);
			strncpy(lb.lb_name, RServSpec, len);
			ASSERT(0<write(LocBrok, &lb, sizeof lb),
			       "write locbrok")
		}
	}
#endif

	if (LogSpec)
		open_log();

	if (PidFile) {
		FILE *f = fopen(PidFile, "w");
		if (!f) {
			perror(PidFile);
		} else {
			fprintf(f, "%d\n", getpid());
			fclose(f);
		}
	}

	WhosOn = (struct whoson **) calloc(getdtablesize(),
					   sizeof(struct whoson **));

	main_loop();
	exit(0);
}

static void
main_loop(void) {
	if (LServ != -1)
		listen(LServ, 10);
	if (RServ != -1)
		listen(RServ, 10);
	dprintf(stderr, "main: LServ=%d, RServ=%d\n", LServ, RServ);
	signal(SIGPIPE, sigpipe);
	signal(SIGHUP, sighup);
	FD_ZERO(&Clients);
	highest_fd = max(max(LServ, RServ), Tty);

	for (;;) {
		fd_set readfds;
		int nfound, fd;

		readfds = Clients;
		if (LServ != -1)
			FD_SET(LServ, &readfds);
		if (RServ != -1)
			FD_SET(RServ, &readfds);
		FD_SET(Tty, &readfds);
#ifdef DEBUG
		if (Debug > 2)
			lprintf(stderr,
				"main_loop: select(%d,%08x) LD=%d\n",
				highest_fd+1, readfds.fds_bits[0],
				LogDirty);
#endif
		nfound = select(highest_fd+1, &readfds, NULL, NULL,
				(LogDirty ?&TOflush :NULL));
		if (nfound < 0 && errno == EINTR)
			continue;
		if (nfound == 0 && LogDirty && LogF) {
			fflush(LogF);
			LogDirty = FALSE;
		}
		Now = time(0);
#ifdef DEBUG
		if (Debug > 2)
			lprintf(stderr, "main_loop: select->%d\n", nfound);
#endif
		for (fd = 0; fd <= highest_fd; fd++) {
			if (!FD_ISSET(fd, &readfds)) {
				continue;
			}
			dprintf(stderr, "main_loop: fd%d readable\n", fd);
			if (fd == Tty) {
				tty_input(fd, FALSE);
				tty_input(fd, TRUE);
			} else if ((fd == LServ) || (fd == RServ)) {
				serv_input(fd);
			} else {
				client_input(fd);
			}
		}
	}
	/*NOTREACHED*/
}

static void
tty_input(int fd, int aggregate) {
	u_char buf[TP_MAXVAR];
	int nchars, x, nloops;

	nchars = 0;
	nloops = 0;
	do {
		nloops++;
		x = read(fd, buf+nchars, TP_MAXVAR-nchars);
	} while ((x > 0) &&
		 ((nchars += x) < TP_MAXVAR) &&
		 (aggregate && !select(0, NULL, NULL, NULL, &TOinput))
		 )
	;
#ifdef DEBUG
	if (Debug) {
		lprintf(stderr, "tty_input: %d bytes read on fd%d (nl %d)",
			nchars, fd, nloops);
		if (Debug > 1) {
			fputs(": \"", stderr);
			cat_v(stderr, buf, nchars);
			fputs("\"", stderr);
		}
		fputc('\n', stderr);
	}
#endif
	if (nchars == 0)
		return;
	if (LogF) {
		if (nchars != fwrite(buf, sizeof(char), nchars, LogF)) {
			perror("fwrite(LogF)");
		} else {
			LogDirty = TRUE;
		}
	}
	broadcast(buf, nchars, TP_DATA);
}

static void
broadcast(u_char *buf, int nchars, u_int typ) {
	int fd, x;

	for (fd = 0;  fd <= highest_fd;  fd++) {
		if (!FD_ISSET(fd, &Clients)) {
			continue;
		}

		Sigpiped = 0;
		x = tp_senddata(fd, buf, nchars, typ);
		dprintf(stderr,
			"tty_input: %d bytes sent to client on fd%d\n",
			x, fd);
		if (Sigpiped) {
			dprintf(stderr, "tty_input: sigpipe on fd%d\n",
				fd);
			close_client(fd);
		}
	}
}

static void
serv_input(int fd) {
	struct sockaddr_un un;
	struct sockaddr_in in;
	struct sockaddr *sa;
	int fromlen;

	if (fd == LServ) {
		sa = (struct sockaddr *) &un;
		fromlen = sizeof(un);
	} else if (fd == RServ) {
		sa = (struct sockaddr *) &in;
		fromlen = sizeof(in);
	} else {
		fprintf(stderr, "%s: panic - serv_input(%d)\n", ProgName, fd);
		abort();
	}

	dprintf(stderr, "serv_input: accepting on fd%d\n", fd);

	if ((fd = accept(fd, sa, &fromlen)) == -1) {
		perror("accept");
		return;
	}

	dprintf(stderr, "serv_input: accepted fd%d\n", fd);

	FD_SET(fd, &Clients);
	if (fd > highest_fd) {
		highest_fd = fd;
	}
	if (!WhosOn[fd]) {
		WhosOn[fd] = (struct whoson *) malloc(sizeof(struct whoson));
	}
	WhosOn[fd]->who = NULL;
	WhosOn[fd]->lastInput = Now;
	WhosOn[fd]->auth = NULL;
	if (sa == (struct sockaddr *) &un) {
		WhosOn[fd]->host = safe_strdup(Hostname);
		WhosOn[fd]->type = local;
		auth_ok(fd);
	} else if (sa == (struct sockaddr *) &in) {
		struct hostent *hp, *gethostbyaddr();

		hp = gethostbyaddr((char *)&in.sin_addr,
				   sizeof(in.sin_addr),
				   in.sin_family);
		WhosOn[fd]->host = safe_strdup(hp
					       ? hp->h_name
					       : inet_ntoa(in.sin_addr));
		WhosOn[fd]->type = remote;
		auth_needed(fd);
	} else {
		fprintf(stderr, "%s: panic - serv_input #2\n", ProgName);
		abort();
	}
}

static void
client_input(int fd) {
	int nchars, i, new, query;
	struct passwd *pw;
	u_short salt;
	char s[3];
	u_int f;

	if (!WhosOn[fd])
		return;
	WhosOn[fd]->lastInput = Now;

	/* read the fixed part of the ttyprot (everything but the array)
	 */
	if (TP_FIXED != (nchars = read(fd, &T, TP_FIXED))) {
		dprintf(stderr, "client_input: read=%d on fd%d: ", nchars, fd);
#ifdef DEBUG
		if (Debug) perror("read");
#endif
		close_client(fd);
		return;
	}
#ifdef DEBUG_not
	cat_v(stderr, &T, nchars);
#endif
	i = ntohs(T.i);
	query = ntohs(T.f) & TP_QUERY;
	f = ntohs(T.f) & TP_TYPEMASK;
#ifdef DEBUG
	if (Debug) {
		lprintf(stderr,
			"client_input: nchars=%d fd%d i=0x%x o='%c' f=0x%x\n",
			nchars, fd, i, query?'Q':' ', f);
	}
#endif
	switch (f) {
	case TP_DATA:
		if (!(nchars = tp_getdata(fd, &T))) {
			close_client(fd);
			break;
		}
		dprintf(stderr, "client_input: TP_DATA, nchars=%d\n", nchars);
		if (WhosOn[fd]->state != auth)
			break;
# if WANT_CLIENT_LOGGING
		if (LogF) {
			if (nchars != fwrite(T.c, sizeof(char), nchars, LogF)){
				perror("fwrite(LogF)");
			} else {
				LogDirty = TRUE;
			}
		}
# endif /*WANT_CLIENT_LOGGING*/
		nchars = write(Tty, T.c, nchars);
#ifdef DEBUG
		if (Debug > 2) {
			lprintf(stderr, "client_input: wrote to tty: \"");
			cat_v(stderr, (u_char *)&T.c, nchars);
			fputs("\"\n", stderr);
		}
#endif
		break;
	case TP_BREAK:
		if (WhosOn[fd]->state != auth)
			break;
		dprintf(stderr, "client_input: sending break\n");
		tcsendbreak(Tty, 0);
		tp_senddata(fd, (u_char *)"BREAK", 5, TP_NOTICE);
		if (LogF)
			fputs("[BREAK]", LogF);
		dprintf(stderr, "client_input: done sending break\n");
		break;
	case TP_BAUD:
		if (WhosOn[fd]->state != auth)
			break;
		if (query) {
			tp_sendctl(fd, TP_BAUD|TP_QUERY, Baud, NULL);
			break;
		}
		if ((set_baud(i) >= 0) &&
		    (install_ttyios(Tty, &Ttyios) >= 0)) {
			Baud = i;
			if (LogF)
				fprintf(LogF, "[baud now %d]", i);
			tp_sendctl(fd, TP_BAUD, 1, NULL);
		} else {
			tp_sendctl(fd, TP_BAUD, 0, NULL);
		}
		break;
	case TP_PARITY:
		if (!query) {
			if (!(nchars = tp_getdata(fd, &T))) {
				close_client(fd);
				break;
			}
			T.c[i] = '\0';		/* XXX */
		}
		if (WhosOn[fd]->state != auth)
			break;
		if (query) {
			tp_sendctl(fd, TP_PARITY|TP_QUERY,
				   strlen(Parity), (u_char *)Parity);
			break;
		}
		if (-1 == (new = find_parity((char *)T.c))) {
			tp_sendctl(fd, TP_PARITY, 0, NULL);
		} else {
			strcpy(ParityBuf, (char *)T.c);
			Parity = ParityBuf;
			set_parity(new);
			install_ttyios(Tty, &Ttyios);
			if (LogF) {
				fprintf(LogF, "[parity now %s]", (char*) T.c);
			}
			tp_sendctl(fd, TP_PARITY, 1, NULL);
		}
		break;
	case TP_WORDSIZE:
		if (WhosOn[fd]->state != auth)
			break;
		if (query) {
			tp_sendctl(fd, TP_WORDSIZE|TP_QUERY, Wordsize, NULL);
			break;
		}
		if (-1 == (new = find_wordsize(i))) {
			tp_sendctl(fd, TP_WORDSIZE, 0, NULL);
		} else {
			Wordsize = i;
			set_wordsize(new);
			install_ttyios(Tty, &Ttyios);
			if (LogF) {
				fprintf(LogF, "[wordsize now %d]", i);
			}
			tp_sendctl(fd, TP_WORDSIZE, 1, NULL);
		}
		break;
	case TP_WHOSON:
		if (!query) {
			if (!(nchars = tp_getdata(fd, &T))) {
				close_client(fd);
				break;
			}
			T.c[i] = '\0';		/* XXX */
		}
		if (WhosOn[fd]->state != auth)
			break;
		if (query) {
			int iwho;

			for (iwho = getdtablesize()-1;  iwho >= 0;  iwho--) {
				struct whoson *who = WhosOn[iwho];
				char data[TP_MAXVAR];
				int idle;

				if (!who)
					continue;
				idle = Now - who->lastInput;
				sprintf(data, "%s [%s] (idle %d sec%s)",
					who->who ?who->who :"undeclared",
					who->host ?who->host :"?",
					idle, (idle==1) ?"" :"s");
				tp_senddata(fd, (u_char *)data, strlen(data),
					    TP_NOTICE);
			}
			break;
		}
		if (WhosOn[fd]) {
			if (WhosOn[fd]->who)
				free(WhosOn[fd]->who);
			WhosOn[fd]->who = safe_strdup((char *)T.c);
		}
		{ /*local*/
			char buf[TP_MAXVAR];

			sprintf(buf, "%-*.*s connected\07", i, i, T.c);
			broadcast((u_char *)buf, strlen(buf), TP_NOTICE);
		}
		break;
	case TP_TAIL:
		if (WhosOn[fd]->state != auth)
			break;
		if (!query)
			break;
		if (!LogF)
			break;
		fflush(LogF);
		LogDirty = FALSE;
		if (ftell(LogF) < 1024L) {
			if (0 > fseek(LogF, 0, SEEK_SET))
				break;
		} else {
			if (0 > fseek(LogF, -1024, SEEK_END))
				break;
		}
		{ /*local*/
			char buf[TP_MAXVAR];
			int len, something = FALSE;

			while (0 < (len = fread(buf, sizeof(char), sizeof buf,
						LogF))) {
				if (!something) {
					tp_senddata(fd, (u_char*)"tail+", 5,
						    TP_NOTICE);
					something = TRUE;
				}
				tp_senddata(fd, (u_char*)buf, len, TP_DATA);
			}
			if (something) {
				tp_senddata(fd, (u_char*)"tail-", 5,
					    TP_NOTICE);
			}
		}
		break;
	case TP_VERSION:
		if (!query)
			break;
		tp_senddata(fd, (u_char*)Version, strlen(Version), TP_NOTICE);
		break;
	case TP_LOGIN:
		if (!(nchars = tp_getdata(fd, &T))) {
			close_client(fd);
			break;
		}
		T.c[i] = '\0';		/* XXX */
		if (query)
			break;
		if (WhosOn[fd]->state != wlogin)
			break;
		pw = getpwnam((char*)T.c);
		if (!pw) {
			char data[TP_MAXVAR];

			sprintf(data, "%s - no such user", T.c);
			tp_senddata(fd, (u_char*)data, strlen(data),
				    TP_NOTICE);
		} else if (!pw->pw_passwd[0]) {
			auth_ok(fd);
		} else {
			WhosOn[fd]->state = wpasswd;
			WhosOn[fd]->auth = safe_strdup(pw->pw_passwd);
			salt = WhosOn[fd]->auth[0]<<8 | WhosOn[fd]->auth[1];
			tp_sendctl(fd, TP_PASSWD|TP_QUERY, salt, NULL);
		}
		break;
	case TP_PASSWD:
		if (!(nchars = tp_getdata(fd, &T))) {
			close_client(fd);
			break;
		}
		T.c[i] = '\0';		/* XXX */
		if (query)
			break;
		if (WhosOn[fd]->state != wpasswd)
			break;
		strncpy(s, WhosOn[fd]->auth, 2);
		if (!strcmp((char*)T.c, WhosOn[fd]->auth)) {
			auth_ok(fd);
		} else {
			char data[TP_MAXVAR];

			sprintf(data, "login incorrect");
			if (++WhosOn[fd]->aux > MAX_AUTH_ATTEMPTS) {
				close_client(fd);
			} else {
				tp_senddata(fd, (u_char*)data, strlen(data),
					    TP_NOTICE);
				auth_needed(fd);
			}
		}
		break;
	default:
		dprintf(stderr, "client_input: bad T: f=0x%x\n", ntohs(T.f));
		break;
	}
}

static void
close_client(int fd) {
	dprintf(stderr, "close_client: fd%d\n", fd);
	close(fd);
	FD_CLR(fd, &Clients);
	if (WhosOn[fd]) {
		if (WhosOn[fd]->who) {
			char buf[TP_MAXVAR];

			sprintf(buf, "%s disconnected\07", WhosOn[fd]->who);
			broadcast((u_char*)buf, strlen(buf), TP_NOTICE);
			free(WhosOn[fd]->who);
		}
		free(WhosOn[fd]->host);
		if (WhosOn[fd]->auth)
			free(WhosOn[fd]->auth);
		free((char *) WhosOn[fd]);
		WhosOn[fd] = (struct whoson *) NULL;
	}
}

struct partab { char *parity; int sysparity; } partab[] = {
	{ "even", PARENB },
	{ "odd", PARENB|PARODD },
	{ "none", 0 },
	{ NULL, -1 }
};

struct cstab { int wordsize, syswordsize; } cstab[] = {
	{ 5, CS5 },
	{ 6, CS6 },
	{ 7, CS7 },
	{ 8, CS8 },
	{ 0, -1 }
};

static int
set_baud(int baud) {
	if (cfsetispeed(&Ttyios, baud) < 0)
		return -1;
	if (cfsetospeed(&Ttyios, baud) < 0)
		return -1;
	return (0);
}

static int
find_parity(char *parity) {
	struct partab *parp;
	int sysparity = -1;

	for (parp = partab;  parp->parity;  parp++) {
		if (!strcmp(parp->parity, parity)) {
			sysparity = parp->sysparity;
		}
	}
	return (sysparity);
}

static void
set_parity(u_int parity) {
	Ttyios.c_cflag &= ~(PARENB|PARODD);
	Ttyios.c_cflag |= parity;
}

static int
find_wordsize(int wordsize) {
	struct cstab *csp;
	int syswordsize = -1;

	for (csp = cstab;  csp->wordsize;  csp++) {
		if (csp->wordsize == wordsize)
			syswordsize = csp->syswordsize;
	}
	return (syswordsize);
}

static void
set_wordsize(u_int wordsize) {
	Ttyios.c_cflag &= ~CSIZE;
	Ttyios.c_cflag |= wordsize;
}

static char *
handle_option(char *option) {
	if (!strcmp("nolocal", option)) {
		Ttyios.c_cflag &= ~CLOCAL;
	} else {
		return "unrecognized";
	}
	return (NULL);
}

static void
auth_needed(int fd) {
	char data[TP_MAXVAR];

	sprintf(data, "authorization needed");
	tp_senddata(fd, (u_char*)data, strlen(data), TP_NOTICE);
	WhosOn[fd]->state = wlogin;
	tp_sendctl(fd, TP_LOGIN|TP_QUERY, 0, NULL);
}

static void
auth_ok(int fd) {
	char data[TP_MAXVAR];

	sprintf(data, "authorized");
	tp_senddata(fd, (u_char*)data, strlen(data), TP_NOTICE);
	WhosOn[fd]->state = auth;
}

static void
sigpipe(int x) {
	Sigpiped++;
}

static void
sighup(int x) {
	if (LogF) {
		fclose(LogF);
		LogF = NULL;
		LogDirty = FALSE;
		open_log();
	}
}

static void
open_log(void) {
	if (!(LogF = fopen(LogSpec, "a+"))) {
		perror(LogSpec);
		fprintf(stderr, "%s: can't open log file\n", ProgName);
	}
}

static void
quit(int x) {
	dprintf(stderr, "\r\nttysrv exiting\r\n");
	if (Ttyios_set && (Tty != -1))
		(void) install_ttyios(Tty, &Ttyios_orig);
	exit(0);
}

#ifdef DEBUG
static void
lprintf(FILE *fp, const char *fmt, ...) {
	va_list ap;
	struct timeval tvnow;
	struct tm *tmnow;
	time_t now;

	va_start(ap, fmt);
	(void) gettimeofday(&tvnow, NULL);
	now = (time_t) tvnow.tv_sec;
	tmnow = localtime(&now);
	fprintf(fp, "%02d:%02d:%02d.%03d ",
		tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec,
		(int) (tvnow.tv_usec / 1000));
	vfprintf(fp, fmt, ap);
	va_end(ap);
}
#endif
