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.