From 0b561052dfd9d0992c8bfd08ef46df9b8d272373 Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Sun, 5 May 1996 14:04:33 +0000 Subject: [PATCH] Vendor-branch import of the 4.4BSD-Lite2 code for lpr. There are several bugfixes in it that are worth considering. Don't be alarmed about the import conflicts... Obtained from: 4.4BSD-Lite2 --- usr.sbin/lpr/common_source/aux.c | 400 +++++++ usr.sbin/lpr/common_source/aux.h | 67 ++ usr.sbin/lpr/common_source/common.c | 377 +++++++ usr.sbin/lpr/common_source/displayq.c | 450 ++++++++ usr.sbin/lpr/common_source/lp.h | 127 +++ usr.sbin/lpr/common_source/printcap.c | 551 +++++++++ usr.sbin/lpr/common_source/rmjob.c | 339 ++++++ usr.sbin/lpr/lpc/cmds.c | 1108 ++++++++++++++++++ usr.sbin/lpr/lpc/lpc.8 | 175 +++ usr.sbin/lpr/lpc/lpc.c | 314 ++++++ usr.sbin/lpr/lpd/lpd.c | 542 +++++++++ usr.sbin/lpr/lpd/printjob.c | 1493 +++++++++++++++++++++++++ usr.sbin/lpr/lpd/recvjob.c | 358 ++++++ usr.sbin/lpr/lpq/lpq.1 | 136 +++ usr.sbin/lpr/lpq/lpq.c | 173 +++ usr.sbin/lpr/lpr/lpr.c | 745 ++++++++++++ 16 files changed, 7355 insertions(+) create mode 100644 usr.sbin/lpr/common_source/aux.c create mode 100644 usr.sbin/lpr/common_source/aux.h create mode 100644 usr.sbin/lpr/common_source/common.c create mode 100644 usr.sbin/lpr/common_source/displayq.c create mode 100644 usr.sbin/lpr/common_source/lp.h create mode 100644 usr.sbin/lpr/common_source/printcap.c create mode 100644 usr.sbin/lpr/common_source/rmjob.c create mode 100644 usr.sbin/lpr/lpc/cmds.c create mode 100644 usr.sbin/lpr/lpc/lpc.8 create mode 100644 usr.sbin/lpr/lpc/lpc.c create mode 100644 usr.sbin/lpr/lpd/lpd.c create mode 100644 usr.sbin/lpr/lpd/printjob.c create mode 100644 usr.sbin/lpr/lpd/recvjob.c create mode 100644 usr.sbin/lpr/lpq/lpq.1 create mode 100644 usr.sbin/lpr/lpq/lpq.c create mode 100644 usr.sbin/lpr/lpr/lpr.c diff --git a/usr.sbin/lpr/common_source/aux.c b/usr.sbin/lpr/common_source/aux.c new file mode 100644 index 00000000000..8d7a93673b8 --- /dev/null +++ b/usr.sbin/lpr/common_source/aux.c @@ -0,0 +1,400 @@ +/* + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Auxillary functions to aid portability to other systems. + * These are 4.4BSD routines that are often not found on other systems. + * + * !!!USE THIS FILE ONLY IF YOU ARE NOT RUNNING 4.4BSD!!! + */ + +#if __STDC__ +#include +#else +#include +#endif + +#ifdef NO_SNPRINTF +#if __STDC__ +snprintf(char *str, size_t n, const char *fmt, ...) +#else +snprintf(str, n, fmt, va_alist) + char *str; + size_t n; + char *fmt; + va_dcl +#endif +{ + int ret; + va_list ap; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + ret = vsprintf(str, fmt, ap); + va_end(ap); + if (strlen(str) > n) + fatal("memory corrupted"); + return (ret); +} + +vsnprintf(str, n, fmt, ap) + char *str; + size_t n; + char *fmt; + va_list ap; +{ + int ret; + + ret = vsprintf(str, fmt, ap); + if (strlen(str) > n) + fatal("memory corrupted"); + return (ret); +} +#endif + +#ifdef NO_STRERROR +char * +strerror(num) + int num; +{ + extern int sys_nerr; + extern char *sys_errlist[]; +#define UPREFIX "Unknown error: " + static char ebuf[40] = UPREFIX; /* 64-bit number + slop */ + register unsigned int errnum; + register char *p, *t; + char tmp[40]; + + errnum = num; /* convert to unsigned */ + if (errnum < sys_nerr) + return(sys_errlist[errnum]); + + /* Do this by hand, so we don't include stdio(3). */ + t = tmp; + do { + *t++ = "0123456789"[errnum % 10]; + } while (errnum /= 10); + for (p = ebuf + sizeof(UPREFIX) - 1;;) { + *p++ = *--t; + if (t <= tmp) + break; + } + return(ebuf); +} +#endif + +#ifdef NO_STRDUP +char * +strdup(str) + char *str; +{ + int n; + char *sp; + + n = strlen(str) + 1; + if (sp = (char *) malloc(n)) + memcpy(sp, str, n); + return (sp); +} +#endif + +#ifdef NO_DAEMON +#include +#include +#include +#include +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +int +daemon(nochdir, noclose) + int nochdir, noclose; +{ + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) + (void)close (fd); + } + return (0); +} +#endif + + +#ifdef NO_SETSID +int +setsid() +{ + int f; + + f = open("/dev/tty", O_RDWR); + if (f > 0) { + ioctl(f, TIOCNOTTY, 0); + (void) close(f); + } + return f; +} +#endif + + +#ifdef NO_VSYSLOG +#include +#include +#if __STDC__ +#include +#else +#include +#endif + +vsyslog(pri, fmt, ap) + int pri; + const char *fmt; + va_list ap; +{ + char buf[2048], fmt_cpy[1024]; + + /* substitute error message for %m */ + { + register char ch, *t1, *t2; + char *strerror(); + + for (t1 = fmt_cpy; ch = *fmt; ++fmt) + if (ch == '%' && fmt[1] == 'm') { + ++fmt; + for (t2 = strerror(errno); + *t1 = *t2++; ++t1); + } + else + *t1++ = ch; + *t1 = '\0'; + } + vsprintf(buf, fmt_cpy, ap); + syslog(pri, "%s", buf); +} +#endif + + +#ifdef NO_IVALIDUSER +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +/* + * Returns 0 if ok, -1 if not ok. + */ +int +__ivaliduser(hostf, raddr, luser, ruser) + FILE *hostf; + struct in_addr raddr; + const char *luser, *ruser; +{ + register char *user, *p; + int ch; + char buf[MAXHOSTNAMELEN + 128]; /* host + login */ + + while (fgets(buf, sizeof(buf), hostf)) { + p = buf; + /* Skip lines that are too long. */ + if (strchr(p, '\n') == NULL) { + while ((ch = getc(hostf)) != '\n' && ch != EOF); + continue; + } + while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { + *p = isupper(*p) ? tolower(*p) : *p; + p++; + } + if (*p == ' ' || *p == '\t') { + *p++ = '\0'; + while (*p == ' ' || *p == '\t') + p++; + user = p; + while (*p != '\n' && *p != ' ' && + *p != '\t' && *p != '\0') + p++; + } else + user = p; + *p = '\0'; + if (__icheckhost(raddr, buf) && + strcmp(ruser, *user ? user : luser) == 0) { + return (0); + } + } + return (-1); +} + +/* + * Returns "true" if match, 0 if no match. + */ +__icheckhost(raddr, lhost) + struct in_addr raddr; + register char *lhost; +{ + register struct hostent *hp; + struct in_addr laddr; + register char **pp; + + /* Try for raw ip address first. */ + if (isdigit(*lhost) && (laddr.s_addr = inet_addr(lhost)) != INADDR_NONE) + return (raddr.s_addr == laddr.s_addr); + + /* Better be a hostname. */ + if ((hp = gethostbyname(lhost)) == NULL) + return (0); + + /* Spin through ip addresses. */ + for (pp = hp->h_addr_list; *pp; ++pp) + if (!bcmp(&raddr, *pp, sizeof(struct in_addr))) + return (1); + + /* No match. */ + return (0); +} +#endif /* NO_IVALIDUSER */ + + +#ifdef NO_STATFS +#include +#include +#include +#include +#include +#include + +/* + * Check to see if there is enough space on the disk for size bytes. + * 1 == OK, 0 == Not OK. + */ +static int +chksize(size) + int size; +{ + struct stat stb; + int spacefree; + struct fs fs; + static int dfd; + static char *find_dev(); + +#ifndef SBOFF +#define SBOFF ((off_t)(BBSIZE)) +#endif + if (dfd <= 0) { + char *ddev; + + if (stat(".", &stb) < 0) { + syslog(LOG_ERR, "%s: %m", "statfs(\".\")"); + return (1); + } + ddev = find_dev(stb.st_dev, S_IFBLK); + if ((dfd = open(ddev, O_RDONLY)) < 0) { + syslog(LOG_WARNING, "%s: %s: %m", printer, ddev); + return (1); + } + } + if (lseek(dfd, (off_t)(SBOFF), 0) < 0) + return(1); + if (read(dfd, (char *)&fs, sizeof fs) != sizeof fs + || fs.fs_magic != FS_MAGIC) { + syslog(LOG_ERR, "Can't calculate free space on spool device"); + return(1); + } + spacefree = freespace(&fs, fs.fs_minfree) * fs.fs_fsize / 512; + size = (size + 511) / 512; + if (minfree + size > spacefree) + return(0); + return(1); +} + +static char * +find_dev(dev, type) + register dev_t dev; + register int type; +{ + register DIR *dfd; + struct direct *dir; + struct stat stb; + char devname[MAXNAMLEN+6]; + char *dp; + int n; + + strcpy(devname, "/dev/dsk"); + if ((dfd = opendir(devname)) == NULL) { + strcpy(devname, "/dev"); + dfd = opendir(devname); + } + strcat(devname, "/"); + n = strlen(devname); + + while ((dir = readdir(dfd))) { + strcpy(devname + n, dir->d_name); + if (stat(devname, &stb)) + continue; + if ((stb.st_mode & S_IFMT) != type) + continue; + if (dev == stb.st_rdev) { + closedir(dfd); + dp = (char *)malloc(strlen(devname)+1); + strcpy(dp, devname); + return(dp); + } + } + closedir(dfd); + frecverr("cannot find device %d, %d", major(dev), minor(dev)); + /*NOTREACHED*/ +} +#endif /* NOSTATFS */ diff --git a/usr.sbin/lpr/common_source/aux.h b/usr.sbin/lpr/common_source/aux.h new file mode 100644 index 00000000000..d9bb0bfe838 --- /dev/null +++ b/usr.sbin/lpr/common_source/aux.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Auxillary functions to aid portability to other systems. + * These are 4.4BSD routines that are often not found on other systems. + * + * !!!USE THIS FILE ONLY IF YOU ARE NOT RUNNING 4.4BSD!!! + */ + +#ifdef PREPOSIX +#define dirent direct +extern int errno; +#endif + +#ifdef NO_RINDEX +#define index strchr +#define rindex strrchr +#endif + +#ifdef BSDWAIT +#define WAITARG_T(a) ((int *)(a)) +#else +#define WAITARG_T(a) (a) +#endif + +#ifdef SETPGID +#define setpgrp(a, b) setpgid((pid_t)(a), (pid_t)(b)) +#endif + +#ifndef FD_COPY +#define FD_COPY(f, t) memcpy((char *)t, (char *)f, sizeof(*(f))) +#endif + +#ifdef NO_SNPRINTF +int snprintf __P((char *str, size_t n, const char *fmt, ...)); +#endif diff --git a/usr.sbin/lpr/common_source/common.c b/usr.sbin/lpr/common_source/common.c new file mode 100644 index 00000000000..8be8c8d4417 --- /dev/null +++ b/usr.sbin/lpr/common_source/common.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)common.c 8.5 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "pathnames.h" + +/* + * Routines and data common to all the line printer functions. + */ + +char *AF; /* accounting file */ +long BR; /* baud rate if lp is a tty */ +char *CF; /* name of cifplot filter (per job) */ +char *DF; /* name of tex filter (per job) */ +long DU; /* daeomon user-id */ +long FC; /* flags to clear if lp is a tty */ +char *FF; /* form feed string */ +long FS; /* flags to set if lp is a tty */ +char *GF; /* name of graph(1G) filter (per job) */ +long HL; /* print header last */ +char *IF; /* name of input filter (created per job) */ +char *LF; /* log file for error messages */ +char *LO; /* lock file name */ +char *LP; /* line printer device name */ +long MC; /* maximum number of copies allowed */ +long MX; /* maximum number of blocks to copy */ +char *NF; /* name of ditroff filter (per job) */ +char *OF; /* name of output filter (created once) */ +char *PF; /* name of vrast filter (per job) */ +long PL; /* page length */ +long PW; /* page width */ +long PX; /* page width in pixels */ +long PY; /* page length in pixels */ +char *RF; /* name of fortran text filter (per job) */ +char *RG; /* resricted group */ +char *RM; /* remote machine name */ +char *RP; /* remote printer name */ +long RS; /* restricted to those with local accounts */ +long RW; /* open LP for reading and writing */ +long SB; /* short banner instead of normal header */ +long SC; /* suppress multiple copies */ +char *SD; /* spool directory */ +long SF; /* suppress FF on each print job */ +long SH; /* suppress header page */ +char *ST; /* status file name */ +char *TF; /* name of troff filter (per job) */ +char *TR; /* trailer string to be output when Q empties */ +char *VF; /* name of vplot filter (per job) */ +long XC; /* flags to clear for local mode */ +long XS; /* flags to set for local mode */ + +char line[BUFSIZ]; +char *bp; /* pointer into printcap buffer. */ +char *name; /* program name */ +char *printer; /* printer name */ + /* host machine name */ +char host[MAXHOSTNAMELEN]; +char *from = host; /* client's machine name */ +int remote; /* true if sending files to a remote host */ +char *printcapdb[2] = { _PATH_PRINTCAP, 0 }; + +static int compar __P((const void *, const void *)); + +/* + * Create a TCP connection to host "rhost" at port "rport". + * If rport == 0, then use the printer service port. + * Most of this code comes from rcmd.c. + */ +int +getport(rhost, rport) + char *rhost; + int rport; +{ + struct hostent *hp; + struct servent *sp; + struct sockaddr_in sin; + int s, timo = 1, lport = IPPORT_RESERVED - 1; + int err; + + /* + * Get the host address and port number to connect to. + */ + if (rhost == NULL) + fatal("no remote host to connect to"); + bzero((char *)&sin, sizeof(sin)); + sin.sin_addr.s_addr = inet_addr(rhost); + if (sin.sin_addr.s_addr != INADDR_NONE) + sin.sin_family = AF_INET; + else { + hp = gethostbyname(rhost); + if (hp == NULL) + fatal("unknown host %s", rhost); + bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + } + if (rport == 0) { + sp = getservbyname("printer", "tcp"); + if (sp == NULL) + fatal("printer/tcp: unknown service"); + sin.sin_port = sp->s_port; + } else + sin.sin_port = htons(rport); + + /* + * Try connecting to the server. + */ +retry: + s = rresvport(&lport); + if (s < 0) + return(-1); + if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + err = errno; + (void) close(s); + errno = err; + if (errno == EADDRINUSE) { + lport--; + goto retry; + } + if (errno == ECONNREFUSED && timo <= 16) { + sleep(timo); + timo *= 2; + goto retry; + } + return(-1); + } + return(s); +} + +/* + * Getline reads a line from the control file cfp, removes tabs, converts + * new-line to null and leaves it in line. + * Returns 0 at EOF or the number of characters read. + */ +int +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. + */ +int +getq(namelist) + struct queue *(*namelist[]); +{ + register struct dirent *d; + register struct queue *q, **queue; + register int nitems; + struct stat stbuf; + DIR *dirp; + int arraysz; + + if ((dirp = opendir(SD)) == NULL) + return(-1); + if (fstat(dirp->dd_fd, &stbuf) < 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 = (stbuf.st_size / 24); + queue = (struct queue **)malloc(arraysz * sizeof(struct queue *)); + if (queue == NULL) + goto errdone; + + nitems = 0; + while ((d = readdir(dirp)) != NULL) { + if (d->d_name[0] != 'c' || d->d_name[1] != 'f') + continue; /* daemon control files only */ + if (stat(d->d_name, &stbuf) < 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 = stbuf.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) { + arraysz *= 2; + queue = (struct queue **)realloc((char *)queue, + arraysz * 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. + */ +static int +compar(p1, p2) + const void *p1, *p2; +{ + if ((*(struct queue **)p1)->q_time < (*(struct queue **)p2)->q_time) + return(-1); + if ((*(struct queue **)p1)->q_time > (*(struct queue **)p2)->q_time) + return(1); + return(0); +} + +/* + * Figure out whether the local machine is the same + * as the remote machine (RM) entry (if it exists). + */ +char * +checkremote() +{ + char name[MAXHOSTNAMELEN]; + register struct hostent *hp; + static char errbuf[128]; + + remote = 0; /* assume printer is local */ + if (RM != NULL) { + /* get the official name of the local host */ + gethostname(name, sizeof(name)); + name[sizeof(name)-1] = '\0'; + hp = gethostbyname(name); + if (hp == (struct hostent *) NULL) { + (void) snprintf(errbuf, sizeof(errbuf), + "unable to get official name for local machine %s", + name); + return errbuf; + } else (void) strcpy(name, hp->h_name); + + /* get the official name of RM */ + hp = gethostbyname(RM); + if (hp == (struct hostent *) NULL) { + (void) snprintf(errbuf, sizeof(errbuf), + "unable to get official name for remote machine %s", + RM); + return errbuf; + } + + /* + * if the two hosts are not the same, + * then the printer must be remote. + */ + if (strcasecmp(name, hp->h_name) != 0) + remote = 1; + } + return NULL; +} + +/* sleep n milliseconds */ +void +delay(n) +{ + struct timeval tdelay; + + if (n <= 0 || n > 10000) + fatal("unreasonable delay period (%d)", n); + tdelay.tv_sec = n / 1000; + tdelay.tv_usec = n * 1000 % 1000000; + (void) select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tdelay); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +fatal(const char *msg, ...) +#else +fatal(msg, va_alist) + char *msg; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, msg); +#else + va_start(ap); +#endif + if (from != host) + (void)printf("%s: ", host); + (void)printf("%s: ", name); + if (printer) + (void)printf("%s: ", printer); + (void)vprintf(msg, ap); + va_end(ap); + (void)putchar('\n'); + exit(1); +} diff --git a/usr.sbin/lpr/common_source/displayq.c b/usr.sbin/lpr/common_source/displayq.c new file mode 100644 index 00000000000..34ca840156b --- /dev/null +++ b/usr.sbin/lpr/common_source/displayq.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)displayq.c 8.4 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" + +/* + * Routines to display the state of the queue. + */ +#define JOBCOL 40 /* column for job # in -l format */ +#define OWNCOL 7 /* start of Owner column in normal */ +#define SIZCOL 62 /* start of Size column in normal */ + +/* + * Stuff for handling job specifications + */ +extern int requ[]; /* job number of spool entries */ +extern int requests; /* # of spool requests */ +extern char *user[]; /* users to process */ +extern int users; /* # of users in user array */ + +static int col; /* column on screen */ +static char current[40]; /* current file being printed */ +static char file[132]; /* print file name */ +static int first; /* first file in ``files'' column? */ +static int garbage; /* # of garbage cf files */ +static int lflag; /* long output option */ +static int rank; /* order to be printed (-1=none, 0=active) */ +static long totsize; /* total print job size in bytes */ + +static char *head0 = "Rank Owner Job Files"; +static char *head1 = "Total Size\n"; + +/* + * Display the current state of the queue. Format = 1 if long format. + */ +void +displayq(format) + int format; +{ + register struct queue *q; + register int i, nitems, fd; + register char *cp; + struct queue **queue; + struct stat statb; + FILE *fp; + + lflag = format; + totsize = 0; + rank = -1; + if ((i = cgetent(&bp, printcapdb, printer)) == -2) + fatal("can't open printer description file"); + else if (i == -1) + fatal("unknown printer"); + else if (i == -3) + fatal("potential reference loop detected in printcap file"); + if (cgetstr(bp, "lp", &LP) < 0) + LP = _PATH_DEFDEVLP; + if (cgetstr(bp, "rp", &RP) < 0) + RP = DEFLP; + if (cgetstr(bp, "sd", &SD) < 0) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp,"lo", &LO) < 0) + LO = DEFLOCK; + if (cgetstr(bp, "st", &ST) < 0) + ST = DEFSTAT; + cgetstr(bp, "rm", &RM); + if (cp = checkremote()) + printf("Warning: %s\n", cp); + + /* + * Print out local queue + * Find all the control files in the spooling directory + */ + if (chdir(SD) < 0) + fatal("cannot chdir to spooling directory"); + if ((nitems = getq(&queue)) < 0) + fatal("cannot examine spooling area\n"); + if (stat(LO, &statb) >= 0) { + if (statb.st_mode & 0100) { + if (remote) + printf("%s: ", host); + printf("Warning: %s is down: ", printer); + fd = open(ST, O_RDONLY); + if (fd >= 0) { + (void) flock(fd, LOCK_SH); + while ((i = read(fd, line, sizeof(line))) > 0) + (void) fwrite(line, 1, i, stdout); + (void) close(fd); /* unlocks as well */ + } else + putchar('\n'); + } + if (statb.st_mode & 010) { + if (remote) + printf("%s: ", host); + printf("Warning: %s queue is turned off\n", printer); + } + } + + if (nitems) { + fp = fopen(LO, "r"); + if (fp == NULL) + warn(); + else { + /* get daemon pid */ + cp = current; + while ((i = getc(fp)) != EOF && i != '\n') + *cp++ = i; + *cp = '\0'; + i = atoi(current); + if (i <= 0 || kill(i, 0) < 0) + warn(); + else { + /* read current file name */ + cp = current; + while ((i = getc(fp)) != EOF && i != '\n') + *cp++ = i; + *cp = '\0'; + /* + * Print the status file. + */ + if (remote) + printf("%s: ", host); + fd = open(ST, O_RDONLY); + if (fd >= 0) { + (void) flock(fd, LOCK_SH); + while ((i = read(fd, line, sizeof(line))) > 0) + (void) fwrite(line, 1, i, stdout); + (void) close(fd); /* unlocks as well */ + } else + putchar('\n'); + } + (void) fclose(fp); + } + /* + * Now, examine the control files and print out the jobs to + * be done for each user. + */ + if (!lflag) + header(); + for (i = 0; i < nitems; i++) { + q = queue[i]; + inform(q->q_name); + free(q); + } + free(queue); + } + if (!remote) { + if (nitems == 0) + puts("no entries"); + return; + } + + /* + * Print foreign queue + * Note that a file in transit may show up in either queue. + */ + if (nitems) + putchar('\n'); + (void) sprintf(line, "%c%s", format + '\3', RP); + cp = line; + for (i = 0; i < requests; i++) { + cp += strlen(cp); + (void) sprintf(cp, " %d", requ[i]); + } + for (i = 0; i < users; i++) { + cp += strlen(cp); + *cp++ = ' '; + (void) strcpy(cp, user[i]); + } + strcat(line, "\n"); + fd = getport(RM, 0); + if (fd < 0) { + if (from != host) + printf("%s: ", host); + printf("connection to %s is down\n", RM); + } + else { + i = strlen(line); + if (write(fd, line, i) != i) + fatal("Lost connection"); + while ((i = read(fd, line, sizeof(line))) > 0) + (void) fwrite(line, 1, i, stdout); + (void) close(fd); + } +} + +/* + * Print a warning message if there is no daemon present. + */ +void +warn() +{ + if (remote) + printf("\n%s: ", host); + puts("Warning: no daemon present"); + current[0] = '\0'; +} + +/* + * Print the header for the short listing format + */ +void +header() +{ + printf(head0); + col = strlen(head0)+1; + blankfill(SIZCOL); + printf(head1); +} + +void +inform(cf) + char *cf; +{ + register int j; + FILE *cfp; + + /* + * There's a chance the control file has gone away + * in the meantime; if this is the case just keep going + */ + if ((cfp = fopen(cf, "r")) == NULL) + return; + + if (rank < 0) + rank = 0; + if (remote || garbage || strcmp(cf, current)) + rank++; + j = 0; + while (getline(cfp)) { + switch (line[0]) { + case 'P': /* Was this file specified in the user's list? */ + if (!inlist(line+1, cf)) { + fclose(cfp); + return; + } + if (lflag) { + printf("\n%s: ", line+1); + col = strlen(line+1) + 2; + prank(rank); + blankfill(JOBCOL); + printf(" [job %s]\n", cf+3); + } else { + col = 0; + prank(rank); + blankfill(OWNCOL); + printf("%-10s %-3d ", line+1, atoi(cf+3)); + col += 16; + first = 1; + } + continue; + default: /* some format specifer and file name? */ + if (line[0] < 'a' || line[0] > 'z') + continue; + if (j == 0 || strcmp(file, line+1) != 0) + (void) strcpy(file, line+1); + j++; + continue; + case 'N': + show(line+1, file, j); + file[0] = '\0'; + j = 0; + } + } + fclose(cfp); + if (!lflag) { + blankfill(SIZCOL); + printf("%ld bytes\n", totsize); + totsize = 0; + } +} + +int +inlist(name, file) + char *name, *file; +{ + register int *r, n; + register char **u, *cp; + + if (users == 0 && requests == 0) + return(1); + /* + * Check to see if it's in the user list + */ + for (u = user; u < &user[users]; u++) + if (!strcmp(*u, name)) + return(1); + /* + * Check the request list + */ + for (n = 0, cp = file+3; isdigit(*cp); ) + n = n * 10 + (*cp++ - '0'); + for (r = requ; r < &requ[requests]; r++) + if (*r == n && !strcmp(cp, from)) + return(1); + return(0); +} + +void +show(nfile, file, copies) + register char *nfile, *file; + int copies; +{ + if (strcmp(nfile, " ") == 0) + nfile = "(standard input)"; + if (lflag) + ldump(nfile, file, copies); + else + dump(nfile, file, copies); +} + +/* + * Fill the line with blanks to the specified column + */ +void +blankfill(n) + register int n; +{ + while (col++ < n) + putchar(' '); +} + +/* + * Give the abbreviated dump of the file names + */ +void +dump(nfile, file, copies) + char *nfile, *file; + int copies; +{ + register short n, fill; + struct stat lbuf; + + /* + * Print as many files as will fit + * (leaving room for the total size) + */ + fill = first ? 0 : 2; /* fill space for ``, '' */ + if (((n = strlen(nfile)) + col + fill) >= SIZCOL-4) { + if (col < SIZCOL) { + printf(" ..."), col += 4; + blankfill(SIZCOL); + } + } else { + if (first) + first = 0; + else + printf(", "); + printf("%s", nfile); + col += n+fill; + } + if (*file && !stat(file, &lbuf)) + totsize += copies * lbuf.st_size; +} + +/* + * Print the long info about the file + */ +void +ldump(nfile, file, copies) + char *nfile, *file; + int copies; +{ + struct stat lbuf; + + putchar('\t'); + if (copies > 1) + printf("%-2d copies of %-19s", copies, nfile); + else + printf("%-32s", nfile); + if (*file && !stat(file, &lbuf)) + printf(" %ld bytes", (long)lbuf.st_size); + else + printf(" ??? bytes"); + putchar('\n'); +} + +/* + * Print the job's rank in the queue, + * update col for screen management + */ +void +prank(n) + int n; +{ + char rline[100]; + static char *r[] = { + "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" + }; + + if (n == 0) { + printf("active"); + col += 6; + return; + } + if ((n/10)%10 == 1) + (void)snprintf(rline, sizeof(rline), "%dth", n); + else + (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]); + col += strlen(rline); + printf("%s", rline); +} diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h new file mode 100644 index 00000000000..ffe42d26350 --- /dev/null +++ b/usr.sbin/lpr/common_source/lp.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)lp.h 8.2 (Berkeley) 4/28/95 + */ + + +/* + * Global definitions for the line printer system. + */ + +extern char *AF; /* accounting file */ +extern long BR; /* baud rate if lp is a tty */ +extern char *CF; /* name of cifplot filter (per job) */ +extern char *DF; /* name of tex filter (per job) */ +extern long DU; /* daeomon user-id */ +extern long FC; /* flags to clear if lp is a tty */ +extern char *FF; /* form feed string */ +extern long FS; /* flags to set if lp is a tty */ +extern char *GF; /* name of graph(1G) filter (per job) */ +extern long HL; /* print header last */ +extern char *IF; /* name of input filter (created per job) */ +extern char *LF; /* log file for error messages */ +extern char *LO; /* lock file name */ +extern char *LP; /* line printer device name */ +extern long MC; /* maximum number of copies allowed */ +extern long MX; /* maximum number of blocks to copy */ +extern char *NF; /* name of ditroff(1) filter (per job) */ +extern char *OF; /* name of output filter (created once) */ +extern long PL; /* page length */ +extern long PW; /* page width */ +extern long PX; /* page width in pixels */ +extern long PY; /* page length in pixels */ +extern char *RF; /* name of fortran text filter (per job) */ +extern char *RG; /* restricted group */ +extern char *RM; /* remote machine name */ +extern char *RP; /* remote printer name */ +extern long RS; /* restricted to those with local accounts */ +extern long RW; /* open LP for reading and writing */ +extern long SB; /* short banner instead of normal header */ +extern long SC; /* suppress multiple copies */ +extern char *SD; /* spool directory */ +extern long SF; /* suppress FF on each print job */ +extern long SH; /* suppress header page */ +extern char *ST; /* status file name */ +extern char *TF; /* name of troff(1) filter (per job) */ +extern char *TR; /* trailer string to be output when Q empties */ +extern char *VF; /* name of raster filter (per job) */ +extern long XC; /* flags to clear for local mode */ +extern long XS; /* flags to set for local mode */ + +extern char line[BUFSIZ]; +extern char *bp; /* pointer into printcap buffer */ +extern char *name; /* program name */ +extern char *printer; /* printer name */ + /* host machine name */ +extern char host[MAXHOSTNAMELEN]; +extern char *from; /* client's machine name */ +extern int remote; /* true if sending files to a remote host */ +extern char *printcapdb[]; /* printcap database array */ +/* + * Structure used for building a sorted list of control files. + */ +struct queue { + time_t q_time; /* modification time */ + char q_name[MAXNAMLEN+1]; /* control file name */ +}; + +#include + +__BEGIN_DECLS +struct dirent; + +void blankfill __P((int)); +char *checkremote __P((void)); +int chk __P((char *)); +void displayq __P((int)); +void dump __P((char *, char *, int)); +void fatal __P((const char *, ...)); +int getline __P((FILE *)); +int getport __P((char *, int)); +int getq __P((struct queue *(*[]))); +void header __P((void)); +void inform __P((char *)); +int inlist __P((char *, char *)); +int iscf __P((struct dirent *)); +int isowner __P((char *, char *)); +void ldump __P((char *, char *, int)); +int lockchk __P((char *)); +void prank __P((int)); +void process __P((char *)); +void rmjob __P((void)); +void rmremote __P((void)); +void show __P((char *, char *, int)); +int startdaemon __P((char *)); +void warn __P((void)); +void delay __P((int)); +__END_DECLS diff --git a/usr.sbin/lpr/common_source/printcap.c b/usr.sbin/lpr/common_source/printcap.c new file mode 100644 index 00000000000..cbbaef13469 --- /dev/null +++ b/usr.sbin/lpr/common_source/printcap.c @@ -0,0 +1,551 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)printcap.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "pathnames.h" + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif +#define MAXHOP 32 /* max number of tc= indirections */ + +/* + * getcap-style interface for the old printcap routines. + * + * !!!USE THIS INTERFACE ONLY IF YOU DON'T HAVE THE REAL GETCAP!!! + */ + +static char *pbp; /* pointer into pbuf for pgetstr() */ +static char pbuf[BUFSIZ]; /* buffer for capability strings */ +extern char line[]; /* buffer for printcap entries */ + +int +cgetnext(bp, db_array) + register char **bp; + char **db_array; +{ + int ret; + char *strdup(); + + pbp = pbuf; + ret = getprent(line); + *bp = strdup(line); + return (ret); +} + +int +cgetent(bp, db_array, name) + char **bp, **db_array, *name; +{ + int i; + + *bp = line; + pbp = pbuf; + i = pgetent(*bp, name); + if (i < 0) + return (-2); + else if (i == 0) + return (-1); + else + return (0); +} + +char * +cgetcap(buf, cap, type) + char *buf, *cap; + int type; +{ + return ((char *) pgetflag(cap)); +} + +int +cgetstr(buf, cap, str) + char *buf, *cap; + char **str; +{ + char *pgetstr __P((char *, char **)); + + if (pbp >= pbuf+BUFSIZ) { + write(2, "Capability string buffer overflow\n", 34); + return (-1); + } + return ((*str = pgetstr(cap, &pbp)) == NULL ? -1 : 0); +} + +int +cgetnum(buf, cap, num) + char *buf, *cap; + long *num; +{ + return ((*num = pgetnum(cap)) < 0 ? -1 : 0); +} + +int +cgetclose() +{ + void endprent __P((void)); + + endprent(); + return (0); +} + + +/* + * termcap - routines for dealing with the terminal capability data base + * + * BUG: Should use a "last" pointer in tbuf, so that searching + * for capabilities alphabetically would not be a n**2/2 + * process when large numbers of capabilities are given. + * Note: If we add a last pointer now we will screw up the + * tc capability. We really should compile termcap. + * + * Essentially all the work here is scanning and decoding escapes + * in string capabilities. We don't use stdio because the editor + * doesn't, and because living w/o it is not hard. + */ + +#define PRINTCAP + +#ifdef PRINTCAP +#define tgetent pgetent +#define tskip pskip +#define tgetstr pgetstr +#define tdecode pdecode +#define tgetnum pgetnum +#define tgetflag pgetflag +#define tdecode pdecode +#define tnchktc pnchktc +#define tnamatch pnamatch +#define V6 +#endif + +static FILE *pfp = NULL; /* printcap data base file pointer */ +static char *tbuf; +static int hopcount; /* detect infinite loops in termcap, init 0 */ +static int tf; + +char *tgetstr __P((char *, char **)); +static char *tskip __P((char *)); +static char *tdecode __P((char *, char **)); + +/* + * Similar to tgetent except it returns the next enrty instead of + * doing a lookup. + */ +int +getprent(bp) + register char *bp; +{ + register int c, skip = 0; + + if (pfp == NULL && (pfp = fopen(_PATH_PRINTCAP, "r")) == NULL) + return(-1); + tbuf = bp; + for (;;) { + switch (c = getc(pfp)) { + case EOF: + fclose(pfp); + pfp = NULL; + return(0); + case '\n': + if (bp == tbuf) { + skip = 0; + continue; + } + if (bp[-1] == '\\') { + bp--; + continue; + } + *bp = '\0'; + return(1); + case '#': + if (bp == tbuf) + skip++; + default: + if (skip) + continue; + if (bp >= tbuf+BUFSIZ) { + write(2, "Termcap entry too long\n", 23); + *bp = '\0'; + return(1); + } + *bp++ = c; + } + } +} + +void +endprent() +{ + if (pfp != NULL) { + /* + * Can't use fclose here because on POSIX-compliant + * systems, fclose() causes the file pointer of the + * underlying file descriptor (which is possibly shared + * with a parent process) to be adjusted, and this + * reeks havoc in the parent because it doesn't know + * the file pointer has changed. + */ + (void) close(fileno(pfp)); + pfp = NULL; + } +} + +/* + * Get an entry for terminal name in buffer bp, + * from the termcap file. Parse is very rudimentary; + * we just notice escaped newlines. + */ +int +tgetent(bp, name) + char *bp, *name; +{ + register char *cp; + register int c; + register int i = 0, cnt = 0; + char ibuf[BUFSIZ]; + + tbuf = bp; +#ifndef V6 + cp = getenv("TERMCAP"); + /* + * TERMCAP can have one of two things in it. It can be the + * name of a file to use instead of /etc/termcap. In this + * case it better start with a "/". Or it can be an entry to + * use so we don't have to read the file. In this case it + * has to already have the newlines crunched out. + */ + if (cp && *cp) { + if (*cp!='/') { + cp2 = getenv("TERM"); + if (cp2==(char *) 0 || strcmp(name,cp2)==0) { + strcpy(bp,cp); + return(tnchktc()); + } else { + tf = open(_PATH_PRINTCAP, 0); + } + } else + tf = open(cp, 0); + } +#endif + if (tf==0) + tf = open(_PATH_PRINTCAP, 0); + if (tf < 0) + return (-1); + for (;;) { + cp = bp; + for (;;) { + if (i == cnt) { + cnt = read(tf, ibuf, BUFSIZ); + if (cnt <= 0) { + close(tf); + tf = 0; + return (0); + } + i = 0; + } + c = ibuf[i++]; + if (c == '\n') { + if (cp > bp && cp[-1] == '\\'){ + cp--; + continue; + } + break; + } + if (cp >= bp+BUFSIZ) { + write(2,"Termcap entry too long\n", 23); + break; + } else + *cp++ = c; + } + *cp = 0; + + /* + * The real work for the match. + */ + if (tnamatch(name)) { + lseek(tf, 0L, 0); + i = tnchktc(); + if (tf) { + close(tf); + tf = 0; + } + return(i); + } + } +} + +/* + * tnchktc: check the last entry, see if it's tc=xxx. If so, + * recursively find xxx and append that entry (minus the names) + * to take the place of the tc=xxx entry. This allows termcap + * entries to say "like an HP2621 but doesn't turn on the labels". + * Note that this works because of the left to right scan. + */ +int +tnchktc() +{ + register char *p, *q; + char tcname[16]; /* name of similar terminal */ + char tcbuf[BUFSIZ]; + char *holdtbuf = tbuf; + int l; + + p = tbuf + strlen(tbuf) - 2; /* before the last colon */ + while (*--p != ':') + if (p MAXHOP) { + write(2, "Infinite tc= loop\n", 18); + return (0); + } + if (tgetent(tcbuf, tcname) != 1) + return(0); + for (q=tcbuf; *q != ':'; q++) + ; + l = p - holdtbuf + strlen(q); + if (l > BUFSIZ) { + write(2, "Termcap entry too long\n", 23); + q[BUFSIZ - (p-tbuf)] = 0; + } + strcpy(p, q+1); + tbuf = holdtbuf; + return(1); +} + +/* + * Tnamatch deals with name matching. The first field of the termcap + * entry is a sequence of names separated by |'s, so we compare + * against each such name. The normal : terminator after the last + * name (before the first field) stops us. + */ +int +tnamatch(np) + char *np; +{ + register char *Np, *Bp; + + Bp = tbuf; + if (*Bp == '#') + return(0); + for (;;) { + for (Np = np; *Np && *Bp == *Np; Bp++, Np++) + continue; + if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) + return (1); + while (*Bp && *Bp != ':' && *Bp != '|') + Bp++; + if (*Bp == 0 || *Bp == ':') + return (0); + Bp++; + } +} + +/* + * Skip to the next field. Notice that this is very dumb, not + * knowing about \: escapes or any such. If necessary, :'s can be put + * into the termcap file in octal. + */ +static char * +tskip(bp) + register char *bp; +{ + + while (*bp && *bp != ':') + bp++; + if (*bp == ':') + bp++; + return (bp); +} + +/* + * Return the (numeric) option id. + * Numeric options look like + * li#80 + * i.e. the option string is separated from the numeric value by + * a # character. If the option is not found we return -1. + * Note that we handle octal numbers beginning with 0. + */ +int +tgetnum(id) + char *id; +{ + register int i, base; + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (*bp == 0) + return (-1); + if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) + continue; + if (*bp == '@') + return(-1); + if (*bp != '#') + continue; + bp++; + base = 10; + if (*bp == '0') + base = 8; + i = 0; + while (isdigit(*bp)) + i *= base, i += *bp++ - '0'; + return (i); + } +} + +/* + * Handle a flag option. + * Flag options are given "naked", i.e. followed by a : or the end + * of the buffer. Return 1 if we find the option, or 0 if it is + * not given. + */ +int +tgetflag(id) + char *id; +{ + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { + if (!*bp || *bp == ':') + return (1); + else if (*bp == '@') + return(0); + } + } +} + +/* + * Get a string valued option. + * These are given as + * cl=^Z + * Much decoding is done on the strings, and the strings are + * placed in area, which is a ref parameter which is updated. + * No checking on area overflow. + */ +char * +tgetstr(id, area) + char *id, **area; +{ + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) + continue; + if (*bp == '@') + return(0); + if (*bp != '=') + continue; + bp++; + return (tdecode(bp, area)); + } +} + +/* + * Tdecode does the grung work to decode the + * string capability escapes. + */ +static char * +tdecode(str, area) + register char *str; + char **area; +{ + register char *cp; + register int c; + register char *dp; + int i; + + cp = *area; + while ((c = *str++) && c != ':') { + switch (c) { + + case '^': + c = *str++ & 037; + break; + + case '\\': + dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; + c = *str++; +nextc: + if (*dp++ == c) { + c = *dp++; + break; + } + dp++; + if (*dp) + goto nextc; + if (isdigit(c)) { + c -= '0', i = 2; + do + c <<= 3, c |= *str++ - '0'; + while (--i && isdigit(*str)); + } + break; + } + *cp++ = c; + } + *cp++ = 0; + str = *area; + *area = cp; + return (str); +} diff --git a/usr.sbin/lpr/common_source/rmjob.c b/usr.sbin/lpr/common_source/rmjob.c new file mode 100644 index 00000000000..bdfe853fcd2 --- /dev/null +++ b/usr.sbin/lpr/common_source/rmjob.c @@ -0,0 +1,339 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)rmjob.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" + +/* + * rmjob - remove the specified jobs from the queue. + */ + +/* + * Stuff for handling lprm specifications + */ +extern char *user[]; /* users to process */ +extern int users; /* # of users in user array */ +extern int requ[]; /* job number of spool entries */ +extern int requests; /* # of spool requests */ +extern char *person; /* name of person doing lprm */ + +static char root[] = "root"; +static int all = 0; /* eliminate all files (root only) */ +static int cur_daemon; /* daemon's pid */ +static char current[40]; /* active control file name */ + +void +rmjob() +{ + register int i, nitems; + int assasinated = 0; + struct dirent **files; + char *cp; + + if ((i = cgetent(&bp, printcapdb, printer)) == -2) + fatal("can't open printer description file"); + else if (i == -1) + fatal("unknown printer"); + else if (i == -3) + fatal("potential reference loop detected in printcap file"); + if (cgetstr(bp, "lp", &LP) < 0) + LP = _PATH_DEFDEVLP; + if (cgetstr(bp, "rp", &RP) < 0) + RP = DEFLP; + if (cgetstr(bp, "sd", &SD) < 0) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp,"lo", &LO) < 0) + LO = DEFLOCK; + cgetstr(bp, "rm", &RM); + if (cp = checkremote()) + printf("Warning: %s\n", cp); + + /* + * If the format was `lprm -' and the user isn't the super-user, + * then fake things to look like he said `lprm user'. + */ + if (users < 0) { + if (getuid() == 0) + all = 1; /* all files in local queue */ + else { + user[0] = person; + users = 1; + } + } + if (!strcmp(person, "-all")) { + if (from == host) + fatal("The login name \"-all\" is reserved"); + all = 1; /* all those from 'from' */ + person = root; + } + + if (chdir(SD) < 0) + fatal("cannot chdir to spool directory"); + if ((nitems = scandir(".", &files, iscf, NULL)) < 0) + fatal("cannot access spool directory"); + + if (nitems) { + /* + * Check for an active printer daemon (in which case we + * kill it if it is reading our file) then remove stuff + * (after which we have to restart the daemon). + */ + if (lockchk(LO) && chk(current)) { + assasinated = kill(cur_daemon, SIGINT) == 0; + if (!assasinated) + fatal("cannot kill printer daemon"); + } + /* + * process the files + */ + for (i = 0; i < nitems; i++) + process(files[i]->d_name); + } + rmremote(); + /* + * Restart the printer daemon if it was killed + */ + if (assasinated && !startdaemon(printer)) + fatal("cannot restart printer daemon\n"); + exit(0); +} + +/* + * Process a lock file: collect the pid of the active + * daemon and the file name of the active spool entry. + * Return boolean indicating existence of a lock file. + */ +int +lockchk(s) + char *s; +{ + register FILE *fp; + register int i, n; + + if ((fp = fopen(s, "r")) == NULL) + if (errno == EACCES) + fatal("can't access lock file"); + else + return(0); + if (!getline(fp)) { + (void) fclose(fp); + return(0); /* no daemon present */ + } + cur_daemon = atoi(line); + if (kill(cur_daemon, 0) < 0) { + (void) fclose(fp); + return(0); /* no daemon present */ + } + for (i = 1; (n = fread(current, sizeof(char), sizeof(current), fp)) <= 0; i++) { + if (i > 5) { + n = 1; + break; + } + sleep(i); + } + current[n-1] = '\0'; + (void) fclose(fp); + return(1); +} + +/* + * Process a control file. + */ +void +process(file) + char *file; +{ + FILE *cfp; + + if (!chk(file)) + return; + if ((cfp = fopen(file, "r")) == NULL) + fatal("cannot open %s", file); + while (getline(cfp)) { + switch (line[0]) { + case 'U': /* unlink associated files */ + if (from != host) + printf("%s: ", host); + printf(unlink(line+1) ? "cannot dequeue %s\n" : + "%s dequeued\n", line+1); + } + } + (void) fclose(cfp); + if (from != host) + printf("%s: ", host); + printf(unlink(file) ? "cannot dequeue %s\n" : "%s dequeued\n", file); +} + +/* + * Do the dirty work in checking + */ +int +chk(file) + char *file; +{ + register int *r, n; + register char **u, *cp; + FILE *cfp; + + /* + * Check for valid cf file name (mostly checking current). + */ + if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f') + return(0); + + if (all && (from == host || !strcmp(from, file+6))) + return(1); + + /* + * get the owner's name from the control file. + */ + if ((cfp = fopen(file, "r")) == NULL) + return(0); + while (getline(cfp)) { + if (line[0] == 'P') + break; + } + (void) fclose(cfp); + if (line[0] != 'P') + return(0); + + if (users == 0 && requests == 0) + return(!strcmp(file, current) && isowner(line+1, file)); + /* + * Check the request list + */ + for (n = 0, cp = file+3; isdigit(*cp); ) + n = n * 10 + (*cp++ - '0'); + for (r = requ; r < &requ[requests]; r++) + if (*r == n && isowner(line+1, file)) + return(1); + /* + * Check to see if it's in the user list + */ + for (u = user; u < &user[users]; u++) + if (!strcmp(*u, line+1) && isowner(line+1, file)) + return(1); + return(0); +} + +/* + * If root is removing a file on the local machine, allow it. + * If root is removing a file from a remote machine, only allow + * files sent from the remote machine to be removed. + * Normal users can only remove the file from where it was sent. + */ +int +isowner(owner, file) + char *owner, *file; +{ + if (!strcmp(person, root) && (from == host || !strcmp(from, file+6))) + return(1); + if (!strcmp(person, owner) && !strcmp(from, file+6)) + return(1); + if (from != host) + printf("%s: ", host); + printf("%s: Permission denied\n", file); + return(0); +} + +/* + * Check to see if we are sending files to a remote machine. If we are, + * then try removing files on the remote machine. + */ +void +rmremote() +{ + register char *cp; + register int i, rem; + char buf[BUFSIZ]; + + if (!remote) + return; /* not sending to a remote machine */ + + /* + * Flush stdout so the user can see what has been deleted + * while we wait (possibly) for the connection. + */ + fflush(stdout); + + (void)snprintf(buf, sizeof(buf), "\5%s %s", RP, all ? "-all" : person); + cp = buf; + for (i = 0; i < users; i++) { + cp += strlen(cp); + *cp++ = ' '; + strcpy(cp, user[i]); + } + for (i = 0; i < requests; i++) { + cp += strlen(cp); + (void) sprintf(cp, " %d", requ[i]); + } + strcat(cp, "\n"); + rem = getport(RM, 0); + if (rem < 0) { + if (from != host) + printf("%s: ", host); + printf("connection to %s is down\n", RM); + } else { + i = strlen(buf); + if (write(rem, buf, i) != i) + fatal("Lost connection"); + while ((i = read(rem, buf, sizeof(buf))) > 0) + (void) fwrite(buf, 1, i, stdout); + (void) close(rem); + } +} + +/* + * Return 1 if the filename begins with 'cf' + */ +int +iscf(d) + struct dirent *d; +{ + return(d->d_name[0] == 'c' && d->d_name[1] == 'f'); +} diff --git a/usr.sbin/lpr/lpc/cmds.c b/usr.sbin/lpr/lpc/cmds.c new file mode 100644 index 00000000000..937fc08fdab --- /dev/null +++ b/usr.sbin/lpr/lpc/cmds.c @@ -0,0 +1,1108 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +/* + * lpc -- line printer control program -- commands: + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "lp.local.h" +#include "lpc.h" +#include "extern.h" +#include "pathnames.h" + +static void abortpr __P((int)); +static void cleanpr __P((void)); +static void disablepr __P((void)); +static int doarg __P((char *)); +static int doselect __P((struct dirent *)); +static void enablepr __P((void)); +static void prstat __P((void)); +static void putmsg __P((int, char **)); +static int sortq __P((const void *, const void *)); +static void startpr __P((int)); +static void stoppr __P((void)); +static int touch __P((struct queue *)); +static void unlinkf __P((char *)); +static void upstat __P((char *)); + +/* + * kill an existing daemon and disable printing. + */ +void +doabort(argc, argv) + int argc; + char *argv[]; +{ + register int c, status; + register char *cp1, *cp2; + char prbuf[100]; + + if (argc == 1) { + printf("Usage: abort {all | printer ...}\n"); + return; + } + if (argc == 2 && !strcmp(argv[1], "all")) { + printer = prbuf; + while (cgetnext(&bp, printcapdb) > 0) { + cp1 = prbuf; + cp2 = bp; + while ((c = *cp2++) && c != '|' && c != ':') + *cp1++ = c; + *cp1 = '\0'; + abortpr(1); + } + return; + } + while (--argc) { + printer = *++argv; + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + printf("cannot open printer description file\n"); + continue; + } else if (status == -1) { + printf("unknown printer %s\n", printer); + continue; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + abortpr(1); + } +} + +static void +abortpr(dis) + int dis; +{ + register FILE *fp; + struct stat stbuf; + int pid, fd; + + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + (void) sprintf(line, "%s/%s", SD, LO); + printf("%s:\n", printer); + + /* + * Turn on the owner execute bit of the lock file to disable printing. + */ + if (dis) { + if (stat(line, &stbuf) >= 0) { + if (chmod(line, (stbuf.st_mode & 0777) | 0100) < 0) + printf("\tcannot disable printing\n"); + else { + upstat("printing disabled\n"); + printf("\tprinting disabled\n"); + } + } else if (errno == ENOENT) { + if ((fd = open(line, O_WRONLY|O_CREAT, 0760)) < 0) + printf("\tcannot create lock file\n"); + else { + (void) close(fd); + upstat("printing disabled\n"); + printf("\tprinting disabled\n"); + printf("\tno daemon to abort\n"); + } + return; + } else { + printf("\tcannot stat lock file\n"); + return; + } + } + /* + * Kill the current daemon to stop printing now. + */ + if ((fp = fopen(line, "r")) == NULL) { + printf("\tcannot open lock file\n"); + return; + } + if (!getline(fp) || flock(fileno(fp), LOCK_SH|LOCK_NB) == 0) { + (void) fclose(fp); /* unlocks as well */ + printf("\tno daemon to abort\n"); + return; + } + (void) fclose(fp); + if (kill(pid = atoi(line), SIGTERM) < 0) + printf("\tWarning: daemon (pid %d) not killed\n", pid); + else + printf("\tdaemon (pid %d) killed\n", pid); +} + +/* + * Write a message into the status file. + */ +static void +upstat(msg) + char *msg; +{ + register int fd; + char statfile[BUFSIZ]; + + if (cgetstr(bp, "st", &ST) == -1) + ST = DEFSTAT; + (void) sprintf(statfile, "%s/%s", SD, ST); + umask(0); + fd = open(statfile, O_WRONLY|O_CREAT, 0664); + if (fd < 0 || flock(fd, LOCK_EX) < 0) { + printf("\tcannot create status file\n"); + return; + } + (void) ftruncate(fd, 0); + if (msg == (char *)NULL) + (void) write(fd, "\n", 1); + else + (void) write(fd, msg, strlen(msg)); + (void) close(fd); +} + +/* + * Remove all spool files and temporaries from the spooling area. + */ +void +clean(argc, argv) + int argc; + char *argv[]; +{ + register int c, status; + register char *cp1, *cp2; + char prbuf[100]; + + if (argc == 1) { + printf("Usage: clean {all | printer ...}\n"); + return; + } + if (argc == 2 && !strcmp(argv[1], "all")) { + printer = prbuf; + while (cgetnext(&bp, printcapdb) > 0) { + cp1 = prbuf; + cp2 = bp; + while ((c = *cp2++) && c != '|' && c != ':') + *cp1++ = c; + *cp1 = '\0'; + cleanpr(); + } + return; + } + while (--argc) { + printer = *++argv; + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + printf("cannot open printer description file\n"); + continue; + } else if (status == -1) { + printf("unknown printer %s\n", printer); + continue; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + cleanpr(); + } +} + +static int +doselect(d) + struct dirent *d; +{ + int c = d->d_name[0]; + + if ((c == 't' || c == 'c' || c == 'd') && d->d_name[1] == 'f') + return(1); + return(0); +} + +/* + * Comparison routine for scandir. Sort by job number and machine, then + * by `cf', `tf', or `df', then by the sequence letter A-Z, a-z. + */ +static int +sortq(a, b) + const void *a, *b; +{ + struct dirent **d1, **d2; + int c1, c2; + + d1 = (struct dirent **)a; + d2 = (struct dirent **)b; + if (c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3)) + return(c1); + c1 = (*d1)->d_name[0]; + c2 = (*d2)->d_name[0]; + if (c1 == c2) + return((*d1)->d_name[2] - (*d2)->d_name[2]); + if (c1 == 'c') + return(-1); + if (c1 == 'd' || c2 == 'c') + return(1); + return(-1); +} + +/* + * Remove incomplete jobs from spooling area. + */ +static void +cleanpr() +{ + register int i, n; + register char *cp, *cp1, *lp; + struct dirent **queue; + int nitems; + + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + printf("%s:\n", printer); + + for (lp = line, cp = SD; *lp++ = *cp++; ) + ; + lp[-1] = '/'; + + nitems = scandir(SD, &queue, doselect, sortq); + if (nitems < 0) { + printf("\tcannot examine spool directory\n"); + return; + } + if (nitems == 0) + return; + i = 0; + do { + cp = queue[i]->d_name; + if (*cp == 'c') { + n = 0; + while (i + 1 < nitems) { + cp1 = queue[i + 1]->d_name; + if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3)) + break; + i++; + n++; + } + if (n == 0) { + strcpy(lp, cp); + unlinkf(line); + } + } else { + /* + * Must be a df with no cf (otherwise, it would have + * been skipped above) or a tf file (which can always + * be removed). + */ + strcpy(lp, cp); + unlinkf(line); + } + } while (++i < nitems); +} + +static void +unlinkf(name) + char *name; +{ + if (unlink(name) < 0) + printf("\tcannot remove %s\n", name); + else + printf("\tremoved %s\n", name); +} + +/* + * Enable queuing to the printer (allow lpr's). + */ +void +enable(argc, argv) + int argc; + char *argv[]; +{ + register int c, status; + register char *cp1, *cp2; + char prbuf[100]; + + if (argc == 1) { + printf("Usage: enable {all | printer ...}\n"); + return; + } + if (argc == 2 && !strcmp(argv[1], "all")) { + printer = prbuf; + while (cgetnext(&bp, printcapdb) > 0) { + cp1 = prbuf; + cp2 = bp; + while ((c = *cp2++) && c != '|' && c != ':') + *cp1++ = c; + *cp1 = '\0'; + enablepr(); + } + return; + } + while (--argc) { + printer = *++argv; + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + printf("cannot open printer description file\n"); + continue; + } else if (status == -1) { + printf("unknown printer %s\n", printer); + continue; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + enablepr(); + } +} + +static void +enablepr() +{ + struct stat stbuf; + + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + (void) sprintf(line, "%s/%s", SD, LO); + printf("%s:\n", printer); + + /* + * Turn off the group execute bit of the lock file to enable queuing. + */ + if (stat(line, &stbuf) >= 0) { + if (chmod(line, stbuf.st_mode & 0767) < 0) + printf("\tcannot enable queuing\n"); + else + printf("\tqueuing enabled\n"); + } +} + +/* + * Disable queuing. + */ +void +disable(argc, argv) + int argc; + char *argv[]; +{ + register int c, status; + register char *cp1, *cp2; + char prbuf[100]; + + if (argc == 1) { + printf("Usage: disable {all | printer ...}\n"); + return; + } + if (argc == 2 && !strcmp(argv[1], "all")) { + printer = prbuf; + while (cgetnext(&bp, printcapdb) > 0) { + cp1 = prbuf; + cp2 = bp; + while ((c = *cp2++) && c != '|' && c != ':') + *cp1++ = c; + *cp1 = '\0'; + disablepr(); + } + return; + } + while (--argc) { + printer = *++argv; + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + printf("cannot open printer description file\n"); + continue; + } else if (status == -1) { + printf("unknown printer %s\n", printer); + continue; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + disablepr(); + } +} + +static void +disablepr() +{ + register int fd; + struct stat stbuf; + + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + (void) sprintf(line, "%s/%s", SD, LO); + printf("%s:\n", printer); + /* + * Turn on the group execute bit of the lock file to disable queuing. + */ + if (stat(line, &stbuf) >= 0) { + if (chmod(line, (stbuf.st_mode & 0777) | 010) < 0) + printf("\tcannot disable queuing\n"); + else + printf("\tqueuing disabled\n"); + } else if (errno == ENOENT) { + if ((fd = open(line, O_WRONLY|O_CREAT, 0670)) < 0) + printf("\tcannot create lock file\n"); + else { + (void) close(fd); + printf("\tqueuing disabled\n"); + } + return; + } else + printf("\tcannot stat lock file\n"); +} + +/* + * Disable queuing and printing and put a message into the status file + * (reason for being down). + */ +void +down(argc, argv) + int argc; + char *argv[]; +{ + register int c, status; + register char *cp1, *cp2; + char prbuf[100]; + + if (argc == 1) { + printf("Usage: down {all | printer} [message ...]\n"); + return; + } + if (!strcmp(argv[1], "all")) { + printer = prbuf; + while (cgetnext(&bp, printcapdb) > 0) { + cp1 = prbuf; + cp2 = bp; + while ((c = *cp2++) && c != '|' && c != ':') + *cp1++ = c; + *cp1 = '\0'; + putmsg(argc - 2, argv + 2); + } + return; + } + printer = argv[1]; + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + printf("cannot open printer description file\n"); + return; + } else if (status == -1) { + printf("unknown printer %s\n", printer); + return; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + putmsg(argc - 2, argv + 2); +} + +static void +putmsg(argc, argv) + int argc; + char **argv; +{ + register int fd; + register char *cp1, *cp2; + char buf[1024]; + struct stat stbuf; + + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + if (cgetstr(bp, "st", &ST) == -1) + ST = DEFSTAT; + printf("%s:\n", printer); + /* + * Turn on the group execute bit of the lock file to disable queuing and + * turn on the owner execute bit of the lock file to disable printing. + */ + (void) sprintf(line, "%s/%s", SD, LO); + if (stat(line, &stbuf) >= 0) { + if (chmod(line, (stbuf.st_mode & 0777) | 0110) < 0) + printf("\tcannot disable queuing\n"); + else + printf("\tprinter and queuing disabled\n"); + } else if (errno == ENOENT) { + if ((fd = open(line, O_WRONLY|O_CREAT, 0770)) < 0) + printf("\tcannot create lock file\n"); + else { + (void) close(fd); + printf("\tprinter and queuing disabled\n"); + } + return; + } else + printf("\tcannot stat lock file\n"); + /* + * Write the message into the status file. + */ + (void) sprintf(line, "%s/%s", SD, ST); + fd = open(line, O_WRONLY|O_CREAT, 0664); + if (fd < 0 || flock(fd, LOCK_EX) < 0) { + printf("\tcannot create status file\n"); + return; + } + (void) ftruncate(fd, 0); + if (argc <= 0) { + (void) write(fd, "\n", 1); + (void) close(fd); + return; + } + cp1 = buf; + while (--argc >= 0) { + cp2 = *argv++; + while (*cp1++ = *cp2++) + ; + cp1[-1] = ' '; + } + cp1[-1] = '\n'; + *cp1 = '\0'; + (void) write(fd, buf, strlen(buf)); + (void) close(fd); +} + +/* + * Exit lpc + */ +void +quit(argc, argv) + int argc; + char *argv[]; +{ + exit(0); +} + +/* + * Kill and restart the daemon. + */ +void +restart(argc, argv) + int argc; + char *argv[]; +{ + register int c, status; + register char *cp1, *cp2; + char prbuf[100]; + + if (argc == 1) { + printf("Usage: restart {all | printer ...}\n"); + return; + } + if (argc == 2 && !strcmp(argv[1], "all")) { + printer = prbuf; + while (cgetnext(&bp, printcapdb) > 0) { + cp1 = prbuf; + cp2 = bp; + while ((c = *cp2++) && c != '|' && c != ':') + *cp1++ = c; + *cp1 = '\0'; + abortpr(0); + startpr(0); + } + return; + } + while (--argc) { + printer = *++argv; + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + printf("cannot open printer description file\n"); + continue; + } else if (status == -1) { + printf("unknown printer %s\n", printer); + continue; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + abortpr(0); + startpr(0); + } +} + +/* + * Enable printing on the specified printer and startup the daemon. + */ +void +startcmd(argc, argv) + int argc; + char *argv[]; +{ + register int c, status; + register char *cp1, *cp2; + char prbuf[100]; + + if (argc == 1) { + printf("Usage: start {all | printer ...}\n"); + return; + } + if (argc == 2 && !strcmp(argv[1], "all")) { + printer = prbuf; + while (cgetnext(&bp, printcapdb) > 0) { + cp1 = prbuf; + cp2 = bp; + while ((c = *cp2++) && c != '|' && c != ':') + *cp1++ = c; + *cp1 = '\0'; + startpr(1); + } + return; + } + while (--argc) { + printer = *++argv; + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + printf("cannot open printer description file\n"); + continue; + } else if (status == -1) { + printf("unknown printer %s\n", printer); + continue; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + startpr(1); + } +} + +static void +startpr(enable) + int enable; +{ + struct stat stbuf; + + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + (void) sprintf(line, "%s/%s", SD, LO); + printf("%s:\n", printer); + + /* + * Turn off the owner execute bit of the lock file to enable printing. + */ + if (enable && stat(line, &stbuf) >= 0) { + if (chmod(line, stbuf.st_mode & (enable==2 ? 0666 : 0677)) < 0) + printf("\tcannot enable printing\n"); + else + printf("\tprinting enabled\n"); + } + if (!startdaemon(printer)) + printf("\tcouldn't start daemon\n"); + else + printf("\tdaemon started\n"); +} + +/* + * Print the status of each queue listed or all the queues. + */ +void +status(argc, argv) + int argc; + char *argv[]; +{ + register int c, status; + register char *cp1, *cp2; + char prbuf[100]; + + if (argc == 1) { + printer = prbuf; + while (cgetnext(&bp, printcapdb) > 0) { + cp1 = prbuf; + cp2 = bp; + while ((c = *cp2++) && c != '|' && c != ':') + *cp1++ = c; + *cp1 = '\0'; + prstat(); + } + return; + } + while (--argc) { + printer = *++argv; + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + printf("cannot open printer description file\n"); + continue; + } else if (status == -1) { + printf("unknown printer %s\n", printer); + continue; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + prstat(); + } +} + +/* + * Print the status of the printer queue. + */ +static void +prstat() +{ + struct stat stbuf; + register int fd, i; + register struct dirent *dp; + DIR *dirp; + + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + if (cgetstr(bp, "st", &ST) == -1) + ST = DEFSTAT; + printf("%s:\n", printer); + (void) sprintf(line, "%s/%s", SD, LO); + if (stat(line, &stbuf) >= 0) { + printf("\tqueuing is %s\n", + (stbuf.st_mode & 010) ? "disabled" : "enabled"); + printf("\tprinting is %s\n", + (stbuf.st_mode & 0100) ? "disabled" : "enabled"); + } else { + printf("\tqueuing is enabled\n"); + printf("\tprinting is enabled\n"); + } + if ((dirp = opendir(SD)) == NULL) { + printf("\tcannot examine spool directory\n"); + return; + } + i = 0; + while ((dp = readdir(dirp)) != NULL) { + if (*dp->d_name == 'c' && dp->d_name[1] == 'f') + i++; + } + closedir(dirp); + if (i == 0) + printf("\tno entries\n"); + else if (i == 1) + printf("\t1 entry in spool area\n"); + else + printf("\t%d entries in spool area\n", i); + fd = open(line, O_RDONLY); + if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) { + (void) close(fd); /* unlocks as well */ + printf("\tno daemon present\n"); + return; + } + (void) close(fd); + putchar('\t'); + (void) sprintf(line, "%s/%s", SD, ST); + fd = open(line, O_RDONLY); + if (fd >= 0) { + (void) flock(fd, LOCK_SH); + while ((i = read(fd, line, sizeof(line))) > 0) + (void) fwrite(line, 1, i, stdout); + (void) close(fd); /* unlocks as well */ + } +} + +/* + * Stop the specified daemon after completing the current job and disable + * printing. + */ +void +stop(argc, argv) + int argc; + char *argv[]; +{ + register int c, status; + register char *cp1, *cp2; + char prbuf[100]; + + if (argc == 1) { + printf("Usage: stop {all | printer ...}\n"); + return; + } + if (argc == 2 && !strcmp(argv[1], "all")) { + printer = prbuf; + while (cgetnext(&bp, printcapdb) > 0) { + cp1 = prbuf; + cp2 = bp; + while ((c = *cp2++) && c != '|' && c != ':') + *cp1++ = c; + *cp1 = '\0'; + stoppr(); + } + return; + } + while (--argc) { + printer = *++argv; + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + printf("cannot open printer description file\n"); + continue; + } else if (status == -1) { + printf("unknown printer %s\n", printer); + continue; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + stoppr(); + } +} + +static void +stoppr() +{ + register int fd; + struct stat stbuf; + + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + (void) sprintf(line, "%s/%s", SD, LO); + printf("%s:\n", printer); + + /* + * Turn on the owner execute bit of the lock file to disable printing. + */ + if (stat(line, &stbuf) >= 0) { + if (chmod(line, (stbuf.st_mode & 0777) | 0100) < 0) + printf("\tcannot disable printing\n"); + else { + upstat("printing disabled\n"); + printf("\tprinting disabled\n"); + } + } else if (errno == ENOENT) { + if ((fd = open(line, O_WRONLY|O_CREAT, 0760)) < 0) + printf("\tcannot create lock file\n"); + else { + (void) close(fd); + upstat("printing disabled\n"); + printf("\tprinting disabled\n"); + } + } else + printf("\tcannot stat lock file\n"); +} + +struct queue **queue; +int nitems; +time_t mtime; + +/* + * Put the specified jobs at the top of printer queue. + */ +void +topq(argc, argv) + int argc; + char *argv[]; +{ + register int i; + struct stat stbuf; + int status, changed; + + if (argc < 3) { + printf("Usage: topq printer [jobnum ...] [user ...]\n"); + return; + } + + --argc; + printer = *++argv; + status = cgetent(&bp, printcapdb, printer); + if (status == -2) { + printf("cannot open printer description file\n"); + return; + } else if (status == -1) { + printf("%s: unknown printer\n", printer); + return; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + printf("%s:\n", printer); + + if (chdir(SD) < 0) { + printf("\tcannot chdir to %s\n", SD); + return; + } + nitems = getq(&queue); + if (nitems == 0) + return; + changed = 0; + mtime = queue[0]->q_time; + for (i = argc; --i; ) { + if (doarg(argv[i]) == 0) { + printf("\tjob %s is not in the queue\n", argv[i]); + continue; + } else + changed++; + } + for (i = 0; i < nitems; i++) + free(queue[i]); + free(queue); + if (!changed) { + printf("\tqueue order unchanged\n"); + return; + } + /* + * Turn on the public execute bit of the lock file to + * get lpd to rebuild the queue after the current job. + */ + if (changed && stat(LO, &stbuf) >= 0) + (void) chmod(LO, (stbuf.st_mode & 0777) | 01); +} + +/* + * Reposition the job by changing the modification time of + * the control file. + */ +static int +touch(q) + struct queue *q; +{ + struct timeval tvp[2]; + + tvp[0].tv_sec = tvp[1].tv_sec = --mtime; + tvp[0].tv_usec = tvp[1].tv_usec = 0; + return(utimes(q->q_name, tvp)); +} + +/* + * Checks if specified job name is in the printer's queue. + * Returns: negative (-1) if argument name is not in the queue. + */ +static int +doarg(job) + char *job; +{ + register struct queue **qq; + register int jobnum, n; + register char *cp, *machine; + int cnt = 0; + FILE *fp; + + /* + * Look for a job item consisting of system name, colon, number + * (example: ucbarpa:114) + */ + if ((cp = index(job, ':')) != NULL) { + machine = job; + *cp++ = '\0'; + job = cp; + } else + machine = NULL; + + /* + * Check for job specified by number (example: 112 or 235ucbarpa). + */ + if (isdigit(*job)) { + jobnum = 0; + do + jobnum = jobnum * 10 + (*job++ - '0'); + while (isdigit(*job)); + for (qq = queue + nitems; --qq >= queue; ) { + n = 0; + for (cp = (*qq)->q_name+3; isdigit(*cp); ) + n = n * 10 + (*cp++ - '0'); + if (jobnum != n) + continue; + if (*job && strcmp(job, cp) != 0) + continue; + if (machine != NULL && strcmp(machine, cp) != 0) + continue; + if (touch(*qq) == 0) { + printf("\tmoved %s\n", (*qq)->q_name); + cnt++; + } + } + return(cnt); + } + /* + * Process item consisting of owner's name (example: henry). + */ + for (qq = queue + nitems; --qq >= queue; ) { + if ((fp = fopen((*qq)->q_name, "r")) == NULL) + continue; + while (getline(fp) > 0) + if (line[0] == 'P') + break; + (void) fclose(fp); + if (line[0] != 'P' || strcmp(job, line+1) != 0) + continue; + if (touch(*qq) == 0) { + printf("\tmoved %s\n", (*qq)->q_name); + cnt++; + } + } + return(cnt); +} + +/* + * Enable everything and start printer (undo `down'). + */ +void +up(argc, argv) + int argc; + char *argv[]; +{ + register int c, status; + register char *cp1, *cp2; + char prbuf[100]; + + if (argc == 1) { + printf("Usage: up {all | printer ...}\n"); + return; + } + if (argc == 2 && !strcmp(argv[1], "all")) { + printer = prbuf; + while (cgetnext(&bp, printcapdb) > 0) { + cp1 = prbuf; + cp2 = bp; + while ((c = *cp2++) && c != '|' && c != ':') + *cp1++ = c; + *cp1 = '\0'; + startpr(2); + } + return; + } + while (--argc) { + printer = *++argv; + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + printf("cannot open printer description file\n"); + continue; + } else if (status == -1) { + printf("unknown printer %s\n", printer); + continue; + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + startpr(2); + } +} diff --git a/usr.sbin/lpr/lpc/lpc.8 b/usr.sbin/lpr/lpc/lpc.8 new file mode 100644 index 00000000000..280a8b27479 --- /dev/null +++ b/usr.sbin/lpr/lpc/lpc.8 @@ -0,0 +1,175 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)lpc.8 8.5 (Berkeley) 4/28/95 +.\" +.Dd April 28, 1995 +.Dt LPC 8 +.Os BSD 4.2 +.Sh NAME +.Nm lpc +.Nd line printer control program +.Sh SYNOPSIS +.Nm lpc +.Oo +.Ar command +.Op Ar argument ... +.Oc +.Sh DESCRIPTION +.Nm Lpc +is used by the system administrator to control the +operation of the line printer system. +For each line printer configured in +.Pa /etc/printcap , +.Nm lpc +may be used to: +.Bl -bullet -offset indent +.It +disable or enable a printer, +.It +disable or enable a printer's spooling queue, +.It +rearrange the order of jobs in a spooling queue, +.It +find the status of printers, and their associated +spooling queues and printer daemons. +.El +.Pp +Without any arguments, +.Nm lpc +will prompt for commands from the standard input. +If arguments are supplied, +.Nm lpc +interprets the first argument as a command and the remaining +arguments as parameters to the command. The standard input +may be redirected causing +.Nm lpc +to read commands from file. +Commands may be abbreviated; +the following is the list of recognized commands. +.Pp +.Bl -tag -width Ds -compact +.It Ic \&? No [ command ... ] +.It Ic help No [ command ... ] +Print a short description of each command specified in the argument list, +or, if no argument is given, a list of the recognized commands. +.Pp +.It Ic abort No {\ all\ |\ printer\ } +Terminate an active spooling daemon on the local host immediately and +then disable printing (preventing new daemons from being started by +.Xr lpr ) +for the specified printers. +.Pp +.It Ic clean No {\ all\ |\ printer\ } +Remove any temporary files, data files, and control files that cannot +be printed (i.e., do not form a complete printer job) +from the specified printer queue(s) on the local machine. +.Pp +.It Ic disable No {\ all\ |\ printer\ } +Turn the specified printer queues off. This prevents new +printer jobs from being entered into the queue by +.Xr lpr . +.Pp +.It Ic down No {\ all\ |\ printer\ } message ... +Turn the specified printer queue off, disable printing and put +.Em message +in the printer status file. The message doesn't need to be quoted, the +remaining arguments are treated like +.Xr echo 1 . +This is normally used to take a printer down and let others know why +.Xr lpq 1 +will indicate the printer is down and print the status message). +.Pp +.It Ic enable No {\ all\ |\ printer\ } +Enable spooling on the local queue for the listed printers. +This will allow +.Xr lpr 1 +to put new jobs in the spool queue. +.Pp +.It Ic exit +.It Ic quit +Exit from lpc. +.ne 1i +.Pp +.It Ic restart No {\ all\ |\ printer\ } +Attempt to start a new printer daemon. +This is useful when some abnormal condition causes the daemon to +die unexpectedly, leaving jobs in the queue. +.Xr Lpq +will report that there is no daemon present when this condition occurs. +If the user is the super-user, +try to abort the current daemon first (i.e., kill and restart a stuck daemon). +.Pp +.It Ic start No {\ all\ |\ printer\ } +Enable printing and start a spooling daemon for the listed printers. +.Pp +.It Ic status No {\ all\ |\ printer\ } +Display the status of daemons and queues on the local machine. +.Pp +.It Ic stop No {\ all\ |\ printer\ } +Stop a spooling daemon after the current job completes and disable +printing. +.Pp +.It Ic topq No printer\ [\ jobnum\ ...\ ]\ [\ user\ ...\ ] +Place the jobs in the order listed at the top of the printer queue. +.Pp +.It Ic up No {\ all\ |\ printer\ } +Enable everything and start a new printer daemon. Undoes the effects of +.Ic down . +.Sh FILES +.Bl -tag -width /var/spool/*/lockx -compact +.It Pa /etc/printcap +printer description file +.It Pa /var/spool/* +spool directories +.It Pa /var/spool/*/lock +lock file for queue control +.El +.Sh SEE ALSO +.Xr lpd 8 , +.Xr lpr 1 , +.Xr lpq 1 , +.Xr lprm 1 , +.Xr printcap 5 +.Sh DIAGNOSTICS +.Bl -tag -width Ds +.It Sy "?Ambiguous command" +abbreviation matches more than one command +.It Sy "?Invalid command" +no match was found +.It Sy "?Privileged command" +you must be a member of group "operator" or root to execute this command +.El +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/usr.sbin/lpr/lpc/lpc.c b/usr.sbin/lpr/lpc/lpc.c new file mode 100644 index 00000000000..64432b22697 --- /dev/null +++ b/usr.sbin/lpr/lpc/lpc.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "lpc.h" +#include "extern.h" + +#ifndef LPR_OPER +#define LPR_OPER "operator" /* group name of lpr operators */ +#endif + +/* + * lpc -- line printer control program + */ + +int fromatty; + +char cmdline[200]; +int margc; +char *margv[20]; +int top; + +jmp_buf toplevel; + +static void cmdscanner __P((int)); +static struct cmd *getcmd __P((char *)); +static void intr __P((int)); +static void makeargv __P((void)); +static int ingroup __P((char *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct cmd *c; + + name = argv[0]; + openlog("lpd", 0, LOG_LPR); + + if (--argc > 0) { + c = getcmd(*++argv); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + exit(1); + } + if (c == 0) { + printf("?Invalid command\n"); + exit(1); + } + if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) { + printf("?Privileged command\n"); + exit(1); + } + (*c->c_handler)(argc, argv); + exit(0); + } + fromatty = isatty(fileno(stdin)); + top = setjmp(toplevel) == 0; + if (top) + signal(SIGINT, intr); + for (;;) { + cmdscanner(top); + top = 1; + } +} + +static void +intr(signo) + int signo; +{ + if (!fromatty) + exit(0); + longjmp(toplevel, 1); +} + +/* + * Command parser. + */ +static void +cmdscanner(top) + int top; +{ + register struct cmd *c; + + if (!top) + putchar('\n'); + for (;;) { + if (fromatty) { + printf("lpc> "); + fflush(stdout); + } + if (fgets(cmdline, sizeof(cmdline), stdin) == 0) + quit(0, NULL); + if (cmdline[0] == 0 || cmdline[0] == '\n') + break; + makeargv(); + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + continue; + } + if (c == 0) { + printf("?Invalid command\n"); + continue; + } + if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) { + printf("?Privileged command\n"); + continue; + } + (*c->c_handler)(margc, margv); + } + longjmp(toplevel, 0); +} + +static struct cmd * +getcmd(name) + register char *name; +{ + register char *p, *q; + register struct cmd *c, *found; + register int nmatches, longest; + + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; p = c->c_name; c++) { + for (q = name; *q == *p++; q++) + if (*q == 0) /* exact match? */ + return(c); + if (!*q) { /* the name was a prefix */ + if (q - name > longest) { + longest = q - name; + nmatches = 1; + found = c; + } else if (q - name == longest) + nmatches++; + } + } + if (nmatches > 1) + return((struct cmd *)-1); + return(found); +} + +/* + * Slice a string up into argc/argv. + */ +static void +makeargv() +{ + register char *cp; + register char **argp = margv; + + margc = 0; + for (cp = cmdline; *cp;) { + while (isspace(*cp)) + cp++; + if (*cp == '\0') + break; + *argp++ = cp; + margc += 1; + while (*cp != '\0' && !isspace(*cp)) + cp++; + if (*cp == '\0') + break; + *cp++ = '\0'; + } + *argp++ = 0; +} + +#define HELPINDENT (sizeof ("directory")) + +/* + * Help command. + */ +void +help(argc, argv) + int argc; + char *argv[]; +{ + register struct cmd *c; + + if (argc == 1) { + register int i, j, w; + int columns, width = 0, lines; + extern int NCMDS; + + printf("Commands may be abbreviated. Commands are:\n\n"); + for (c = cmdtab; c->c_name; c++) { + int len = strlen(c->c_name); + + if (len > width) + width = len; + } + width = (width + 8) &~ 7; + columns = 80 / width; + if (columns == 0) + columns = 1; + lines = (NCMDS + columns - 1) / columns; + for (i = 0; i < lines; i++) { + for (j = 0; j < columns; j++) { + c = cmdtab + j * lines + i; + if (c->c_name) + printf("%s", c->c_name); + if (c + lines >= &cmdtab[NCMDS]) { + printf("\n"); + break; + } + w = strlen(c->c_name); + while (w < width) { + w = (w + 8) &~ 7; + putchar('\t'); + } + } + } + return; + } + while (--argc > 0) { + register char *arg; + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + printf("?Ambiguous help command %s\n", arg); + else if (c == (struct cmd *)0) + printf("?Invalid help command %s\n", arg); + else + printf("%-*s\t%s\n", HELPINDENT, + c->c_name, c->c_help); + } +} + +/* + * return non-zero if the user is a member of the given group + */ +static int +ingroup(grname) + char *grname; +{ + static struct group *gptr=NULL; + static gid_t groups[NGROUPS]; + register gid_t gid; + register int i; + + if (gptr == NULL) { + if ((gptr = getgrnam(grname)) == NULL) { + fprintf(stderr, "Warning: unknown group '%s'\n", + grname); + return(0); + } + if (getgroups(NGROUPS, groups) < 0) { + perror("getgroups"); + exit(1); + } + } + gid = gptr->gr_gid; + for (i = 0; i < NGROUPS; i++) + if (gid == groups[i]) + return(1); + return(0); +} diff --git a/usr.sbin/lpr/lpd/lpd.c b/usr.sbin/lpr/lpd/lpd.c new file mode 100644 index 00000000000..3b233598a7b --- /dev/null +++ b/usr.sbin/lpr/lpd/lpd.c @@ -0,0 +1,542 @@ +/* + * Copyright (c) 1983, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)lpd.c 8.7 (Berkeley) 5/10/95"; +#endif /* not lint */ + +/* + * lpd -- line printer daemon. + * + * Listen for a connection and perform the requested operation. + * Operations are: + * \1printer\n + * check the queue for jobs and print any found. + * \2printer\n + * receive a job from another machine and queue it. + * \3printer [users ...] [jobs ...]\n + * return the current state of the queue (short form). + * \4printer [users ...] [jobs ...]\n + * return the current state of the queue (long form). + * \5printer person [users ...] [jobs ...]\n + * remove jobs from the queue. + * + * Strategy to maintain protected spooling area: + * 1. Spooling area is writable only by daemon and spooling group + * 2. lpr runs setuid root and setgrp spooling group; it uses + * root to access any file it wants (verifying things before + * with an access call) and group id to know how it should + * set up ownership of files in the spooling area. + * 3. Files in spooling area are owned by root, group spooling + * group, with mode 660. + * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to + * access files and printer. Users can't get to anything + * w/o help of lpq and lprm programs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" +#include "extern.h" + +int lflag; /* log requests flag */ +int from_remote; /* from remote socket */ + +static void reapchild __P((int)); +static void mcleanup __P((int)); +static void doit __P((void)); +static void startup __P((void)); +static void chkhost __P((struct sockaddr_in *)); +static int ckqueue __P((char *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + int f, funix, finet, options, fromlen; + fd_set defreadfds; + struct sockaddr_un un, fromunix; + struct sockaddr_in sin, frominet; + int omask, lfd; + + options = 0; + gethostname(host, sizeof(host)); + name = argv[0]; + + while (--argc > 0) { + argv++; + if (argv[0][0] == '-') + switch (argv[0][1]) { + case 'd': + options |= SO_DEBUG; + break; + case 'l': + lflag++; + break; + } + } + +#ifndef DEBUG + /* + * Set up standard environment by detaching from the parent. + */ + daemon(0, 0); +#endif + + openlog("lpd", LOG_PID, LOG_LPR); + syslog(LOG_INFO, "restarted"); + (void) umask(0); + lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT, 0644); + if (lfd < 0) { + syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); + exit(1); + } + if (flock(lfd, LOCK_EX|LOCK_NB) < 0) { + if (errno == EWOULDBLOCK) /* active deamon present */ + exit(0); + syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); + exit(1); + } + ftruncate(lfd, 0); + /* + * write process id for others to know + */ + sprintf(line, "%u\n", getpid()); + f = strlen(line); + if (write(lfd, line, f) != f) { + syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); + exit(1); + } + signal(SIGCHLD, reapchild); + /* + * Restart all the printers. + */ + startup(); + (void) unlink(_PATH_SOCKETNAME); + funix = socket(AF_UNIX, SOCK_STREAM, 0); + if (funix < 0) { + syslog(LOG_ERR, "socket: %m"); + exit(1); + } +#define mask(s) (1 << ((s) - 1)) + omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM)); + signal(SIGHUP, mcleanup); + signal(SIGINT, mcleanup); + signal(SIGQUIT, mcleanup); + signal(SIGTERM, mcleanup); + memset(&un, 0, sizeof(un)); + un.sun_family = AF_UNIX; + strcpy(un.sun_path, _PATH_SOCKETNAME); +#ifndef SUN_LEN +#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) +#endif + if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) { + syslog(LOG_ERR, "ubind: %m"); + exit(1); + } + sigsetmask(omask); + FD_ZERO(&defreadfds); + FD_SET(funix, &defreadfds); + listen(funix, 5); + finet = socket(AF_INET, SOCK_STREAM, 0); + if (finet >= 0) { + struct servent *sp; + + if (options & SO_DEBUG) + if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) { + syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); + mcleanup(0); + } + sp = getservbyname("printer", "tcp"); + if (sp == NULL) { + syslog(LOG_ERR, "printer/tcp: unknown service"); + mcleanup(0); + } + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = sp->s_port; + if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + syslog(LOG_ERR, "bind: %m"); + mcleanup(0); + } + FD_SET(finet, &defreadfds); + listen(finet, 5); + } + /* + * Main loop: accept, do a request, continue. + */ + memset(&frominet, 0, sizeof(frominet)); + memset(&fromunix, 0, sizeof(fromunix)); + for (;;) { + int domain, nfds, s; + fd_set readfds; + + FD_COPY(&defreadfds, &readfds); + nfds = select(20, &readfds, 0, 0, 0); + if (nfds <= 0) { + if (nfds < 0 && errno != EINTR) + syslog(LOG_WARNING, "select: %m"); + continue; + } + if (FD_ISSET(funix, &readfds)) { + domain = AF_UNIX, fromlen = sizeof(fromunix); + s = accept(funix, + (struct sockaddr *)&fromunix, &fromlen); + } else /* if (FD_ISSET(finet, &readfds)) */ { + domain = AF_INET, fromlen = sizeof(frominet); + s = accept(finet, + (struct sockaddr *)&frominet, &fromlen); + } + if (s < 0) { + if (errno != EINTR) + syslog(LOG_WARNING, "accept: %m"); + continue; + } + if (fork() == 0) { + signal(SIGCHLD, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + (void) close(funix); + (void) close(finet); + dup2(s, 1); + (void) close(s); + if (domain == AF_INET) { + from_remote = 1; + chkhost(&frominet); + } else + from_remote = 0; + doit(); + exit(0); + } + (void) close(s); + } +} + +static void +reapchild(signo) + int signo; +{ + union wait status; + + while (wait3((int *)&status, WNOHANG, 0) > 0) + ; +} + +static void +mcleanup(signo) + int signo; +{ + if (lflag) + syslog(LOG_INFO, "exiting"); + unlink(_PATH_SOCKETNAME); + exit(0); +} + +/* + * Stuff for handling job specifications + */ +char *user[MAXUSERS]; /* users to process */ +int users; /* # of users in user array */ +int requ[MAXREQUESTS]; /* job number of spool entries */ +int requests; /* # of spool requests */ +char *person; /* name of person doing lprm */ + +char fromb[MAXHOSTNAMELEN]; /* buffer for client's machine name */ +char cbuf[BUFSIZ]; /* command line buffer */ +char *cmdnames[] = { + "null", + "printjob", + "recvjob", + "displayq short", + "displayq long", + "rmjob" +}; + +static void +doit() +{ + register char *cp; + register int n; + + for (;;) { + cp = cbuf; + do { + if (cp >= &cbuf[sizeof(cbuf) - 1]) + fatal("Command line too long"); + if ((n = read(1, cp, 1)) != 1) { + if (n < 0) + fatal("Lost connection"); + return; + } + } while (*cp++ != '\n'); + *--cp = '\0'; + cp = cbuf; + if (lflag) { + if (*cp >= '\1' && *cp <= '\5') + syslog(LOG_INFO, "%s requests %s %s", + from, cmdnames[*cp], cp+1); + else + syslog(LOG_INFO, "bad request (%d) from %s", + *cp, from); + } + switch (*cp++) { + case '\1': /* check the queue and print any jobs there */ + printer = cp; + printjob(); + break; + case '\2': /* receive files to be queued */ + if (!from_remote) { + syslog(LOG_INFO, "illegal request (%d)", *cp); + exit(1); + } + printer = cp; + recvjob(); + break; + case '\3': /* display the queue (short form) */ + case '\4': /* display the queue (long form) */ + printer = cp; + while (*cp) { + if (*cp != ' ') { + cp++; + continue; + } + *cp++ = '\0'; + while (isspace(*cp)) + cp++; + if (*cp == '\0') + break; + if (isdigit(*cp)) { + if (requests >= MAXREQUESTS) + fatal("Too many requests"); + requ[requests++] = atoi(cp); + } else { + if (users >= MAXUSERS) + fatal("Too many users"); + user[users++] = cp; + } + } + displayq(cbuf[0] - '\3'); + exit(0); + case '\5': /* remove a job from the queue */ + if (!from_remote) { + syslog(LOG_INFO, "illegal request (%d)", *cp); + exit(1); + } + printer = cp; + while (*cp && *cp != ' ') + cp++; + if (!*cp) + break; + *cp++ = '\0'; + person = cp; + while (*cp) { + if (*cp != ' ') { + cp++; + continue; + } + *cp++ = '\0'; + while (isspace(*cp)) + cp++; + if (*cp == '\0') + break; + if (isdigit(*cp)) { + if (requests >= MAXREQUESTS) + fatal("Too many requests"); + requ[requests++] = atoi(cp); + } else { + if (users >= MAXUSERS) + fatal("Too many users"); + user[users++] = cp; + } + } + rmjob(); + break; + } + fatal("Illegal service request"); + } +} + +/* + * Make a pass through the printcap database and start printing any + * files left from the last time the machine went down. + */ +static void +startup() +{ + char *buf; + register char *cp; + int pid; + + /* + * Restart the daemons. + */ + while (cgetnext(&buf, printcapdb) > 0) { + if (ckqueue(buf) <= 0) { + free(buf); + continue; /* no work to do for this printer */ + } + for (cp = buf; *cp; cp++) + if (*cp == '|' || *cp == ':') { + *cp = '\0'; + break; + } + if (lflag) + syslog(LOG_INFO, "work for %s", buf); + if ((pid = fork()) < 0) { + syslog(LOG_WARNING, "startup: cannot fork"); + mcleanup(0); + } + if (!pid) { + printer = buf; + cgetclose(); + printjob(); + /* NOTREACHED */ + } + else free(buf); + } +} + +/* + * Make sure there's some work to do before forking off a child + */ +static int +ckqueue(cap) + char *cap; +{ + register struct dirent *d; + DIR *dirp; + char *spooldir; + + if (cgetstr(cap, "sd", &spooldir) == -1) + spooldir = _PATH_DEFSPOOL; + if ((dirp = opendir(spooldir)) == NULL) + return (-1); + while ((d = readdir(dirp)) != NULL) { + if (d->d_name[0] != 'c' || d->d_name[1] != 'f') + continue; /* daemon control files only */ + closedir(dirp); + return (1); /* found something */ + } + closedir(dirp); + return (0); +} + +#define DUMMY ":nobody::" + +/* + * Check to see if the from host has access to the line printer. + */ +static void +chkhost(f) + struct sockaddr_in *f; +{ + register struct hostent *hp; + register FILE *hostf; + int first = 1; + extern char *inet_ntoa(); + + f->sin_port = ntohs(f->sin_port); + if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED) + fatal("Malformed from address"); + + /* Need real hostname for temporary filenames */ + hp = gethostbyaddr((char *)&f->sin_addr, + sizeof(struct in_addr), f->sin_family); + if (hp == NULL) + fatal("Host name for your address (%s) unknown", + inet_ntoa(f->sin_addr)); + + (void) strncpy(fromb, hp->h_name, sizeof(fromb)); + from[sizeof(fromb) - 1] = '\0'; + from = fromb; + + hostf = fopen(_PATH_HOSTSEQUIV, "r"); +again: + if (hostf) { + if (__ivaliduser(hostf, f->sin_addr.s_addr, + DUMMY, DUMMY) == 0) { + (void) fclose(hostf); + return; + } + (void) fclose(hostf); + } + if (first == 1) { + first = 0; + hostf = fopen(_PATH_HOSTSLPD, "r"); + goto again; + } + fatal("Your host does not have line printer access"); + /*NOTREACHED*/ +} + + + + + + + + + + + + diff --git a/usr.sbin/lpr/lpd/printjob.c b/usr.sbin/lpr/lpd/printjob.c new file mode 100644 index 00000000000..2d176196147 --- /dev/null +++ b/usr.sbin/lpr/lpd/printjob.c @@ -0,0 +1,1493 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95"; +#endif /* not lint */ + + +/* + * printjob -- print jobs in the queue. + * + * NOTE: the lock file is used to pass information to lpq and lprm. + * it does not need to be removed because file locks are dynamic. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" +#include "extern.h" + +#define DORETURN 0 /* absorb fork error */ +#define DOABORT 1 /* abort if dofork fails */ + +/* + * Error tokens + */ +#define REPRINT -2 +#define ERROR -1 +#define OK 0 +#define FATALERR 1 +#define NOACCT 2 +#define FILTERERR 3 +#define ACCESS 4 + +static dev_t fdev; /* device of file pointed to by symlink */ +static ino_t fino; /* inode of file pointed to by symlink */ +static FILE *cfp; /* control file */ +static int child; /* id of any filters */ +static int lfd; /* lock file descriptor */ +static int ofd; /* output filter file descriptor */ +static int ofilter; /* id of output filter, if any */ +static int pfd; /* prstatic inter file descriptor */ +static int pid; /* pid of lpd process */ +static int prchild; /* id of pr process */ +static char title[80]; /* ``pr'' title */ +static int tof; /* true if at top of form */ + +static char class[32]; /* classification field */ +static char fromhost[32]; /* user's host machine */ + /* indentation size in static characters */ +static char indent[10] = "-i0"; +static char jobname[100]; /* job or file name */ +static char length[10] = "-l"; /* page length in lines */ +static char logname[32]; /* user's login name */ +static char pxlength[10] = "-y"; /* page length in pixels */ +static char pxwidth[10] = "-x"; /* page width in pixels */ +static char tempfile[] = "errsXXXXXX"; /* file name for filter output */ +static char width[10] = "-w"; /* page width in static characters */ + +static void abortpr __P((int)); +static void banner __P((char *, char *)); +static int dofork __P((int)); +static int dropit __P((int)); +static void init __P((void)); +static void openpr __P((void)); +static void opennet __P((char *)); +static void opentty __P((void)); +static void openrem __P((void)); +static int print __P((int, char *)); +static int printit __P((char *)); +static void pstatus __P((const char *, ...)); +static char response __P((void)); +static void scan_out __P((int, char *, int)); +static char *scnline __P((int, char *, int)); +static int sendfile __P((int, char *)); +static int sendit __P((char *)); +static void sendmail __P((char *, int)); +static void setty __P((void)); + +void +printjob() +{ + struct stat stb; + register struct queue *q, **qp; + struct queue **queue; + register int i, nitems; + off_t pidoff; + int errcnt, count = 0; + + init(); /* set up capabilities */ + (void) write(1, "", 1); /* ack that daemon is started */ + (void) close(2); /* set up log file */ + if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) { + syslog(LOG_ERR, "%s: %m", LF); + (void) open(_PATH_DEVNULL, O_WRONLY); + } + setgid(getegid()); + pid = getpid(); /* for use with lprm */ + setpgrp(0, pid); + signal(SIGHUP, abortpr); + signal(SIGINT, abortpr); + signal(SIGQUIT, abortpr); + signal(SIGTERM, abortpr); + + (void) mktemp(tempfile); + + /* + * uses short form file names + */ + if (chdir(SD) < 0) { + syslog(LOG_ERR, "%s: %m", SD); + exit(1); + } + if (stat(LO, &stb) == 0 && (stb.st_mode & 0100)) + exit(0); /* printing disabled */ + lfd = open(LO, O_WRONLY|O_CREAT, 0644); + if (lfd < 0) { + syslog(LOG_ERR, "%s: %s: %m", printer, LO); + exit(1); + } + if (flock(lfd, LOCK_EX|LOCK_NB) < 0) { + if (errno == EWOULDBLOCK) /* active deamon present */ + exit(0); + syslog(LOG_ERR, "%s: %s: %m", printer, LO); + exit(1); + } + ftruncate(lfd, 0); + /* + * write process id for others to know + */ + sprintf(line, "%u\n", pid); + pidoff = i = strlen(line); + if (write(lfd, line, i) != i) { + syslog(LOG_ERR, "%s: %s: %m", printer, LO); + exit(1); + } + /* + * search the spool directory for work and sort by queue order. + */ + if ((nitems = getq(&queue)) < 0) { + syslog(LOG_ERR, "%s: can't scan %s", printer, SD); + exit(1); + } + if (nitems == 0) /* no work to do */ + exit(0); + if (stb.st_mode & 01) { /* reset queue flag */ + if (fchmod(lfd, stb.st_mode & 0776) < 0) + syslog(LOG_ERR, "%s: %s: %m", printer, LO); + } + openpr(); /* open printer or remote */ +again: + /* + * we found something to do now do it -- + * write the name of the current control file into the lock file + * so the spool queue program can tell what we're working on + */ + for (qp = queue; nitems--; free((char *) q)) { + q = *qp++; + if (stat(q->q_name, &stb) < 0) + continue; + errcnt = 0; + restart: + (void) lseek(lfd, pidoff, 0); + (void) sprintf(line, "%s\n", q->q_name); + i = strlen(line); + if (write(lfd, line, i) != i) + syslog(LOG_ERR, "%s: %s: %m", printer, LO); + if (!remote) + i = printit(q->q_name); + else + i = sendit(q->q_name); + /* + * Check to see if we are supposed to stop printing or + * if we are to rebuild the queue. + */ + if (fstat(lfd, &stb) == 0) { + /* stop printing before starting next job? */ + if (stb.st_mode & 0100) + goto done; + /* rebuild queue (after lpc topq) */ + if (stb.st_mode & 01) { + for (free((char *) q); nitems--; free((char *) q)) + q = *qp++; + if (fchmod(lfd, stb.st_mode & 0776) < 0) + syslog(LOG_WARNING, "%s: %s: %m", + printer, LO); + break; + } + } + if (i == OK) /* file ok and printed */ + count++; + else if (i == REPRINT && ++errcnt < 5) { + /* try reprinting the job */ + syslog(LOG_INFO, "restarting %s", printer); + if (ofilter > 0) { + kill(ofilter, SIGCONT); /* to be sure */ + (void) close(ofd); + while ((i = wait(NULL)) > 0 && i != ofilter) + ; + ofilter = 0; + } + (void) close(pfd); /* close printer */ + if (ftruncate(lfd, pidoff) < 0) + syslog(LOG_WARNING, "%s: %s: %m", printer, LO); + openpr(); /* try to reopen printer */ + goto restart; + } else { + syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer, + remote ? "sent to remote host" : "printed", q->q_name); + if (i == REPRINT) { + /* insure we don't attempt this job again */ + (void) unlink(q->q_name); + q->q_name[0] = 'd'; + (void) unlink(q->q_name); + if (logname[0]) + sendmail(logname, FATALERR); + } + } + } + free((char *) queue); + /* + * search the spool directory for more work. + */ + if ((nitems = getq(&queue)) < 0) { + syslog(LOG_ERR, "%s: can't scan %s", printer, SD); + exit(1); + } + if (nitems == 0) { /* no more work to do */ + done: + if (count > 0) { /* Files actually printed */ + if (!SF && !tof) + (void) write(ofd, FF, strlen(FF)); + if (TR != NULL) /* output trailer */ + (void) write(ofd, TR, strlen(TR)); + } + (void) unlink(tempfile); + exit(0); + } + goto again; +} + +char fonts[4][50]; /* fonts for troff */ + +char ifonts[4][40] = { + _PATH_VFONTR, + _PATH_VFONTI, + _PATH_VFONTB, + _PATH_VFONTS, +}; + +/* + * The remaining part is the reading of the control file (cf) + * and performing the various actions. + */ +static int +printit(file) + char *file; +{ + register int i; + char *cp; + int bombed = OK; + + /* + * open control file; ignore if no longer there. + */ + if ((cfp = fopen(file, "r")) == NULL) { + syslog(LOG_INFO, "%s: %s: %m", printer, file); + return(OK); + } + /* + * Reset troff fonts. + */ + for (i = 0; i < 4; i++) + strcpy(fonts[i], ifonts[i]); + sprintf(&width[2], "%d", PW); + strcpy(indent+2, "0"); + + /* + * read the control file for work to do + * + * file format -- first character in the line is a command + * rest of the line is the argument. + * valid commands are: + * + * S -- "stat info" for symbolic link protection + * J -- "job name" on banner page + * C -- "class name" on banner page + * L -- "literal" user's name to print on banner + * T -- "title" for pr + * H -- "host name" of machine where lpr was done + * P -- "person" user's login name + * I -- "indent" amount to indent output + * R -- laser dpi "resolution" + * f -- "file name" name of text file to print + * l -- "file name" text file with control chars + * p -- "file name" text file to print with pr(1) + * t -- "file name" troff(1) file to print + * n -- "file name" ditroff(1) file to print + * d -- "file name" dvi file to print + * g -- "file name" plot(1G) file to print + * v -- "file name" plain raster file to print + * c -- "file name" cifplot file to print + * 1 -- "R font file" for troff + * 2 -- "I font file" for troff + * 3 -- "B font file" for troff + * 4 -- "S font file" for troff + * N -- "name" of file (used by lpq) + * U -- "unlink" name of file to remove + * (after we print it. (Pass 2 only)). + * M -- "mail" to user when done printing + * + * getline reads a line and expands tabs to blanks + */ + + /* pass 1 */ + + while (getline(cfp)) + switch (line[0]) { + case 'H': + strcpy(fromhost, line+1); + if (class[0] == '\0') + strncpy(class, line+1, sizeof(class)-1); + continue; + + case 'P': + strncpy(logname, line+1, sizeof(logname)-1); + if (RS) { /* restricted */ + if (getpwnam(logname) == NULL) { + bombed = NOACCT; + sendmail(line+1, bombed); + goto pass2; + } + } + continue; + + case 'S': + cp = line+1; + i = 0; + while (*cp >= '0' && *cp <= '9') + i = i * 10 + (*cp++ - '0'); + fdev = i; + cp++; + i = 0; + while (*cp >= '0' && *cp <= '9') + i = i * 10 + (*cp++ - '0'); + fino = i; + continue; + + case 'J': + if (line[1] != '\0') + strncpy(jobname, line+1, sizeof(jobname)-1); + else + strcpy(jobname, " "); + continue; + + case 'C': + if (line[1] != '\0') + strncpy(class, line+1, sizeof(class)-1); + else if (class[0] == '\0') + gethostname(class, sizeof(class)); + continue; + + case 'T': /* header title for pr */ + strncpy(title, line+1, sizeof(title)-1); + continue; + + case 'L': /* identification line */ + if (!SH && !HL) + banner(line+1, jobname); + continue; + + case '1': /* troff fonts */ + case '2': + case '3': + case '4': + if (line[1] != '\0') + strcpy(fonts[line[0]-'1'], line+1); + continue; + + case 'W': /* page width */ + strncpy(width+2, line+1, sizeof(width)-3); + continue; + + case 'I': /* indent amount */ + strncpy(indent+2, line+1, sizeof(indent)-3); + continue; + + default: /* some file to print */ + switch (i = print(line[0], line+1)) { + case ERROR: + if (bombed == OK) + bombed = FATALERR; + break; + case REPRINT: + (void) fclose(cfp); + return(REPRINT); + case FILTERERR: + case ACCESS: + bombed = i; + sendmail(logname, bombed); + } + title[0] = '\0'; + continue; + + case 'N': + case 'U': + case 'M': + case 'R': + continue; + } + + /* pass 2 */ + +pass2: + fseek(cfp, 0L, 0); + while (getline(cfp)) + switch (line[0]) { + case 'L': /* identification line */ + if (!SH && HL) + banner(line+1, jobname); + continue; + + case 'M': + if (bombed < NOACCT) /* already sent if >= NOACCT */ + sendmail(line+1, bombed); + continue; + + case 'U': + (void) unlink(line+1); + } + /* + * clean-up in case another control file exists + */ + (void) fclose(cfp); + (void) unlink(file); + return(bombed == OK ? OK : ERROR); +} + +/* + * Print a file. + * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}. + * Return -1 if a non-recoverable error occured, + * 2 if the filter detected some errors (but printed the job anyway), + * 1 if we should try to reprint this job and + * 0 if all is well. + * Note: all filters take stdin as the file, stdout as the printer, + * stderr as the log file, and must not ignore SIGINT. + */ +static int +print(format, file) + int format; + char *file; +{ + register int n; + register char *prog; + int fi, fo; + FILE *fp; + char *av[15], buf[BUFSIZ]; + int pid, p[2], stopped = 0; + union wait status; + struct stat stb; + + if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) + return(ERROR); + /* + * Check to see if data file is a symbolic link. If so, it should + * still point to the same file or someone is trying to print + * something he shouldn't. + */ + if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && + (stb.st_dev != fdev || stb.st_ino != fino)) + return(ACCESS); + if (!SF && !tof) { /* start on a fresh page */ + (void) write(ofd, FF, strlen(FF)); + tof = 1; + } + if (IF == NULL && (format == 'f' || format == 'l')) { + tof = 0; + while ((n = read(fi, buf, BUFSIZ)) > 0) + if (write(ofd, buf, n) != n) { + (void) close(fi); + return(REPRINT); + } + (void) close(fi); + return(OK); + } + switch (format) { + case 'p': /* print file using 'pr' */ + if (IF == NULL) { /* use output filter */ + prog = _PATH_PR; + av[0] = "pr"; + av[1] = width; + av[2] = length; + av[3] = "-h"; + av[4] = *title ? title : " "; + av[5] = 0; + fo = ofd; + goto start; + } + pipe(p); + if ((prchild = dofork(DORETURN)) == 0) { /* child */ + dup2(fi, 0); /* file is stdin */ + dup2(p[1], 1); /* pipe is stdout */ + closelog(); + for (n = 3; n < NOFILE; n++) + (void) close(n); + execl(_PATH_PR, "pr", width, length, + "-h", *title ? title : " ", 0); + syslog(LOG_ERR, "cannot execl %s", _PATH_PR); + exit(2); + } + (void) close(p[1]); /* close output side */ + (void) close(fi); + if (prchild < 0) { + prchild = 0; + (void) close(p[0]); + return(ERROR); + } + fi = p[0]; /* use pipe for input */ + case 'f': /* print plain text file */ + prog = IF; + av[1] = width; + av[2] = length; + av[3] = indent; + n = 4; + break; + case 'l': /* like 'f' but pass control characters */ + prog = IF; + av[1] = "-c"; + av[2] = width; + av[3] = length; + av[4] = indent; + n = 5; + break; + case 'r': /* print a fortran text file */ + prog = RF; + av[1] = width; + av[2] = length; + n = 3; + break; + case 't': /* print troff output */ + case 'n': /* print ditroff output */ + case 'd': /* print tex output */ + (void) unlink(".railmag"); + if ((fo = creat(".railmag", FILMOD)) < 0) { + syslog(LOG_ERR, "%s: cannot create .railmag", printer); + (void) unlink(".railmag"); + } else { + for (n = 0; n < 4; n++) { + if (fonts[n][0] != '/') + (void) write(fo, _PATH_VFONT, + sizeof(_PATH_VFONT) - 1); + (void) write(fo, fonts[n], strlen(fonts[n])); + (void) write(fo, "\n", 1); + } + (void) close(fo); + } + prog = (format == 't') ? TF : (format == 'n') ? NF : DF; + av[1] = pxwidth; + av[2] = pxlength; + n = 3; + break; + case 'c': /* print cifplot output */ + prog = CF; + av[1] = pxwidth; + av[2] = pxlength; + n = 3; + break; + case 'g': /* print plot(1G) output */ + prog = GF; + av[1] = pxwidth; + av[2] = pxlength; + n = 3; + break; + case 'v': /* print raster output */ + prog = VF; + av[1] = pxwidth; + av[2] = pxlength; + n = 3; + break; + default: + (void) close(fi); + syslog(LOG_ERR, "%s: illegal format character '%c'", + printer, format); + return(ERROR); + } + if (prog == NULL) { + (void) close(fi); + syslog(LOG_ERR, + "%s: no filter found in printcap for format character '%c'", + printer, format); + return(ERROR); + } + if ((av[0] = rindex(prog, '/')) != NULL) + av[0]++; + else + av[0] = prog; + av[n++] = "-n"; + av[n++] = logname; + av[n++] = "-h"; + av[n++] = fromhost; + av[n++] = AF; + av[n] = 0; + fo = pfd; + if (ofilter > 0) { /* stop output filter */ + write(ofd, "\031\1", 2); + while ((pid = + wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter) + ; + if (status.w_stopval != WSTOPPED) { + (void) close(fi); + syslog(LOG_WARNING, + "%s: output filter died (retcode=%d termsig=%d)", + printer, status.w_retcode, status.w_termsig); + return(REPRINT); + } + stopped++; + } +start: + if ((child = dofork(DORETURN)) == 0) { /* child */ + dup2(fi, 0); + dup2(fo, 1); + n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664); + if (n >= 0) + dup2(n, 2); + closelog(); + for (n = 3; n < NOFILE; n++) + (void) close(n); + execv(prog, av); + syslog(LOG_ERR, "cannot execv %s", prog); + exit(2); + } + (void) close(fi); + if (child < 0) + status.w_retcode = 100; + else + while ((pid = wait((int *)&status)) > 0 && pid != child) + ; + child = 0; + prchild = 0; + if (stopped) { /* restart output filter */ + if (kill(ofilter, SIGCONT) < 0) { + syslog(LOG_ERR, "cannot restart output filter"); + exit(1); + } + } + tof = 0; + + /* Copy filter output to "lf" logfile */ + if (fp = fopen(tempfile, "r")) { + while (fgets(buf, sizeof(buf), fp)) + fputs(buf, stderr); + fclose(fp); + } + + if (!WIFEXITED(status)) { + syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", + printer, format, status.w_termsig); + return(ERROR); + } + switch (status.w_retcode) { + case 0: + tof = 1; + return(OK); + case 1: + return(REPRINT); + case 2: + return(ERROR); + default: + syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", + printer, format, status.w_retcode); + return(FILTERERR); + } +} + +/* + * Send the daemon control file (cf) and any data files. + * Return -1 if a non-recoverable error occured, 1 if a recoverable error and + * 0 if all is well. + */ +static int +sendit(file) + char *file; +{ + register int i, err = OK; + char *cp, last[BUFSIZ]; + + /* + * open control file + */ + if ((cfp = fopen(file, "r")) == NULL) + return(OK); + /* + * read the control file for work to do + * + * file format -- first character in the line is a command + * rest of the line is the argument. + * commands of interest are: + * + * a-z -- "file name" name of file to print + * U -- "unlink" name of file to remove + * (after we print it. (Pass 2 only)). + */ + + /* + * pass 1 + */ + while (getline(cfp)) { + again: + if (line[0] == 'S') { + cp = line+1; + i = 0; + while (*cp >= '0' && *cp <= '9') + i = i * 10 + (*cp++ - '0'); + fdev = i; + cp++; + i = 0; + while (*cp >= '0' && *cp <= '9') + i = i * 10 + (*cp++ - '0'); + fino = i; + continue; + } + if (line[0] >= 'a' && line[0] <= 'z') { + strcpy(last, line); + while (i = getline(cfp)) + if (strcmp(last, line)) + break; + switch (sendfile('\3', last+1)) { + case OK: + if (i) + goto again; + break; + case REPRINT: + (void) fclose(cfp); + return(REPRINT); + case ACCESS: + sendmail(logname, ACCESS); + case ERROR: + err = ERROR; + } + break; + } + } + if (err == OK && sendfile('\2', file) > 0) { + (void) fclose(cfp); + return(REPRINT); + } + /* + * pass 2 + */ + fseek(cfp, 0L, 0); + while (getline(cfp)) + if (line[0] == 'U') + (void) unlink(line+1); + /* + * clean-up in case another control file exists + */ + (void) fclose(cfp); + (void) unlink(file); + return(err); +} + +/* + * Send a data file to the remote machine and spool it. + * Return positive if we should try resending. + */ +static int +sendfile(type, file) + int type; + char *file; +{ + register int f, i, amt; + struct stat stb; + char buf[BUFSIZ]; + int sizerr, resp; + + if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0) + return(ERROR); + /* + * Check to see if data file is a symbolic link. If so, it should + * still point to the same file or someone is trying to print something + * he shouldn't. + */ + if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 && + (stb.st_dev != fdev || stb.st_ino != fino)) + return(ACCESS); + (void) sprintf(buf, "%c%ld %s\n", type, (long)stb.st_size, file); + amt = strlen(buf); + for (i = 0; ; i++) { + if (write(pfd, buf, amt) != amt || + (resp = response()) < 0 || resp == '\1') { + (void) close(f); + return(REPRINT); + } else if (resp == '\0') + break; + if (i == 0) + pstatus("no space on remote; waiting for queue to drain"); + if (i == 10) + syslog(LOG_ALERT, "%s: can't send to %s; queue full", + printer, RM); + sleep(5 * 60); + } + if (i) + pstatus("sending to %s", RM); + sizerr = 0; + for (i = 0; i < stb.st_size; i += BUFSIZ) { + amt = BUFSIZ; + if (i + amt > stb.st_size) + amt = stb.st_size - i; + if (sizerr == 0 && read(f, buf, amt) != amt) + sizerr = 1; + if (write(pfd, buf, amt) != amt) { + (void) close(f); + return(REPRINT); + } + } + + + + + (void) close(f); + if (sizerr) { + syslog(LOG_INFO, "%s: %s: changed size", printer, file); + /* tell recvjob to ignore this file */ + (void) write(pfd, "\1", 1); + return(ERROR); + } + if (write(pfd, "", 1) != 1 || response()) + return(REPRINT); + return(OK); +} + +/* + * Check to make sure there have been no errors and that both programs + * are in sync with eachother. + * Return non-zero if the connection was lost. + */ +static char +response() +{ + char resp; + + if (read(pfd, &resp, 1) != 1) { + syslog(LOG_INFO, "%s: lost connection", printer); + return(-1); + } + return(resp); +} + +/* + * Banner printing stuff + */ +static void +banner(name1, name2) + char *name1, *name2; +{ + time_t tvec; + extern char *ctime(); + + time(&tvec); + if (!SF && !tof) + (void) write(ofd, FF, strlen(FF)); + if (SB) { /* short banner only */ + if (class[0]) { + (void) write(ofd, class, strlen(class)); + (void) write(ofd, ":", 1); + } + (void) write(ofd, name1, strlen(name1)); + (void) write(ofd, " Job: ", 7); + (void) write(ofd, name2, strlen(name2)); + (void) write(ofd, " Date: ", 8); + (void) write(ofd, ctime(&tvec), 24); + (void) write(ofd, "\n", 1); + } else { /* normal banner */ + (void) write(ofd, "\n\n\n", 3); + scan_out(ofd, name1, '\0'); + (void) write(ofd, "\n\n", 2); + scan_out(ofd, name2, '\0'); + if (class[0]) { + (void) write(ofd,"\n\n\n",3); + scan_out(ofd, class, '\0'); + } + (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15); + (void) write(ofd, name2, strlen(name2)); + (void) write(ofd, "\n\t\t\t\t\tDate: ", 12); + (void) write(ofd, ctime(&tvec), 24); + (void) write(ofd, "\n", 1); + } + if (!SF) + (void) write(ofd, FF, strlen(FF)); + tof = 1; +} + +static char * +scnline(key, p, c) + register int key; + register char *p; + int c; +{ + register scnwidth; + + for (scnwidth = WIDTH; --scnwidth;) { + key <<= 1; + *p++ = key & 0200 ? c : BACKGND; + } + return (p); +} + +#define TRC(q) (((q)-' ')&0177) + +static void +scan_out(scfd, scsp, dlm) + int scfd, dlm; + char *scsp; +{ + register char *strp; + register nchrs, j; + char outbuf[LINELEN+1], *sp, c, cc; + int d, scnhgt; + extern char scnkey[][HEIGHT]; /* in lpdchar.c */ + + for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { + strp = &outbuf[0]; + sp = scsp; + for (nchrs = 0; ; ) { + d = dropit(c = TRC(cc = *sp++)); + if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) + for (j = WIDTH; --j;) + *strp++ = BACKGND; + else + strp = scnline(scnkey[c][scnhgt-1-d], strp, cc); + if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1) + break; + *strp++ = BACKGND; + *strp++ = BACKGND; + } + while (*--strp == BACKGND && strp >= outbuf) + ; + strp++; + *strp++ = '\n'; + (void) write(scfd, outbuf, strp-outbuf); + } +} + +static int +dropit(c) + int c; +{ + switch(c) { + + case TRC('_'): + case TRC(';'): + case TRC(','): + case TRC('g'): + case TRC('j'): + case TRC('p'): + case TRC('q'): + case TRC('y'): + return (DROP); + + default: + return (0); + } +} + +/* + * sendmail --- + * tell people about job completion + */ +static void +sendmail(user, bombed) + char *user; + int bombed; +{ + register int i; + int p[2], s; + register char *cp; + char buf[100]; + struct stat stb; + FILE *fp; + + pipe(p); + if ((s = dofork(DORETURN)) == 0) { /* child */ + dup2(p[0], 0); + closelog(); + for (i = 3; i < NOFILE; i++) + (void) close(i); + if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL) + cp++; + else + cp = _PATH_SENDMAIL; + sprintf(buf, "%s@%s", user, fromhost); + execl(_PATH_SENDMAIL, cp, buf, 0); + exit(0); + } else if (s > 0) { /* parent */ + dup2(p[1], 1); + printf("To: %s@%s\n", user, fromhost); + printf("Subject: %s printer job \"%s\"\n", printer, + *jobname ? jobname : ""); + printf("Reply-To: root@%s\n\n", host); + printf("Your printer job "); + if (*jobname) + printf("(%s) ", jobname); + switch (bombed) { + case OK: + printf("\ncompleted successfully\n"); + cp = "OK"; + break; + default: + case FATALERR: + printf("\ncould not be printed\n"); + cp = "FATALERR"; + break; + case NOACCT: + printf("\ncould not be printed without an account on %s\n", host); + cp = "NOACCT"; + break; + case FILTERERR: + if (stat(tempfile, &stb) < 0 || stb.st_size == 0 || + (fp = fopen(tempfile, "r")) == NULL) { + printf("\nhad some errors and may not have printed\n"); + break; + } + printf("\nhad the following errors and may not have printed:\n"); + while ((i = getc(fp)) != EOF) + putchar(i); + (void) fclose(fp); + cp = "FILTERERR"; + break; + case ACCESS: + printf("\nwas not printed because it was not linked to the original file\n"); + cp = "ACCESS"; + } + fflush(stdout); + (void) close(1); + } + (void) close(p[0]); + (void) close(p[1]); + wait(NULL); + syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", + user, *jobname ? jobname : "", printer, cp); +} + +/* + * dofork - fork with retries on failure + */ +static int +dofork(action) + int action; +{ + register int i, pid; + + for (i = 0; i < 20; i++) { + if ((pid = fork()) < 0) { + sleep((unsigned)(i*i)); + continue; + } + /* + * Child should run as daemon instead of root + */ + if (pid == 0) + setuid(DU); + return(pid); + } + syslog(LOG_ERR, "can't fork"); + + switch (action) { + case DORETURN: + return (-1); + default: + syslog(LOG_ERR, "bad action (%d) to dofork", action); + /*FALL THRU*/ + case DOABORT: + exit(1); + } + /*NOTREACHED*/ +} + +/* + * Kill child processes to abort current job. + */ +static void +abortpr(signo) + int signo; +{ + (void) unlink(tempfile); + kill(0, SIGINT); + if (ofilter > 0) + kill(ofilter, SIGCONT); + while (wait(NULL) > 0) + ; + exit(0); +} + +static void +init() +{ + int status; + char *s; + + if ((status = cgetent(&bp, printcapdb, printer)) == -2) { + syslog(LOG_ERR, "can't open printer description file"); + exit(1); + } else if (status == -1) { + syslog(LOG_ERR, "unknown printer: %s", printer); + exit(1); + } else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + if (cgetstr(bp, "lp", &LP) == -1) + LP = _PATH_DEFDEVLP; + if (cgetstr(bp, "rp", &RP) == -1) + RP = DEFLP; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + if (cgetstr(bp, "st", &ST) == -1) + ST = DEFSTAT; + if (cgetstr(bp, "lf", &LF) == -1) + LF = _PATH_CONSOLE; + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetnum(bp, "du", &DU) < 0) + DU = DEFUID; + if (cgetstr(bp,"ff", &FF) == -1) + FF = DEFFF; + if (cgetnum(bp, "pw", &PW) < 0) + PW = DEFWIDTH; + sprintf(&width[2], "%d", PW); + if (cgetnum(bp, "pl", &PL) < 0) + PL = DEFLENGTH; + sprintf(&length[2], "%d", PL); + if (cgetnum(bp,"px", &PX) < 0) + PX = 0; + sprintf(&pxwidth[2], "%d", PX); + if (cgetnum(bp, "py", &PY) < 0) + PY = 0; + sprintf(&pxlength[2], "%d", PY); + cgetstr(bp, "rm", &RM); + if (s = checkremote()) + syslog(LOG_WARNING, s); + + cgetstr(bp, "af", &AF); + cgetstr(bp, "of", &OF); + cgetstr(bp, "if", &IF); + cgetstr(bp, "rf", &RF); + cgetstr(bp, "tf", &TF); + cgetstr(bp, "nf", &NF); + cgetstr(bp, "df", &DF); + cgetstr(bp, "gf", &GF); + cgetstr(bp, "vf", &VF); + cgetstr(bp, "cf", &CF); + cgetstr(bp, "tr", &TR); + + RS = (cgetcap(bp, "rs", ':') != NULL); + SF = (cgetcap(bp, "sf", ':') != NULL); + SH = (cgetcap(bp, "sh", ':') != NULL); + SB = (cgetcap(bp, "sb", ':') != NULL); + HL = (cgetcap(bp, "hl", ':') != NULL); + RW = (cgetcap(bp, "rw", ':') != NULL); + + cgetnum(bp, "br", &BR); + if (cgetnum(bp, "fc", &FC) < 0) + FC = 0; + if (cgetnum(bp, "fs", &FS) < 0) + FS = 0; + if (cgetnum(bp, "xc", &XC) < 0) + XC = 0; + if (cgetnum(bp, "xs", &XS) < 0) + XS = 0; + + tof = (cgetcap(bp, "fo", ':') == NULL); +} + +/* + * Acquire line printer or remote connection. + */ +static void +openpr() +{ + register int i; + char *cp; + + if (!remote && *LP) { + if (cp = index(LP, '@')) + opennet(cp); + else + opentty(); + } else if (remote) { + openrem(); + } else { + syslog(LOG_ERR, "%s: no line printer device or host name", + printer); + exit(1); + } + + /* + * Start up an output filter, if needed. + */ + if (!remote && OF) { + int p[2]; + + pipe(p); + if ((ofilter = dofork(DOABORT)) == 0) { /* child */ + dup2(p[0], 0); /* pipe is std in */ + dup2(pfd, 1); /* printer is std out */ + closelog(); + for (i = 3; i < NOFILE; i++) + (void) close(i); + if ((cp = rindex(OF, '/')) == NULL) + cp = OF; + else + cp++; + execl(OF, cp, width, length, 0); + syslog(LOG_ERR, "%s: %s: %m", printer, OF); + exit(1); + } + (void) close(p[0]); /* close input side */ + ofd = p[1]; /* use pipe for output */ + } else { + ofd = pfd; + ofilter = 0; + } +} + +/* + * Printer connected directly to the network + * or to a terminal server on the net + */ +static void +opennet(cp) + char *cp; +{ + register int i; + int resp, port; + char save_ch; + + save_ch = *cp; + *cp = '\0'; + port = atoi(LP); + if (port <= 0) { + syslog(LOG_ERR, "%s: bad port number: %s", printer, LP); + exit(1); + } + *cp++ = save_ch; + + for (i = 1; ; i = i < 256 ? i << 1 : i) { + resp = -1; + pfd = getport(cp, port); + if (pfd < 0 && errno == ECONNREFUSED) + resp = 1; + else if (pfd >= 0) { + /* + * need to delay a bit for rs232 lines + * to stabilize in case printer is + * connected via a terminal server + */ + delay(500); + break; + } + if (i == 1) { + if (resp < 0) + pstatus("waiting for %s to come up", LP); + else + pstatus("waiting for access to printer on %s", LP); + } + sleep(i); + } + pstatus("sending to %s port %d", cp, port); +} + +/* + * Printer is connected to an RS232 port on this host + */ +static void +opentty() +{ + register int i; + int resp, port; + + for (i = 1; ; i = i < 32 ? i << 1 : i) { + pfd = open(LP, RW ? O_RDWR : O_WRONLY); + if (pfd >= 0) { + delay(500); + break; + } + if (errno == ENOENT) { + syslog(LOG_ERR, "%s: %m", LP); + exit(1); + } + if (i == 1) + pstatus("waiting for %s to become ready (offline ?)", + printer); + sleep(i); + } + if (isatty(pfd)) + setty(); + pstatus("%s is ready and printing", printer); +} + +/* + * Printer is on a remote host + */ +static void +openrem() +{ + register int i, n; + int resp, port; + + for (i = 1; ; i = i < 256 ? i << 1 : i) { + resp = -1; + pfd = getport(RM, 0); + if (pfd >= 0) { + (void) sprintf(line, "\2%s\n", RP); + n = strlen(line); + if (write(pfd, line, n) == n && + (resp = response()) == '\0') + break; + (void) close(pfd); + } + if (i == 1) { + if (resp < 0) + pstatus("waiting for %s to come up", RM); + else { + pstatus("waiting for queue to be enabled on %s", + RM); + i = 256; + } + } + sleep(i); + } + pstatus("sending to %s", RM); +} + +struct bauds { + int baud; + int speed; +} bauds[] = { + 50, B50, + 75, B75, + 110, B110, + 134, B134, + 150, B150, + 200, B200, + 300, B300, + 600, B600, + 1200, B1200, + 1800, B1800, + 2400, B2400, + 4800, B4800, + 9600, B9600, + 19200, EXTA, + 38400, EXTB, + 0, 0 +}; + +/* + * setup tty lines. + */ +static void +setty() +{ + struct sgttyb ttybuf; + register struct bauds *bp; + + if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { + syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer); + exit(1); + } + if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) { + syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer); + exit(1); + } + if (BR > 0) { + for (bp = bauds; bp->baud; bp++) + if (BR == bp->baud) + break; + if (!bp->baud) { + syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR); + exit(1); + } + ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed; + } + ttybuf.sg_flags &= ~FC; + ttybuf.sg_flags |= FS; + if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) { + syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer); + exit(1); + } + if (XC) { + if (ioctl(pfd, TIOCLBIC, &XC) < 0) { + syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer); + exit(1); + } + } + if (XS) { + if (ioctl(pfd, TIOCLBIS, &XS) < 0) { + syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer); + exit(1); + } + } +} + +#if __STDC__ +#include +#else +#include +#endif + +static void +#if __STDC__ +pstatus(const char *msg, ...) +#else +pstatus(msg, va_alist) + char *msg; + va_dcl +#endif +{ + register int fd; + char buf[BUFSIZ]; + va_list ap; +#if __STDC__ + va_start(ap, msg); +#else + va_start(ap); +#endif + + umask(0); + fd = open(ST, O_WRONLY|O_CREAT, 0664); + if (fd < 0 || flock(fd, LOCK_EX) < 0) { + syslog(LOG_ERR, "%s: %s: %m", printer, ST); + exit(1); + } + ftruncate(fd, 0); + (void)vsnprintf(buf, sizeof(buf), msg, ap); + va_end(ap); + strcat(buf, "\n"); + (void) write(fd, buf, strlen(buf)); + (void) close(fd); +} diff --git a/usr.sbin/lpr/lpd/recvjob.c b/usr.sbin/lpr/lpd/recvjob.c new file mode 100644 index 00000000000..0b4156c19aa --- /dev/null +++ b/usr.sbin/lpr/lpd/recvjob.c @@ -0,0 +1,358 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)recvjob.c 8.2 (Berkeley) 4/27/95"; +#endif /* not lint */ + +/* + * Receive printer jobs from the network, queue them and + * start the printer daemon. + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "lp.local.h" +#include "extern.h" +#include "pathnames.h" + +#define ack() (void) write(1, sp, 1); + +static char dfname[40]; /* data files */ +static int minfree; /* keep at least minfree blocks available */ +static char *sp = ""; +static char tfname[40]; /* tmp copy of cf before linking */ + +static int chksize __P((int)); +static void frecverr __P((const char *, ...)); +static int noresponse __P((void)); +static void rcleanup __P((int)); +static int read_number __P((char *)); +static int readfile __P((char *, int)); +static int readjob __P((void)); + + +void +recvjob() +{ + struct stat stb; + int status; + + /* + * Perform lookup for printer name or abbreviation + */ + if ((status = cgetent(&bp, printcapdb, printer)) == -2) + frecverr("cannot open printer description file"); + else if (status == -1) + frecverr("unknown printer %s", printer); + else if (status == -3) + fatal("potential reference loop detected in printcap file"); + + if (cgetstr(bp, "lf", &LF) == -1) + LF = _PATH_CONSOLE; + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + + (void) close(2); /* set up log file */ + if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) { + syslog(LOG_ERR, "%s: %m", LF); + (void) open(_PATH_DEVNULL, O_WRONLY); + } + + if (chdir(SD) < 0) + frecverr("%s: %s: %m", printer, SD); + if (stat(LO, &stb) == 0) { + if (stb.st_mode & 010) { + /* queue is disabled */ + putchar('\1'); /* return error code */ + exit(1); + } + } else if (stat(SD, &stb) < 0) + frecverr("%s: %s: %m", printer, SD); + minfree = 2 * read_number("minfree"); /* scale KB to 512 blocks */ + signal(SIGTERM, rcleanup); + signal(SIGPIPE, rcleanup); + + if (readjob()) + printjob(); +} + +/* + * Read printer jobs sent by lpd and copy them to the spooling directory. + * Return the number of jobs successfully transfered. + */ +static int +readjob() +{ + register int size, nfiles; + register char *cp; + + ack(); + nfiles = 0; + for (;;) { + /* + * Read a command to tell us what to do + */ + cp = line; + do { + if ((size = read(1, cp, 1)) != 1) { + if (size < 0) + frecverr("%s: Lost connection",printer); + return(nfiles); + } + } while (*cp++ != '\n'); + *--cp = '\0'; + cp = line; + switch (*cp++) { + case '\1': /* cleanup because data sent was bad */ + rcleanup(0); + continue; + + case '\2': /* read cf file */ + size = 0; + while (*cp >= '0' && *cp <= '9') + size = size * 10 + (*cp++ - '0'); + if (*cp++ != ' ') + break; + /* + * host name has been authenticated, we use our + * view of the host name since we may be passed + * something different than what gethostbyaddr() + * returns + */ + strcpy(cp + 6, from); + strcpy(tfname, cp); + tfname[0] = 't'; + if (!chksize(size)) { + (void) write(1, "\2", 1); + continue; + } + if (!readfile(tfname, size)) { + rcleanup(0); + continue; + } + if (link(tfname, cp) < 0) + frecverr("%s: %m", tfname); + (void) unlink(tfname); + tfname[0] = '\0'; + nfiles++; + continue; + + case '\3': /* read df file */ + size = 0; + while (*cp >= '0' && *cp <= '9') + size = size * 10 + (*cp++ - '0'); + if (*cp++ != ' ') + break; + if (!chksize(size)) { + (void) write(1, "\2", 1); + continue; + } + (void) strcpy(dfname, cp); + if (index(dfname, '/')) + frecverr("readjob: %s: illegal path name", + dfname); + (void) readfile(dfname, size); + continue; + } + frecverr("protocol screwup: %s", line); + } +} + +/* + * Read files send by lpd and copy them to the spooling directory. + */ +static int +readfile(file, size) + char *file; + int size; +{ + register char *cp; + char buf[BUFSIZ]; + register int i, j, amt; + int fd, err; + + fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD); + if (fd < 0) + frecverr("readfile: %s: illegal path name: %m", file); + ack(); + err = 0; + for (i = 0; i < size; i += BUFSIZ) { + amt = BUFSIZ; + cp = buf; + if (i + amt > size) + amt = size - i; + do { + j = read(1, cp, amt); + if (j <= 0) + frecverr("Lost connection"); + amt -= j; + cp += j; + } while (amt > 0); + amt = BUFSIZ; + if (i + amt > size) + amt = size - i; + if (write(fd, buf, amt) != amt) { + err++; + break; + } + } + (void) close(fd); + if (err) + frecverr("%s: write error", file); + if (noresponse()) { /* file sent had bad data in it */ + (void) unlink(file); + return(0); + } + ack(); + return(1); +} + +static int +noresponse() +{ + char resp; + + if (read(1, &resp, 1) != 1) + frecverr("Lost connection"); + if (resp == '\0') + return(0); + return(1); +} + +/* + * Check to see if there is enough space on the disk for size bytes. + * 1 == OK, 0 == Not OK. + */ +static int +chksize(size) + int size; +{ + int spacefree; + struct statfs sfb; + + if (statfs(".", &sfb) < 0) { + syslog(LOG_ERR, "%s: %m", "statfs(\".\")"); + return (1); + } + spacefree = sfb.f_bavail * (sfb.f_bsize / 512); + size = (size + 511) / 512; + if (minfree + size > spacefree) + return(0); + return(1); +} + +static int +read_number(fn) + char *fn; +{ + char lin[80]; + register FILE *fp; + + if ((fp = fopen(fn, "r")) == NULL) + return (0); + if (fgets(lin, 80, fp) == NULL) { + fclose(fp); + return (0); + } + fclose(fp); + return (atoi(lin)); +} + +/* + * Remove all the files associated with the current job being transfered. + */ +static void +rcleanup(signo) + int signo; +{ + if (tfname[0]) + (void) unlink(tfname); + if (dfname[0]) + do { + do + (void) unlink(dfname); + while (dfname[2]-- != 'A'); + dfname[2] = 'z'; + } while (dfname[0]-- != 'd'); + dfname[0] = '\0'; +} + +#if __STDC__ +#include +#else +#include +#endif + +static void +#if __STDC__ +frecverr(const char *msg, ...) +#else +frecverr(msg, va_alist) + char *msg; + va_dcl +#endif +{ + extern char fromb[]; + va_list ap; +#if __STDC__ + va_start(ap, msg); +#else + va_start(ap); +#endif + rcleanup(0); + syslog(LOG_ERR, "%s", fromb); + vsyslog(LOG_ERR, msg, ap); + va_end(ap); + putchar('\1'); /* return error code */ + exit(1); +} diff --git a/usr.sbin/lpr/lpq/lpq.1 b/usr.sbin/lpr/lpq/lpq.1 new file mode 100644 index 00000000000..d5eadc581f8 --- /dev/null +++ b/usr.sbin/lpr/lpq/lpq.1 @@ -0,0 +1,136 @@ +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)lpq.1 8.2 (Berkeley) 4/28/95 +.\" +.Dd April 28, 1995 +.Dt LPQ 1 +.Os BSD 4.2 +.Sh NAME +.Nm lpq +.Nd spool queue examination program +.Sh SYNOPSIS +.Nm lpq +.OP Fl a +.Op Fl l +.Op Fl P Ns Ar printer +.Op job # ... +.Op user ... +.Sh DESCRIPTION +.Nm Lpq +examines the spooling area used by +.Xr lpd 8 +for printing files on the line printer, and reports the status of the +specified jobs or all jobs associated with a user. +.Nm Lpq +invoked +without any arguments reports on any jobs currently in the queue. +.Pp +Options: +.Pp +.Bl -tag -width indent +.It Fl P +Specify a particular printer, otherwise the default +line printer is used (or the value of the +.Ev PRINTER +variable in the +environment). All other arguments supplied are interpreted as user +names or job numbers to filter out only those jobs of interest. +.It Fl l +Information about each of the files comprising the job entry +is printed. +Normally, only as much information as will fit on one line is displayed. +.It Fl a +Report on the local queues for all printers, +rather than just the specified printer. +.El +.Pp +For each job submitted (i.e. invocation of +.Xr lpr 1 ) +.Nm lpq +reports the user's name, current rank in the queue, the +names of files comprising the job, the job identifier (a number which +may be supplied to +.Xr lprm 1 +for removing a specific job), and the total size in bytes. +Job ordering is dependent on +the algorithm used to scan the spooling directory and is supposed +to be +.Tn FIFO +(First in First Out). +File names comprising a job may be unavailable +(when +.Xr lpr 1 +is used as a sink in a pipeline) in which case the file +is indicated as ``(standard input)''. +.Pp +If +.Nm lpq +warns that there is no daemon present (i.e. due to some malfunction), +the +.Xr lpc 8 +command can be used to restart the printer daemon. +.Sh ENVIRONMENT +If the following environment variable exists, it is used by +.Nm lpq : +.Bl -tag -width PRINTER +.It Ev PRINTER +Specifies an alternate default printer. +.El +.Sh FILES +.Bl -tag -width "/var/spool/*/lock" -compact +.It Pa /etc/printcap +To determine printer characteristics. +.It Pa /var/spool/* +The spooling directory, as determined from printcap. +.It Pa /var/spool/*/cf* +Control files specifying jobs. +.It Pa /var/spool/*/lock +The lock file to obtain the currently active job. +.El +.Sh SEE ALSO +.Xr lpr 1 , +.Xr lprm 1 , +.Xr lpc 8 , +.Xr lpd 8 +.Sh HISTORY +.Nm Lpq +appeared in +.Bx 3 . +.Sh BUGS +Due to the dynamic nature of the information in the spooling directory +.Nm lpq +may report unreliably. +Output formatting is sensitive to the line length of the terminal; +this can results in widely spaced columns. +.Sh DIAGNOSTICS +Unable to open various files. The lock file being malformed. Garbage +files when there is no daemon active, but files in the spooling directory. diff --git a/usr.sbin/lpr/lpq/lpq.c b/usr.sbin/lpr/lpq/lpq.c new file mode 100644 index 00000000000..7d1b520ea5e --- /dev/null +++ b/usr.sbin/lpr/lpq/lpq.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)lpq.c 8.3 (Berkeley) 5/10/95"; +#endif /* not lint */ + +/* + * Spool Queue examination program + * + * lpq [-a] [-l] [-Pprinter] [user...] [job...] + * + * -a show all non-null queues on the local machine + * -l long output + * -P used to identify printer as per lpr/lprm + */ + +#include + +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" + +int requ[MAXREQUESTS]; /* job number of spool entries */ +int requests; /* # of spool requests */ +char *user[MAXUSERS]; /* users to process */ +int users; /* # of users in user array */ + +static int ckqueue __P((char *)); +void usage __P((void)); + +int +main(argc, argv) + register int argc; + register char **argv; +{ + extern char *optarg; + extern int optind; + int ch, aflag, lflag; + char *buf, *cp; + + name = *argv; + if (gethostname(host, sizeof(host))) { + perror("lpq: gethostname"); + exit(1); + } + openlog("lpd", 0, LOG_LPR); + + aflag = lflag = 0; + while ((ch = getopt(argc, argv, "alP:")) != EOF) + switch((char)ch) { + case 'a': + ++aflag; + break; + case 'l': /* long output */ + ++lflag; + break; + case 'P': /* printer name */ + printer = optarg; + break; + case '?': + default: + usage(); + } + + if (!aflag && printer == NULL && (printer = getenv("PRINTER")) == NULL) + printer = DEFLP; + + for (argc -= optind, argv += optind; argc; --argc, ++argv) + if (isdigit(argv[0][0])) { + if (requests >= MAXREQUESTS) + fatal("too many requests"); + requ[requests++] = atoi(*argv); + } + else { + if (users >= MAXUSERS) + fatal("too many users"); + user[users++] = *argv; + } + + if (aflag) { + while (cgetnext(&buf, printcapdb) > 0) { + if (ckqueue(buf) <= 0) { + free(buf); + continue; /* no jobs */ + } + for (cp = buf; *cp; cp++) + if (*cp == '|' || *cp == ':') { + *cp = '\0'; + break; + } + printer = buf; + printf("%s:\n", printer); + displayq(lflag); + free(buf); + printf("\n"); + } + } else + displayq(lflag); + exit(0); +} + +static int +ckqueue(cap) + char *cap; +{ + register struct dirent *d; + DIR *dirp; + char *spooldir; + + if (cgetstr(cap, "sd", &spooldir) == -1) + spooldir = _PATH_DEFSPOOL; + if ((dirp = opendir(spooldir)) == NULL) + return (-1); + while ((d = readdir(dirp)) != NULL) { + if (d->d_name[0] != 'c' || d->d_name[1] != 'f') + continue; /* daemon control files only */ + closedir(dirp); + return (1); /* found something */ + } + closedir(dirp); + return (0); +} + +void +usage() +{ + puts("usage: lpq [-a] [-l] [-Pprinter] [user ...] [job ...]"); + exit(1); +} diff --git a/usr.sbin/lpr/lpr/lpr.c b/usr.sbin/lpr/lpr/lpr.c new file mode 100644 index 00000000000..6b79d6379c4 --- /dev/null +++ b/usr.sbin/lpr/lpr/lpr.c @@ -0,0 +1,745 @@ +/* + * Copyright (c) 1983, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)lpr.c 8.4 (Berkeley) 4/28/95"; +#endif /* not lint */ + +/* + * lpr -- off line print + * + * Allows multiple printers and printers on remote machines by + * using information from a printer data base. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" + +static char *cfname; /* daemon control files, linked from tf's */ +static char *class = host; /* class title on header page */ +static char *dfname; /* data files */ +static char *fonts[4]; /* troff font names */ +static char format = 'f'; /* format char for printing files */ +static int hdr = 1; /* print header or not (default is yes) */ +static int iflag; /* indentation wanted */ +static int inchar; /* location to increment char in file names */ +static int indent; /* amount to indent */ +static char *jobname; /* job name on header page */ +static int mailflg; /* send mail */ +static int nact; /* number of jobs to act on */ +static int ncopies = 1; /* # of copies to make */ +static char *person; /* user name */ +static int qflag; /* q job, but don't exec daemon */ +static int rflag; /* remove files upon completion */ +static int sflag; /* symbolic link flag */ +static int tfd; /* control file descriptor */ +static char *tfname; /* tmp copy of cf before linking */ +static char *title; /* pr'ing title */ +static int userid; /* user id */ +static char *width; /* width for versatec printing */ + +static struct stat statb; + +static void card __P((int, char *)); +static void chkprinter __P((char *)); +static void cleanup __P((int)); +static void copy __P((int, char [])); +static void fatal2 __P((const char *, ...)); +static char *itoa __P((int)); +static char *linked __P((char *)); +static char *lmktemp __P((char *, int, int)); +static void mktemps __P((void)); +static int nfile __P((char *)); +static int test __P((char *)); + +void +main(argc, argv) + int argc; + char *argv[]; +{ + struct passwd *pw; + struct group *gptr; + extern char *itoa(); + register char *arg, *cp; + char buf[BUFSIZ]; + int i, f; + struct stat stb; + + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, cleanup); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, cleanup); + if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) + signal(SIGQUIT, cleanup); + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, cleanup); + + name = argv[0]; + gethostname(host, sizeof (host)); + openlog("lpd", 0, LOG_LPR); + + while (argc > 1 && argv[1][0] == '-') { + argc--; + arg = *++argv; + switch (arg[1]) { + + case 'P': /* specifiy printer name */ + if (arg[2]) + printer = &arg[2]; + else if (argc > 1) { + argc--; + printer = *++argv; + } + break; + + case 'C': /* classification spec */ + hdr++; + if (arg[2]) + class = &arg[2]; + else if (argc > 1) { + argc--; + class = *++argv; + } + break; + + case 'U': /* user name */ + hdr++; + if (arg[2]) + person = &arg[2]; + else if (argc > 1) { + argc--; + person = *++argv; + } + break; + + case 'J': /* job name */ + hdr++; + if (arg[2]) + jobname = &arg[2]; + else if (argc > 1) { + argc--; + jobname = *++argv; + } + break; + + case 'T': /* pr's title line */ + if (arg[2]) + title = &arg[2]; + else if (argc > 1) { + argc--; + title = *++argv; + } + break; + + case 'l': /* literal output */ + case 'p': /* print using ``pr'' */ + case 't': /* print troff output (cat files) */ + case 'n': /* print ditroff output */ + case 'd': /* print tex output (dvi files) */ + case 'g': /* print graph(1G) output */ + case 'c': /* print cifplot output */ + case 'v': /* print vplot output */ + format = arg[1]; + break; + + case 'f': /* print fortran output */ + format = 'r'; + break; + + case '4': /* troff fonts */ + case '3': + case '2': + case '1': + if (argc > 1) { + argc--; + fonts[arg[1] - '1'] = *++argv; + } + break; + + case 'w': /* versatec page width */ + width = arg+2; + break; + + case 'r': /* remove file when done */ + rflag++; + break; + + case 'm': /* send mail when done */ + mailflg++; + break; + + case 'h': /* toggle want of header page */ + hdr = !hdr; + break; + + case 's': /* try to link files */ + sflag++; + break; + + case 'q': /* just q job */ + qflag++; + break; + + case 'i': /* indent output */ + iflag++; + indent = arg[2] ? atoi(&arg[2]) : 8; + break; + + case '#': /* n copies */ + if (isdigit(arg[2])) { + i = atoi(&arg[2]); + if (i > 0) + ncopies = i; + } + } + } + if (printer == NULL && (printer = getenv("PRINTER")) == NULL) + printer = DEFLP; + chkprinter(printer); + if (SC && ncopies > 1) + fatal2("multiple copies are not allowed"); + if (MC > 0 && ncopies > MC) + fatal2("only %d copies are allowed", MC); + /* + * Get the identity of the person doing the lpr using the same + * algorithm as lprm. + */ + userid = getuid(); + if (userid != DU || person == 0) { + if ((pw = getpwuid(userid)) == NULL) + fatal2("Who are you?"); + person = pw->pw_name; + } + /* + * Check for restricted group access. + */ + if (RG != NULL && userid != DU) { + if ((gptr = getgrnam(RG)) == NULL) + fatal2("Restricted group specified incorrectly"); + if (gptr->gr_gid != getgid()) { + while (*gptr->gr_mem != NULL) { + if ((strcmp(person, *gptr->gr_mem)) == 0) + break; + gptr->gr_mem++; + } + if (*gptr->gr_mem == NULL) + fatal2("Not a member of the restricted group"); + } + } + /* + * Check to make sure queuing is enabled if userid is not root. + */ + (void) sprintf(buf, "%s/%s", SD, LO); + if (userid && stat(buf, &stb) == 0 && (stb.st_mode & 010)) + fatal2("Printer queue is disabled"); + /* + * Initialize the control file. + */ + mktemps(); + tfd = nfile(tfname); + (void) fchown(tfd, DU, -1); /* owned by daemon for protection */ + card('H', host); + card('P', person); + if (hdr) { + if (jobname == NULL) { + if (argc == 1) + jobname = "stdin"; + else + jobname = (arg = rindex(argv[1], '/')) ? arg+1 : argv[1]; + } + card('J', jobname); + card('C', class); + card('L', person); + } + if (iflag) + card('I', itoa(indent)); + if (mailflg) + card('M', person); + if (format == 't' || format == 'n' || format == 'd') + for (i = 0; i < 4; i++) + if (fonts[i] != NULL) + card('1'+i, fonts[i]); + if (width != NULL) + card('W', width); + + /* + * Read the files and spool them. + */ + if (argc == 1) + copy(0, " "); + else while (--argc) { + if ((f = test(arg = *++argv)) < 0) + continue; /* file unreasonable */ + + if (sflag && (cp = linked(arg)) != NULL) { + (void) sprintf(buf, "%d %d", statb.st_dev, statb.st_ino); + card('S', buf); + if (format == 'p') + card('T', title ? title : arg); + for (i = 0; i < ncopies; i++) + card(format, &dfname[inchar-2]); + card('U', &dfname[inchar-2]); + if (f) + card('U', cp); + card('N', arg); + dfname[inchar]++; + nact++; + continue; + } + if (sflag) + printf("%s: %s: not linked, copying instead\n", name, arg); + if ((i = open(arg, O_RDONLY)) < 0) { + printf("%s: cannot open %s\n", name, arg); + continue; + } + copy(i, arg); + (void) close(i); + if (f && unlink(arg) < 0) + printf("%s: %s: not removed\n", name, arg); + } + + if (nact) { + (void) close(tfd); + tfname[inchar]--; + /* + * Touch the control file to fix position in the queue. + */ + if ((tfd = open(tfname, O_RDWR)) >= 0) { + char c; + + if (read(tfd, &c, 1) == 1 && + lseek(tfd, (off_t)0, 0) == 0 && + write(tfd, &c, 1) != 1) { + printf("%s: cannot touch %s\n", name, tfname); + tfname[inchar]++; + cleanup(0); + } + (void) close(tfd); + } + if (link(tfname, cfname) < 0) { + printf("%s: cannot rename %s\n", name, cfname); + tfname[inchar]++; + cleanup(0); + } + unlink(tfname); + if (qflag) /* just q things up */ + exit(0); + if (!startdaemon(printer)) + printf("jobs queued, but cannot start daemon.\n"); + exit(0); + } + cleanup(0); + /* NOTREACHED */ +} + +/* + * Create the file n and copy from file descriptor f. + */ +static void +copy(f, n) + int f; + char n[]; +{ + register int fd, i, nr, nc; + char buf[BUFSIZ]; + + if (format == 'p') + card('T', title ? title : n); + for (i = 0; i < ncopies; i++) + card(format, &dfname[inchar-2]); + card('U', &dfname[inchar-2]); + card('N', n); + fd = nfile(dfname); + nr = nc = 0; + while ((i = read(f, buf, BUFSIZ)) > 0) { + if (write(fd, buf, i) != i) { + printf("%s: %s: temp file write error\n", name, n); + break; + } + nc += i; + if (nc >= BUFSIZ) { + nc -= BUFSIZ; + nr++; + if (MX > 0 && nr > MX) { + printf("%s: %s: copy file is too large\n", name, n); + break; + } + } + } + (void) close(fd); + if (nc==0 && nr==0) + printf("%s: %s: empty input file\n", name, f ? n : "stdin"); + else + nact++; +} + +/* + * Try and link the file to dfname. Return a pointer to the full + * path name if successful. + */ +static char * +linked(file) + register char *file; +{ + register char *cp; + static char buf[BUFSIZ]; + + if (*file != '/') { + if (getwd(buf) == NULL) + return(NULL); + while (file[0] == '.') { + switch (file[1]) { + case '/': + file += 2; + continue; + case '.': + if (file[2] == '/') { + if ((cp = rindex(buf, '/')) != NULL) + *cp = '\0'; + file += 3; + continue; + } + } + break; + } + strcat(buf, "/"); + strcat(buf, file); + file = buf; + } + return(symlink(file, dfname) ? NULL : file); +} + +/* + * Put a line into the control file. + */ +static void +card(c, p2) + register int c; + register char *p2; +{ + char buf[BUFSIZ]; + register char *p1 = buf; + register int len = 2; + + *p1++ = c; + while ((c = *p2++) != '\0') { + *p1++ = (c == '\n') ? ' ' : c; + len++; + } + *p1++ = '\n'; + write(tfd, buf, len); +} + +/* + * Create a new file in the spool directory. + */ +static int +nfile(n) + char *n; +{ + register int f; + int oldumask = umask(0); /* should block signals */ + + f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD); + (void) umask(oldumask); + if (f < 0) { + printf("%s: cannot create %s\n", name, n); + cleanup(0); + } + if (fchown(f, userid, -1) < 0) { + printf("%s: cannot chown %s\n", name, n); + cleanup(0); + } + if (++n[inchar] > 'z') { + if (++n[inchar-2] == 't') { + printf("too many files - break up the job\n"); + cleanup(0); + } + n[inchar] = 'A'; + } else if (n[inchar] == '[') + n[inchar] = 'a'; + return(f); +} + +/* + * Cleanup after interrupts and errors. + */ +static void +cleanup(signo) + int signo; +{ + register i; + + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + i = inchar; + if (tfname) + do + unlink(tfname); + while (tfname[i]-- != 'A'); + if (cfname) + do + unlink(cfname); + while (cfname[i]-- != 'A'); + if (dfname) + do { + do + unlink(dfname); + while (dfname[i]-- != 'A'); + dfname[i] = 'z'; + } while (dfname[i-2]-- != 'd'); + exit(1); +} + +/* + * Test to see if this is a printable file. + * Return -1 if it is not, 0 if its printable, and 1 if + * we should remove it after printing. + */ +static int +test(file) + char *file; +{ + struct exec execb; + register int fd; + register char *cp; + + if (access(file, 4) < 0) { + printf("%s: cannot access %s\n", name, file); + return(-1); + } + if (stat(file, &statb) < 0) { + printf("%s: cannot stat %s\n", name, file); + return(-1); + } + if ((statb.st_mode & S_IFMT) == S_IFDIR) { + printf("%s: %s is a directory\n", name, file); + return(-1); + } + if (statb.st_size == 0) { + printf("%s: %s is an empty file\n", name, file); + return(-1); + } + if ((fd = open(file, O_RDONLY)) < 0) { + printf("%s: cannot open %s\n", name, file); + return(-1); + } + if (read(fd, &execb, sizeof(execb)) == sizeof(execb) && + !N_BADMAG(execb)) { + printf("%s: %s is an executable program", name, file); + goto error1; + } + (void) close(fd); + if (rflag) { + if ((cp = rindex(file, '/')) == NULL) { + if (access(".", 2) == 0) + return(1); + } else { + if (cp == file) { + fd = access("/", 2); + } else { + *cp = '\0'; + fd = access(file, 2); + *cp = '/'; + } + if (fd == 0) + return(1); + } + printf("%s: %s: is not removable by you\n", name, file); + } + return(0); + +error1: + printf(" and is unprintable\n"); + (void) close(fd); + return(-1); +} + +/* + * itoa - integer to string conversion + */ +static char * +itoa(i) + register int i; +{ + static char b[10] = "########"; + register char *p; + + p = &b[8]; + do + *p-- = i%10 + '0'; + while (i /= 10); + return(++p); +} + +/* + * Perform lookup for printer name or abbreviation -- + */ +static void +chkprinter(s) + char *s; +{ + int status; + + if ((status = cgetent(&bp, printcapdb, s)) == -2) + fatal2("cannot open printer description file"); + else if (status == -1) + fatal2("%s: unknown printer", s); + if (cgetstr(bp, "sd", &SD) == -1) + SD = _PATH_DEFSPOOL; + if (cgetstr(bp, "lo", &LO) == -1) + LO = DEFLOCK; + cgetstr(bp, "rg", &RG); + if (cgetnum(bp, "mx", &MX) < 0) + MX = DEFMX; + if (cgetnum(bp,"mc", &MC) < 0) + MC = DEFMAXCOPIES; + if (cgetnum(bp, "du", &DU) < 0) + DU = DEFUID; + SC = (cgetcap(bp, "sc", ':') != NULL); +} + +/* + * Make the temp files. + */ +static void +mktemps() +{ + register int len, fd, n; + register char *cp; + char buf[BUFSIZ]; + char *lmktemp(); + + (void) sprintf(buf, "%s/.seq", SD); + if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) { + printf("%s: cannot create %s\n", name, buf); + exit(1); + } + if (flock(fd, LOCK_EX)) { + printf("%s: cannot lock %s\n", name, buf); + exit(1); + } + n = 0; + if ((len = read(fd, buf, sizeof(buf))) > 0) { + for (cp = buf; len--; ) { + if (*cp < '0' || *cp > '9') + break; + n = n * 10 + (*cp++ - '0'); + } + } + len = strlen(SD) + strlen(host) + 8; + tfname = lmktemp("tf", n, len); + cfname = lmktemp("cf", n, len); + dfname = lmktemp("df", n, len); + inchar = strlen(SD) + 3; + n = (n + 1) % 1000; + (void) lseek(fd, (off_t)0, 0); + sprintf(buf, "%03d\n", n); + (void) write(fd, buf, strlen(buf)); + (void) close(fd); /* unlocks as well */ +} + +/* + * Make a temp file name. + */ +static char * +lmktemp(id, num, len) + char *id; + int num, len; +{ + register char *s; + + if ((s = malloc(len)) == NULL) + fatal2("out of memory"); + (void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host); + return(s); +} + +#if __STDC__ +#include +#else +#include +#endif + +static void +#if __STDC__ +fatal2(const char *msg, ...) +#else +fatal2(msg, va_alist) + char *msg; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, msg); +#else + va_start(ap); +#endif + printf("%s: ", name); + vprintf(msg, ap); + putchar('\n'); + va_end(ap); + exit(1); +}