Net2/usr/src/contrib/isode/others/quipu/uips/fred/dad.c

Compare this file to the similar file:
Show the results in this format:

/* dad.c - "directory assistance"
	   lightweight interface to the Directory Service */

#ifndef	lint
static char *rcsid = "$Header: /f/osi/others/quipu/uips/fred/RCS/dad.c,v 7.8 91/03/09 11:54:45 mrose Exp $";
#endif

/* 
 * $Header: /f/osi/others/quipu/uips/fred/RCS/dad.c,v 7.8 91/03/09 11:54:45 mrose Exp $
 *
 *
 * $Log:	dad.c,v $
 * Revision 7.8  91/03/09  11:54:45  mrose
 * update
 * 
 * Revision 7.7  91/02/22  09:30:35  mrose
 * Interim 6.8
 * 
 * Revision 7.6  91/02/12  18:25:33  mrose
 * update
 * 
 * Revision 7.5  91/01/07  12:43:21  mrose
 * update
 * 
 * Revision 7.4  90/10/30  14:25:42  mrose
 * update
 * 
 * Revision 7.3  90/10/29  18:39:22  mrose
 * updates
 * 
 * Revision 7.2  90/10/29  11:50:12  mrose
 * more stuff
 * 
 * Revision 7.1  90/10/28  22:40:59  mrose
 * update
 * 
 * Revision 7.0  90/10/28  13:16:14  mrose
 * *** empty log message ***
 * 
 */

/*
 *				  NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */


#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <varargs.h>
#include <pwd.h>

#include "config.h"
#include "general.h"
#include <sys/ioctl.h>
#ifdef	BSD42
#include <sys/file.h>
#endif
#ifdef	SYS5
#include <fcntl.h>
#endif
#ifdef	BSD42
#include <sys/wait.h>
#endif


#include "internet.h"
#include "tailor.h"

/*    DATA */

static	int	debug = 0;
static	int	nbits = FD_SETSIZE;

static	int	uid = 1;
static	int	gid = 1;

static	int	dishpid = NOTOK;
static	struct sockaddr_in dishsock;

static	int	doomsday = 0;

static LLog _pgm_log = {
    "dad.log", NULLCP, NULLCP, LLOG_FATAL | LLOG_EXCEPTIONS | LLOG_NOTICE,
    LLOG_FATAL, -1, LLOGCLS | LLOGCRT | LLOGZER, NOTOK
};
	LLog   *pgm_log = &_pgm_log;

static	char   *myname = "dad";

static	struct sockaddr_in lo_socket;


void	arginit (), envinit ();

void	advise (), adios ();


#ifdef	SYS5
struct passwd  *getpwnam ();
#endif

/*    MAIN */

/* ARGSUSED */

main (argc, argv, envp)
int	argc;
char  **argv,
      **envp;
{
    int	    nfds,
	    fd,
	    sd;
    fd_set  sfds;
    struct sockaddr_in in_socket;
    register struct sockaddr_in *isock = &in_socket;

    arginit (argv);
    envinit ();

    if ((sd = start_tcp_server (&lo_socket, SOMAXCONN,
				debug ? SO_DEBUG : 0, 0)) == NOTOK)
	adios ("failed", "start_tcp_server");
#ifdef	FIOCLEX
    (void) ioctl (sd, FIOCLEX, NULLCP);
#endif

    (void) setgid (gid);
    (void) setuid (uid);

    nfds = sd + 1;

    FD_ZERO (&sfds);
    FD_SET (sd, &sfds);

    for (;;) {
	fd_set	ifds;

	if (dishpid == NOTOK || kill (dishpid, 0) == NOTOK)
	    (void) start_dish (1);

	ifds = sfds;	/* struct copy */
	switch (xselect (nfds, &ifds, NULLFD, NULLFD, 5 * 60L)) {
	    case OK:
		continue;

	    case NOTOK:
#ifdef	BSD42
		if (errno == EINTR)
		    continue;
#endif
	        adios ("failed", "xselect");

	    default:
		if (!FD_ISSET (sd, &ifds))
		    continue;
	        break;
	}

	if ((fd = join_tcp_client (sd, isock)) == NOTOK) {
	    advise (LLOG_EXCEPTIONS, "failed", "join_tcp_client");
	    continue;
	}
	advise (LLOG_NOTICE, NULLCP, "incoming connection from %s/%d",
		inet_ntoa (isock -> sin_addr), ntohs (isock -> sin_port));

	if (debug)
	    break;

	switch (fork ()) {
	    case NOTOK:
		advise (LLOG_EXCEPTIONS, "failed", "fork");
		(void) close_tcp_socket (fd);
		continue;

	    case OK:	/* child continues listening for new connections */
		(void) close_tcp_socket (fd);
		isodexport (NULLCP);
		ll_hdinit (pgm_log, myname);
		dishpid = NOTOK;
		continue;

	    default:	/* parent handles request 'cause dish knows its pid */
#ifdef	BSD42
		(void) signal (SIGCHLD, SIG_DFL);
#else
		(void) signal (SIGCLD, SIG_DFL);
#endif
		break;
	}
	break;
    }

    (void) close_tcp_socket (sd);
    (void) dadser (fd, isock);
    advise (LLOG_NOTICE, NULLCP, "terminating");
    (void) close_tcp_socket (fd);

    _exit (0);
}

/*  */

/* ARGSUSED */

#ifdef	BSD42
static SFD  dishser (sig, code, sc)
int	sig;
long	code;
struct sigcontext *sc;
#else
static SFD  dishser (i)
int	i;
#endif
{
    int	    pid;
#ifdef	BSD42
    union wait status;
#else
    int	    status;

    (void) signal (SIGCLD, dishser);
#endif

    while ((pid = wait (&status)) != NOTOK && pid != dishpid)
	continue;

    if (pid == dishpid) {
	advise (LLOG_NOTICE, NULLCP, "dish has terminated");
	doomsday++;
    }
}

/*  */

/* ARGSUSED */

static	dadser (fd, isock)
int	fd;
struct sockaddr_in *isock;
{
    int	    i,
	    nfds;
    char   buffer[BUFSIZ],
	  *vec[NVEC + NSLACK + 1];
    fd_set  sfds;

    if ((dishpid == NOTOK || kill (dishpid, 0) == NOTOK)
	    && start_dish (0) == NOTOK) {
	da_response (fd, "-ERR unable to start service");
	sleep (5);
	return;
    }

    da_response (fd, "+OK %s", getenv ("DISHPROC"));
#ifdef	BSD42
    (void) signal (SIGCHLD, dishser);
#else
    (void) signal (SIGCLD, dishser);
#endif

    nfds = fd + 1;

    FD_ZERO (&sfds);
    FD_SET (fd, &sfds);

    for (;;) {
	int	eof,
		n;
	register char   *cp,
			*ep;
	fd_set	ifds;

	ifds = sfds;	/* struct copy */
	switch (xselect (nfds, &ifds, NULLFD, NULLFD,
			 doomsday ? 5 * 60L : NOTOK)) {
	    case OK:
	        if (doomsday) {
		    da_response (fd, "-ERR time-out due to inactivity");
		    return;
		}
	        continue;

	    case NOTOK:
		if (errno == EINTR && doomsday)
		    continue;
		adios ("failed", "xselect");

	    default:
		if (!FD_ISSET (fd, &ifds))
		    continue;
		break;
	}

	eof = 0;
	for (ep = (cp = buffer) + sizeof buffer - 1; cp < ep;) {
	    switch (read_tcp_socket (fd, cp, sizeof *cp)) {
		case NOTOK:
	            advise (LLOG_EXCEPTIONS, "failed",
			    "read_tcp_socket on control connection");
		    goto were_history;

		case OK:
		    advise (LLOG_NOTICE, NULLCP,
			    "client closed control connection");
		    eof = 1;
		    break;

		default:
		    switch (*cp & 0xff) {
			case '\n':
			    cp++;
			    break;

			case 255:	/* IAC */
			    if (read_tcp_socket (fd, cp, sizeof *cp) != 1)
				adios ("control connection",
				       "eof or error reading");
			    continue;

			default:
			    cp++;
			    continue;
		    }
		    break;
	    }
	    break;
	}
	*cp = NULL;

	if (eof && (cp == buffer))
	    break;

	if (cp > buffer)
	    cp--;
	if (*cp == '\n') {
	    *cp = NULL;
	    if (cp > buffer)
		cp--;
	}
	if (*cp == '\r')
	    *cp = NULL;

	if (debug)
	    fprintf (stderr, "---> %s\n", buffer);

	if ((n = str2vec (buffer, vec)) < 1)
	    cp = "-ERR null command";
	else
	    if (lexequ (vec[0], "intr") == 0 && n == 1)
		(void) kill (dishpid, SIGINT), cp = "+OK interrupted";
	    else
		if (lexequ (vec[0], "quit") == 0 && n == 1)
		    eof = 1, cp = "+OK";
		else
		    if (lexequ (vec[0], "stat") == 0 && n == 1)
			cp = kill (dishpid, 0) != NOTOK ? "+OK" : "-ERR";
		    else
			cp = "-ERR command not understood";

	da_response (fd, cp);

	if (eof)
	    break;
    }
#ifdef	BSD42
    (void) signal (SIGCHLD, SIG_DFL);
#else
    (void) signal (SIGCLD, SIG_DFL);
#endif
    advise (LLOG_NOTICE, NULLCP, "terminating dish");
    (void) kill (dishpid, SIGHUP);

    for (i = 5; i-- <= 0; sleep (1))
	if (kill (dishpid, 0) == NOTOK)
	    break;

were_history: ;
    (void) kill (dishpid, SIGKILL);
}

/*  */

#ifndef	lint
static int  da_response (va_alist)
va_dcl
{
    int	    val;
    va_list ap;

    va_start (ap);

    val = _da_response (ap);

    va_end (ap);

    return val;
}

static int  _da_response (ap)
va_list ap;
{
    int	    cc,
	    fd,
	    len;
    char    buffer[BUFSIZ];

    fd = va_arg (ap, int);

    _asprintf (buffer, NULLCP, ap);
    if (debug)
	fprintf (stderr, "<--- %s\n", buffer);

    (void) strcat (buffer, "\r\n");
    len = strlen (buffer);

    if (write_tcp_socket (fd, buffer, len) != len)
	adios ("failed", "write_tcp_socket to control connection");
}
#else
/* VARARGS1 */

static	da_response (fd, fmt)
int	fd;
char   *fmt;
{
    da_response (fd, fmt);
}
#endif

/*  */

static int  start_dish (binding)
int	binding;
{
    int	    ntries,
	    vecp;
    char    buffer[BUFSIZ],
	   *vec[5];

    if (dishpid != NOTOK) {
	(void) kill (dishpid, SIGKILL);
	dishpid = NOTOK;
    }

    (void) unsetenv ("DISHPROC");
    (void) unsetenv ("DISHPARENT");
    (void) unsetenv ("DISPLAY");
    (void) unsetenv ("TERM");
    (void) unsetenv ("TERMCAP");

    if (get_dish_sock (&dishsock, getpid (), 0) == NOTOK) {
	advise (LLOG_EXCEPTIONS, NULLCP, "get_dish_sock failed");
	return NOTOK;
    }

    (void) strcpy (buffer, _isodefile (isodebinpath, "dish"));

fork_again: ;
    advise (LLOG_NOTICE, NULLCP, "starting %s on %s", buffer,
	    getenv ("DISHPROC"));
   
    switch (dishpid = vfork ()) {
	case NOTOK:
	    advise (LLOG_EXCEPTIONS, "fork", "unable to");
	    return NOTOK;

	case OK:
	    vecp = 0;
	    vec[vecp++] = "dish";
	    vec[vecp++] = "-pipe";
	    vec[vecp++] = "-dad";
	    vec[vecp++] = "-fast";
	    vec[vecp] = NULL;
	    (void) execv (buffer, vec);

	    advise (LLOG_FATAL, buffer, "unable to exec");
	    _exit (1);
	    /* NOTREACHED */

	default:
	    for (ntries = 5; ntries-- > 0;) {
		int	n,
			sd;

		if ((sd = start_tcp_client ((struct sockaddr_in *) 0, 0))
		        == NOTOK)
		    break;
		n = join_tcp_server (sd, &dishsock);
		(void) close_tcp_socket (sd);

		if (n != NOTOK)
		    break;

		sleep (5);

		if (kill (dishpid, 0) == NOTOK) {
		    advise (LLOG_EXCEPTIONS, NULLCP, "dish not started");
		    goto fork_again;
		}
	    }
	    if (ntries <= 0) {
		advise (LLOG_EXCEPTIONS, NULLCP, "unable to start service");
		(void) kill (dishpid, SIGKILL);
		dishpid = NOTOK;
	    }
	    if (dishpid != NOTOK && binding)
		rcfile ();
	    break;
    }

    return (dishpid != NOTOK ? OK : NOTOK);
}

/*  */

static	rcfile ()
{
    register char   *bp;
    char    buffer[BUFSIZ],
	    command[BUFSIZ],
	   *vec[NVEC + 1];
    FILE   *fp;

    if ((fp = fopen (isodefile ("fredrc", 0), "r")) == NULL)
	return;

    while (fgets (buffer, sizeof buffer, fp)) {
	if (*buffer == '#')
	    continue;
	if (bp = index (buffer, '\n'))
	    *bp = NULL;

	bzero ((char *) vec, sizeof vec);
	switch (str2vec (buffer, vec)) {
	    case 0:
	    case 1:
		continue;

	    default:
		if (lexequ (vec[0], "area") == 0)
		    break;
		continue;
	}

	(void) sprintf (command, "moveto \"%s\"\n", vec[2] ? vec[2] : vec[1]);

	if (rcpipe (command) == NOTOK) {
failed: ;
	    advise (LLOG_EXCEPTIONS, NULLCP, "pre-caching failed!");
	    (void) kill (dishpid, SIGKILL);
	    dishpid = NOTOK;
	    break;
	}
    }
    (void) fclose (fp);

    if (rcpipe ("unbind -noquit\n") == NOTOK)
	goto failed;
}

/*  */

static int  rcpipe (command)
char   *command;
{
    int	    cc,
	    len,
	    result,
	    sd;
    char    buffer[BUFSIZ];

    if (debug) {
	fprintf (stderr, "%s", command);
	(void) fflush (stderr);
    }
    if ((sd = start_tcp_client ((struct sockaddr_in *) 0, 0)) == NOTOK)
	return OK;
    result = NOTOK;
    if (join_tcp_server (sd, &dishsock) == NOTOK) {
	advise (LLOG_EXCEPTIONS, "failed", "join to dish");
	goto done;
    }

    cc = send (sd, command, len = strlen (command), 0);
    if (cc != len) {
	if (cc == NOTOK)
	    advise (LLOG_EXCEPTIONS, "failed", "write to dish");
	else
	    advise (LLOG_EXCEPTIONS, NULLCP, "write to dish truncated");
	goto done;
    }

    for (;;) {
	if ((cc = recv (sd, buffer, sizeof buffer - 1, 0)) == NOTOK) {
	    advise (LLOG_EXCEPTIONS, "failed", "read from dish");
	    goto done;
	}
	if (cc == 0)
	    break;
	if (debug) {
	    buffer[cc] = NULL;
	    fprintf (stderr, "%*.*s", cc, cc, buffer);
	}
    }
    if (debug) {
	fprintf (stderr, "///////\n");
	(void) fflush (stderr);
    }
    result = OK;

done: ;
    (void) close_tcp_socket (sd);

    return result;
}

/*  */

#ifdef	BSD42
/* ARGSUSED */

static SFD  chldser (sig, code, sc)
int	sig;
long	code;
struct sigcontext *sc;
{
    union wait status;

    while (wait3 (&status, WNOHANG, (struct rusage *) NULL) > 0)
	continue;
}
#endif

/*  */

static void  arginit (vec)
char	**vec;
{
    int	    port;
    register char  *ap;
    register struct sockaddr_in *lsock = &lo_socket;
    register struct servent *sp;

    if (myname = rindex (*vec, '/'))
	myname++;
    if (myname == NULL || *myname == NULL)
	myname = *vec;

    isodetailor (myname, 0);
    ll_hdinit (pgm_log, myname);

    bzero ((char *) lsock, sizeof *lsock);
    lsock -> sin_family = AF_INET;
    
    if ((sp = getservbyname ("da", "tcp")) == NULL) {
	advise (LLOG_EXCEPTIONS, NULLCP, "%s/%s: unknown service",
		"tcp", "da");
	lsock -> sin_port = htons ((u_short) 411);
    }
    else
	lsock -> sin_port = sp -> s_port;

    for (vec++; ap = *vec; vec++) {
	if (*ap == '-')
	    switch (*++ap) {
		case 'd':
		    debug++;
		    continue;
		
		case 'p': 
		    if ((ap = *++vec) == NULL
			    || *ap == '-'
			    || (port = atoi (ap)) <= 0)
			adios (NULLCP, "usage: %s -p portno", myname);
		    lsock -> sin_port = htons ((u_short) port);
		    continue;

		default: 
		    adios (NULLCP, "-%s: unknown switch", ap);
	    }

	adios (NULLCP, "usage: %s [switches]", myname);
    }

    {
	register struct passwd *pw = getpwnam ("fred");

	if (pw && pw -> pw_uid)
	    uid = pw -> pw_uid, gid = pw -> pw_gid;
    }
}

/*  */

static void  envinit () {
    int     i,
            sd;

    nbits = getdtablesize ();

    if (debug == 0 && !(debug = isatty (2))) {
	for (i = 0; i < 5; i++) {
	    switch (fork ()) {
		case NOTOK: 
		    sleep (5);
		    continue;

		case OK: 
		    break;

		default: 
		    _exit (0);
	    }
	    break;
	}

	(void) chdir ("/");

	if ((sd = open ("/dev/null", O_RDWR)) == NOTOK)
	    adios ("/dev/null", "unable to read");
	if (sd != 0)
	    (void) dup2 (sd, 0), (void) close (sd);
	(void) dup2 (0, 1);
	(void) dup2 (0, 2);

#ifdef	SETSID
	if (setsid () == NOTOK)
	    advise (LLOG_EXCEPTIONS, "failed", "setsid");
#endif
#ifdef	TIOCNOTTY
	if ((sd = open ("/dev/tty", O_RDWR)) != NOTOK) {
	    (void) ioctl (sd, TIOCNOTTY, NULLCP);
	    (void) close (sd);
	}
#else
#ifdef	SYS5
	(void) setpgrp ();
	(void) signal (SIGINT, SIG_IGN);
	(void) signal (SIGQUIT, SIG_IGN);
#endif
#endif

#ifdef	BSD42
	(void) signal (SIGCHLD, chldser);
#else
	(void) signal (SIGCLD, SIG_IGN);
#endif
    }
    else
	ll_dbinit (pgm_log, myname);

#ifndef	sun		/* damn YP... */
    for (sd = 3; sd < nbits; sd++)
	if (pgm_log -> ll_fd != sd)
	    (void) close (sd);
#endif

    (void) signal (SIGPIPE, SIG_IGN);

    ll_hdinit (pgm_log, myname);
    advise (LLOG_NOTICE, NULLCP, "starting");
}

/*    ERRORS */

#ifndef	lint
void	adios (va_alist)
va_dcl
{
    va_list ap;

    va_start (ap);
    
    _ll_log (pgm_log, LLOG_FATAL, ap);

    va_end (ap);

    _exit (1);
}
#else
/* VARARGS */

void	adios (what, fmt)
char   *what,
       *fmt;
{
    adios (what, fmt);
}
#endif


#ifndef	lint
void	advise (va_alist)
va_dcl
{
    int	    code;
    va_list ap;

    va_start (ap);
    
    code = va_arg (ap, int);

    _ll_log (pgm_log, code, ap);

    va_end (ap);
}
#else
/* VARARGS */

void	advise (code, what, fmt)
char   *what,
       *fmt;
int	code;
{
    advise (code, what, fmt);
}
#endif