jmc@jimlad.UUCP (John M Collins M.A.) (07/27/84)
/* * SCCS: @(#)spshed.c 1.2 25/6/84 19:50:29 * Spool scheduler process. * * Copyright J.M. Collins 1984 */ #include <stdio.h> #include <signal.h> #include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/dir.h> #include <sys/stat.h> #include "spq.h" #include "filenams.h" #include <pwd.h> #define CMASK 0077 /* Umask value */ #define CMODE 0666 #define PENDTIME 200 unsigned alarm(); char *malloc(), *realloc(); void syncfls(), enqueue(), addptr(), delptr(), halt(), heoj(); void addoper(), selectj(), doabort(), gop(), chjob(), haltall(); void restart(), report(), prdone(), qprocess(), deloper(), startp(); void tellopers(), killops(); struct sp_req *qread(); int errno; int fifofd, jfilefd, pfilefd; int njobs, jbsize, nptrs; int numopers, readers; int jqpend, pqpend; #define INITJBUF 50 struct spq *jbuf; struct spptr pbuf[MAXPTRS]; char spdir[] = SPDIR; char fifo[] = FIFO; char jfile[] = JFILE; char pfile[] = PFILE; void nomem() { report("Run out of memory"); exit(100); } /* * Try to exit gracefully and quickly.... */ void niceend() { haltall(); syncfls(); killops(); (void) unlink(fifo); exit(0); } /* * Open FIFO file, which also constitutes the lock file for the process. */ void openrfile() { if (mknod(fifo, S_IFIFO|CMODE, 0) < 0) { if (errno == EEXIST) exit(0); report("could not create control file"); } (void) chown(fifo, DAEMUID, getgid()); /* * Open in read/write mode, not because we are intending * to write, but because otherwise when we read, we will * get a zero-length read if no one has it open for writing. */ if ((fifofd = open(fifo, O_RDWR)) < 0) report("could not open control file"); /* * Set close-on-exec flag. */ (void) fcntl(fifofd, F_SETFD, 1); } /* * Create/open job file. Read it into memory if it exists. */ void openjfile() { struct stat sbuf; register int i, njs; if ((jfilefd = open(jfile, O_RDWR|O_CREAT, CMODE)) < 0) report("Could not open job file"); (void) chown(jfile, DAEMUID, getgid()); (void) fcntl(jfilefd, F_SETFD, 1); (void) fstat(jfilefd, &sbuf); njs = sbuf.st_size / sizeof(struct spq); jbsize = njs; if (jbsize < INITJBUF) jbsize = INITJBUF; jbuf = (struct spq *)malloc(jbsize * sizeof(struct spq)); if (jbuf == NULL) nomem(); if (njs > 0) (void) read(jfilefd, (char *)jbuf, njs * sizeof(struct spq)); /* * Count entries in job queue. * Clear extra ones. */ njobs = 0; for (i = 0; i < njs; i++) { if (jbuf[i].spq_job == 0) break; njobs++; } for (; i < jbsize; i++) jbuf[i].spq_job = 0; } /* * Create/open print file. Read it into memory if it exists. */ void openpfile() { struct stat sbuf; int lng; register int i; if ((pfilefd = open(pfile, O_RDWR|O_CREAT, CMODE)) < 0) report("Could not open printer file"); (void) chown(pfile, DAEMUID, getgid()); (void) fcntl(pfilefd, F_SETFD, 1); (void) fstat(pfilefd, &sbuf); lng = sbuf.st_size / sizeof(struct spptr); if (lng > MAXPTRS) lng = MAXPTRS; (void) read(pfilefd, (char *)pbuf, lng * sizeof(struct spptr)); /* * For each printer in list, which is not actually halted, * set ready, and kick off process. */ for (i = 0; i < MAXPTRS; i++) if (pbuf[i].spp_state != SPP_NULL) { nptrs++; pbuf[i].spp_job = 0; if (pbuf[i].spp_state != SPP_HALT) { pbuf[i].spp_state = SPP_WAIT; startp(&pbuf[i]); } } } /* * This routine is used to catch alarm messages to refresh the files. */ int catchalrm() { (void) signal(SIGALRM, catchalrm); /* Reset signal */ syncfls(); /* Write files */ } /* * This routine is the main process. */ void process() { struct sp_req inreq; register struct sp_req *irp; int bytes; (void) signal(SIGALRM, catchalrm); for (;;) { /* * Dont fiddle with queue if people are reading it. */ if (readers > 0) qprocess(); if ((irp = qread()) == NULL) { irp = &inreq; /* * Refresh files if nothing happens for a while. */ if (jqpend + pqpend > 0) (void) alarm(PENDTIME); if ((bytes = read(fifofd,(char *)irp,sizeof(struct sp_req)))!= sizeof(struct sp_req)) { if (bytes < 0 && errno == EINTR) continue; report("Bad read from control file"); } if (jqpend + pqpend > 0) (void) alarm(0); } switch (irp->spr_act) { default: report("Invalid code"); case SP_ENQ: enqueue(irp); break; case SP_CHNG: chjob(irp); break; case SP_MON: addoper(irp); continue; case SP_DMON: deloper(irp); case SP_READ: /* Ignored here */ continue; case SP_AB: doabort(irp); break; case SP_RSP: restart(irp); break; case SP_PHLT: heoj(irp); break; case SP_PSTP: halt(irp); break; case SP_PGO: gop(irp); break; case SP_ADDP: case SP_CHGP: addptr(irp); break; case SP_DELP: delptr(irp); break; case SP_DONE: prdone(irp, 0); break; case SP_DAB: prdone(irp, 1); break; case SP_SSTOP: niceend(); } /* * Fall through to here if we can select a new job. */ selectj(); if (numopers > 0 && jqpend + pqpend > 0) tellopers(); } } /* * Ye olde main routine. * I don't expect any arguments & will ignore any the fool gives me. */ main() { int pid; register int i; /* * As parent process, close files etc and open request FIFO. * This is because if the scheduler is invoked by 'spr', the * latter does not want to restart until the FIFO has been * created. */ for (i = 0; i < 20; i++) (void) close(i); signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); setuid(geteuid()); (void) umask(CMASK); (void) setpgrp(); if (chdir(spdir) < 0) report("Unable to chdir"); openrfile(); /* * Fork off to leave an orphaned child process. */ pid = fork(); if (pid > 0) /* Main path */ exit(0); if (pid < 0) report("Unable to fork"); /* * Ignore PIPE errors in case daemon processes quit. */ (void) signal(SIGPIPE, SIG_IGN); /* * Open job and printer files. */ openjfile(); openpfile(); selectj(); /* Initial job selection */ process(); /* Never returns */ }