ftp.nice.ch/pub/next/unix/network/system/cap.5.0.s.tar.gz#/cap_5.0/applications/aufs/aufs.c

This is aufs.c in view mode; [Download] [Up]

/*
 * $Author: cck $ $Date: 88/05/19 13:11:35 $
 * $Header: aufs.c,v 1.53 88/05/19 13:11:35 cck Rel $
 * $Revision: 1.53 $
*/

/*
 * aufs - UNIX AppleTalk Unix File Server
 *
 *
 * AppleTalk package for UNIX (4.2 BSD).
 *
 * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the
 * City of New York.
 *
 * Edit History:
 *
 *  March 1987 	Schilit		Created.
 *
 */

char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the City of New York";

#include <stdio.h>
#include <sys/param.h>
#ifndef _TYPES
 /* assume included by param.h */
#include <sys/types.h>
#endif
#include <sys/file.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <netinet/in.h>		/* for htons, etc. */
#include <signal.h>

#include <netat/appletalk.h>		/* include appletalk definitions */
#include <netat/compat.h>
#include <netat/afp.h>
#include "afps.h"			/* server includes */

#ifdef USETIMES
# include <time.h>
#endif

#ifndef NOWAIT3
# include <sys/wait.h>
#endif

#ifdef USESTRINGDOTH
# include <string.h>
#else
# include <strings.h>
#endif

#ifdef USEVPRINTF
# include <varargs.h>
#endif

#ifdef NOPGRP
# define NOSHUTDOWNCODE
#endif

/* known attention codes */
#define AFPSHUTDOWNTIME(x) (0x8000|((x)&0xfff))
#define AFPSHUTDOWNCANCEL (0x8fff)
#define AFPSHUTDOWNNOW (0x8000)

char *mytod();			/* return tod */

export int statflg = FALSE;	/* default is not to show stats */

private char *sysvolfile = NULL; /* system afpvols file */
private char *srvrname = NULL;
private char *passwdlookaside = NULL; /* local password file??? */
private char *guestid = NULL;	/* any guest ids? */
private char *coredir = NULL;	/* place to put coredumps */
export int mcs, qs;		/* maxcmdsize and quantum size */
export int sqs = atpMaxNum;	/* maximum send quantum */
export int n_rrpkts = atpMaxNum; /* maximum send quantum to allow remote */
				/* (used in spwrtcontinue) */
private int maxsess = 10;

private EntityName srvr_entity_name; /* our entity name */
private char zonetorun[34];	/* zone to run in if different than std. */
private char logfile[MAXPATHLEN]; /* log file name if any */
private int parent_pid;		/* pid of main server */
private int mypid;		/* pid of running process */
private int nomoresessions = FALSE; /* set true of out of asp sessions */
private int sesscount = 0;	/* number of asp sessions active */
private int cno = -1;		/* current connection */
#ifndef NOSHUTDOWNCODE
private int minutes_to_shutdown = -1;
#endif

/* if wait3 and usetimes isn't set, then we handle rusage from wait3 */
#ifndef NOWAIT3
# ifndef USETIMES
#  define DORUSAGE
# endif
#endif

/*
 * CNO TO PID handling
 *
 * Must keep a table to session reference numbers (aka connection
 * numbers) to pid mappings for the server since sessions are
 * "half"-closed.  This handling was previously in the asp protocol
 * handler, but this doesn't make sense and there is much better
 * control over things here
 *
 * internal strategy at this point is to record fork terminations
 * and handling via garbage collector at a later point.  maximum time
 * to scan is around 5 seconds at this point.  to minimize scan time
 * for gc, the "dead" ones are pushed onto a stack.
 *
 * an alternative strategy is to have the signal handler write to an
 * internal pipe watched by a fdlistener.  if this is done the sleeps
 * can be made much longer.  carefully handled this will work very
 * well, but it is a little ugly.  (note: best way to do is probably
 * to have the listener set a variable and the inferior termination
 * handler write the pids out.  the scan process would then pick up
 * the pids from the pipe.)
 *
*/

typedef struct cno_to_pid {
  int state;			/* back reference */
#define CP_NOTINUSE 0x1		/* to mark not in use */
#define CP_RUNNING 0x2		/* inferior is running? */
#define CP_TIMEDOUT 0x4		/* tickle timeout on inferior */
  int pid;			/* recorded pid */
  long timeval;			/* time when pid died */
#ifdef NOWAIT3
  int status;
#else
  union wait status;
#endif
#ifdef DORUSAGE
  struct rusage rusage;
#endif
} CTP, *CTPP;

private struct cno_to_pid *ctp_tab;
private int *ctp_stack;
private int ctp_stack_cnt;

usage(name)
char *name;
{
  char *DBLevelOpts();

  fprintf(stderr,"usage: %s [-d cap-flags] [-a afp-levels] ",name);
  fprintf(stderr,"[-n name] [-t packets] [-s] [-V afpvols]\n");
  fprintf(stderr,"\t-d for CAP debugging flags:\n");
  fprintf(stderr,"\t   l = lap, d = ddp, a = atp, n = nbp, p = pap,");
  fprintf(stderr,"i = ini, s = asp\n");
  fprintf(stderr,"\t-Ds[shct] for ASP debug limiting flags:\n");
  fprintf(stderr,"\t   s = socket, h = handlers, c = call, t = tickle\n");
  fprintf(stderr,"\t-a for AFP debugging by level (or setenv AUFSDEBUG):\n");
  fprintf(stderr,"\t   %s\n",DBLevelOpts());
  fprintf(stderr,"\t-t for packet traces (or setenv AUFSTRACE):\n");
  fprintf(stderr,"\t   [I|O|B]CmdName\n");
  fprintf(stderr,"\t-n for setting the server's name\n");
  fprintf(stderr,"\t-s for statistics\n");
  fprintf(stderr,"\t-V VolsFile for server wide afp volumes\n");
  fprintf(stderr,"\t-G <username> to set guest id for <anonymous> logins\n");
  fprintf(stderr,"\t-P LookAsidePasswordFile for scrambled transactions\n");
  fprintf(stderr,"\t-U <number> to allow <number> sessions\n");
  fprintf(stderr,"\t-c directory to specify a directory to coredump to\n");
  fprintf(stderr,"\t-l file to send logfile to other than <servername>.log\n");
  fprintf(stderr,"\t-S <n> limit responses to <n> packets\n");
  fprintf(stderr,"\t-R <n> limit remote to sending <n> packets\n");
  fprintf(stderr,"\nExample: %s -t 'bdelete irename' -a 'file fork dir' -s\n",
	  name);
  exit(1);
}

/*
 * generate default name from host name
 *
*/
char *
afs_default_name()
{
  char hostname[255];
  static char afsname[255];
  char *ap;

  gethostname(hostname,(int) sizeof(hostname));
  if ((ap = index(hostname,'.')) != NULL)
    *ap = '\0';				/* remove trailing domain name */
  sprintf(afsname,"%s Aufs",hostname);
  return(afsname);
}

doargs(argc,argv)
int argc;
char **argv;
{
  int c;
  extern char *optarg;
  extern int optind;
  
  while ((c = getopt(argc,argv,"a:d:D:n:N:t:sV:U:G:P:c:l:z:S:R:")) != EOF) {
    switch (c) {
    case 'z':
      strncpy(zonetorun, optarg, 32);
      break;
    case 'l':
      strncpy(logfile, optarg, sizeof(logfile)-1);
      break;
    case 'c':
      coredir = optarg;
      break;
    case 's':
      statflg = TRUE;
      break;
    case 'd':
      dbugarg(optarg);			/* '-d' is debug */
      break;
    case 'D':
      if (optarg[0] != 's')
	usage(argv[0]);
      aspdebug(optarg+1);		/* call asp debug */
      break;
    case 'a':
      if (!SetDBLevel(optarg))		/* -a for afp debug */
	usage(argv[0]);
      break;
    case 'n':
    case 'N':
      srvrname = optarg;		/* '-n' to set server name */
      break;
    case 't':
      if (!SetPktTrace(optarg))
	usage(argv[0]);
      break;
    case 'V':				/* system afpvols file */
      sysvolfile = optarg;
      break;
    case 'U':
      maxsess = atoi(optarg);
      break;
    case 'P':
      passwdlookaside = optarg;
      break;
    case 'G':
      guestid = optarg;
      break;
    case 'S':
      sqs = atoi(optarg);
      if (sqs <= 0) {
	fprintf(stderr, "Must have at least one packet in a response, resetting to one\n");
	sqs = 1;
      }
      if (sqs > atpMaxNum) {
	fprintf(stderr, "No more than %d packets allowed in a response\n",
		atpMaxNum);
	sqs = atpMaxNum;
      }
      break;
    case 'R':
      n_rrpkts = atoi(optarg);
      if (n_rrpkts <= 0) {
	fprintf(stderr, "Must have at least one packet in a response, resetting to one\n");
	n_rrpkts = 1;
      }
      if (n_rrpkts > atpMaxNum) {
	fprintf(stderr, "No more than %d packets allowed in a response\n",
		atpMaxNum);
	n_rrpkts = atpMaxNum;
      }
      break;
    default:
      usage(argv[0]);
      break;
    }
  }
}


main(argc,argv)
int argc;
char **argv;
{
  int inferiordone(), timedout(), dienow();
#ifndef NOSHUTDOWNCODE
  int killnow(), killin5(), diein5();
#endif
  int err,atpskt,slsref;
  AddrBlock addr;
  int comp,comp2;
  byte *srvinfo;
  int srvinfolen;
  import word this_net;
  import byte this_node;
  char *getenv(),*env;
  int pid;
  int mask;
  byte *buf;			/* [atpMaxData]; */
  byte *rspbuf;			/* [atpMaxData*atpMaxNum]; */
  import byte aufsicon[];	/* aufs icon */
  import int aufsiconsize;	/* and its size */
  import char *aufs_versiondate;
  import int aufs_version[];
  extern int errno;
  
  /* initial os dependent items first */
  OSEnable();			/* enable OS dependent items */

  IniServer();
  srvrname = afs_default_name();	/* set default server name */
  logfile[0] = '\0';
  zonetorun[0] = '\0';
  doargs(argc,argv);			/* handle command line */

  env = getenv("AUFSTRACE");		/* See if user wants to */
  if (env != NULL)			/*  trace some packets */
    SetPktTrace(env);
  env = getenv("AUFSDEBUG");
  if (env != NULL)
    SetDBLevel(env);

  if (!DBDEB)
    fprintf(stderr,"Apple Unix File Server (%s:%s@*) starting\n",
	    srvrname,AFSTYPE);

  /* here's the place to fork off */
  if (!DBDEB) {
    if (fork())
      _exit(0);
    {
      int f;
      for (f = 0; f < 10; f++)
	(void) close(f);
    }
    (void) open("/", 0);
#ifndef NODUP2
    (void) dup2(0, 1);
    (void) dup2(0, 2);
#else
    (void)dup(0);		/* for slot 1 */
    (void)dup(0);		/* for slot 2 */
#endif
#ifdef TIOCNOTTY
    {
      int t;
      t = open("/dev/tty", 2);	
      if (t >= 0) {
	ioctl(t, TIOCNOTTY, (char *)0);
	(void) close(t);
      }
    }
#endif    
  }
  
  mypid = parent_pid = getpid();	/* remember who we are */
  if (logfile[0] == '\0')
    sprintf(logfile, "%s.log", srvrname);
  log("****************************************");
  log("Apple Unix File Server (%s:%s@*) starting", srvrname, AFSTYPE);
  log("Aufs version: %d(%d) as of %s",aufs_version[0], aufs_version[1],
      aufs_versiondate);
  if (sysvolfile != NULL) {		/* if known system vols file */
    FILE *fd = fopen(sysvolfile, "r");

    if (fd == NULL)
      log("Aufs: no such system volumes file %s", sysvolfile);
    else if (!VRdVFile(fd))	/* then try to read it now */
      log("Aufs: system volumes file %s bad",sysvolfile);
  }
  if (zonetorun[0] != '\0')
    zoneset(zonetorun);
  abInit(TRUE);				/* initialize applebus */
  nbpInit();				/* initialize nbp */
  maxsess = aspInit(maxsess);	/* init asp */
  log("%d sessions allowed",maxsess);
  if ((ctp_tab = (CTPP)malloc(sizeof(struct cno_to_pid)*maxsess)) == NULL) {
    log("couldn't malloc table for pid recording, fatal!");
  }
  if ((ctp_stack = (int *)malloc(sizeof(int)*maxsess)) == NULL) {
    log("couldn't malloc stack for pid recording, fatal!");
  }
  ctp_stack_cnt = 0;
  { int i;
    for (i = 0 ; i < maxsess; i++) {
      ctp_tab[i].state = CP_NOTINUSE;
      ctp_tab[i].pid = -1;
    }
  }
  
  tellaboutos();		/* tell about os stuff */

  if (sqs < atpMaxNum)
    log("maximum of %d packet%s will be sent on a response", sqs,
	sqs > 1 ? "s" : "");
  sqs *= atpMaxData;
  if (n_rrpkts < atpMaxNum)
    log("remote limited to %d packet%s in a response", n_rrpkts,
	n_rrpkts > 1 ? "s" : "");
  SPGetParms(&mcs, &qs);
  if (DBDEB)
    printf("Command buffer size is %d, Quantum size is %d\n", mcs, qs);
  buf = (byte *)malloc(mcs);
  rspbuf = (byte *)malloc(qs);
  if (buf == NULL || rspbuf == NULL) {
    log("memory allocation failure!\n");
    exit(999);
  }

  addr.net = addr.node = addr.skt = 0;	/* use any */
  atpskt = 0;			/* make sure we use dynamic skt */
  err = ATPOpenSocket(&addr,&atpskt);
  if (err != noErr) {
    log("ATPOpenSocket failed with error %d\n",err);
    exit(0);
  }
  
  addr.net = this_net;
  addr.node = this_node;
  addr.skt = atpskt;

  if (SrvrRegister(atpskt,srvrname,AFSTYPE,"*", &srvr_entity_name) != noErr) {
    log("SrvrRegister for %s failed...",srvrname); 
    exit(2);
  }

  if (guestid != NULL)
    allowguestid(guestid);
  allowcleartext();
  if (passwdlookaside != NULL) 
    allowrandnum(passwdlookaside);


  if (coredir)
    if (chdir(coredir) < 0) {
      perror("chdir for coredumps");
      log("chdir to %s for coredumps failed",coredir);
    } else
      log("***CORE DUMPS WILL BE IN %s",coredir);
    
  /* Get server info once and stash away*/
  srvinfolen = GetSrvrInfo(rspbuf,srvrname,aufsicon,aufsiconsize);
  srvinfo = (byte *) malloc(srvinfolen);
  bcopy(rspbuf,srvinfo,srvinfolen);

  if (DBSRV)
    PrtSrvrInfo(srvinfo,srvinfolen);
  
  /* Init asp */
  err = SPInit(&addr,srvinfo,srvinfolen,&slsref);
  if (err != noErr) {
    log("SPInit failed with code %d, fatal",err);
    exit(0);
  }

  log("Aufs Starting (%s)",srvrname);
  if (sysvolfile)
    log("System vols in '%s'",sysvolfile);
  log("SPInit Completed.  Waiting for connection...");

#ifndef NOSHUTDOWNCODE
  setpgrp(0, parent_pid);	/* set our process group */
				/* (inherited) */
#endif

#ifndef DEBUGFORK
  if (!DBDEB)
#endif
    signal(SIGCHLD, inferiordone);
#ifndef NOSHUTDOWNCODE
  signal(SIGHUP, killnow);	/* kill -HUP -- Immediate shutdown */
  signal(SIGTERM, killin5);	/* kill -TERM -- Timed (5 min) shutdown */
#endif

  do {
    pid = -1;			/* make sure zero at start */
    SPGetSession(slsref,&cno,&comp);
    if (comp > 0)
      log("Waiting for session %d to activate", cno);
    /* won't wait if we set comp above */
    while (comp > 0) {
      abSleep(sectotick(5),TRUE);
      gcinferior();
    }
    if (comp == NoMoreSessions) {
      nomoresessions = TRUE;
      log("Woops, no more sessions");
      while (nomoresessions) {
	gcinferior();
	abSleep(sectotick(5), TRUE);
      }
      log("Okay, sessions should be available");
      continue;
    }
    if (comp != noErr) {
      log("GetSession returned %d on server socket %d",comp,slsref);
      continue;
    } else {
      sesscount++;
      log("New session %d started on server socket %d, count %d",
	  cno,slsref,sesscount);
      if ((err = SPGetNetworkInfo(cno, &addr)) != noErr) {
	log("Get Network info failed with error %d", err);
      } else {
	log("Session %d from [Network %d.%d, node %d, socket %d]",
	    cno,
	    nkipnetnumber(addr.net), nkipsubnetnumber(addr.net),
	    addr.node, addr.skt);
      }
    }
#ifndef DEBUGFORK
    if (!DBDEB) {
#endif
#ifndef NOSHUTDOWNCODE
      mask = sigblock(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGTERM));
#else
      mask = sigblock(sigmask(SIGCHLD)|sigmask(SIGHUP));
#endif
      /* fork on connection - only tickle from parent */
      if ((pid = SPFork(cno, TRUE, FALSE)) < 0) {
	log("SPFork failed on session %d, last system error %d",cno, errno);
	/* try to close, but don't worry too much */
	SPCloseSession(cno, 1, 1, &comp2);
	sigsetmask(mask);
	continue;
      }
      if (pid) {
	SPTickleUserRoutine(cno, timedout, pid);
	log("pid %d starting for session %d",pid, cno);
	addinferior(cno, pid);	/* addinferior scans (phew) */
      } else {
#ifndef NOSHUTDOWNCODE
	alarm(0);		/* make sure off */
#endif
	nbpShutdown();		/* don't need this in inferior */
	signal(SIGCHLD, SIG_DFL);
#ifndef NOSHUTDOWNCODE
	signal(SIGTERM, diein5); /* die in 5 minutes */
#endif
	signal(SIGHUP, dienow);	/* superior signaled us to shutdown */
	mypid = getpid();
#ifndef NOSHUTDOWNCODE
	setpgrp(0, parent_pid);	/* set our process group */
	dying();		/* are we dying? if so, handle it*/
#endif
      }
      sigsetmask(mask);
#ifndef DEBUGFORK
    } else pid = 0;
#endif
  } while (pid != 0);		/* pid = 0 implies inferior process */

  if (pid == 0)
    inferior_handler(buf, rspbuf);

  if (statflg)
    SrvrPrintStats();			/* print stats */
  if (mypid == parent_pid)
    if ((err = SrvrShutdown(&srvr_entity_name)) != noErr)
      log("NBPRemove failed: code %d\n", err);
  exit(0);
}

inferior_handler(buf, rspbuf)
byte *buf;
byte *rspbuf;
{
  int mask;
  OSErr err;
  int comp;
  int type,rlen,rsplen;
  ReqRefNumType reqref;

  umask(0);			/* file creates have explict modes */
  for (;;) {
    SPGetRequest(cno,buf,mcs,&reqref,&type,&rlen,&comp);
    while (comp > 0) { abSleep(sectotick(60),TRUE); }
    if (comp == SessClosed || comp == ParamErr) {
      log("Session (%d) closed",cno);
      return;
    }
    if (comp < 0) {
      log("SPGetRequest failed %d",comp);
      continue;
    }
    if (rlen == 0)
      continue;
#ifndef NOSHUTDOWNCODE
    /* mask off potential race condition */
    mask = sigblock(sigmask(SIGTERM)|sigmask(SIGALRM));
#endif
    switch (type) {
    case aspWrite:
    case aspCommand:
      err = SrvrDispatch(buf,rlen,rspbuf,&rsplen,cno,reqref);
      if (DBSRV) {
	printf("Sending reply len=%d err=%s ...",rsplen,afperr(err));
	fflush(stdout);			/* force out */
      }
      if (type == aspWrite)
	SPWrtReply(cno,reqref,(dword) err,rspbuf,rsplen,&comp);
      else
	SPCmdReply(cno,reqref,(dword) err,rspbuf,rsplen,&comp);
      while (comp > 0) { abSleep(sectotick(60),TRUE); }
      if (DBSRV)
	printf("done\n");
      break;
    case aspCloseSession:
      log("Closing ASP Session...");
      SPCloseSession(cno,10,3,&comp); /* 5 times, .75 seconds */
      while (comp > 0)
	abSleep(1, TRUE);
      sigsetmask(mask);
      return;
    default:
      log("Unknown asp command type = %d",type);
      break;
    }
#ifndef NOSHUTDOWNCODE
    sigsetmask(mask);
#endif
  }
}

/*
 * Deals with inferior process termination - just close off the
 * "half closed" socket
 *
*/
inferiordone()
{
#ifndef NOWAIT3
  union wait status;
#else
  int status;
#endif
#ifdef DORUSAGE
  struct rusage rusage;
# define RUSAGEVAR &rusage
#else
# define RUSAGEVAR 0		/* may not be used, but.. */
#endif
  int pid;
  int i;
  struct cno_to_pid *cp;

#ifndef NOWAIT3
# define DOWAIT while ((pid=wait3(&status, WNOHANG, RUSAGEVAR)) > 0) 
#else
# define DOWAIT  if ((pid=wait(&status)) > 0)
#endif
  DOWAIT {
    /* remember for later */
    for (i = 0, cp = ctp_tab; i < maxsess; i++, cp++) {
      if (cp->pid == pid && (cp->state & CP_NOTINUSE) == 0) {
	/* one of alive, dead or timedout */
	/* if alive, move to dead */
	/* if dead, shouldn't be here */
	/* if timedout, then leave state */
	if ((cp->state & CP_RUNNING) == 0)
	  log("Internal error: pid %d has died twice according to wait",pid);
	cp->state &= ~CP_RUNNING; /* not running anymore */
	(void)time(&cp->timeval); /* log time */
	bcopy(&status, &cp->status, sizeof(status)); /* copy status */
#ifdef DORUSAGE
	bcopy(&rusage, &cp->rusage, sizeof(rusage));
#endif
	ctp_stack[ctp_stack_cnt++] = i;	/* mark cno */
	log("Recorded terminated inferior Aufs PID %d", pid);
	break;
      }
    }
    if (i == maxsess)
      log("Unknown terminating inferior pid %d ignored", pid);
  }
  signal(SIGCHLD, inferiordone);
}

/*
 * scan for dead inferiors and handle gracefully.  Know we are never
 * called from a "bad" context.  e.g. we won't be called while in a
 * "critical" section where data structures are being updated
 *
*/
gcinferior()
{
  char tmpbuf[30];		/* reasonable */
  int srn, comp;
  struct cno_to_pid *cp;
  int mask;

  if (ctp_stack_cnt <= 0) {	/* stack of died pids empty? */
    if (ctp_stack_cnt < 0)
      log("internal error: unsafe condition: ctp stack count less than zero");
    return;			/* yes, just return */
  }

  mask = sigblock(sigmask(SIGCHLD));
  do {
    srn = ctp_stack[--ctp_stack_cnt]; /* get pid */
    cp = &ctp_tab[srn];		/* pointer to info on died pid */
    if (cp->state & CP_NOTINUSE) /* nothing to do */
      continue;
    /* fork termination has three cases: */
    /* [?,0177] - stopped */
    /* [?, 0] - exit status */
    /* [0, ?] - terminated due to signal */
    if (cp->state & CP_TIMEDOUT) {
      log("process %d, session %d was terminated due to a timeout",
	  cp->pid, srn);
    } else if (WIFSTOPPED(cp->status)) {
      log("process %d, session %d was suspended! gads what is happening?",
	  cp->pid, srn);
    } else if (WIFSIGNALED(cp->status)) {
      SPAttention(srn, AFPSHUTDOWNNOW, 1, -1, &comp);
      while (comp > 0) {abSleep(30, TRUE);} /* ignore error */
      SPCloseSession(srn, 3, 2, &comp); /* try 3 times every .5 seconds */
      while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */
      log("process %d, session %d was terminated on signal %d",
	  cp->pid, srn, W_TERMSIG(cp->status));
      if (W_COREDUMP(cp->status)) {
	log("woops: we just dumped core on pid %d", cp->pid);
	sprintf(tmpbuf, "core%d",cp->pid);
	if (link("core", tmpbuf) == 0) { /* making copy */
	  if (unlink("core") != 0)
	    log("woops: couldn't unlink core for pid %d", cp->pid);
	  log("core dump for pid %d is in %s",cp->pid, tmpbuf);
	} else
	  log("core dump for pid %d is in core - plz be careful though!",
	      cp->pid);
      }
    } else {
      log("process %d, session %d terminated with exit code %d",
	  cp->pid, srn, W_RETCODE(cp->status));
    }
    SPShutdown(srn);
    nomoresessions = FALSE;	/* if this was set, unset now */
#ifdef DORUSAGE
    log("%d messages out, %d in, CPU %.2f user %.2f system",
	cp->rusage.ru_msgsnd,cp->rusage.ru_msgrcv,
	((float)cp->rusage.ru_utime.tv_sec)+
	((float)cp->rusage.ru_utime.tv_usec)/1000000.0,
	((float)cp->rusage.ru_stime.tv_sec)+
	((float)cp->rusage.ru_stime.tv_usec)/1000000.0);
#endif
    log("Process %d terminated at %s",cp->pid,
	mytod(cp->timeval));
    cp->state = CP_NOTINUSE;
    cp->pid = -1;		/* make -1 to speed up sigchld handler */
  } while (ctp_stack_cnt);
  sigsetmask(mask);		/* restore mask */
}

/*
 *
 * record inferior pid for later handling
 *
 * must be called with sigchild and other signals,etc. that could
 * affect ctp_tab off. 
 * 
 *
*/
addinferior(cno, pid)
int cno;
int pid;
{
  struct cno_to_pid *cp;

  /* need to scan here in case something timed out */
  gcinferior();
  cp = &ctp_tab[cno];		/* get pointer to table entry */
  /* If was in table, just smash because we have a new pid on cno */
  /* should we scan table for duplicates? */
  cp->state = CP_RUNNING;
  cp->pid = pid;
}

/*
 * Connection timed because remote didn't tickle us enough
 * Kill inferior and shutdown half closed skt.
 *
 * Note: called from abSleep scheduler.
 *
*/
timedout(srn, pid)
int srn;
int pid;
{
  nomoresessions = FALSE;	/* if this was set, unset now */
  log("Server timeout on session %d pid %d, not talking to remote anymore",
      srn, pid);
  /* shouldn't need to do anymore here since remote stopped talking */
  /* assume sigchild interlocked here */
  if (ctp_tab[srn].state & CP_RUNNING)
    ctp_tab[srn].state |= CP_TIMEDOUT;
  SPShutdown(srn);		/* ignore errors */
  kill(pid, SIGHUP);		/* hangup inferior */
}

/* got a hup and are inferior of server */
dienow()
{
  int comp;

  log("Superior told us to shutdown - probably tickle timeout");
  /* The following shouldn't really do anything since remote should be gone */
  /* be in case it really isn't, let's go through this rigamorle */
  /* Tell remote we are shutting down */
  SPAttention(cno, AFPSHUTDOWNNOW, 1, -1, &comp);
  while (comp > 0) {abSleep(30, TRUE);} /* ignore error */
  /* Try closing just in case */
  SPCloseSession(cno, 3, 2, &comp); /* 3 times, .5 seconds */
  while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */
  exit(0);
}

#ifndef NOSHUTDOWNCODE

/*
 * inferior handler: setup and handle: terminate in minutes_to_shutdown
 *
*/
dying()
{
  int comp;
  int dying();

  if (minutes_to_shutdown < 0)		/* not in die mode */
    return;
  if (!minutes_to_shutdown)
    dienow();
  signal(SIGALRM, dying);
  /* Tell remote we are shutting down */
  if (minutes_to_shutdown % 2) {		/* all odd minutes */
    /* there is a potential race condition here */
    SPAttention(cno, AFPSHUTDOWNTIME(minutes_to_shutdown), 1, -1, &comp);
    while (comp > 0) {abSleep(30, TRUE);} /* ignore error */
  }
  minutes_to_shutdown--;
  alarm(60);
}

/*
 * inferior handler: setup and handle: terminate in 5 minutes 
 *
*/
diein5()
{
  int dying();

  if (minutes_to_shutdown >= 0)		/* already in shutdown mode */
    return;
  log("Superior told us to shutdown by time -- initiating 5 minute shutdown");
  minutes_to_shutdown = 5;
  dying();			/* start */
}

/*
 * Call when the master process receives a SIGHUP -- immediate shutdown
 *
 */
killnow()
{
  OSErr err;

  signal (SIGHUP, SIG_IGN);
  log("Parent received SIGHUP -- immediate shutdown");
  killpg (parent_pid, SIGHUP);
  if ((err = SrvrShutdown(&srvr_entity_name)) != noErr)
    log("NBPRemove failed: code %d\n", err);
  exit(0);
}

/*
 * Called with the master process receives a SIGTERM
 */
killin5()
{
  int killinn();

  signal (SIGTERM, SIG_IGN);
  log("Shutdown by time -- initiating 5 minute shutdown");
  killpg (parent_pid, SIGTERM);
  minutes_to_shutdown = 4;
  /* in case children get blocked up */
  signal(SIGALRM, killinn);
  alarm(68);			/* a litte more than a minute */
}

killinn()
{
  int killinn();

  if (minutes_to_shutdown < 0)		/* not in die mode */
    return;
  if (!minutes_to_shutdown)
    killnow();
  killpg (parent_pid, SIGTERM);	/* in case inferior was blocked up */
  signal(SIGALRM, killinn);
  minutes_to_shutdown--;
  alarm(60);
}


#endif /* NOSHUTDOWNCODE */

/*
 * log an error message 
 */
#ifndef USEVPRINTF
/* Bletch - gotta do it because pyramids don't work the other way */
/* (using _doprnt and &args) and don't have vprintf */
/* of course, there will be something that is just one arg larger :-) */
/*VARARGS1*/
log(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af)
char *fmt;
#else /* USEVPRINTF */
log(va_alist)
va_dcl
#endif /* USEVPRINTF */
{
  static FILE *fp = NULL;
#ifdef USEVPRINTF
  register char *fmt;
  va_list args;
#endif

  if (fp == NULL)
    if (DBDEB)
      fp = stderr;
    else
      if ((fp = fopen(logfile, "a+")) == NULL) 
	return;
  
  if (mypid != parent_pid)
    fprintf(fp, "%05d: ", mypid);
  else
    fprintf(fp, "%05d* ",mypid);
  tmprt(fp);
#ifdef USEVPRINTF
  va_start(args);
  fmt = va_arg(args, char *);
  vfprintf(fp, fmt, args);
  va_end(args);
#else /* USEVPRINTF */
  fprintf(fp, fmt,  a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af);
#endif /* USEVPRINTF */
  putc('\n', fp);
  fflush(fp);
}

tmprt(fp)
FILE *fp;
{
  fprintf(fp, "%s", mytod(0L));
}

/*
 * return tod in a static buffer, rotate among <n> buffers to
 * allow at least <n> calls "uses" to be active at a time
 *
 * <n> is currently 2
 *
*/
char *
mytod(ptime)
long ptime;
{
  long tloc;
  struct tm *tm, *localtime();
#define NTODBUF 2
  char * buf;
  static char buffers[NTODBUF][100];
  static int idx = 0;

  if (ptime == 0L)
    (void)time(&tloc);
  else
    tloc = ptime;
  tm = localtime(&tloc);
  buf = buffers[idx];
  ++idx;			/* move to next */
  idx %= NTODBUF;		/* make sure in range */
  if (tm->tm_year > 99)
    sprintf(buf, "%02d:%02d:%02d %02d/%02d/%04d ",
	    tm->tm_hour, tm->tm_min, tm->tm_sec,
	    tm->tm_mon+1, tm->tm_mday, tm->tm_year+1900);
  else
    sprintf(buf, "%02d:%02d:%02d %02d/%02d/%02d ",
	    tm->tm_hour, tm->tm_min, tm->tm_sec,
	    tm->tm_mon+1, tm->tm_mday, tm->tm_year);
  return(buf);
}


These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.