/*
 * Steven Schultz - sms@moe.2bsd.com
 *
 *	@(#)ctimed.c	1.0 (2.11BSD) 1996/6/25
 *
 * ctimed - the daemon that supports the ctime() and getpw*() stubs
 * 	    in 'libcstubs.a'.
*/

#include	<signal.h>
#include	<stdio.h>
#include	<setjmp.h>
#include	<sys/ioctl.h>
#include	<sys/types.h>
#include	<sys/time.h>
#include	<pwd.h>
#include	<utmp.h>
#include "ckcfnp.h"                     /* Prototypes (must be last) */

/*
 * These should probably be placed in an include file.  If you add anything
 * here then you will also have to modify /usr/src/usr.lib/libstubs/stubs.c
 * (if for no other reason than to add the stub code).
*/

#define	CTIME	1
#define	ASCTIME	2
#define	TZSET	3
#define	LOCALTIME 4
#define	GMTIME	5
#define	OFFTIME	6

#define	GETPWENT	7
#define	GETPWNAM	8
#define	GETPWUID	9
#define	SETPASSENT	10
#define	ENDPWENT	11

extern	struct	tm	*offtime();

	jmp_buf	env;
	char	*cp;
	char	junk[256 + sizeof (struct passwd) + 4];
	long	off;
	time_t	l;
	void	timeout(), checkppid();
	struct	tm	tmtmp, *tp;

main()
	{
	register int i;
	register struct passwd *pw;
	struct	itimerval it;
	u_char	c, xxx;
	int	len, tosslen;
	uid_t	uid;

	signal(SIGPIPE, SIG_DFL);
	for	(i = getdtablesize(); --i > 2; )
		close(i);
/*
 * Need a timer running while we disassociate from the control terminal
 * in case of a modem line which has lost carrier.
*/
	timerclear(&it.it_interval);
	it.it_value.tv_sec = 5;
	it.it_value.tv_usec = 0;
	signal(SIGALRM, timeout);
	setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL);
	if	(setjmp(env) == 0)
		{
		i = open("/dev/tty", 0);
		if	(i >= 0)
			{
			ioctl(i, TIOCNOTTY, NULL);
			close(i);
			}
		}
/*
 * Now start a timer with one minute refresh.  In the signal service
 * routine, check the parent process id to see if this process has
 * been orphaned and if so exit.  This is primarily aimed at removing
 * the 'ctimed' process left behind by 'sendmail's multi-fork startup
 * but may prove useful in preventing accumulation of 'ctimed' processes
 * in other circumstances as well.  Normally this process is short
 * lived.
*/
	it.it_interval.tv_sec = 60;
	it.it_interval.tv_usec = 0;
	it.it_value.tv_sec = 60;
	it.it_value.tv_usec = 0;
	signal(SIGALRM, checkppid);
	setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL);

	while	(read(fileno(stdin), &c, 1) == 1)
		{
		switch	(c)
			{
			case	CTIME:
				l = 0L;
				getb(fileno(stdin), &l, sizeof l);
				cp = ctime(&l);
				write(fileno(stdout), cp, 26);
				break;
			case	ASCTIME:
				getb(fileno(stdin), &tmtmp, sizeof tmtmp);
				cp = asctime(&tmtmp);
				write(fileno(stdout), cp, 26);
				break;
			case	TZSET:
				(void) tzset();
				break;
			case	LOCALTIME:
				l = 0L;
				getb(fileno(stdin), &l, sizeof l);
				tp = localtime(&l);
				write(fileno(stdout), tp, sizeof (*tp));
				strcpy(junk, tp->tm_zone);
				junk[24] = '\0';
				write(fileno(stdout), junk, 24);
				break;
			case	GMTIME:
				l = 0L;
				getb(fileno(stdin), &l, sizeof l);
				tp = gmtime(&l);
				write(fileno(stdout), tp, sizeof (*tp));
				strcpy(junk, tp->tm_zone);
				junk[24] = '\0';
				write(fileno(stdout), junk, 24);
				break;
			case	OFFTIME:
				getb(fileno(stdin), &l, sizeof l);
				getb(fileno(stdin), &off, sizeof off);
#ifdef	__bsdi__
				l += off;
				tp = localtime(&l);
#else
				tp = offtime(&l, off);
#endif
				write(fileno(stdout), tp, sizeof (*tp));
				break;
			case	GETPWENT:
				pw = getpwent();
				do_pw(pw);
				break;
			case	GETPWNAM:
				getb(fileno(stdin), &len, sizeof (int));
				if	(len > UT_NAMESIZE)
					{
					tosslen = len - UT_NAMESIZE;
					len = UT_NAMESIZE;
					}
				else
					tosslen = 0;
				getb(fileno(stdin), junk, len);
				for	(;tosslen; tosslen--)
					getb(fileno(stdin), &xxx, 1);
				junk[len] = '\0';
				pw = getpwnam(junk);
				do_pw(pw);
				break;
			case	GETPWUID:
				getb(fileno(stdin), &uid, sizeof (uid_t));
				pw = getpwuid(uid);
				do_pw(pw);
				break;
			case	SETPASSENT:
				getb(fileno(stdin), &len, sizeof (int));
				if	(setpassent(len))
					len = 1;
				else
					len = 0;
				write(fileno(stdout), &len, sizeof (int));
				break;
			case	ENDPWENT:
				endpwent();
				break;
			default:
				abort("switch");
			}
		}
	}

getb(f, p, n)
	int	f;
	register char	*p;
	register int	n;
	{
	register int	i;

	while	(n)
		{
		i = read(f, p, n);
		if	(i <= 0)
			return;
		p += i;
		n -= i;
		}
	}

void
timeout()
	{

	longjmp(env, 1);
	}

void
checkppid()
	{

	if	(getppid() == 1)
		exit(0);
	}

do_pw(pw)
	struct passwd *pw;
	{
	int	len;

	if	(!pw)
		{
		len = 0;
		write(fileno(stdout), &len, sizeof (int));
		return;
		}
	len = packpwtobuf(pw, junk);
	write(fileno(stdout), &len, sizeof (int));
	write(fileno(stdout), pw, sizeof (*pw));
	write(fileno(stdout), junk, len);
	return;
	}

packpwtobuf(pw, buf)
	register struct passwd *pw;
	char	*buf;
	{
	register char *cp = buf;
	register char *dp;

	dp = pw->pw_name;
	pw->pw_name = (char*) 0;
	while	(*cp++ = *dp++)
		;
	dp = pw->pw_passwd;
	pw->pw_passwd = (char*) (cp - buf);
	while	(*cp++ = *dp++)
		;
	dp = pw->pw_class;
	pw->pw_class = (char*) (cp - buf);
	while	(*cp++ = *dp++)
		;
	dp = pw->pw_gecos;
	pw->pw_gecos = (char*) (cp - buf);
	while	(*cp++ = *dp++)
		;
	dp = pw->pw_dir;
	pw->pw_dir = (char*) (cp - buf);
	while	(*cp++ = *dp++)
		;
	dp = pw->pw_shell;
	pw->pw_shell = (char*) (cp - buf);
	while	(*cp++ = *dp++)
		;
	return(cp - buf);
	}
