/* * Copyright (c) 1990, The Regents of the University of California. * Edward Moy, Workstation Software Support Group, Workstation Support Services, * Information Systems and Technology. * * Permission is granted to any individual or institution to use, copy, * or redistribute this software so long as it is not sold for profit, * provided that this notice and the original copyright notices are * retained. The University of California makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. */ #include #ifdef NEEDFCNTLDOTH #include #endif NEEDFCNTLDOTH #include #ifndef _TYPES # include /* sometimes needed for sys/file.h */ #endif _TYPES #include #ifdef USEDIRENT #include #else USEDIRENT #include #endif USEDIRENT #include #include #include #include #include #ifdef USESTRINGDOTH #include #else USESTRINGDOTH #include #endif USESTRINGDOTH #include #include #include #define ASYNC 1 #define BDSSIZE 512 #define BITMASK ((1 << NBITSPERFIELD) - 1) #define HOLDTIME 60 #define MACID 4 #define MORESIZE (sizeof(moremsg) - 1) #define NBDS 8 #define NBITSPERFIELD (NUSERBITS / (NFIELDS - 1)) #define NFIELDS 6 #define NUSERBITS 32 #define SYNC 0 struct Queue { time_t q_time; /* modification time */ char q_name[MAXNAMLEN+1]; /* control file name */ }; AddrBlock addr; ABusRecord abrecord; BDS bds[NBDS]; char bdsbuffer[NBDS * BDSSIZE]; /* bds buffer */ int bdsfull; /* bds buffer full */ char *bdsptr; /* current position in bds buffer */ int colwidth[NFIELDS]; /* column widths */ char current[40]; /* current file being printed */ char empty[] = ""; /* empty string */ EntityName entity; /* Entity name of register naming */ int holdtime = HOLDTIME; /* minimum time between looking at queue */ long lasttime; /* last time queue looked at */ char line[BUFSIZ]; /* line buffer */ char *lock = "lock"; /* lock file name */ char moremsg[] = " (More ...)\r"; char *myname; /* name of program */ char *printer; /* name of LW spooler */ int rank; /* order to be printed (-1=none, 0=active) */ struct stat statbuf; char *status = "status"; /* status file name */ char temp[512]; /* temp buffer for bds */ char type[] = "Printer Queue"; /* AppleTalk type */ char zone[] = "*"; /* AppleTalk zone */ char *pgetstr(); char *malloc(); main(argc, argv) int argc; char **argv; { register ABusRecord *abr = &abrecord; register OSErr err; register int i, j, k; int sock; long l; void cleanup(); if(myname = rindex(*argv, '/')) myname++; else myname = *argv; for(argc--, argv++ ; argc > 0 && **argv == '-' ; argc--, argv++) { switch((*argv)[1]) { case 'l': /* alternate lock file */ if((*argv)[2]) lock = &(*argv)[2]; else if(argc < 2) Usage(); /* never returns */ else { argc--; lock = *++argv; } break; case 's': /* alternate status file */ if((*argv)[2]) status = &(*argv)[2]; else if(argc < 2) Usage(); /* never returns */ else { argc--; status = *++argv; } break; case 't': /* new hold time */ if((*argv)[2]) holdtime = atoi(&(*argv)[2]); else if(argc < 2) Usage(); /* never returns */ else { argc--; holdtime = atoi(*++argv); } if(holdtime < 0) Usage(); break; default: Usage(); /* never returns */ } } if(argc < 2) Usage(); /* never returns */ printer = *argv++; if (chdir(*argv) < 0) fatal("cannot chdir to spooling directory"); disassociate(); for(i = 0 ; i < NBDS ; i++) bds[i].buffPtr = &bdsbuffer[i * BDSSIZE]; signal(SIGHUP, cleanup); signal(SIGQUIT, cleanup); signal(SIGINT, cleanup); signal(SIGTERM, cleanup); abInit(TRUE); nbpInit(); sock = 0; if((err = ATPOpenSocket(&addr, &sock)) != noErr) fatal("Error opening ATP socket (%d)\n", err); if((err = nbp_register(printer, type, zone, sock)) != noErr) fatal("Error registering name (%d)\n", err); for( ; ; ) { abr->proto.atp.atpSocket = sock; abr->proto.atp.atpReqCount = BDSSIZE; abr->proto.atp.atpDataPtr = line; if((err = ATPGetRequest(abr, SYNC)) != noErr) fatal("Error setting up ATPGetRequest (%d)\n", err); if(abr->abResult != 1) { time(&l); if(l - lasttime >= holdtime) { lasttime = l; bdsptr = bdsbuffer; bdsfull = FALSE; tobds("\"%s\" Printer Queue %s", printer, ctime(&lasttime)); displayq(); *bdsptr = 0; k = bdsptr - bdsbuffer; abr->proto.atp.atpNumBufs = abr->proto.atp.atpBDSSize = j = (k + (BDSSIZE - 1)) / BDSSIZE; for(i = 0 ; i < j ; i++) { bds[i].buffSize = k > BDSSIZE ? BDSSIZE : k; k -= BDSSIZE; } l = 0; for(i = (NFIELDS - 2) ; i >= 0 ; i--) l = (l << NBITSPERFIELD) | (colwidth[i] > BITMASK ? BITMASK : colwidth[i]); /* * We shouldn't have to do this, but we do! */ bds[0].userBytes = htonl(l); } abr->proto.atp.fatpEOM = TRUE; abr->proto.atp.atpRspBDSPtr = bds; if((err = ATPSndRsp(abr, SYNC)) != noErr) fprintf(stderr, "%s: Error sending response (%d)\n", myname, err); } } } disassociate() { int i; if (fork()) _exit(0); /* kill parent */ for (i=0; i < 3; i++) close(i); /* kill */ (void)open("/",0); (void)dup2(0,1); (void)dup2(0,2); #ifndef POSIX #ifdef TIOCNOTTY if ((i = open("/dev/tty",2)) > 0) { (void)ioctl(i, TIOCNOTTY, (caddr_t)0); (void)close(i); } #endif TIOCNOTTY #else POSIX (void) setsid(); #endif POSIX } void cleanup() { NBPRemove(&entity); exit(1); } Usage() { fprintf(stderr, "Usage: %s [-l lockfile] [-s statusfile] [-t holdtime] name spooldir\n", myname); exit(1); } /* * Put unformated text into BDS buffers. */ writebds(buf, size) char *buf; { bcopy(buf, bdsptr, size); bdsptr += size; } /* * Put formatted text into BDS buffers. */ tobds(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) char *fmt; { register int len, bdslen; if(bdsfull) return; sprintf(temp, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); if((bdslen = bdsptr - bdsbuffer) + (len = strlen(temp)) > NBDS * BDSSIZE) { bdsfull = TRUE; while(NBDS * BDSSIZE - bdslen < MORESIZE) { if(*--bdsptr != '\r') bdsptr++; while(*--bdsptr != '\r') {} bdsptr++; bdslen = bdsptr - bdsbuffer; } strcpy(bdsptr, moremsg); bdsptr += MORESIZE; return; } strcpy(bdsptr, temp); bdsptr += len; } /* * Display the current state of the queue. Format = 1 if long format. */ displayq() { register struct Queue *q; register int i, nitems, fd; struct Queue **Queue; FILE *fp; rank = -1; for(i = 0 ; i < (NFIELDS - 1) ; i++) colwidth[i] = 0; if ((nitems = Getq(&Queue)) < 0) fatal("cannot examine spooling area\n"); if (stat(lock, &statbuf) >= 0) { if (statbuf.st_mode & 0100) { tobds("Warning: \"%s\" is down: ", printer); fd = open(status, O_RDONLY); if (fd >= 0) { OSLockFileforRead(fd); while ((i = read(fd, line, BUFSIZ)) > 0) writebds(line, i); OSUnlockFile(fd); (void) close(fd); } else writebds("\n", 1); } if (statbuf.st_mode & 010) tobds("Warning: \"%s\" queue is turned off\n", printer); } if (nitems) { fp = fopen(lock, "r"); if (fp == NULL) warn(); else { register char *cp; /* get daemon pid */ cp = current; while ((*cp = getc(fp)) != EOF && *cp != '\n') cp++; *cp = '\0'; i = atoi(current); if (i <= 0 || kill(i, 0) < 0) warn(); else { /* read current file name */ cp = current; while ((*cp = getc(fp)) != EOF && *cp != '\n') cp++; *cp = '\0'; /* * Print the status file. */ fd = open(status, O_RDONLY); if (fd >= 0) { OSLockFileforRead(fd); while ((i = read(fd, line, BUFSIZ)) > 0) writebds(line, i); OSUnlockFile(fd); (void) close(fd); } else writebds("\n", 1); } (void) fclose(fp); } /* * Now, examine the control files and print out the jobs to * be done for each user. */ for (i = 0; i < nitems; i++) { q = Queue[i]; inform(q); free(q); } free(Queue); } else if (nitems == 0) tobds("no entries\n"); } /* * Print a warning message if there is no daemon present. */ warn() { tobds("Warning: print spooler not running\n"); current[0] = '\0'; } inform(q) register struct Queue *q; { register int copies, pc; register char *cp; register long size; int mac; FILE *cfp; char *alias; char *class; char cstr[128]; /* 'H' */ char fstr[128]; /* [a-z] */ char jstr[128]; /* 'J' */ char nstr[128]; /* 'N' */ char ustr[128]; /* 'P' */ char *job; char *user; /* * There's a chance the control file has gone away * in the meantime; if this is the case just keep going */ if ((cfp = fopen(q->q_name, "r")) == NULL) return; alias = class = user = job = empty; *cstr = *fstr = *jstr = *nstr = *ustr = '\0'; pc = -1; size = -1L; mac = FALSE; if (rank < 0) rank = 0; if (strcmp(q->q_name, current) != 0) rank++; copies = 0; while (getline(cfp)) { switch (line[0]) { case 'H': strcpy(cstr, line + 1); continue; case 'J': strcpy(jstr, line + 1); continue; case 'P': strcpy(ustr, line + 1); continue; default: if(line[0] < 'a' || line[0] > 'z') continue; if(copies == 0) strcpy(fstr, line + 1); copies++; continue; case 'N': if(*fstr && stat(fstr, &statbuf) >= 0) size = copies * statbuf.st_size; strcpy(nstr, line + 1); continue; } } fclose(cfp); if(strcmp(ustr, "root") == 0 && strncmp(jstr, "MacUser: ", 9) == 0) { mac = TRUE; cp = jstr + 9; for( ; ; ) { if((cp = index(cp, ' ')) == NULL) { job = "Untitled"; pc = pagecount(jstr + 9); break; } if(strncmp(cp, " Job: ", 6) == 0) { *cp = 0; job = cp + 6; pc = pagecount(job); break; } cp++; } strcpy(ustr, jstr + 9); if((cp = rindex(ustr, '!')) && strlen(cp) == (MACID + 1)) { *cp++ = 0; class = cp; if(cp = rindex(ustr, ' ')) { *cp++ = 0; alias = ustr; user = cp; } else user = ustr; } else { user = ustr; class = "Macintosh"; } } else { if(*jstr) job = jstr; else if(strcmp(nstr, " ") == 0) job = "(standard input)"; else job = nstr; user = ustr; class = cstr; if((copies = strlen(class)) >= 13 && strcmp(cp = &class[copies - 13], ".berkeley.edu") == 0) *cp = 0; } cp = bdsptr; prank(rank); if(pc >= 0) tobds("%s\t%s\t%s\t%s\t%d Page%s\n", alias, user, job, class, pc, pc == 1 ? "" : "s"); else if(size >= 0L) tobds("%s\t%s\t%s\t%s\t%ld byte%s\n", alias, user, job, class, size, size == 1 ? "" : "s"); else tobds("%s\t%s\t%s\t%s\t?? bytes\n", alias, user, job, class); *bdsptr = 0; measurecols(cp); } measurecols(cp) register char *cp; { register char *tp; register int i, j; for(i = 0 ; i < (NFIELDS - 1) ; i++) { if((tp = index(cp, '\t')) == NULL) { if((j = strlen(cp)) > colwidth[i]) colwidth[i] = j; break; } if((j = tp - cp) > colwidth[i]) colwidth[i] = j; cp = tp + 1; } } pagecount(str) register char *str; { for( ; ; ) { if((str = index(str, ' ')) == NULL) return(-1); if(strncmp(str, " Pages: ", 8) == 0) { *str = 0; str += 8; return(atoi(str)); } str++; } } /* * Print the job's rank in the queue, * update col for screen management */ prank(n) { char line[100]; static char *r[] = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; if (n == 0) { tobds("active\t"); return; } if ((n/10) == 1) tobds("%dth\t", n); else tobds("%d%s\t", n, r[n%10]); } /* * fatal error */ fatal(fmt, arg1, arg2, arg3) char *fmt; { fprintf(stderr, "%s: ", myname); fprintf(stderr, fmt, arg1, arg2, arg3); exit(1); } /* * register the specified entity * */ nbp_register(sobj, stype, szone, skt) char *sobj, *stype, *szone; int skt; { nbpProto nbpr; /* nbp proto */ NBPTEntry nbpt[1]; /* table of entity names */ int err; strcpy((char *)entity.objStr.s, sobj); strcpy((char *)entity.typeStr.s, stype); strcpy((char *)entity.zoneStr.s, szone); nbpr.nbpAddress.skt = skt; nbpr.nbpRetransmitInfo.retransInterval = 4; nbpr.nbpRetransmitInfo.retransCount = 3; nbpr.nbpBufPtr = nbpt; nbpr.nbpBufSize = sizeof(nbpt); nbpr.nbpDataField = 1; /* max entries */ nbpr.nbpEntityPtr = &entity; err = NBPRegister(&nbpr,FALSE); /* try synchronous */ return(err); } getline(cfp) FILE *cfp; { register int linel = 0; register char *lp = line; register c; while ((c = getc(cfp)) != '\n') { if (c == EOF) return(0); if (c == '\t') { do { *lp++ = ' '; linel++; } while ((linel & 07) != 0); continue; } *lp++ = c; linel++; } *lp++ = '\0'; return(linel); } /* * Scan the current directory and make a list of daemon files sorted by * creation time. * Return the number of entries and a pointer to the list. */ Getq(namelist) struct Queue *(*namelist[]); { #ifdef USEDIRENT register struct dirent *d; #else USEDIRENT register struct direct *d; #endif USEDIRENT register struct Queue *q, **Queue; register int nitems; int arraysz, compar(); DIR *dirp; if ((dirp = opendir(".")) == NULL) return(-1); if (fstat(dirp->dd_fd, &statbuf) < 0) goto errdone; /* * Estimate the array size by taking the size of the directory file * and dividing it by a multiple of the minimum size entry. */ arraysz = (statbuf.st_size / 24); Queue = (struct Queue **)malloc(arraysz * sizeof(struct Queue *)); if (Queue == NULL) goto errdone; nitems = 0; while ((d = readdir(dirp)) != NULL) { #ifdef __hpux if (d->d_name[0] != 'c' || d->d_name[1] != 'A') #else /* __hpux */ if (d->d_name[0] != 'c' || d->d_name[1] != 'f') #endif /* __hpux */ continue; /* daemon control files only */ if (stat(d->d_name, &statbuf) < 0) continue; /* Doesn't exist */ q = (struct Queue *)malloc(sizeof(time_t)+strlen(d->d_name)+1); if (q == NULL) goto errdone; q->q_time = statbuf.st_mtime; strcpy(q->q_name, d->d_name); /* * Check to make sure the array has space left and * realloc the maximum size. */ if (++nitems > arraysz) { Queue = (struct Queue **)realloc((char *)Queue, (statbuf.st_size/12) * sizeof(struct Queue *)); if (Queue == NULL) goto errdone; } Queue[nitems-1] = q; } closedir(dirp); if (nitems) qsort(Queue, nitems, sizeof(struct Queue *), compar); *namelist = Queue; return(nitems); errdone: closedir(dirp); return(-1); } /* * Compare modification times. */ compar(p1, p2) register struct Queue **p1, **p2; { if ((*p1)->q_time < (*p2)->q_time) return(-1); if ((*p1)->q_time > (*p2)->q_time) return(1); return(0); }