view support/pop/popd.c @ 10:a6481689f99c

*** empty log message ***
author kono
date Wed, 06 Dec 2006 03:17:53 +0900
parents bce86c4163a3
children 441a2190cfae
line wrap: on
line source

/* popd.c - the POP server */
#ifndef	lint
static char ident[] = "@(#)$Id$";
#endif	/* lint */

/* Author:	Marshall T. Rose	<MRose@UDel>	(MTR)
		Department of Computer Science and Information Sciences
		University of Delaware
		Newark, DE  19716
		302/451-1951

   Date:	Sun Oct 28 16:23:26 1984
 */

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include "../h/strings.h"
#include "syslog.h"
#include <sys/types.h>
#include <sys/file.h>
#ifndef	NOIOCTLH
#include <sys/ioctl.h>
#endif
#include <sys/socket.h>
#include <sys/time.h>
#ifdef	SIGTSTP
#include <sys/resource.h>
#include <sys/wait.h>
#endif
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#ifdef KPOP
#include <krb.h>

static Key_schedule schedule;
static KTEXT_ST ticket;
static AUTH_DAT kdata;
#endif
#ifdef	SYS5
#include <fcntl.h>
#endif


#ifdef	SYS5
#define	u_short	ushort
#endif

#define	NOTOK	(-1)
#define	OK	0

#define	NULLCP	((char *) 0)
#define NULLRP	((struct rusage *) 0)

#define	FAST			/* fast start-up of BBoards */

/*  */

#if !defined(BSD44) && !defined(__GNU_LIBRARY__)
extern int  sys_nerr;
extern char *sys_errlist[];
#if !defined(KPOP) && defined(RESTART)
extern char *sys_siglist[];
#endif /* !KPOP & RESTART */
#endif /* BSD44 */


int  debug = 0;
static int  nbits = ((sizeof (int)) * 8);
static int  options = 0;

#ifndef	POPSERVICE
#define	POPSERVICE	"pop"
#endif

char *myname = "popd";
char myhost[BUFSIZ];
static char *myprotocol = "tcp";
static char *myservice = POPSERVICE;

static struct sockaddr_in   in_socket;
static struct sockaddr_in  *isock = &in_socket;

#ifdef KPOP
static AUTH_DAT kdata;
#endif

static TYPESIG	chldser ();
void	padios (), padvise ();

static	server(), arginit(), envinit();
/*  */

/* ARGSUSED */

main (argc, argv, envp)
int     argc;
char  **argv,
      **envp;
{
#ifdef KPOP
    int     i;
#else
    int     fd,
            sd;
    int	    on = 1;
    struct servent *sp;
    struct sockaddr_in  out_socket,
                       *osock = &out_socket;
#endif

#ifdef KPOP
    i = sizeof(in_socket);
    if (getpeername(0, &in_socket, &i) < 0)
      padios("getpeername", "bad status");
#else
    if ((sp = getservbyname (myservice, myprotocol)) == NULL)
	padios (NULLCP, "%s/%s: unknown service", myprotocol, myservice);
    isock -> sin_family = AF_INET;
    isock -> sin_port = sp -> s_port;
    isock -> sin_addr.s_addr = INADDR_ANY;
#endif
    arginit (argv);
    envinit ();

#ifndef KPOP
#ifdef	RESTART
    for (;;) {
	char    reason[BUFSIZ];
#if defined(BSD42) && !defined(WAITINT)
	union wait status;

/* Solaris <sys/wait.h> has WCOREDUMP but SunOS does not */
#if !defined(WCOREDUMP)
#define WCOREDUMP(x) (((union wait*)&(x))->w_coredump)
#endif /* !defined(WCOREDUMP) */

#else /* defined(BSD42) && !defined(WAITINT) */
	int status;
#endif /* defined(BSD42) && !defined(WAITINT) */

	switch (fork ()) {
	    case NOTOK: 
		sleep (5);
		continue;

	    case OK: 
		break;

	    default: 
		sleep (60);
#ifdef SYS5
		(void) wait(&status);
#else
		(void) wait3 (&status, 0, NULLRP);
#endif
		if (WIFEXITED (status))
		    (void) sprintf (reason, "exit=0%o", WEXITSTATUS (status));
		else
		    if (WIFSIGNALED (status))
			(void) sprintf (reason, "signal=%s%s",
				WTERMSIG (status) < NSIG
				? sys_siglist[WTERMSIG (status)] : "unknown",
				WCOREDUMP (status) ? " (core dumped)" : NULL);
		    else
			(void) strcpy (reason, "stopped(!!)");
		padvise (NULLCP, LOG_WARNING, "%s/%s server has terminated -- %s",
			sp -> s_proto, sp -> s_name, reason);
		continue;
	}
	break;
    }

    closelog ();
#ifndef	BSD43
    openlog (myname, LOG_PID);
#else	/* BSD43 */
    openlog (myname, LOG_PID, LOG_DAEMON);
#endif	/* BSD43 */
    padvise (NULLCP, LOG_INFO, "restart");
#endif	/* RESTART */

/*  */

    if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == NOTOK)
	padios ("socket", "unable to create");
#ifndef	BSD43
    if (options & SO_DEBUG)
	if (setsockopt (sd, SOL_SOCKET, SO_DEBUG, NULL, 0) == NOTOK)
	    padvise ("SO_DEBUG", LOG_WARNING, "unable to set socket option");
    if (setsockopt (sd, SOL_SOCKET, SO_KEEPALIVE, NULL, 0) == NOTOK)
	padvise ("SO_KEEPALIVE", LOG_WARNING, "unable to set socket option");
#else	/* BSD43 */
    if (options & SO_DEBUG)
	if (setsockopt (sd, SOL_SOCKET, SO_DEBUG, &on, sizeof on) == NOTOK)
	    padvise ("SO_DEBUG", LOG_WARNING, "unable to set socket option");
    if (setsockopt (sd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof on) == NOTOK)
	padvise ("SO_KEEPALIVE", LOG_WARNING, "unable to set socket option");
#endif	/* BSD43 */
    if (bind (sd, (struct sockaddr *) isock, sizeof *isock) == NOTOK)
	padios ("socket", "unable to bind");

#ifdef	SIGCHLD
    (void) signal (SIGCHLD, chldser);
#endif	/* SIGCHLD */
#ifndef SOMAXCONN
/* No limit on the number of connections? this seems wrong... */
#define SOMAXCONN 5
#endif
    (void) listen (sd, SOMAXCONN);
#endif /* KPOP */
#ifdef	FAST
    popinit ();
#endif	/* FAST */
#ifndef KPOP
    for (;;) {
	int     i = sizeof *osock;

	if ((fd = accept (sd, (struct sockaddr *) osock, &i)) == NOTOK) {
	    if (errno != EINTR)
		padvise ("socket", LOG_WARNING,
		    "unable to accept connection on");
	    continue;
	}
#endif
#ifdef	FAST
	popassert ();
#endif	/* FAST */
#ifndef KPOP
	switch (fork ()) {
	    case OK: 
		(void) close (sd);
#ifdef	SIGCHLD
		(void) signal (SIGCHLD, SIG_DFL);
#endif	/* SIGCHLD */
		server (fd, osock);
		_exit (0);

	    case NOTOK: 
		padvise ("socket", LOG_WARNING,
		    "no forks, so rejecting connection on");
	    default: 
		(void) close (fd);
	}
    }
#else
    server (0, isock);
#endif
}

/*  */

struct hostent *mh_gethostbyname();
struct hostent *mh_gethostbyaddr();

static	server (fd, sin)
int	fd;
struct sockaddr_in *sin;
{
    u_short port;
    struct hostent *hp;
    struct in_addr *addr;
#ifdef KPOP
    int auth;
    int sin_len;
    struct sockaddr_in faddr;
    char instance[INST_SZ];
    char version[9];
    char user[ANAME_SZ];
#endif

    closelog ();
#ifndef	BSD43
    openlog (myname, LOG_PID);
#else	/* BSD43 */
    openlog (myname, LOG_PID, LOG_DAEMON);
#endif	/* BSD43 */
    port = ntohs (sin -> sin_port);
    addr = &sin -> sin_addr;
    hp = mh_gethostbyaddr ((char *)addr, sizeof *addr, sin -> sin_family);
    padvise (NULLCP, LOG_INFO, "servicing %s/%d",
	    hp ? hp -> h_name : inet_ntoa (*addr), port);
    if (fd != 0)
      {
	(void) dup2 (fd, 0);
	(void) close (fd);
      }
    (void) dup2 (0, 1);

#ifdef KPOP
    sin_len = sizeof (struct sockaddr_in);
    if (getpeername(0, &faddr, &sin_len) < 0) {
      padvise("getpeername", LOG_INFO, "");
      exit(1);
    }
    strcpy(instance, "*");
    auth = krb_recvauth(0L, 0, &ticket, "pop", instance,
			&faddr, (struct sockaddr_in *)NULL,
			&kdata, "", schedule, version);
    if (auth == KSUCCESS)
      auth = krb_kntoln(&kdata, user);

    if (auth != KSUCCESS) {
      padvise(NULLCP, LOG_INFO, "bad kerberos data, not ok'ing");
      kpop (0, 1, NULLCP, NULLCP, auth); /* respond(NOTOK, krb_err_txt[auth]); */
    } else {
      kpop (0, 1, user, (hp ? hp -> h_name : NULLCP), 0);
    }
#else
    pop (0, 1, sin -> sin_family == AF_INET && port < IPPORT_RESERVED && hp,
	    hp ? hp -> h_name : NULLCP);
#endif
}
	
/*  */

static	arginit (vec)
char	**vec;
{
    int	    port;
    register char  *ap;
    struct hostent *hp;

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

    (void) gethostname (myhost, sizeof myhost);
    if (hp = mh_gethostbyname (myhost))
	(void) strcpy (myhost, hp -> h_name);
#if !defined(BSD42) && !defined(linux) && defined(_NFILE)
    nbits = _NFILE;
#else   /* BSD42 || linux */
    nbits = getdtablesize ();
#endif  /* BSD42 || linux */

    for (vec++; ap = *vec; vec++) {
	if (*ap == '-')
	    switch (*++ap) {
		case 'd': 
		    options |= SO_DEBUG;
		    continue;

		case 'p': 
		    if ((ap = *++vec) == NULL
			    || *ap == '-'
			    || (port = atoi (ap)) <= 0)
			padios (NULLCP, "usage: %s -p portno", myname);
		    isock -> sin_port = htons ((u_short) port);
		    continue;

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

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

/*  */

static  envinit () {
#ifndef KPOP
    int     i,
            sd;

    if (!(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)
	    padios ("/dev/null", "unable to read");
	if (sd != 0)
	    (void) dup2 (sd, 0), (void) close (sd);
	(void) dup2 (0, 1);
	(void) dup2 (0, 2);

	if ((sd = open ("/dev/tty", O_RDWR)) != NOTOK) {
#ifdef	TIOCNOTTY
	    (void) ioctl (sd, TIOCNOTTY, NULLCP);
#endif	/* TIOCNOTTY */
	    (void) close (sd);
	}
    }

    for (sd = 3; sd < nbits; sd++)
	(void) close (sd);
#endif /* KPOP */

    (void) signal (SIGPIPE, SIG_IGN);

#ifndef	BSD43
    openlog (myname, LOG_PID);
#else	/* BSD43 */
    openlog (myname, LOG_PID, LOG_DAEMON);
#endif	/* BSD43 */
    padvise (NULLCP, LOG_INFO, "starting");
    if (debug)
	padvise (NULLCP, LOG_DEBUG, "options=0x%x port=%d",
		options, ntohs (isock -> sin_port));
}

/*  */

#ifndef KPOP

/* ARGSUSED */

#ifdef	SIGCHLD

static TYPESIG chldser (sig, code, sc)
int	sig;
long    code;
struct sigcontext *sc;
{
#if defined(BSD42) && !defined(WAITINT)
    union wait status;
#else
    int status;
#endif

#ifdef SVR4
    while (waitpid((pid_t)-1, &status, WNOHANG) > 0)
	continue;
#else
#ifdef SYS5
    /* I don't know... this might work.. TN 3 Feb 94 */
    alarm(1);
    while (wait (&status) > 0)
      continue;
    alarm(0);
#else
    while (wait3 (&status, WNOHANG, NULLRP) > 0)
	continue;
#endif
#endif /* SVR4 */
#if defined(SYS5)
    /*
    ** SYS5 needs to have the signal reset everytime it pops
    */
    (void) signal (SIGCHLD, chldser);
#endif /* SYS5 */
}

#endif /* SIGCHLD */
#endif /* KPOP */

/*  */

/* VARARGS2 */

void	padios (what, fmt, a, b, c, d, e, f, g, h, i, j)
char   *what,
       *fmt,
       *a,
       *b,
       *c,
       *d,
       *e,
       *f,
       *g,
       *h,
       *i,
       *j;
{
    padvise (what, LOG_ERR, fmt, a, b, c, d, e, f, g, h, i, j);
    _exit (1);
}

/*  */

/* VARARGS3 */

void	padvise (what, code, fmt, a, b, c, d, e, f, g, h, i, j)
char   *what,
       *fmt,
       *a,
       *b,
       *c,
       *d,
       *e,
       *f,
       *g,
       *h,
       *i,
       *j;
int	code;
{
    int     eindex = errno;
    char    buffer[BUFSIZ];

    (void) sprintf (buffer, fmt, a, b, c, d, e, f, g, h, i, j);
    if (what)
	if (eindex > 0 && eindex < sys_nerr)
	    syslog (code, "%s %s: %s", buffer, what, sys_errlist[eindex]);
	else
	    syslog (code, "%s %s: Error %d", buffer, what, eindex);
    else
	syslog (code, "%s", buffer);

    if (debug) {
	fprintf (stderr, "[%d] %s", code, buffer);
	if (what)
	    (void) fputc (' ', stderr), perror (what);
	else
	    (void) fputc ('\n', stderr);
	(void) fflush (stderr);
    }

    errno = eindex;
}

struct hostent *
mh_gethostbyaddr(addr, len, type)
	char *addr;
	int len;
	int type;
{
#if defined(SOLARIS) && SOLARIS < 20400
# if SOLARIS == 20300
	static struct hostent hp;
	static char buf[1000];
	extern struct hostent *_switch_gethostbyaddr_r();

	return _switch_gethostbyaddr_r(addr, len, type, &hp, buf, sizeof(buf), &h_errno);
# else
	extern struct hostent *__switch_gethostbyaddr();

	return __switch_gethostbyaddr(addr, len, type);
# endif
#else
	return gethostbyaddr(addr, len, type);
#endif
}