diff options
Diffstat (limited to 'usr.sbin/lpr/common_source')
-rw-r--r-- | usr.sbin/lpr/common_source/Makefile | 15 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/Makefile.depend | 14 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/common.c | 778 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/ctlinfo.c | 914 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/ctlinfo.h | 73 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/displayq.c | 636 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/lp.cdefs.h | 122 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/lp.h | 317 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/lp.local.h | 78 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/matchjobs.c | 568 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/matchjobs.h | 102 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/net.c | 298 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/pathnames.h | 49 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/printcap.c | 451 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/request.c | 81 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/rmjob.c | 392 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/startdaemon.c | 105 |
17 files changed, 4993 insertions, 0 deletions
diff --git a/usr.sbin/lpr/common_source/Makefile b/usr.sbin/lpr/common_source/Makefile new file mode 100644 index 0000000..4f148f9 --- /dev/null +++ b/usr.sbin/lpr/common_source/Makefile @@ -0,0 +1,15 @@ +# $FreeBSD$ + +# +# Library of internal routines for the print spooler suite. +# Originally these were compiled separately into each program, +# but the library makes it much easier to modularize them. +# +LIB= lpr +INTERNALLIB= +SRCS= common.c ctlinfo.c displayq.c matchjobs.c net.c \ + printcap.c request.c rmjob.c startdaemon.c + +WARNS?= 1 + +.include <bsd.lib.mk> diff --git a/usr.sbin/lpr/common_source/Makefile.depend b/usr.sbin/lpr/common_source/Makefile.depend new file mode 100644 index 0000000..56ba329 --- /dev/null +++ b/usr.sbin/lpr/common_source/Makefile.depend @@ -0,0 +1,14 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/arpa \ + include/xlocale \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/lpr/common_source/common.c b/usr.sbin/lpr/common_source/common.c new file mode 100644 index 0000000..52d6c9f --- /dev/null +++ b/usr.sbin/lpr/common_source/common.c @@ -0,0 +1,778 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)common.c 8.5 (Berkeley) 4/28/95"; +#endif /* not lint */ +#endif + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> + +#include <ctype.h> +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" + +/* + * Routines and data common to all the line printer functions. + */ +char line[BUFSIZ]; +const char *progname; /* program name */ + +static int compar(const void *_p1, const void *_p2); + +/* + * isdigit() takes a parameter of 'int', but expect values in the range + * of unsigned char. Define a wrapper which takes a value of type 'char', + * whether signed or unsigned, and ensure it ends up in the right range. + */ +#define isdigitch(Anychar) isdigit((u_char)(Anychar)) + +/* + * 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(FILE *cfp) +{ + register int linel = 0; + register char *lp = line; + register int c; + + while ((c = getc(cfp)) != '\n' && (size_t)(linel+1) < sizeof(line)) { + if (c == EOF) + return(0); + if (c == '\t') { + do { + *lp++ = ' '; + linel++; + } while ((linel & 07) != 0 && (size_t)(linel+1) < + sizeof(line)); + 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(const struct printer *pp, struct jobqueue *(*namelist[])) +{ + register struct dirent *d; + register struct jobqueue *q, **queue; + size_t arraysz, entrysz, nitems; + struct stat stbuf; + DIR *dirp; + int statres; + + PRIV_START + if ((dirp = opendir(pp->spool_dir)) == NULL) { + PRIV_END + return (-1); + } + if (fstat(dirfd(dirp), &stbuf) < 0) + goto errdone; + PRIV_END + + /* + * 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); + if (arraysz < 16) + arraysz = 16; + queue = (struct jobqueue **)malloc(arraysz * sizeof(struct jobqueue *)); + 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 */ + PRIV_START + statres = stat(d->d_name, &stbuf); + PRIV_END + if (statres < 0) + continue; /* Doesn't exist */ + entrysz = sizeof(struct jobqueue) - sizeof(q->job_cfname) + + strlen(d->d_name) + 1; + q = (struct jobqueue *)malloc(entrysz); + if (q == NULL) + goto errdone; + q->job_matched = 0; + q->job_processed = 0; + q->job_time = stbuf.st_mtime; + strcpy(q->job_cfname, d->d_name); + /* + * Check to make sure the array has space left and + * realloc the maximum size. + */ + if (++nitems > arraysz) { + arraysz *= 2; + queue = (struct jobqueue **)realloc((char *)queue, + arraysz * sizeof(struct jobqueue *)); + if (queue == NULL) + goto errdone; + } + queue[nitems-1] = q; + } + closedir(dirp); + if (nitems) + qsort(queue, nitems, sizeof(struct jobqueue *), compar); + *namelist = queue; + return(nitems); + +errdone: + closedir(dirp); + PRIV_END + return (-1); +} + +/* + * Compare modification times. + */ +static int +compar(const void *p1, const void *p2) +{ + const struct jobqueue *qe1, *qe2; + + qe1 = *(const struct jobqueue * const *)p1; + qe2 = *(const struct jobqueue * const *)p2; + + if (qe1->job_time < qe2->job_time) + return (-1); + if (qe1->job_time > qe2->job_time) + return (1); + /* + * At this point, the two files have the same last-modification time. + * return a result based on filenames, so that 'cfA001some.host' will + * come before 'cfA002some.host'. Since the jobid ('001') will wrap + * around when it gets to '999', we also assume that '9xx' jobs are + * older than '0xx' jobs. + */ + if ((qe1->job_cfname[3] == '9') && (qe2->job_cfname[3] == '0')) + return (-1); + if ((qe1->job_cfname[3] == '0') && (qe2->job_cfname[3] == '9')) + return (1); + return (strcmp(qe1->job_cfname, qe2->job_cfname)); +} + +/* + * A simple routine to determine the job number for a print job based on + * the name of its control file. The algorithm used here may look odd, but + * the main issue is that all parts of `lpd', `lpc', `lpq' & `lprm' must be + * using the same algorithm, whatever that algorithm may be. If the caller + * provides a non-null value for ''hostpp', then this returns a pointer to + * the start of the hostname (or IP address?) as found in the filename. + * + * Algorithm: The standard `cf' file has the job number start in position 4, + * but some implementations have that as an extra file-sequence letter, and + * start the job number in position 5. The job number is usually three bytes, + * but may be as many as five. Confusing matters still more, some Windows + * print servers will append an IP address to the job number, instead of + * the expected hostname. So, if the job number ends with a '.', then + * assume the correct jobnum value is the first three digits. + */ +int +calc_jobnum(const char *cfname, const char **hostpp) +{ + int jnum; + const char *cp, *numstr, *hoststr; + + numstr = cfname + 3; + if (!isdigitch(*numstr)) + numstr++; + jnum = 0; + for (cp = numstr; (cp < numstr + 5) && isdigitch(*cp); cp++) + jnum = jnum * 10 + (*cp - '0'); + hoststr = cp; + + /* + * If the filename was built with an IP number instead of a hostname, + * then recalculate using only the first three digits found. + */ + while(isdigitch(*cp)) + cp++; + if (*cp == '.') { + jnum = 0; + for (cp = numstr; (cp < numstr + 3) && isdigitch(*cp); cp++) + jnum = jnum * 10 + (*cp - '0'); + hoststr = cp; + } + if (hostpp != NULL) + *hostpp = hoststr; + return (jnum); +} + +/* sleep n milliseconds */ +void +delay(int millisec) +{ + struct timeval tdelay; + + if (millisec <= 0 || millisec > 10000) + fatal((struct printer *)0, /* fatal() knows how to deal */ + "unreasonable delay period (%d)", millisec); + tdelay.tv_sec = millisec / 1000; + tdelay.tv_usec = millisec * 1000 % 1000000; + (void) select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tdelay); +} + +char * +lock_file_name(const struct printer *pp, char *buf, size_t len) +{ + static char staticbuf[MAXPATHLEN]; + + if (buf == 0) + buf = staticbuf; + if (len == 0) + len = MAXPATHLEN; + + if (pp->lock_file[0] == '/') + strlcpy(buf, pp->lock_file, len); + else + snprintf(buf, len, "%s/%s", pp->spool_dir, pp->lock_file); + + return buf; +} + +char * +status_file_name(const struct printer *pp, char *buf, size_t len) +{ + static char staticbuf[MAXPATHLEN]; + + if (buf == 0) + buf = staticbuf; + if (len == 0) + len = MAXPATHLEN; + + if (pp->status_file[0] == '/') + strlcpy(buf, pp->status_file, len); + else + snprintf(buf, len, "%s/%s", pp->spool_dir, pp->status_file); + + return buf; +} + +/* + * Routine to change operational state of a print queue. The operational + * state is indicated by the access bits on the lock file for the queue. + * At present, this is only called from various routines in lpc/cmds.c. + * + * XXX - Note that this works by changing access-bits on the + * file, and you can only do that if you are the owner of + * the file, or root. Thus, this won't really work for + * userids in the "LPR_OPER" group, unless lpc is running + * setuid to root (or maybe setuid to daemon). + * Generally lpc is installed setgid to daemon, but does + * not run setuid. + */ +int +set_qstate(int action, const char *lfname) +{ + struct stat stbuf; + mode_t chgbits, newbits, oldmask; + const char *failmsg, *okmsg; + static const char *nomsg = "no state msg"; + int chres, errsav, fd, res, statres; + + /* + * Find what the current access-bits are. + */ + memset(&stbuf, 0, sizeof(stbuf)); + PRIV_START + statres = stat(lfname, &stbuf); + errsav = errno; + PRIV_END + if ((statres < 0) && (errsav != ENOENT)) { + printf("\tcannot stat() lock file\n"); + return (SQS_STATFAIL); + /* NOTREACHED */ + } + + /* + * Determine which bit(s) should change for the requested action. + */ + chgbits = stbuf.st_mode; + newbits = LOCK_FILE_MODE; + okmsg = NULL; + failmsg = NULL; + if (action & SQS_QCHANGED) { + chgbits |= LFM_RESET_QUE; + newbits |= LFM_RESET_QUE; + /* The okmsg is not actually printed for this case. */ + okmsg = nomsg; + failmsg = "set queue-changed"; + } + if (action & SQS_DISABLEQ) { + chgbits |= LFM_QUEUE_DIS; + newbits |= LFM_QUEUE_DIS; + okmsg = "queuing disabled"; + failmsg = "disable queuing"; + } + if (action & SQS_STOPP) { + chgbits |= LFM_PRINT_DIS; + newbits |= LFM_PRINT_DIS; + okmsg = "printing disabled"; + failmsg = "disable printing"; + if (action & SQS_DISABLEQ) { + okmsg = "printer and queuing disabled"; + failmsg = "disable queuing and printing"; + } + } + if (action & SQS_ENABLEQ) { + chgbits &= ~LFM_QUEUE_DIS; + newbits &= ~LFM_QUEUE_DIS; + okmsg = "queuing enabled"; + failmsg = "enable queuing"; + } + if (action & SQS_STARTP) { + chgbits &= ~LFM_PRINT_DIS; + newbits &= ~LFM_PRINT_DIS; + okmsg = "printing enabled"; + failmsg = "enable printing"; + } + if (okmsg == NULL) { + /* This routine was called with an invalid action. */ + printf("\t<error in set_qstate!>\n"); + return (SQS_PARMERR); + /* NOTREACHED */ + } + + res = 0; + if (statres >= 0) { + /* The file already exists, so change the access. */ + PRIV_START + chres = chmod(lfname, chgbits); + errsav = errno; + PRIV_END + res = SQS_CHGOK; + if (chres < 0) + res = SQS_CHGFAIL; + } else if (newbits == LOCK_FILE_MODE) { + /* + * The file does not exist, but the state requested is + * the same as the default state when no file exists. + * Thus, there is no need to create the file. + */ + res = SQS_SKIPCREOK; + } else { + /* + * The file did not exist, so create it with the + * appropriate access bits for the requested action. + * Push a new umask around that create, to make sure + * all the read/write bits are set as desired. + */ + oldmask = umask(S_IWOTH); + PRIV_START + fd = open(lfname, O_WRONLY|O_CREAT, newbits); + errsav = errno; + PRIV_END + umask(oldmask); + res = SQS_CREFAIL; + if (fd >= 0) { + res = SQS_CREOK; + close(fd); + } + } + + switch (res) { + case SQS_CHGOK: + case SQS_CREOK: + case SQS_SKIPCREOK: + if (okmsg != nomsg) + printf("\t%s\n", okmsg); + break; + case SQS_CREFAIL: + printf("\tcannot create lock file: %s\n", + strerror(errsav)); + break; + default: + printf("\tcannot %s: %s\n", failmsg, strerror(errsav)); + break; + } + + return (res); +} + +/* routine to get a current timestamp, optionally in a standard-fmt string */ +void +lpd_gettime(struct timespec *tsp, char *strp, size_t strsize) +{ + struct timespec local_ts; + struct timeval btime; + char tempstr[TIMESTR_SIZE]; +#ifdef STRFTIME_WRONG_z + char *destp; +#endif + + if (tsp == NULL) + tsp = &local_ts; + + /* some platforms have a routine called clock_gettime, but the + * routine does nothing but return "not implemented". */ + memset(tsp, 0, sizeof(struct timespec)); + if (clock_gettime(CLOCK_REALTIME, tsp)) { + /* nanosec-aware rtn failed, fall back to microsec-aware rtn */ + memset(tsp, 0, sizeof(struct timespec)); + gettimeofday(&btime, NULL); + tsp->tv_sec = btime.tv_sec; + tsp->tv_nsec = btime.tv_usec * 1000; + } + + /* caller may not need a character-ized version */ + if ((strp == NULL) || (strsize < 1)) + return; + + strftime(tempstr, TIMESTR_SIZE, LPD_TIMESTAMP_PATTERN, + localtime(&tsp->tv_sec)); + + /* + * This check is for implementations of strftime which treat %z + * (timezone as [+-]hhmm ) like %Z (timezone as characters), or + * completely ignore %z. This section is not needed on freebsd. + * I'm not sure this is completely right, but it should work OK + * for EST and EDT... + */ +#ifdef STRFTIME_WRONG_z + destp = strrchr(tempstr, ':'); + if (destp != NULL) { + destp += 3; + if ((*destp != '+') && (*destp != '-')) { + char savday[6]; + int tzmin = timezone / 60; + int tzhr = tzmin / 60; + if (daylight) + tzhr--; + strcpy(savday, destp + strlen(destp) - 4); + snprintf(destp, (destp - tempstr), "%+03d%02d", + (-1*tzhr), tzmin % 60); + strcat(destp, savday); + } + } +#endif + + if (strsize > TIMESTR_SIZE) { + strsize = TIMESTR_SIZE; + strp[TIMESTR_SIZE+1] = '\0'; + } + strlcpy(strp, tempstr, strsize); +} + +/* routines for writing transfer-statistic records */ +void +trstat_init(struct printer *pp, const char *fname, int filenum) +{ + register const char *srcp; + register char *destp, *endp; + + /* + * Figure out the job id of this file. The filename should be + * 'cf', 'df', or maybe 'tf', followed by a letter (or sometimes + * two), followed by the jobnum, followed by a hostname. + * The jobnum is usually 3 digits, but might be as many as 5. + * Note that some care has to be taken parsing this, as the + * filename could be coming from a remote-host, and thus might + * not look anything like what is expected... + */ + memset(pp->jobnum, 0, sizeof(pp->jobnum)); + pp->jobnum[0] = '0'; + srcp = strchr(fname, '/'); + if (srcp == NULL) + srcp = fname; + destp = &(pp->jobnum[0]); + endp = destp + 5; + while (*srcp != '\0' && (*srcp < '0' || *srcp > '9')) + srcp++; + while (*srcp >= '0' && *srcp <= '9' && destp < endp) + *(destp++) = *(srcp++); + + /* get the starting time in both numeric and string formats, and + * save those away along with the file-number */ + pp->jobdfnum = filenum; + lpd_gettime(&pp->tr_start, pp->tr_timestr, (size_t)TIMESTR_SIZE); + + return; +} + +void +trstat_write(struct printer *pp, tr_sendrecv sendrecv, size_t bytecnt, + const char *userid, const char *otherhost, const char *orighost) +{ +#define STATLINE_SIZE 1024 + double trtime; + size_t remspace; + int statfile; + char thishost[MAXHOSTNAMELEN], statline[STATLINE_SIZE]; + char *eostat; + const char *lprhost, *recvdev, *recvhost, *rectype; + const char *sendhost, *statfname; +#define UPD_EOSTAT(xStr) do { \ + eostat = strchr(xStr, '\0'); \ + remspace = eostat - xStr; \ +} while(0) + + lpd_gettime(&pp->tr_done, NULL, (size_t)0); + trtime = DIFFTIME_TS(pp->tr_done, pp->tr_start); + + gethostname(thishost, sizeof(thishost)); + lprhost = sendhost = recvhost = recvdev = NULL; + switch (sendrecv) { + case TR_SENDING: + rectype = "send"; + statfname = pp->stat_send; + sendhost = thishost; + recvhost = otherhost; + break; + case TR_RECVING: + rectype = "recv"; + statfname = pp->stat_recv; + sendhost = otherhost; + recvhost = thishost; + break; + case TR_PRINTING: + /* + * This case is for copying to a device (presumably local, + * though filters using things like 'net/CAP' can confuse + * this assumption...). + */ + rectype = "prnt"; + statfname = pp->stat_send; + sendhost = thishost; + recvdev = _PATH_DEFDEVLP; + if (pp->lp) recvdev = pp->lp; + break; + default: + /* internal error... should we syslog/printf an error? */ + return; + } + if (statfname == NULL) + return; + + /* + * the original-host and userid are found out by reading thru the + * cf (control-file) for the job. Unfortunately, on incoming jobs + * the df's (data-files) are sent before the matching cf, so the + * orighost & userid are generally not-available for incoming jobs. + * + * (it would be nice to create a work-around for that..) + */ + if (orighost && (*orighost != '\0')) + lprhost = orighost; + else + lprhost = ".na."; + if (*userid == '\0') + userid = NULL; + + /* + * Format of statline. + * Some of the keywords listed here are not implemented here, but + * they are listed to reserve the meaning for a given keyword. + * Fields are separated by a blank. The fields in statline are: + * <tstamp> - time the transfer started + * <ptrqueue> - name of the printer queue (the short-name...) + * <hname> - hostname the file originally came from (the + * 'lpr host'), if known, or "_na_" if not known. + * <xxx> - id of job from that host (generally three digits) + * <n> - file count (# of file within job) + * <rectype> - 4-byte field indicating the type of transfer + * statistics record. "send" means it's from the + * host sending a datafile, "recv" means it's from + * a host as it receives a datafile. + * user=<userid> - user who sent the job (if known) + * secs=<n> - seconds it took to transfer the file + * bytes=<n> - number of bytes transfered (ie, "bytecount") + * bps=<n.n>e<n> - Bytes/sec (if the transfer was "big enough" + * for this to be useful) + * ! top=<str> - type of printer (if the type is defined in + * printcap, and if this statline is for sending + * a file to that ptr) + * ! qls=<n> - queue-length at start of send/print-ing a job + * ! qle=<n> - queue-length at end of send/print-ing a job + * sip=<addr> - IP address of sending host, only included when + * receiving a job. + * shost=<hname> - sending host (if that does != the original host) + * rhost=<hname> - hostname receiving the file (ie, "destination") + * rdev=<dev> - device receiving the file, when the file is being + * send to a device instead of a remote host. + * + * Note: A single print job may be transferred multiple times. The + * original 'lpr' occurs on one host, and that original host might + * send to some interim host (or print server). That interim host + * might turn around and send the job to yet another host (most likely + * the real printer). The 'shost=' parameter is only included if the + * sending host for this particular transfer is NOT the same as the + * host which did the original 'lpr'. + * + * Many values have 'something=' tags before them, because they are + * in some sense "optional", or their order may vary. "Optional" may + * mean in the sense that different SITES might choose to have other + * fields in the record, or that some fields are only included under + * some circumstances. Programs processing these records should not + * assume the order or existence of any of these keyword fields. + */ + snprintf(statline, STATLINE_SIZE, "%s %s %s %s %03ld %s", + pp->tr_timestr, pp->printer, lprhost, pp->jobnum, + pp->jobdfnum, rectype); + UPD_EOSTAT(statline); + + if (userid != NULL) { + snprintf(eostat, remspace, " user=%s", userid); + UPD_EOSTAT(statline); + } + snprintf(eostat, remspace, " secs=%#.2f bytes=%lu", trtime, + (unsigned long)bytecnt); + UPD_EOSTAT(statline); + + /* + * The bps field duplicates info from bytes and secs, so do + * not bother to include it for very small files. + */ + if ((bytecnt > 25000) && (trtime > 1.1)) { + snprintf(eostat, remspace, " bps=%#.2e", + ((double)bytecnt/trtime)); + UPD_EOSTAT(statline); + } + + if (sendrecv == TR_RECVING) { + if (remspace > 5+strlen(from_ip) ) { + snprintf(eostat, remspace, " sip=%s", from_ip); + UPD_EOSTAT(statline); + } + } + if (0 != strcmp(lprhost, sendhost)) { + if (remspace > 7+strlen(sendhost) ) { + snprintf(eostat, remspace, " shost=%s", sendhost); + UPD_EOSTAT(statline); + } + } + if (recvhost) { + if (remspace > 7+strlen(recvhost) ) { + snprintf(eostat, remspace, " rhost=%s", recvhost); + UPD_EOSTAT(statline); + } + } + if (recvdev) { + if (remspace > 6+strlen(recvdev) ) { + snprintf(eostat, remspace, " rdev=%s", recvdev); + UPD_EOSTAT(statline); + } + } + if (remspace > 1) { + strcpy(eostat, "\n"); + } else { + /* probably should back up to just before the final " x=".. */ + strcpy(statline+STATLINE_SIZE-2, "\n"); + } + statfile = open(statfname, O_WRONLY|O_APPEND, 0664); + if (statfile < 0) { + /* statfile was given, but we can't open it. should we + * syslog/printf this as an error? */ + return; + } + write(statfile, statline, strlen(statline)); + close(statfile); + + return; +#undef UPD_EOSTAT +} + +#include <stdarg.h> + +void +fatal(const struct printer *pp, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + /* this error message is being sent to the 'from_host' */ + if (from_host != local_host) + (void)printf("%s: ", local_host); + (void)printf("%s: ", progname); + if (pp && pp->printer) + (void)printf("%s: ", pp->printer); + (void)vprintf(msg, ap); + va_end(ap); + (void)putchar('\n'); + exit(1); +} + +/* + * Close all file descriptors from START on up. + */ +void +closeallfds(int start) +{ + int stop; + + if (USE_CLOSEFROM) /* The faster, modern solution */ + closefrom(start); + else { + /* This older logic can be pretty awful on some OS's. The + * getdtablesize() might return ``infinity'', and then this + * will waste a lot of time closing file descriptors which + * had never been open()-ed. */ + stop = getdtablesize(); + for (; start < stop; start++) + close(start); + } +} + diff --git a/usr.sbin/lpr/common_source/ctlinfo.c b/usr.sbin/lpr/common_source/ctlinfo.c new file mode 100644 index 0000000..c23e419 --- /dev/null +++ b/usr.sbin/lpr/common_source/ctlinfo.c @@ -0,0 +1,914 @@ +/* + * ------+---------+---------+---------+---------+---------+---------+---------* + * Copyright (c) 2001,2011 - Garance Alistair Drosehn <gad@FreeBSD.org>. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the FreeBSD Project. + * + * ------+---------+---------+---------+---------+---------+---------+---------* + */ + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +/* + * ctlinfo - This collection of routines will know everything there is to + * know about the information inside a control file ('cf*') which is used + * to describe a print job in lpr & friends. The eventual goal is that it + * will be the ONLY source file to know what's inside these control-files. + */ + +/* + * Some define's useful for debuging. + * TRIGGERTEST_FNAME and DEBUGREADCF_FNAME, allow us to do testing on + * a per-spool-directory basis. + */ +/* #define TRIGGERTEST_FNAME "LpdTestRenameTF" */ +/* #define DEBUGREADCF_FNAME "LpdDebugReadCF" */ +/* #define LEAVE_TMPCF_FILES 1 */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <netdb.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include "ctlinfo.h" + +struct cjprivate { + struct cjobinfo pub; + char *cji_buff; /* buffer for getline */ + char *cji_eobuff; /* last byte IN the buffer */ + FILE *cji_fstream; + int cji_buffsize; /* # bytes in the buffer */ + int cji_dumpit; +}; + +/* + * All the following take a parameter of 'int', but expect values in the + * range of unsigned char. Define wrappers which take values of type 'char', + * whether signed or unsigned, and ensure they end up in the right range. + */ +#define isdigitch(Anychar) isdigit((u_char)(Anychar)) +#define islowerch(Anychar) islower((u_char)(Anychar)) +#define isupperch(Anychar) isupper((u_char)(Anychar)) +#define tolowerch(Anychar) tolower((u_char)(Anychar)) + +#define OTHER_USERID_CHARS "-_" /* special chars valid in a userid */ + +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) + +/* + * This has to be large enough to fit the maximum length of a single line + * in a control-file, including the leading 'command id', a trailing '\n' + * and ending '\0'. The max size of an 'U'nlink line, for instance, is + * 1 ('U') + PATH_MAX (filename) + 2 ('\n\0'). The maximum 'H'ost line is + * 1 ('H') + NI_MAXHOST (remote hostname) + 2 ('\n\0'). Other lines can be + * even longer than those. So, pick some nice, large, arbitrary value. + */ +#define CTI_LINEMAX PATH_MAX+NI_MAXHOST+5 + +extern const char *from_host; /* client's machine name */ +extern const char *from_ip; /* client machine's IP address */ + +__BEGIN_DECLS +void ctl_dumpcji(FILE *_dbg_stream, const char *_heading, + struct cjobinfo *_cjinf); +static char *ctl_getline(struct cjobinfo *_cjinf); +static void ctl_rewindcf(struct cjobinfo *_cjinf); +char *ctl_rmjob(const char *_ptrname, const char *_cfname); +__END_DECLS + +/* + * Here are some things which might be needed when compiling this under + * platforms other than FreeBSD. + */ +#ifndef __FreeBSD__ +# ifndef NAME_MAX +# define NAME_MAX 255 +# endif +# ifndef NI_MAXHOST +# define NI_MAXHOST 1025 +# endif +# ifndef PATH_MAX +# define PATH_MAX 1024 +# endif +__BEGIN_DECLS +char *strdup(const char *_src); +size_t strlcpy(char *_dst, const char *_src, size_t _siz); +__END_DECLS +#endif + +/* + * Control-files (cf*) have the following format. + * + * Each control-file describes a single job. It will list one or more + * "datafiles" (df*) which should be copied to some printer. Usually + * there is only one datafile per job. For the curious, RFC 1179 is an + * informal and out-of-date description of lpr/lpd circa 1990. + * + * Each line in the file gives an attribute of the job as a whole, or one + * of the datafiles in the job, or a "command" indicating something to do + * with one of the datafiles. Each line starts with an 'id' that indicates + * what that line is there for. The 'id' is historically a single byte, + * but may be multiple bytes (obviously it would be best if multi-byte ids + * started with some letter not already used as a single-byte id!). + * After the 'id', the remainder of the line will be the value of the + * indicated attribute, or a name of the datafile to be operated on. + * + * In the following lists of ids, the ids with a '!' in front of them are + * NOT explicitly supported by this version of lpd, or at least "not yet + * supported". They are only listed for reference purposes, so people + * won't be tempted to reuse the same id for a different purpose. + * + * The following are attributes of the job which should not appear more + * than once in a control file. Only the 'H' and 'P' lines are required + * by the RFC, but some implementations of lpr won't even get that right. + * + * ! A - [used by lprNG] + * B - As far as I know, this is never used as a single-byte id. + * Therefore, I intend to use it for multi-byte id codes. + * C - "class name" to display on banner page (this is sometimes + * used to hold options for print filters) + * ! D - [in lprNG, "timestamp" of when the job was submitted] + * ! E - "environment variables" to set [some versions of linux] + * H - "host name" of machine where the original 'lpr' was done + * I - "indent", the amount to indent output + * J - "job name" to display on banner page + * L - "literal" user's name as it should be displayed on the + * banner page (it is the existence of an 'L' line which + * indicates that a job should have a banner page). + * M - "mail", userid to mail to when done printing (with email + * going to 'M'@'H', so to speak). + * P - "person", the user's login name (e.g. for accounting) + * ! Q - [used by lprNG for queue-name] + * R - "resolution" in dpi, for some laser printer queues + * T - "title" for files sent thru 'pr' + * W - "width" to use for printing plain-text files + * Z - In BSD, "locale" to use for datafiles sent thru 'pr'. + * (this BSD usage should move to a different id...) + * [in lprNG - this line holds the "Z options"] + * 1 - "R font file" for files sent thru troff + * 2 - "I font file" for files sent thru troff + * 3 - "B font file" for files sent thru troff + * 4 - "S font file" for files sent thru troff + * + * The following are attributes attached to a datafile, and thus may + * appear multiple times in a control file (once per datafile): + * + * N - "name" of file (for display purposes, used by 'lpq') + * S - "stat() info" used for symbolic link ('lpr -s') + * security checks. + * + * The following indicate actions to take on a given datafile. The same + * datafile may appear on more than one "print this file" command in the + * control file. Note that ALL ids with lowercase letters are expected + * to be actions to "print this file": + * + * c - "file name", cifplot file to print. This action appears + * when the user has requested 'lpr -c'. + * d - "file name", dvi file to print, user requested 'lpr -d' + * f - "file name", a plain-text file to print = "standard" + * g - "file name", plot(1G) file to print, ie 'lpr -g' + * l - "file name", text file with control chars which should + * be printed literally, ie 'lpr -l' (note: some printers + * take this id as a request to print a postscript file, + * and because of *that* some OS's use 'l' to indicate + * that a datafile is a postscript file) + * n - "file name", ditroff(1) file to print, ie 'lpr -n' + * o - "file name", a postscript file to print. This id is + * described in the original RFC, but not much has been + * done with it. This 'lpr' does not generate control + * lines with 'o'-actions, but lpd's printjob processing + * will treat it the same as 'l'. + * p - "file name", text file to print with pr(1), ie 'lpr -p' + * t - "file name", troff(1) file to print, ie 'lpr -t' + * v - "file name", plain raster file to print + * + * U - "file name" of datafile to unlink (ie, remove file + * from spool directory. To be done in a 'Pass 2', + * AFTER having processed all datafiles in the job). + * + */ + +void +ctl_freeinf(struct cjobinfo *cjinf) +{ +#define FREESTR(xStr) \ + if (xStr != NULL) { \ + free(xStr); \ + xStr = NULL;\ + } + + struct cjprivate *cpriv; + + if (cjinf == NULL) + return; + cpriv = cjinf->cji_priv; + if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) { + syslog(LOG_ERR, "in ctl_freeinf(%p): invalid cjinf (cpriv %p)", + (void *)cjinf, (void *)cpriv); + return; + } + + FREESTR(cpriv->pub.cji_accthost); + FREESTR(cpriv->pub.cji_acctuser); + FREESTR(cpriv->pub.cji_class); + FREESTR(cpriv->pub.cji_curqueue); + /* [cpriv->pub.cji_fname is part of cpriv-malloced area] */ + FREESTR(cpriv->pub.cji_jobname); + FREESTR(cpriv->pub.cji_mailto); + FREESTR(cpriv->pub.cji_headruser); + + if (cpriv->cji_fstream != NULL) { + fclose(cpriv->cji_fstream); + cpriv->cji_fstream = NULL; + } + + cjinf->cji_priv = NULL; + free(cpriv); +#undef FREESTR +} + +#ifdef DEBUGREADCF_FNAME +static FILE *ctl_dbgfile = NULL; +static struct stat ctl_dbgstat; +#endif +static int ctl_dbgline = 0; + +struct cjobinfo * +ctl_readcf(const char *ptrname, const char *cfname) +{ + int id; + char *lbuff; + void *cstart; + FILE *cfile; + struct cjprivate *cpriv; + struct cjobinfo *cjinf; + size_t msize, sroom, sroom2; + + cfile = fopen(cfname, "r"); + if (cfile == NULL) { + syslog(LOG_ERR, "%s: ctl_readcf error fopen(%s): %s", + ptrname, cfname, strerror(errno)); + return NULL; + } + + sroom = roundup(sizeof(struct cjprivate), 8); + sroom2 = sroom + strlen(cfname) + 1; + sroom2 = roundup(sroom2, 8); + msize = sroom2 + CTI_LINEMAX; + msize = roundup(msize, 8); + cstart = malloc(msize); + if (cstart == NULL) + return NULL; + memset(cstart, 0, msize); + cpriv = (struct cjprivate *)cstart; + cpriv->pub.cji_priv = cpriv; + + cpriv->pub.cji_fname = (char *)cstart + sroom; + strcpy(cpriv->pub.cji_fname, cfname); + cpriv->cji_buff = (char *)cstart + sroom2; + cpriv->cji_buffsize = (int)(msize - sroom2); + cpriv->cji_eobuff = (char *)cstart + msize - 1; + + cpriv->cji_fstream = cfile; + cpriv->pub.cji_curqueue = strdup(ptrname); + + ctl_dbgline = 0; +#ifdef DEBUGREADCF_FNAME + ctl_dbgfile = NULL; + id = stat(DEBUGREADCF_FNAME, &ctl_dbgstat); + if (id != -1) { + /* the file exists in this spool directory, write some simple + * debugging info to it */ + ctl_dbgfile = fopen(DEBUGREADCF_FNAME, "a"); + if (ctl_dbgfile != NULL) { + fprintf(ctl_dbgfile, "%s: s=%p r=%ld e=%p %p->%s\n", + ptrname, (void *)cpriv, (long)sroom, + cpriv->cji_eobuff, cpriv->pub.cji_fname, + cpriv->pub.cji_fname); + } + } +#endif + /* + * Copy job-attribute values from control file to the struct of + * "public" information. In some cases, it is invalid for the + * value to be a null-string, so that is ignored. + */ + cjinf = &(cpriv->pub); + lbuff = ctl_getline(cjinf); + while (lbuff != NULL) { + id = *lbuff++; + switch (id) { + case 'C': + cpriv->pub.cji_class = strdup(lbuff); + break; + case 'H': + if (*lbuff == '\0') + break; + cpriv->pub.cji_accthost = strdup(lbuff); + break; + case 'J': + cpriv->pub.cji_jobname = strdup(lbuff); + break; + case 'L': + cpriv->pub.cji_headruser = strdup(lbuff); + break; + case 'M': + /* + * No valid mail-to address would start with a minus. + * If this one does, it is probably some trickster who + * is trying to trigger options on sendmail. Ignore. + */ + if (*lbuff == '-') + break; + if (*lbuff == '\0') + break; + cpriv->pub.cji_mailto = strdup(lbuff); + break; + case 'P': + if (*lbuff == '\0') + break; + /* The userid must not start with a minus sign */ + if (*lbuff == '-') + *lbuff = '_'; + cpriv->pub.cji_acctuser = strdup(lbuff); + break; + default: + if (islower(id)) { + cpriv->pub.cji_dfcount++; + } + break; + } + lbuff = ctl_getline(cjinf); + } + + /* the 'H'ost and 'P'erson fields are *always* supposed to be there */ + if (cpriv->pub.cji_accthost == NULL) + cpriv->pub.cji_accthost = strdup(".na."); + if (cpriv->pub.cji_acctuser == NULL) + cpriv->pub.cji_acctuser = strdup(".na."); + +#ifdef DEBUGREADCF_FNAME + if (ctl_dbgfile != NULL) { + if (cpriv->cji_dumpit) + ctl_dumpcji(ctl_dbgfile, "end readcf", &(cpriv->pub)); + fclose(ctl_dbgfile); + ctl_dbgfile = NULL; + } +#endif + return &(cpriv->pub); +} + +/* + * This routine renames the temporary control file as received from some + * other (remote) host. That file will almost always with `tfA*', because + * recvjob.c creates the file by changing `c' to `t' in the original name + * for the control file. Now if you read the RFC, you would think that all + * control filenames start with `cfA*'. However, it seems there are some + * implementations which send control filenames which start with `cf' + * followed by *any* letter, so this routine can not assume what the third + * letter will (or will not) be. Sigh. + * + * So this will rewrite the temporary file to `rf*' (correcting any lines + * which need correcting), rename that `rf*' file to `cf*', and then remove + * the original `tf*' temporary file. + * + * The *main* purpose of this routine is to be paranoid about the contents + * of that control file. It is partially meant to protect against people + * TRYING to cause trouble (perhaps after breaking into root of some host + * that this host will accept print jobs from). The fact that we're willing + * to print jobs from some remote host does not mean that we should blindly + * do anything that host tells us to do. + * + * This is also meant to protect us from errors in other implementations of + * lpr, particularly since we may want to use some values from the control + * file as environment variables when it comes time to print, or as parameters + * to commands which will be exec'ed, or values in statistics records. + * + * This may also do some "conversions" between how different versions of + * lpr or lprNG define the contents of various lines in a control file. + * + * If there is an error, it returns a pointer to a descriptive error message. + * Error messages which are RETURNED (as opposed to syslog-ed) do not include + * the printer-queue name. Let the caller add that if it is wanted. + */ +char * +ctl_renametf(const char *ptrname, const char *tfname) +{ + int chk3rd, has_uc, newfd, nogood, res; + FILE *newcf; + struct cjobinfo *cjinf; + char *lbuff, *slash, *cp; + char tfname2[NAME_MAX+1], cfname2[NAME_MAX+1]; + char errm[CTI_LINEMAX]; + +#ifdef TRIGGERTEST_FNAME + struct stat tstat; + res = stat(TRIGGERTEST_FNAME, &tstat); + if (res == -1) { + /* + * if the trigger file does NOT exist in this spool directory, + * then do the exact same steps that the pre-ctlinfo code had + * been doing. Ie, very little. + */ + strlcpy(cfname2, tfname, sizeof(cfname2)); + cfname2[0] = 'c'; + res = link(tfname, cfname2); + if (res < 0) { + snprintf(errm, sizeof(errm), + "ctl_renametf error link(%s,%s): %s", tfname, + cfname2, strerror(errno)); + return strdup(errm); + } + unlink(tfname); + return NULL; + } +#endif + cjinf = NULL; /* in case of early jump to error_ret */ + newcf = NULL; /* in case of early jump to error_ret */ + *errm = '\0'; /* in case of early jump to error_ret */ + + chk3rd = tfname[2]; + if ((tfname[0] != 't') || (tfname[1] != 'f') || (!isalpha(chk3rd))) { + snprintf(errm, sizeof(errm), + "ctl_renametf invalid filename: %s", tfname); + goto error_ret; + } + + cjinf = ctl_readcf(ptrname, tfname); + if (cjinf == NULL) { + snprintf(errm, sizeof(errm), + "ctl_renametf error cti_readcf(%s)", tfname); + goto error_ret; + } + + /* + * This uses open+fdopen instead of fopen because that combination + * gives us greater control over file-creation issues. + */ + strlcpy(tfname2, tfname, sizeof(tfname2)); + tfname2[0] = 'r'; /* rf<letter><job><hostname> */ + newfd = open(tfname2, O_WRONLY|O_CREAT|O_TRUNC, 0660); + if (newfd == -1) { + snprintf(errm, sizeof(errm), + "ctl_renametf error open(%s): %s", tfname2, + strerror(errno)); + goto error_ret; + } + newcf = fdopen(newfd, "w"); + if (newcf == NULL) { + close(newfd); + snprintf(errm, sizeof(errm), + "ctl_renametf error fopen(%s): %s", tfname2, + strerror(errno)); + goto error_ret; + } + + /* + * Do extra sanity checks on some key job-attribute fields, and + * write them out first (thus making sure they are written in the + * order we generally expect them to be in). + */ + /* + * Some lpr implementations on PC's set a null-string for their + * hostname. A MacOS 10 system which has not correctly setup + * /etc/hostconfig will claim a hostname of 'localhost'. Anything + * with blanks in it would be an invalid value for hostname. For + * any of these invalid hostname values, replace the given value + * with the name of the host that this job is coming from. + */ + nogood = 0; + if (cjinf->cji_accthost == NULL) + nogood = 1; + else if (strcmp(cjinf->cji_accthost, ".na.") == 0) + nogood = 1; + else if (strcmp(cjinf->cji_accthost, "localhost") == 0) + nogood = 1; + else { + for (cp = cjinf->cji_accthost; *cp != '\0'; cp++) { + if (*cp <= ' ') { + nogood = 1; + break; + } + } + } + if (nogood) + fprintf(newcf, "H%s\n", from_host); + else + fprintf(newcf, "H%s\n", cjinf->cji_accthost); + + /* + * Now do some sanity checks on the 'P' (original userid) value. Note + * that the 'P'erson line is the second line which is ALWAYS supposed + * to be present in a control file. + * + * There is no particularly good value to use for replacements, but + * at least make sure the value is something reasonable to use in + * environment variables and statistics records. Again, some PC + * implementations send a null-string for a value. Various Mac + * implementations will set whatever string the user has set for + * their 'Owner Name', which usually includes blanks, etc. + */ + nogood = 0; + if (cjinf->cji_acctuser == NULL) + nogood = 1; + else if (strcmp(cjinf->cji_acctuser, ".na.") == 0) + ; /* No further checks needed... */ + else { + has_uc = 0; + cp = cjinf->cji_acctuser; + if (*cp == '-') + *cp++ = '_'; + for (; *cp != '\0'; cp++) { + if (islowerch(*cp) || isdigitch(*cp)) + continue; /* Standard valid characters */ + if (strchr(OTHER_USERID_CHARS, *cp) != NULL) + continue; /* Some more valid characters */ + if (isupperch(*cp)) { + has_uc = 1; /* These may be valid... */ + continue; + } + *cp = '_'; + } + /* + * Some Windows hosts send print jobs where the correct userid + * has been converted to uppercase, and that can cause trouble + * for sites that expect the correct value (for something like + * accounting). On the other hand, some sites do use uppercase + * in their userids, so we can't blindly convert to lowercase. + */ + if (has_uc && (getpwnam(cjinf->cji_acctuser) == NULL)) { + for (cp = cjinf->cji_acctuser; *cp != '\0'; cp++) { + if (isupperch(*cp)) + *cp = tolowerch(*cp); + } + } + } + if (nogood) + fprintf(newcf, "P%s\n", ".na."); + else + fprintf(newcf, "P%s\n", cjinf->cji_acctuser); + + /* No need for sanity checks on class, jobname, "literal" user. */ + if (cjinf->cji_class != NULL) + fprintf(newcf, "C%s\n", cjinf->cji_class); + if (cjinf->cji_jobname != NULL) + fprintf(newcf, "J%s\n", cjinf->cji_jobname); + if (cjinf->cji_headruser != NULL) + fprintf(newcf, "L%s\n", cjinf->cji_headruser); + + /* + * This should probably add more sanity checks on mailto value. + * Note that if the mailto value is "wrong", then there's no good + * way to know what the "correct" value would be, and we should not + * semd email to some random address. At least for now, just ignore + * any invalid values. + */ + nogood = 0; + if (cjinf->cji_mailto == NULL) + nogood = 1; + else { + for (cp = cjinf->cji_mailto; *cp != '\0'; cp++) { + if (*cp <= ' ') { + nogood = 1; + break; + } + } + } + if (!nogood) + fprintf(newcf, "M%s\n", cjinf->cji_mailto); + + /* + * Now go thru the old control file, copying all information which + * hasn't already been written into the new file. + */ + ctl_rewindcf(cjinf); + lbuff = ctl_getline(cjinf); + while (lbuff != NULL) { + switch (lbuff[0]) { + case 'H': + case 'P': + case 'C': + case 'J': + case 'L': + case 'M': + /* already wrote values for these to the newcf */ + break; + case 'N': + /* see comments under 'U'... */ + if (cjinf->cji_dfcount == 0) { + /* in this case, 'N's will be done in 'U' */ + break; + } + fprintf(newcf, "%s\n", lbuff); + break; + case 'U': + /* + * check for the very common case where the remote + * host had to process 'lpr -s -r', but it did not + * remove the Unlink line from the control file. + * Such Unlink lines will legitimately have a '/' in + * them, but it is the original lpr host which would + * have done the unlink of such files, and not any + * host receiving that job. + */ + slash = strchr(lbuff, '/'); + if (slash != NULL) { + break; /* skip this line */ + } + /* + * Okay, another kind of broken lpr implementation + * is one which send datafiles, and Unlink's those + * datafiles, but never includes any PRINT request + * for those files. Experimentation shows that one + * copy of those datafiles should be printed with a + * format of 'f'. If this is an example of such a + * screwed-up control file, fix it here. + */ + if (cjinf->cji_dfcount == 0) { + lbuff++; + if (strncmp(lbuff, "df", (size_t)2) == 0) { + fprintf(newcf, "f%s\n", lbuff); + fprintf(newcf, "U%s\n", lbuff); + fprintf(newcf, "N%s\n", lbuff); + } + break; + } + fprintf(newcf, "%s\n", lbuff); + break; + default: + fprintf(newcf, "%s\n", lbuff); + break; + } + lbuff = ctl_getline(cjinf); + } + + ctl_freeinf(cjinf); + cjinf = NULL; + + res = fclose(newcf); + newcf = NULL; + if (res != 0) { + snprintf(errm, sizeof(errm), + "ctl_renametf error fclose(%s): %s", tfname2, + strerror(errno)); + goto error_ret; + } + + strlcpy(cfname2, tfname, sizeof(cfname2)); + cfname2[0] = 'c'; /* rename new file to 'cfA*' */ + res = link(tfname2, cfname2); + if (res != 0) { + snprintf(errm, sizeof(errm), + "ctl_renametf error link(%s,%s): %s", tfname2, cfname2, + strerror(errno)); + goto error_ret; + } + + /* All the important work is done. Now just remove temp files */ +#ifdef LEAVE_TMPCF_FILES + { + struct stat tfstat; + size_t size1; + tfstat.st_size = 1; /* certainly invalid value */ + res = stat(tfname, &tfstat); + size1 = tfstat.st_size; + tfstat.st_size = 2; /* certainly invalid value */ + res = stat(tfname2, &tfstat); + /* + * If the sizes do not match, or either stat call failed, + * then do not remove the temp files, but just move them + * out of the way. This is so I can see what this routine + * had changed (and the files won't interfere with some + * later job coming in from the same host). In this case, + * we don't care if we clobber some previous file. + */ + if (size1 != tfstat.st_size) { + strlcpy(cfname2, tfname, sizeof(cfname2)); + strlcat(cfname2, "._T", sizeof(cfname2)); + rename(tfname, cfname2); + strlcpy(cfname2, tfname2, sizeof(cfname2)); + strlcat(cfname2, "._T", sizeof(cfname2)); + rename(tfname2, cfname2); + return NULL; + } + } +#endif + unlink(tfname); + unlink(tfname2); + + return NULL; + +error_ret: + if (cjinf != NULL) + ctl_freeinf(cjinf); + if (newcf != NULL) + fclose(newcf); + + if (*errm != '\0') + return strdup(errm); + return strdup("ctl_renametf internal (missed) error"); +} + +void +ctl_rewindcf(struct cjobinfo *cjinf) +{ + struct cjprivate *cpriv; + + if (cjinf == NULL) + return; + cpriv = cjinf->cji_priv; + if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) { + syslog(LOG_ERR, "in ctl_rewindcf(%p): invalid cjinf (cpriv %p)", + (void *)cjinf, (void *)cpriv); + return; + } + + rewind(cpriv->cji_fstream); /* assume no errors... :-) */ +} + +char * +ctl_rmjob(const char *ptrname, const char *cfname) +{ + struct cjobinfo *cjinf; + char *lbuff; + char errm[CTI_LINEMAX]; + + cjinf = ctl_readcf(ptrname, cfname); + if (cjinf == NULL) { + snprintf(errm, sizeof(errm), + "ctl_renametf error cti_readcf(%s)", cfname); + return strdup(errm); + } + + ctl_rewindcf(cjinf); + lbuff = ctl_getline(cjinf); + while (lbuff != NULL) { + /* obviously we need to fill in the following... */ + switch (lbuff[0]) { + case 'S': + break; + case 'U': + break; + default: + break; + } + lbuff = ctl_getline(cjinf); + } + + ctl_freeinf(cjinf); + cjinf = NULL; + + return NULL; +} + +/* + * The following routine was originally written to pin down a bug. It is + * no longer needed for that problem, but may be useful to keep around for + * other debugging. + */ +void +ctl_dumpcji(FILE *dbg_stream, const char *heading, struct cjobinfo *cjinf) +{ +#define PRINTSTR(xHdr,xStr) \ + astr = xStr; \ + ctl_dbgline++; \ + fprintf(dbg_stream, "%4d] %12s = ", ctl_dbgline, xHdr); \ + if (astr == NULL) \ + fprintf(dbg_stream, "NULL\n"); \ + else \ + fprintf(dbg_stream, "%p -> %s\n", astr, astr) + + struct cjprivate *cpriv; + char *astr; + + if (cjinf == NULL) { + fprintf(dbg_stream, + "ctl_dumpcji: ptr to cjobinfo for '%s' is NULL\n", + heading); + return; + } + cpriv = cjinf->cji_priv; + + fprintf(dbg_stream, "ctl_dumpcji: Dump '%s' of cjobinfo at %p->%p\n", + heading, (void *)cjinf, cpriv->cji_buff); + + PRINTSTR("accthost.H", cpriv->pub.cji_accthost); + PRINTSTR("acctuser.P", cpriv->pub.cji_acctuser); + PRINTSTR("class.C", cpriv->pub.cji_class); + PRINTSTR("cf-qname", cpriv->pub.cji_curqueue); + PRINTSTR("cf-fname", cpriv->pub.cji_fname); + PRINTSTR("jobname.J", cpriv->pub.cji_jobname); + PRINTSTR("mailto.M", cpriv->pub.cji_mailto); + PRINTSTR("headruser.L", cpriv->pub.cji_headruser); + + ctl_dbgline++; + fprintf(dbg_stream, "%4d] %12s = ", ctl_dbgline, "*cjprivate"); + if (cpriv->pub.cji_priv == NULL) + fprintf(dbg_stream, "NULL !!\n"); + else + fprintf(dbg_stream, "%p\n", (void *)cpriv->pub.cji_priv); + + fprintf(dbg_stream, "|- - - - --> Dump '%s' complete\n", heading); + + /* flush output for the benefit of anyone doing a 'tail -f' */ + fflush(dbg_stream); + +#undef PRINTSTR +} + +/* + * This routine reads in the next line from the control-file, and removes + * the trailing newline character. + * + * Historical note: Earlier versions of this routine did tab-expansion for + * ALL lines read in, which did not make any sense for most of the lines + * in a control file. For the lines where tab-expansion is useful, it will + * now have to be done by the calling routine. + */ +static char * +ctl_getline(struct cjobinfo *cjinf) +{ + char *strp, *nl; + struct cjprivate *cpriv; + + if (cjinf == NULL) + return NULL; + cpriv = cjinf->cji_priv; + if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) { + syslog(LOG_ERR, "in ctl_getline(%p): invalid cjinf (cpriv %p)", + (void *)cjinf, (void *)cpriv); + return NULL; + } + + errno = 0; + strp = fgets(cpriv->cji_buff, cpriv->cji_buffsize, cpriv->cji_fstream); + if (strp == NULL) { + if (errno != 0) + syslog(LOG_ERR, "%s: ctl_getline error fgets(%s): %s", + cpriv->pub.cji_curqueue, cpriv->pub.cji_fname, + strerror(errno)); + return NULL; + } + nl = strchr(strp, '\n'); + if (nl != NULL) + *nl = '\0'; + +#ifdef DEBUGREADCF_FNAME + /* I'd like to find out if the previous work to expand tabs was ever + * really used, and if so, on what lines and for what reason. + * Yes, all this work probably means I'm obsessed about this 'tab' + * issue, but isn't programming a matter of obsession? + */ + { + int tabcnt; + char *ch; + + tabcnt = 0; + ch = strp; + for (ch = strp; *ch != '\0'; ch++) { + if (*ch == '\t') + tabcnt++; + } + + if (tabcnt && (ctl_dbgfile != NULL)) { + cpriv->cji_dumpit++; + fprintf(ctl_dbgfile, "%s: tabs=%d '%s'\n", + cpriv->pub.cji_fname, tabcnt, cpriv->cji_buff); + } + } +#endif + return strp; +} diff --git a/usr.sbin/lpr/common_source/ctlinfo.h b/usr.sbin/lpr/common_source/ctlinfo.h new file mode 100644 index 0000000..49bf532 --- /dev/null +++ b/usr.sbin/lpr/common_source/ctlinfo.h @@ -0,0 +1,73 @@ +/* + * ------+---------+---------+---------+---------+---------+---------+---------* + * Copyright (c) 2001,2011 - Garance Alistair Drosehn <gad@FreeBSD.org>. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the FreeBSD Project. + * + * ------+---------+---------+---------+---------+---------+---------+---------* + * $FreeBSD$ + * ------+---------+---------+---------+---------+---------+---------+---------* + */ + +/* + * ctlinfo - This collection of routines will know everything there is to + * know about the information inside a control file ('cf*') which is used + * to describe a print job in lpr & friends. The eventual goal is that it + * will be the ONLY source file to know what's inside these control-files. + */ + +struct cjprivate; /* used internal to ctl* routines */ + +struct cjobinfo { + int cji_dfcount; /* number of data files to print */ + int cji_uncount; /* number of unlink-file requests */ + char *cji_accthost; /* the host that this job came from, + * for accounting purposes (usually + * the host where the original 'lpr' + * was done) */ + char *cji_acctuser; /* userid who should be charged for + * this job (usually, the userid which + * did the original 'lpr') */ + char *cji_class; /* class-name */ + char *cji_curqueue; /* printer-queue that this cf-file is + * curently sitting in (mainly used + * in syslog error messages) */ + char *cji_fname; /* filename of the control file */ + char *cji_jobname; /* job-name (for banner) */ + char *cji_mailto; /* userid to send email to (or null) */ + char *cji_headruser; /* "literal" user-name (for banner) or + * NULL if no banner-page is wanted */ + struct cjprivate *cji_priv; +}; + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ + +__BEGIN_DECLS +void ctl_freeinf(struct cjobinfo *_cjinf); +struct cjobinfo *ctl_readcf(const char *_ptrname, const char *_cfname); +char *ctl_renametf(const char *_ptrname, const char *_tfname); +__END_DECLS diff --git a/usr.sbin/lpr/common_source/displayq.c b/usr.sbin/lpr/common_source/displayq.c new file mode 100644 index 0000000..3c038d6 --- /dev/null +++ b/usr.sbin/lpr/common_source/displayq.c @@ -0,0 +1,636 @@ +/* + * 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. + * 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. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)displayq.c 8.4 (Berkeley) 4/28/95"; +#endif /* not lint */ +#endif + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#define psignal foil_gcc_psignal +#define sys_siglist foil_gcc_siglist +#include <unistd.h> +#undef psignal +#undef sys_siglist + +#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 */ + +/* + * isprint() takes a parameter of 'int', but expect values in the range + * of unsigned char. Define a wrapper which takes a value of type 'char', + * whether signed or unsigned, and ensure it ends up in the right range. + */ +#define isprintch(Anychar) isprint((u_char)(Anychar)) + +/* + * Stuff for handling job specifications + */ +static int col; /* column on screen */ +static char current[MAXNAMLEN+1]; /* current file being printed */ +static char file[MAXNAMLEN+1]; /* 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 const char *head0 = "Rank Owner Job Files"; +static const char *head1 = "Total Size\n"; + +static void alarmhandler(int _signo); +static void filtered_write(char *_obuffer, int _wlen, FILE *_wstream); +static void daemonwarn(const struct printer *_pp); + +/* + * Display the current state of the queue. Format = 1 if long format. + */ +void +displayq(struct printer *pp, int format) +{ + register struct jobqueue *q; + register int i, nitems, fd, ret; + char *cp, *endp; + struct jobqueue **queue; + struct stat statb; + FILE *fp; + void (*savealrm)(int); + + lflag = format; + totsize = 0; + rank = -1; + + if ((cp = checkremote(pp))) { + printf("Warning: %s\n", cp); + free(cp); + } + + /* + * Print out local queue + * Find all the control files in the spooling directory + */ + PRIV_START + if (chdir(pp->spool_dir) < 0) + fatal(pp, "cannot chdir to spooling directory: %s", + strerror(errno)); + PRIV_END + if ((nitems = getq(pp, &queue)) < 0) + fatal(pp, "cannot examine spooling area\n"); + PRIV_START + ret = stat(pp->lock_file, &statb); + PRIV_END + if (ret >= 0) { + if (statb.st_mode & LFM_PRINT_DIS) { + if (pp->remote) + printf("%s: ", local_host); + printf("Warning: %s is down: ", pp->printer); + PRIV_START + fd = open(pp->status_file, O_RDONLY|O_SHLOCK); + PRIV_END + if (fd >= 0) { + 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 & LFM_QUEUE_DIS) { + if (pp->remote) + printf("%s: ", local_host); + printf("Warning: %s queue is turned off\n", + pp->printer); + } + } + + if (nitems) { + PRIV_START + fp = fopen(pp->lock_file, "r"); + PRIV_END + if (fp == NULL) + daemonwarn(pp); + else { + /* get daemon pid */ + cp = current; + endp = cp + sizeof(current) - 1; + while ((i = getc(fp)) != EOF && i != '\n') { + if (cp < endp) + *cp++ = i; + } + *cp = '\0'; + i = atoi(current); + if (i <= 0) { + ret = -1; + } else { + PRIV_START + ret = kill(i, 0); + PRIV_END + } + if (ret < 0) { + daemonwarn(pp); + } else { + /* read current file name */ + cp = current; + endp = cp + sizeof(current) - 1; + while ((i = getc(fp)) != EOF && i != '\n') { + if (cp < endp) + *cp++ = i; + } + *cp = '\0'; + /* + * Print the status file. + */ + if (pp->remote) + printf("%s: ", local_host); + PRIV_START + fd = open(pp->status_file, O_RDONLY|O_SHLOCK); + PRIV_END + if (fd >= 0) { + while ((i = read(fd, line, + sizeof(line))) > 0) + fwrite(line, 1, i, stdout); + 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(pp, q->job_cfname); + free(q); + } + free(queue); + } + if (!pp->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) snprintf(line, sizeof(line), "%c%s", format ? '\4' : '\3', + pp->remote_queue); + cp = line; + for (i = 0; i < requests && cp-line+10 < sizeof(line) - 1; i++) { + cp += strlen(cp); + (void) sprintf(cp, " %d", requ[i]); + } + for (i = 0; i < users && cp - line + 1 + strlen(user[i]) < + sizeof(line) - 1; i++) { + cp += strlen(cp); + *cp++ = ' '; + (void) strcpy(cp, user[i]); + } + strcat(line, "\n"); + savealrm = signal(SIGALRM, alarmhandler); + alarm(pp->conn_timeout); + fd = getport(pp, pp->remote_host, 0); + alarm(0); + (void)signal(SIGALRM, savealrm); + if (fd < 0) { + if (from_host != local_host) + printf("%s: ", local_host); + printf("connection to %s is down\n", pp->remote_host); + } + else { + i = strlen(line); + if (write(fd, line, i) != i) + fatal(pp, "Lost connection"); + while ((i = read(fd, line, sizeof(line))) > 0) + filtered_write(line, i, stdout); + filtered_write(NULL, -1, stdout); + (void) close(fd); + } +} + +/* + * The lpq-info read from remote hosts may contain unprintable characters, + * or carriage-returns instead of line-feeds. Clean those up before echoing + * the lpq-info line(s) to stdout. The info may also be missing any kind of + * end-of-line character. This also turns CRLF and LFCR into a plain LF. + * + * This routine may be called multiple times to process a single set of + * information, and after a set is finished this routine must be called + * one extra time with NULL specified as the buffer address. + */ +static void +filtered_write(char *wbuffer, int wlen, FILE *wstream) +{ + static char lastchar, savedchar; + char *chkptr, *dest_end, *dest_ch, *nxtptr, *w_end; + int destlen; + char destbuf[BUFSIZ]; + + if (wbuffer == NULL) { + if (savedchar != '\0') { + if (savedchar == '\r') + savedchar = '\n'; + fputc(savedchar, wstream); + lastchar = savedchar; + savedchar = '\0'; + } + if (lastchar != '\0' && lastchar != '\n') + fputc('\n', wstream); + lastchar = '\0'; + return; + } + + dest_ch = &destbuf[0]; + dest_end = dest_ch + sizeof(destbuf); + chkptr = wbuffer; + w_end = wbuffer + wlen; + lastchar = '\0'; + if (savedchar != '\0') { + chkptr = &savedchar; + nxtptr = wbuffer; + } else + nxtptr = chkptr + 1; + + while (chkptr < w_end) { + if (nxtptr < w_end) { + if ((*chkptr == '\r' && *nxtptr == '\n') || + (*chkptr == '\n' && *nxtptr == '\r')) { + *dest_ch++ = '\n'; + /* want to skip past that second character */ + nxtptr++; + goto check_next; + } + } else { + /* This is the last byte in the buffer given on this + * call, so check if it could be the first-byte of a + * significant two-byte sequence. If it is, then + * don't write it out now, but save for checking in + * the next call. + */ + savedchar = '\0'; + if (*chkptr == '\r' || *chkptr == '\n') { + savedchar = *chkptr; + break; + } + } + if (*chkptr == '\r') + *dest_ch++ = '\n'; +#if 0 /* XXX - don't translate unprintable characters (yet) */ + else if (*chkptr != '\t' && *chkptr != '\n' && + !isprintch(*chkptr)) + *dest_ch++ = '?'; +#endif + else + *dest_ch++ = *chkptr; + +check_next: + chkptr = nxtptr; + nxtptr = chkptr + 1; + if (dest_ch >= dest_end) { + destlen = dest_ch - &destbuf[0]; + fwrite(destbuf, 1, destlen, wstream); + lastchar = destbuf[destlen - 1]; + dest_ch = &destbuf[0]; + } + } + destlen = dest_ch - &destbuf[0]; + if (destlen > 0) { + fwrite(destbuf, 1, destlen, wstream); + lastchar = destbuf[destlen - 1]; + } +} + +/* + * Print a warning message if there is no daemon present. + */ +static void +daemonwarn(const struct printer *pp) +{ + if (pp->remote) + printf("%s: ", local_host); + puts("Warning: no daemon present"); + current[0] = '\0'; +} + +/* + * Print the header for the short listing format + */ +void +header(void) +{ + printf("%s", head0); + col = strlen(head0)+1; + blankfill(SIZCOL); + printf("%s", head1); +} + +void +inform(const struct printer *pp, char *cf) +{ + int copycnt, jnum; + char savedname[MAXPATHLEN+1]; + FILE *cfp; + + /* + * There's a chance the control file has gone away + * in the meantime; if this is the case just keep going + */ + PRIV_START + if ((cfp = fopen(cf, "r")) == NULL) + return; + PRIV_END + + if (rank < 0) + rank = 0; + if (pp->remote || garbage || strcmp(cf, current)) + rank++; + + /* + * The cf-file may include commands to print more than one datafile + * from the user. For each datafile, the cf-file contains at least + * one line which starts with some format-specifier ('a'-'z'), and + * a second line ('N'ame) which indicates the original name the user + * specified for that file. There can be multiple format-spec lines + * for a single Name-line, if the user requested multiple copies of + * that file. Standard lpr puts the format-spec line(s) before the + * Name-line, while lprNG puts the Name-line before the format-spec + * line(s). This section needs to handle the lines in either order. + */ + copycnt = 0; + file[0] = '\0'; + savedname[0] = '\0'; + jnum = calc_jobnum(cf, NULL); + 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, jnum); + col += 16; + first = 1; + } + continue; + default: /* some format specifer and file name? */ + if (line[0] < 'a' || line[0] > 'z') + break; + if (copycnt == 0 || strcmp(file, line+1) != 0) { + strlcpy(file, line + 1, sizeof(file)); + } + copycnt++; + /* + * deliberately 'continue' to another getline(), so + * all format-spec lines for this datafile are read + * in and counted before calling show() + */ + continue; + case 'N': + strlcpy(savedname, line + 1, sizeof(savedname)); + break; + } + if ((file[0] != '\0') && (savedname[0] != '\0')) { + show(savedname, file, copycnt); + copycnt = 0; + file[0] = '\0'; + savedname[0] = '\0'; + } + } + fclose(cfp); + /* check for a file which hasn't been shown yet */ + if (file[0] != '\0') { + if (savedname[0] == '\0') { + /* a safeguard in case the N-ame line is missing */ + strlcpy(savedname, file, sizeof(savedname)); + } + show(savedname, file, copycnt); + } + if (!lflag) { + blankfill(SIZCOL); + printf("%ld bytes\n", totsize); + totsize = 0; + } +} + +int +inlist(char *uname, char *cfile) +{ + int *r, jnum; + char **u; + const char *cfhost; + + 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, uname)) + return(1); + /* + * Check the request list + */ + jnum = calc_jobnum(cfile, &cfhost); + for (r = requ; r < &requ[requests]; r++) + if (*r == jnum && !strcmp(cfhost, from_host)) + return(1); + return(0); +} + +void +show(const char *nfile, const char *datafile, int copies) +{ + if (strcmp(nfile, " ") == 0) + nfile = "(standard input)"; + if (lflag) + ldump(nfile, datafile, copies); + else + dump(nfile, datafile, copies); +} + +/* + * Fill the line with blanks to the specified column + */ +void +blankfill(int tocol) +{ + while (col++ < tocol) + putchar(' '); +} + +/* + * Give the abbreviated dump of the file names + */ +void +dump(const char *nfile, const char *datafile, int copies) +{ + struct stat lbuf; + const char etctmpl[] = ", ..."; + char etc[sizeof(etctmpl)]; + char *lastsep; + short fill, nlen; + short rem, remetc; + + /* + * Print as many filenames as will fit + * (leaving room for the 'total size' field) + */ + fill = first ? 0 : 2; /* fill space for ``, '' */ + nlen = strlen(nfile); + rem = SIZCOL - 1 - col; + if (nlen + fill > rem) { + if (first) { + /* print the right-most part of the name */ + printf("...%s ", &nfile[3+nlen-rem]); + col = SIZCOL; + } else if (rem > 0) { + /* fit as much of the etc-string as we can */ + remetc = rem; + if (rem > strlen(etctmpl)) + remetc = strlen(etctmpl); + etc[0] = '\0'; + strncat(etc, etctmpl, remetc); + printf("%s", etc); + col += remetc; + rem -= remetc; + /* room for the last segment of this filename? */ + lastsep = strrchr(nfile, '/'); + if ((lastsep != NULL) && (rem > strlen(lastsep))) { + /* print the right-most part of this name */ + printf("%s", lastsep); + col += strlen(lastsep); + } else { + /* do not pack any more names in here */ + blankfill(SIZCOL); + } + } + } else { + if (!first) + printf(", "); + printf("%s", nfile); + col += nlen + fill; + } + first = 0; + + PRIV_START + if (*datafile && !stat(datafile, &lbuf)) + totsize += copies * lbuf.st_size; + PRIV_END +} + +/* + * Print the long info about the file + */ +void +ldump(const char *nfile, const char *datafile, int copies) +{ + struct stat lbuf; + + putchar('\t'); + if (copies > 1) + printf("%-2d copies of %-19s", copies, nfile); + else + printf("%-32s", nfile); + if (*datafile && !stat(datafile, &lbuf)) + printf(" %qd bytes", (long long) lbuf.st_size); + else + printf(" ??? bytes"); + putchar('\n'); +} + +/* + * Print the job's rank in the queue, + * update col for screen management + */ +void +prank(int n) +{ + char rline[100]; + static const 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); +} + +void +alarmhandler(int signo __unused) +{ + /* the signal is ignored */ + /* (the '__unused' is just to avoid a compile-time warning) */ +} diff --git a/usr.sbin/lpr/common_source/lp.cdefs.h b/usr.sbin/lpr/common_source/lp.cdefs.h new file mode 100644 index 0000000..5806f39 --- /dev/null +++ b/usr.sbin/lpr/common_source/lp.cdefs.h @@ -0,0 +1,122 @@ +/*- + * ------+---------+---------+---------+---------+---------+---------+---------* + * Copyright (c) 2003,2013 - Garance Alistair Drosehn <gad@FreeBSD.org>. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the FreeBSD Project. + * + * ------+---------+---------+---------+---------+---------+---------+---------* + * $FreeBSD$ + * ------+---------+---------+---------+---------+---------+---------+---------* + */ + +/* + * The main goal of this include file is to provide a platform-neutral way + * to define some macros that lpr wants from FreeBSD's <sys/cdefs.h>. This + * will simply use the standard <sys/cdefs.h> when compiled in FreeBSD, but + * other OS's may not have /usr/include/sys/cdefs.h (or even if that file + * exists, it may not define all the macros that lpr will use). + */ + +#if !defined(_LP_CDEFS_H_) +#define _LP_CDEFS_H_ + +/* + * For non-BSD platforms, you can compile lpr with -DHAVE_SYS_CDEFS_H + * if <sys/cdefs.h> should be included. + */ +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# define HAVE_SYS_CDEFS_H +#endif +#if defined(HAVE_SYS_CDEFS_H) +# include <sys/cdefs.h> +#endif + +/* + * FreeBSD added a closefrom() routine in release 8.0. When compiling + * `lpr' on other platforms you might want to include bsd-closefrom.c + * from the portable-openssh project. + */ +#ifndef USE_CLOSEFROM +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# define USE_CLOSEFROM 1 +# endif +#endif +/* The macro USE_CLOSEFROM must be defined with a value of 0 or 1. */ +#ifndef USE_CLOSEFROM +# define USE_CLOSEFROM 0 +#endif + +/* + * __unused is a compiler-specific trick which can be used to avoid + * warnings about a variable which is defined but never referenced. + * Some lpr files use this, so define a null version if it was not + * defined by <sys/cdefs.h>. + */ +#if !defined(__unused) +# define __unused +#endif + +/* + * All the lpr source files will want to reference __FBSDID() to + * handle rcs id's. + */ +#if !defined(__FBSDID) +# if defined(lint) || defined(STRIP_FBSDID) +# define __FBSDID(s) struct skip_rcsid_struct +# elif defined(__IDSTRING) /* NetBSD */ +# define __FBSDID(s) __IDSTRING(rcsid,s) +# else +# define __FBSDID(s) static const char rcsid[] __unused = s +# endif +#endif /* __FBSDID */ + +/* + * Some lpr include files use __BEGIN_DECLS and __END_DECLS. + */ +#if !defined(__BEGIN_DECLS) +# if defined(__cplusplus) +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +# else +# define __BEGIN_DECLS +# define __END_DECLS +# endif +#endif + +/* + * __printflike and __printf0like are a compiler-specific tricks to + * tell the compiler to check the format-codes in printf-like + * routines wrt the args that will be formatted. + */ +#if !defined(__printflike) +# define __printflike(fmtarg, firstvararg) +#endif +#if !defined(__printf0like) +# define __printf0like(fmtarg, firstvararg) +#endif + +#endif /* !_LP_CDEFS_H_ */ diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h new file mode 100644 index 0000000..fb05d2f --- /dev/null +++ b/usr.sbin/lpr/common_source/lp.h @@ -0,0 +1,317 @@ +/* + * 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. + * 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. + * + * From: @(#)lp.h 8.2 (Berkeley) 4/28/95 + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <time.h> +#include <netdb.h> + +/* + * All this information used to be in global static variables shared + * mysteriously by various parts of the lpr/lpd suite. + * This structure attempts to centralize all these declarations in the + * hope that they can later be made more dynamic. + */ +enum lpd_filters { LPF_CIFPLOT, LPF_DVI, LPF_GRAPH, LPF_INPUT, + LPF_DITROFF, LPF_OUTPUT, LPF_FORTRAN, LPF_TROFF, + LPF_RASTER, LPF_COUNT }; +/* NB: there is a table in common.c giving the mapping from capability names */ + +struct printer { + char *printer; /* printer name */ + int remote; /* true if RM points to a remote host */ + int rp_matches_local; /* true if rp has same name as us */ + int tof; /* true if we are at top-of-form */ + /* ------------------------------------------------------ */ + char *acct_file; /* AF: accounting file */ + long baud_rate; /* BR: baud rate if lp is a tty */ + char *filters[LPF_COUNT]; /* CF, DF, GF, IF, NF, OF, RF, TF, VF */ + long conn_timeout; /* CT: TCP connection timeout */ + long daemon_user; /* DU: daemon user id -- XXX belongs ???? */ + char *form_feed; /* FF: form feed */ + long header_last; /* HL: print header last */ + char *log_file; /* LF: log file */ + char *lock_file; /* LO: lock file */ + char *lp; /* LP: device name or network address */ + long max_copies; /* MC: maximum number of copies allowed */ + long max_blocks; /* MX: maximum number of blocks to copy */ + long price100; /* PC: price per 100 units of output */ + long page_length; /* PL: page length */ + long page_width; /* PW: page width */ + long page_pwidth; /* PX: page width in pixels */ + long page_plength; /* PY: page length in pixels */ + long resend_copies; /* RC: resend copies to remote host */ + char *restrict_grp; /* RG: restricted group */ + char *remote_host; /* RM: remote machine name */ + char *remote_queue; /* RP: remote printer name */ + long restricted; /* RS: restricted to those with local accts */ + long rw; /* RW: open LP for reading and writing */ + long short_banner; /* SB: short banner */ + long no_copies; /* SC: suppress multiple copies */ + char *spool_dir; /* SD: spool directory */ + long no_formfeed; /* SF: suppress FF on each print job */ + long no_header; /* SH: suppress header page */ + char *stat_recv; /* SR: statistics file, receiving jobs */ + char *stat_send; /* SS: statistics file, sending jobs */ + char *status_file; /* ST: status file name */ + char *trailer; /* TR: trailer string send when Q empties */ + char *mode_set; /* MS: mode set, a la stty */ + + /* variables used by trstat*() to keep statistics on file transfers */ +#define JOBNUM_SIZE 8 + char jobnum[JOBNUM_SIZE]; + long jobdfnum; /* current datafile number within job */ + struct timespec tr_start, tr_done; +#define TIMESTR_SIZE 40 /* holds result from LPD_TIMESTAMP_PATTERN */ + char tr_timestr[TIMESTR_SIZE]; +#define DIFFTIME_TS(endTS,startTS) \ + ((double)(endTS.tv_sec - startTS.tv_sec) \ + + (endTS.tv_nsec - startTS.tv_nsec) * 1.0e-9) +}; + +/* + * Lists of user names and job numbers, for the benefit of the structs + * defined below. We use TAILQs so that requests don't get mysteriously + * reversed in process. + */ +struct req_user { + TAILQ_ENTRY(req_user) ru_link; /* macro glue */ + char ru_uname[1]; /* name of user */ +}; +TAILQ_HEAD(req_user_head, req_user); + +struct req_file { + TAILQ_ENTRY(req_file) rf_link; /* macro glue */ + char rf_type; /* type (lowercase cf file letter) of file */ + char *rf_prettyname; /* user-visible name of file */ + char rf_fname[1]; /* name of file */ +}; +TAILQ_HEAD(req_file_head, req_file); + +struct req_jobid { + TAILQ_ENTRY(req_jobid) rj_link; /* macro glue */ + int rj_job; /* job number */ +}; +TAILQ_HEAD(req_jobid_head, req_jobid); + +/* + * Encapsulate all the information relevant to a request in the + * lpr/lpd protocol. + */ +enum req_type { REQ_START, REQ_RECVJOB, REQ_LIST, REQ_DELETE }; + +struct request { + enum req_type type; /* what sort of request is this? */ + struct printer prtr; /* which printer is it for? */ + int remote; /* did request arrive over network? */ + char *logname; /* login name of requesting user */ + char *authname; /* authenticated identity of requesting user */ + char *prettyname; /* ``pretty'' name of requesting user */ + int privileged; /* was the request from a privileged user? */ + void *authinfo; /* authentication information */ + int authentic; /* was the request securely authenticated? */ + + /* Information for queries and deletes... */ + int nusers; /* length of following list... */ + struct req_user_head users; /* list of users to query/delete */ + int njobids; /* length of following list... */ + struct req_jobid_head jobids; /* list of jobids to query/delete */ +}; + +/* + * Global definitions for the line printer system. + */ +extern char line[BUFSIZ]; +extern const char *progname; /* program name (lpr, lpq, etc) */ + + /* + * 'local_host' is the name of the machine that lpd (lpr, whatever) + * is actually running on. + * + * 'from_host' will point to the 'host' variable when receiving a job + * from a user on the same host, or "somewhere else" when receiving a + * job from a remote host. If 'from_host != local_host', then 'from_ip' + * is the character representation of the IP address of from_host (note + * that string could be an IPv6 address). + * + * Also note that when 'from_host' is not pointing at 'local_host', the + * string it is pointing at may be as long as NI_MAXHOST (which is very + * likely to be much longer than MAXHOSTNAMELEN). + */ +extern char local_host[MAXHOSTNAMELEN]; +extern const char *from_host; /* client's machine name */ +extern const char *from_ip; /* client machine's IP address */ + +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 */ +extern char *person; /* name of person doing lprm */ +extern u_char family; /* address family */ + +/* + * Structure used for building a sorted list of control files. + * The job_processed value can be used by callers of getq(), to keep + * track of whatever processing they are doing. + */ +struct jobqueue { + time_t job_time; /* last-mod time of cf-file */ + int job_matched; /* used by match_jobspec() */ + int job_processed; /* set to zero by getq() */ + char job_cfname[MAXNAMLEN+1]; /* control file name */ +}; + +/* lpr/lpd generates readable timestamps for logfiles, etc. Have all those + * timestamps be in the same format wrt strftime(). This is ISO 8601 format, + * with the addition of an easy-readable day-of-the-week field. Note that + * '%T' = '%H:%M:%S', and that '%z' is not available on all platforms. + */ +#define LPD_TIMESTAMP_PATTERN "%Y-%m-%dT%T%z %a" + +/* + * Codes to indicate which statistic records trstat_write should write. + */ +typedef enum { TR_SENDING, TR_RECVING, TR_PRINTING } tr_sendrecv; + +/* + * Error codes for our mini printcap library. + */ +#define PCAPERR_TCLOOP (-3) +#define PCAPERR_OSERR (-2) +#define PCAPERR_NOTFOUND (-1) +#define PCAPERR_SUCCESS 0 +#define PCAPERR_TCOPEN 1 + +/* + * File modes for the various status files maintained by lpd. + */ +#define LOCK_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) +#define LFM_PRINT_DIS (S_IXUSR) +#define LFM_QUEUE_DIS (S_IXGRP) +#define LFM_RESET_QUE (S_IXOTH) + +#define STAT_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) +#define LOG_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) +#define TEMP_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) + +/* + * Bit-flags for set_qstate() actions, followed by the return values. + */ +#define SQS_DISABLEQ 0x01 /* Disable the queuing of new jobs */ +#define SQS_STOPP 0x02 /* Stop the printing of jobs */ +#define SQS_ENABLEQ 0x10 /* Enable the queuing of new jobs */ +#define SQS_STARTP 0x20 /* Start the printing of jobs */ +#define SQS_QCHANGED 0x80 /* The queue has changed (new jobs, etc) */ + +#define SQS_PARMERR -9 /* Invalid parameters from caller */ +#define SQS_CREFAIL -3 /* File did not exist, and create failed */ +#define SQS_CHGFAIL -2 /* File exists, but unable to change state */ +#define SQS_STATFAIL -1 /* Unable to stat() the lock file */ +#define SQS_CHGOK 1 /* File existed, and the state was changed */ +#define SQS_CREOK 2 /* File did not exist, but was created OK */ +#define SQS_SKIPCREOK 3 /* File did not exist, and there was */ + /* no need to create it */ + +/* + * Command codes used in the protocol. + */ +#define CMD_CHECK_QUE '\1' +#define CMD_TAKE_THIS '\2' +#define CMD_SHOWQ_SHORT '\3' +#define CMD_SHOWQ_LONG '\4' +#define CMD_RMJOB '\5' + +/* + * seteuid() macros. +*/ + +extern uid_t uid, euid; + +#define PRIV_START { \ + if (seteuid(euid) != 0) err(1, "seteuid failed"); \ +} +#define PRIV_END { \ + if (seteuid(uid) != 0) err(1, "seteuid failed"); \ +} + + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ + +__BEGIN_DECLS +struct dirent; + +void blankfill(int _tocol); +int calc_jobnum(const char *_cfname, const char **_hostpp); +char *checkremote(struct printer *_pp); +int chk(char *_file); +void closeallfds(int _start); +void delay(int _millisec); +void displayq(struct printer *_pp, int _format); +void dump(const char *_nfile, const char *_datafile, int _copies); +void fatal(const struct printer *_pp, const char *_msg, ...) + __printflike(2, 3); +int firstprinter(struct printer *_pp, int *_error); +void free_printer(struct printer *_pp); +void free_request(struct request *_rp); +int getline(FILE *_cfp); +int getport(const struct printer *_pp, const char *_rhost, int _rport); +int getprintcap(const char *_printer, struct printer *_pp); +int getq(const struct printer *_pp, struct jobqueue *(*_namelist[])); +void header(void); +void inform(const struct printer *_pp, char *_cf); +void init_printer(struct printer *_pp); +void init_request(struct request *_rp); +int inlist(char *_uname, char *_cfile); +int iscf(const struct dirent *_d); +void ldump(const char *_nfile, const char *_datafile, int _copies); +void lastprinter(void); +int lockchk(struct printer *_pp, char *_slockf); +char *lock_file_name(const struct printer *_pp, char *_buf, size_t _len); +void lpd_gettime(struct timespec *_tsp, char *_strp, size_t _strsize); +int nextprinter(struct printer *_pp, int *_error); +const +char *pcaperr(int _error); +void prank(int _n); +void process(const struct printer *_pp, char *_file); +void rmjob(const char *_printer); +void rmremote(const struct printer *_pp); +void setprintcap(char *_newfile); +int set_qstate(int _action, const char *_lfname); +void show(const char *_nfile, const char *_datafile, int _copies); +int startdaemon(const struct printer *_pp); +char *status_file_name(const struct printer *_pp, char *_buf, + size_t _len); +void trstat_init(struct printer *_pp, const char *_fname, int _filenum); +void trstat_write(struct printer *_pp, tr_sendrecv _sendrecv, + size_t _bytecnt, const char *_userid, const char *_otherhost, + const char *_orighost); +ssize_t writel(int _strm, ...); +__END_DECLS diff --git a/usr.sbin/lpr/common_source/lp.local.h b/usr.sbin/lpr/common_source/lp.local.h new file mode 100644 index 0000000..79cc52a --- /dev/null +++ b/usr.sbin/lpr/common_source/lp.local.h @@ -0,0 +1,78 @@ +/* + * 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. + * 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.local.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD$ + */ + +/* + * Possibly, local parameters to the spooling system + */ + +/* + * Defaults for line printer capabilities data base + */ +#define DEFLP "lp" +#define DEFLOCK "lock" +#define DEFSTAT "status" +#define DEFMX 0 +#define DEFMAXCOPIES 0 +#define DEFFF "\f" +#define DEFWIDTH 132 +#define DEFLENGTH 66 +#define DEFUID 1 +#define DEFTIMEOUT 120 + +/* + * When files are created in the spooling area, they are normally + * readable only by their owner and the spooling group. If you + * want otherwise, change this mode. + */ +#define FILMOD 0660 + +/* + * Printer is assumed to support LINELEN (for block chars) + * and background character (blank) is a space + */ +#define LINELEN 132 +#define BACKGND ' ' + +#define HEIGHT 9 /* height of characters */ +#define WIDTH 8 /* width of characters */ +#define DROP 3 /* offset to drop characters with descenders */ + +/* + * Define TERMCAP if the terminal capabilites are to be used for lpq. + */ +#define TERMCAP + +/* + * Maximum number of user and job requests for lpq and lprm. + */ +#define MAXUSERS 50 +#define MAXREQUESTS 50 diff --git a/usr.sbin/lpr/common_source/matchjobs.c b/usr.sbin/lpr/common_source/matchjobs.c new file mode 100644 index 0000000..c7dd165 --- /dev/null +++ b/usr.sbin/lpr/common_source/matchjobs.c @@ -0,0 +1,568 @@ +/* + * ------+---------+---------+---------+---------+---------+---------+---------* + * Copyright (c) 2002,2011 - Garance Alistair Drosehn <gad@FreeBSD.org>. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the FreeBSD Project + * or FreeBSD, Inc. + * + * ------+---------+---------+---------+---------+---------+---------+---------* + */ + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +/* + * movejobs.c - The lpc commands which move jobs around. + */ + +#include <sys/file.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/time.h> + +#include <dirent.h> /* for MAXNAMLEN, for job_cfname in lp.h! */ +#include <ctype.h> +#include <errno.h> +#include <fnmatch.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "ctlinfo.h" +#include "lp.h" +#include "matchjobs.h" + +#define DEBUG_PARSEJS 0 /* set to 1 when testing */ +#define DEBUG_SCANJS 0 /* set to 1 when testing */ + +static int match_jobspec(struct jobqueue *_jq, struct jobspec *_jspec); + +/* + * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF. + * Define a wrapper which can take 'char', either signed or unsigned. + */ +#define isdigitch(Anychar) isdigit(((int) Anychar) & 255) + +/* + * Format a single jobspec into a string fit for printing. + */ +void +format_jobspec(struct jobspec *jspec, int fmt_wanted) +{ + char rangestr[40], buildstr[200]; + const char fromuser[] = "from user "; + const char fromhost[] = "from host "; + size_t strsize; + + /* + * If the struct already has a fmtstring, then release it + * before building a new one. + */ + if (jspec->fmtoutput != NULL) { + free(jspec->fmtoutput); + jspec->fmtoutput = NULL; + } + + jspec->pluralfmt = 1; /* assume a "plural result" */ + rangestr[0] = '\0'; + if (jspec->startnum >= 0) { + if (jspec->startnum != jspec->endrange) + snprintf(rangestr, sizeof(rangestr), "%ld-%ld", + jspec->startnum, jspec->endrange); + else { + jspec->pluralfmt = 0; + snprintf(rangestr, sizeof(rangestr), "%ld", + jspec->startnum); + } + } + + strsize = sizeof(buildstr); + buildstr[0] = '\0'; + switch (fmt_wanted) { + case FMTJS_TERSE: + /* Build everything but the hostname in a temp string. */ + if (jspec->wanteduser != NULL) + strlcat(buildstr, jspec->wanteduser, strsize); + if (rangestr[0] != '\0') { + if (buildstr[0] != '\0') + strlcat(buildstr, ":", strsize); + strlcat(buildstr, rangestr, strsize); + } + if (jspec->wantedhost != NULL) + strlcat(buildstr, "@", strsize); + + /* Get space for the final result, including hostname */ + strsize = strlen(buildstr) + 1; + if (jspec->wantedhost != NULL) + strsize += strlen(jspec->wantedhost); + jspec->fmtoutput = malloc(strsize); + + /* Put together the final result */ + strlcpy(jspec->fmtoutput, buildstr, strsize); + if (jspec->wantedhost != NULL) + strlcat(jspec->fmtoutput, jspec->wantedhost, strsize); + break; + + case FMTJS_VERBOSE: + default: + /* Build everything but the hostname in a temp string. */ + strlcat(buildstr, rangestr, strsize); + if (jspec->wanteduser != NULL) { + if (rangestr[0] != '\0') + strlcat(buildstr, " ", strsize); + strlcat(buildstr, fromuser, strsize); + strlcat(buildstr, jspec->wanteduser, strsize); + } + if (jspec->wantedhost != NULL) { + if (jspec->wanteduser == NULL) { + if (rangestr[0] != '\0') + strlcat(buildstr, " ", strsize); + strlcat(buildstr, fromhost, strsize); + } else + strlcat(buildstr, "@", strsize); + } + + /* Get space for the final result, including hostname */ + strsize = strlen(buildstr) + 1; + if (jspec->wantedhost != NULL) + strsize += strlen(jspec->wantedhost); + jspec->fmtoutput = malloc(strsize); + + /* Put together the final result */ + strlcpy(jspec->fmtoutput, buildstr, strsize); + if (jspec->wantedhost != NULL) + strlcat(jspec->fmtoutput, jspec->wantedhost, strsize); + break; + } +} + +/* + * Free all the jobspec-related information. + */ +void +free_jobspec(struct jobspec_hdr *js_hdr) +{ + struct jobspec *jsinf; + + while (!STAILQ_EMPTY(js_hdr)) { + jsinf = STAILQ_FIRST(js_hdr); + STAILQ_REMOVE_HEAD(js_hdr, nextjs); + if (jsinf->fmtoutput) + free(jsinf->fmtoutput); + if (jsinf->matcheduser) + free(jsinf->matcheduser); + free(jsinf); + } +} + +/* + * This routine takes a string as typed in from the user, and parses it + * into a job-specification. A job specification would match one or more + * jobs in the queue of some single printer (the specification itself does + * not indicate which queue should be searched). + * + * This recognizes a job-number range by itself (all digits, or a range + * indicated by "digits-digits"), or a userid by itself. If a `:' is + * found, it is treated as a separator between a job-number range and + * a userid, where the job number range is the side which has a digit as + * the first character. If an `@' is found, everything to the right of + * it is treated as the hostname the job originated from. + * + * So, the user can specify: + * jobrange userid userid:jobrange jobrange:userid + * jobrange@hostname jobrange:userid@hostname + * userid@hostname userid:jobrange@hostname + * + * XXX - it would be nice to add "not options" too, such as ^user, + * ^jobrange, and @^hostname. + * + * This routine may modify the original input string if that input is + * valid. If the input was *not* valid, then this routine should return + * with the input string the same as when the routine was called. + */ +int +parse_jobspec(char *jobstr, struct jobspec_hdr *js_hdr) +{ + struct jobspec *jsinfo; + char *atsign, *colon, *lhside, *numstr, *period, *rhside; + int jobnum; + +#if DEBUG_PARSEJS + printf("\t [ pjs-input = %s ]\n", jobstr); +#endif + + if ((jobstr == NULL) || (*jobstr == '\0')) + return (0); + + jsinfo = malloc(sizeof(struct jobspec)); + memset(jsinfo, 0, sizeof(struct jobspec)); + jsinfo->startnum = jsinfo->endrange = -1; + + /* Find the separator characters, and nullify them. */ + numstr = NULL; + atsign = strchr(jobstr, '@'); + colon = strchr(jobstr, ':'); + if (atsign != NULL) + *atsign = '\0'; + if (colon != NULL) + *colon = '\0'; + + /* The at-sign always indicates a hostname. */ + if (atsign != NULL) { + rhside = atsign + 1; + if (*rhside != '\0') + jsinfo->wantedhost = rhside; + } + + /* Finish splitting the input into three parts. */ + rhside = NULL; + if (colon != NULL) { + rhside = colon + 1; + if (*rhside == '\0') + rhside = NULL; + } + lhside = NULL; + if (*jobstr != '\0') + lhside = jobstr; + + /* + * If there is a `:' here, then it's either jobrange:userid, + * userid:jobrange, or (if @hostname was not given) perhaps it + * might be hostname:jobnum. The side which has a digit as the + * first character is assumed to be the jobrange. It is an + * input error if both sides start with a digit, or if neither + * side starts with a digit. + */ + if ((lhside != NULL) && (rhside != NULL)) { + if (isdigitch(*lhside)) { + if (isdigitch(*rhside)) + goto bad_input; + numstr = lhside; + jsinfo->wanteduser = rhside; + } else if (isdigitch(*rhside)) { + numstr = rhside; + /* + * The original implementation of 'lpc topq' accepted + * hostname:jobnum. If the input did not include a + * @hostname, then assume the userid is a hostname if + * it includes a '.'. + */ + period = strchr(lhside, '.'); + if ((atsign == NULL) && (period != NULL)) + jsinfo->wantedhost = lhside; + else + jsinfo->wanteduser = lhside; + } else { + /* Neither side is a job number = user error */ + goto bad_input; + } + } else if (lhside != NULL) { + if (isdigitch(*lhside)) + numstr = lhside; + else + jsinfo->wanteduser = lhside; + } else if (rhside != NULL) { + if (isdigitch(*rhside)) + numstr = rhside; + else + jsinfo->wanteduser = rhside; + } + + /* + * Break down the numstr. It should be all digits, or a range + * specified as "\d+-\d+". + */ + if (numstr != NULL) { + errno = 0; + jobnum = strtol(numstr, &numstr, 10); + if (errno != 0) /* error in conversion */ + goto bad_input; + if (jobnum < 0) /* a bogus value for this purpose */ + goto bad_input; + if (jobnum > 99999) /* too large for job number */ + goto bad_input; + jsinfo->startnum = jsinfo->endrange = jobnum; + + /* Check for a range of numbers */ + if ((*numstr == '-') && (isdigitch(*(numstr + 1)))) { + numstr++; + errno = 0; + jobnum = strtol(numstr, &numstr, 10); + if (errno != 0) /* error in conversion */ + goto bad_input; + if (jobnum < jsinfo->startnum) + goto bad_input; + if (jobnum > 99999) /* too large for job number */ + goto bad_input; + jsinfo->endrange = jobnum; + } + + /* + * If there is anything left in the numstr, and if the + * original string did not include a userid or a hostname, + * then this might be the ancient form of '\d+hostname' + * (with no separator between jobnum and hostname). Accept + * that for backwards compatibility, but otherwise any + * remaining characters mean a user-error. Note that the + * ancient form accepted only a single number, but this + * will also accept a range of numbers. + */ + if (*numstr != '\0') { + if (atsign != NULL) + goto bad_input; + if (jsinfo->wantedhost != NULL) + goto bad_input; + if (jsinfo->wanteduser != NULL) + goto bad_input; + /* Treat as the rest of the string as a hostname */ + jsinfo->wantedhost = numstr; + } + } + + if ((jsinfo->startnum < 0) && (jsinfo->wanteduser == NULL) && + (jsinfo->wantedhost == NULL)) + goto bad_input; + + /* + * The input was valid, in the sense that it could be parsed + * into the individual parts. Add this jobspec to the list + * of jobspecs. + */ + STAILQ_INSERT_TAIL(js_hdr, jsinfo, nextjs); + +#if DEBUG_PARSEJS + printf("\t [ will check for"); + if (jsinfo->startnum >= 0) { + if (jsinfo->startnum == jsinfo->endrange) + printf(" jobnum = %ld", jsinfo->startnum); + else + printf(" jobrange = %ld to %ld", jsinfo->startnum, + jsinfo->endrange); + } else { + printf(" jobs"); + } + if ((jsinfo->wanteduser != NULL) || (jsinfo->wantedhost != NULL)) { + printf(" from"); + if (jsinfo->wanteduser != NULL) + printf(" user = %s", jsinfo->wanteduser); + if (jsinfo->wantedhost != NULL) + printf(" host = %s", jsinfo->wantedhost); + } + printf("]\n"); +#endif + + return (1); + +bad_input: + /* + * Restore any `@' and `:', in case the calling routine wants to + * write an error message which includes the input string. + */ + if (atsign != NULL) + *atsign = '@'; + if (colon != NULL) + *colon = ':'; + if (jsinfo != NULL) + free(jsinfo); + return (0); +} + +/* + * Check to see if a given job (specified by a jobqueue entry) matches + * all of the specifications in a given jobspec. + * + * Returns 0 if no match, 1 if the job does match. + */ +static int +match_jobspec(struct jobqueue *jq, struct jobspec *jspec) +{ + struct cjobinfo *cfinf; + const char *cf_hoststr; + int jnum, match; + +#if DEBUG_SCANJS + printf("\t [ match-js checking %s ]\n", jq->job_cfname); +#endif + + if (jspec == NULL || jq == NULL) + return (0); + + /* + * Keep track of which jobs have already been matched by this + * routine, and thus (probably) already processed. + */ + if (jq->job_matched) + return (0); + + jnum = calc_jobnum(jq->job_cfname, &cf_hoststr); + cfinf = NULL; + match = 0; /* assume the job will not match */ + jspec->matcheduser = NULL; + + /* + * Check the job-number range. + */ + if (jspec->startnum >= 0) { + if (jnum < jspec->startnum) + goto nomatch; + if (jnum > jspec->endrange) + goto nomatch; + } + + /* + * Check the hostname. Strictly speaking this should be done by + * reading the control file, but it is less expensive to check + * the hostname-part of the control file name. Also, this value + * can be easily seen in 'lpq -l', while there is no easy way for + * a user/operator to see the hostname in the control file. + */ + if (jspec->wantedhost != NULL) { + if (fnmatch(jspec->wantedhost, cf_hoststr, 0) != 0) + goto nomatch; + } + + /* + * Check for a match on the user name. This has to be done + * by reading the control file. + */ + if (jspec->wanteduser != NULL) { + cfinf = ctl_readcf("fakeq", jq->job_cfname); + if (cfinf == NULL) + goto nomatch; + if (fnmatch(jspec->wanteduser, cfinf->cji_acctuser, 0) != 0) + goto nomatch; + } + + /* This job matches all of the specified criteria. */ + match = 1; + jq->job_matched = 1; /* avoid matching the job twice */ + jspec->matchcnt++; + if (jspec->wanteduser != NULL) { + /* + * If the user specified a userid (which may have been a + * pattern), then the caller's "doentry()" routine might + * want to know the userid of this job that matched. + */ + jspec->matcheduser = strdup(cfinf->cji_acctuser); + } +#if DEBUG_SCANJS + printf("\t [ job matched! ]\n"); +#endif + +nomatch: + if (cfinf != NULL) + ctl_freeinf(cfinf); + return (match); +} + +/* + * Scan a queue for all jobs which match a jobspec. The queue is scanned + * from top to bottom. + * + * The caller can provide a routine which will be executed for each job + * that does match. Note that the processing routine might do anything + * to the matched job -- including the removal of it. + * + * This returns the number of jobs which were matched. + */ +int +scanq_jobspec(int qcount, struct jobqueue **squeue, int sopts, struct + jobspec_hdr *js_hdr, process_jqe doentry, void *doentryinfo) +{ + struct jobqueue **qent; + struct jobspec *jspec; + int cnt, matched, total; + + if (qcount < 1) + return (0); + if (js_hdr == NULL) + return (-1); + + /* The caller must specify one of the scanning orders */ + if ((sopts & (SCQ_JSORDER|SCQ_QORDER)) == 0) + return (-1); + + total = 0; + if (sopts & SCQ_JSORDER) { + /* + * For each job specification, scan through the queue + * looking for every job that matches. + */ + STAILQ_FOREACH(jspec, js_hdr, nextjs) { + for (qent = squeue, cnt = 0; cnt < qcount; + qent++, cnt++) { + matched = match_jobspec(*qent, jspec); + if (!matched) + continue; + total++; + if (doentry != NULL) + doentry(doentryinfo, *qent, jspec); + if (jspec->matcheduser != NULL) { + free(jspec->matcheduser); + jspec->matcheduser = NULL; + } + } + /* + * The entire queue has been scanned for this + * jobspec. Call the user's routine again with + * a NULL queue-entry, so it can print out any + * kind of per-jobspec summary. + */ + if (doentry != NULL) + doentry(doentryinfo, NULL, jspec); + } + } else { + /* + * For each job in the queue, check all of the job + * specifications to see if any one of them matches + * that job. + */ + for (qent = squeue, cnt = 0; cnt < qcount; + qent++, cnt++) { + STAILQ_FOREACH(jspec, js_hdr, nextjs) { + matched = match_jobspec(*qent, jspec); + if (!matched) + continue; + total++; + if (doentry != NULL) + doentry(doentryinfo, *qent, jspec); + if (jspec->matcheduser != NULL) { + free(jspec->matcheduser); + jspec->matcheduser = NULL; + } + /* + * Once there is a match, then there is no + * point in checking this same job against + * all the other jobspec's. + */ + break; + } + } + } + + return (total); +} diff --git a/usr.sbin/lpr/common_source/matchjobs.h b/usr.sbin/lpr/common_source/matchjobs.h new file mode 100644 index 0000000..6e4b9ff --- /dev/null +++ b/usr.sbin/lpr/common_source/matchjobs.h @@ -0,0 +1,102 @@ +/* + * ------+---------+---------+---------+---------+---------+---------+---------* + * Copyright (c) 2002 - Garance Alistair Drosehn <gad@FreeBSD.org>. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the FreeBSD Project + * or FreeBSD, Inc. + * + * ------+---------+---------+---------+---------+---------+---------+---------* + * $FreeBSD$ + * ------+---------+---------+---------+---------+---------+---------+---------* + */ + +#include <sys/queue.h> + +/* + * The "matcheduser" field is *only* valid during the call to the + * given "doentry()" routine, and is only set if the specification + * included a userid. + */ +struct jobspec { + STAILQ_ENTRY(jobspec) nextjs; + char *wantedhost; + char *wanteduser; + char *matcheduser; /* only valid for "doentry()" */ + char *fmtoutput; /* set by format_jobspec() */ + long startnum; + long endrange; + int pluralfmt; /* boolean set by format_jobspec() */ + uint matchcnt; +}; +STAILQ_HEAD(jobspec_hdr, jobspec); + +/* + * Format options for format_jobspec. + */ +#define FMTJS_TERSE 1 /* user:jobrange@host */ +#define FMTJS_VERBOSE 2 /* jobrange from user@host */ + +/* + * Options for scanq_jobspec. + * + * The caller must choose the order that entries should be scanned: + * 1) JSORDER: Matched jobs are processed (by calling the "doentry()" + * routine) in the order that the user specified those jobs. + * 2) QORDER: Matched jobs are processed in the order that the jobs are + * listed the queue. This guarantees that the "doentry()" routine + * will be called only once per job. + * + * There is a "job_matched" variable in struct jobqueue, which is used + * to make sure that the "doentry()" will only be called once for any + * given job in JSORDER processing. The "doentry()" routine can turn + * that off, if it does want to be called multiple times when the job + * is matched by multiple specifiers. + * + * The JSORDER processing will also call the "doentry()" routine once + * after each scan of the queue, with the jobqueue set to null. This + * provides a way for the caller to print out a summary message for + * each jobspec that was given. + */ +#define SCQ_JSORDER 0x0001 /* follow the user-specified order */ +#define SCQ_QORDER 0x0002 /* the order of jobs in the queue */ + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ + +__BEGIN_DECLS +struct jobqueue; + +typedef int process_jqe(void *_myinfo, struct jobqueue *_jq, + struct jobspec *_jspec); + +void format_jobspec(struct jobspec *_jspec, int _fmt_wanted); +void free_jobspec(struct jobspec_hdr *_js_hdr); +int scanq_jobspec(int _qitems, struct jobqueue **_squeue, int _sopts, + struct jobspec_hdr *_js_hdr, process_jqe _doentry, + void *_doentryinfo); +int parse_jobspec(char *_jobstr, struct jobspec_hdr *_js_hdr); +__END_DECLS + diff --git a/usr.sbin/lpr/common_source/net.c b/usr.sbin/lpr/common_source/net.c new file mode 100644 index 0000000..ab6fa16 --- /dev/null +++ b/usr.sbin/lpr/common_source/net.c @@ -0,0 +1,298 @@ +/* + * 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. + * + * From: @(#)common.c 8.5 (Berkeley) 4/28/95 + */ + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <dirent.h> /* required for lp.h, not used here */ +#include <err.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" + +/* + * 'local_host' is always the hostname of the machine which is running + * lpr (lpd, whatever), while 'from_host' either points at 'local_host' + * or points at a different buffer when receiving a job from a remote + * machine (and that buffer has the hostname of that remote machine). + */ +char local_host[MAXHOSTNAMELEN]; /* host running lpd/lpr */ +const char *from_host = local_host; /* client's machine name */ +const char *from_ip = ""; /* client machine's IP address */ + +#ifdef INET6 +u_char family = PF_UNSPEC; +#else +u_char family = PF_INET; +#endif + +/* + * 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(const struct printer *pp, const char *rhost, int rport) +{ + struct addrinfo hints, *res, *ai; + int s, timo = 1, lport = IPPORT_RESERVED - 1; + int error, refused = 0; + + /* + * Get the host address and port number to connect to. + */ + if (rhost == NULL) + fatal(pp, "no remote host to connect to"); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL), + &hints, &res); + if (error) + fatal(pp, "%s\n", gai_strerror(error)); + if (rport != 0) + ((struct sockaddr_in *) res->ai_addr)->sin_port = htons(rport); + + /* + * Try connecting to the server. + */ + ai = res; +retry: + PRIV_START + s = rresvport_af(&lport, ai->ai_family); + PRIV_END + if (s < 0) { + if (errno != EAGAIN) { + if (ai->ai_next) { + ai = ai->ai_next; + goto retry; + } + if (refused && timo <= 16) { + sleep(timo); + timo *= 2; + refused = 0; + ai = res; + goto retry; + } + } + freeaddrinfo(res); + return(-1); + } + if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) { + error = errno; + (void) close(s); + errno = error; + /* + * This used to decrement lport, but the current semantics + * of rresvport do not provide such a function (in fact, + * rresvport should guarantee that the chosen port will + * never result in an EADDRINUSE). + */ + if (errno == EADDRINUSE) { + goto retry; + } + + if (errno == ECONNREFUSED) + refused++; + + if (ai->ai_next != NULL) { + ai = ai->ai_next; + goto retry; + } + if (refused && timo <= 16) { + sleep(timo); + timo *= 2; + refused = 0; + ai = res; + goto retry; + } + freeaddrinfo(res); + return(-1); + } + freeaddrinfo(res); + return(s); +} + +/* + * Figure out whether the local machine is the same + * as the remote machine (RM) entry (if it exists). + * We do this by counting the intersection of our + * address list and theirs. This is better than the + * old method (comparing the canonical names), as it + * allows load-sharing between multiple print servers. + * The return value is an error message which must be + * free()d. + */ +char * +checkremote(struct printer *pp) +{ + char lclhost[MAXHOSTNAMELEN]; + struct addrinfo hints, *local_res, *remote_res, *lr, *rr; + char *error; + int ncommonaddrs, errno; + char h1[NI_MAXHOST], h2[NI_MAXHOST]; + + if (!pp->rp_matches_local) { /* Remote printer doesn't match local */ + pp->remote = 1; + return NULL; + } + + pp->remote = 0; /* assume printer is local */ + if (pp->remote_host == NULL) + return NULL; + + /* get the addresses of the local host */ + gethostname(lclhost, sizeof(lclhost)); + lclhost[sizeof(lclhost) - 1] = '\0'; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + if ((errno = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) { + asprintf(&error, "unable to get official name " + "for local machine %s: %s", + lclhost, gai_strerror(errno)); + return error; + } + + /* get the official name of RM */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + if ((errno = getaddrinfo(pp->remote_host, NULL, + &hints, &remote_res)) != 0) { + asprintf(&error, "unable to get address list for " + "remote machine %s: %s", + pp->remote_host, gai_strerror(errno)); + freeaddrinfo(local_res); + return error; + } + + ncommonaddrs = 0; + for (lr = local_res; lr; lr = lr->ai_next) { + h1[0] = '\0'; + if (getnameinfo(lr->ai_addr, lr->ai_addrlen, h1, sizeof(h1), + NULL, 0, NI_NUMERICHOST) != 0) + continue; + for (rr = remote_res; rr; rr = rr->ai_next) { + h2[0] = '\0'; + if (getnameinfo(rr->ai_addr, rr->ai_addrlen, + h2, sizeof(h2), NULL, 0, + NI_NUMERICHOST) != 0) + continue; + if (strcmp(h1, h2) == 0) + ncommonaddrs++; + } + } + + /* + * if the two hosts do not share at least one IP address + * then the printer must be remote. + */ + if (ncommonaddrs == 0) + pp->remote = 1; + freeaddrinfo(local_res); + freeaddrinfo(remote_res); + return NULL; +} + +/* + * This isn't really network-related, but it's used here to write + * multi-part strings onto sockets without using stdio. Return + * values are as for writev(2). + */ +ssize_t +writel(int strm, ...) +{ + va_list ap; + int i, n; + const char *cp; +#define NIOV 12 + struct iovec iov[NIOV], *iovp = iov; + ssize_t retval; + + /* first count them */ + va_start(ap, strm); + n = 0; + do { + cp = va_arg(ap, char *); + n++; + } while (cp); + va_end(ap); + n--; /* correct for count of trailing null */ + + if (n > NIOV) { + iovp = malloc(n * sizeof *iovp); + if (iovp == 0) + return -1; + } + + /* now make up iovec and send */ + va_start(ap, strm); + for (i = 0; i < n; i++) { + iovp[i].iov_base = va_arg(ap, char *); + iovp[i].iov_len = strlen(iovp[i].iov_base); + } + va_end(ap); + retval = writev(strm, iovp, n); + if (iovp != iov) + free(iovp); + return retval; +} diff --git a/usr.sbin/lpr/common_source/pathnames.h b/usr.sbin/lpr/common_source/pathnames.h new file mode 100644 index 0000000..0be302a --- /dev/null +++ b/usr.sbin/lpr/common_source/pathnames.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1989, 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. + * 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. + * + * $FreeBSD$ + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#include <paths.h> + +#define _PATH_DEFDEVLP "/dev/lp" +#define _PATH_DEFSPOOL "/var/spool/output/lpd" +#define _PATH_HOSTSEQUIV "/etc/hosts.equiv" +#define _PATH_HOSTSLPD "/etc/hosts.lpd" +#define _PATH_MASTERLOCK "/var/spool/output/lpd.lock" +#define _PATH_PR "/usr/bin/pr" +#define _PATH_PRINTCAP "/etc/printcap" +#define _PATH_SOCKETNAME "/var/run/printer" +#define _PATH_VFONT "/usr/libdata/vfont/" +#define _PATH_VFONTB "/usr/libdata/vfont/B" +#define _PATH_VFONTI "/usr/libdata/vfont/I" +#define _PATH_VFONTR "/usr/libdata/vfont/R" +#define _PATH_VFONTS "/usr/libdata/vfont/S" +#define _PATH_CHKPRINTCAP "/usr/sbin/chkprintcap" diff --git a/usr.sbin/lpr/common_source/printcap.c b/usr.sbin/lpr/common_source/printcap.c new file mode 100644 index 0000000..0d3644b --- /dev/null +++ b/usr.sbin/lpr/common_source/printcap.c @@ -0,0 +1,451 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)printcap.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ +#endif + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/param.h> /* required for lp.h, but not used here */ +#include <sys/dirent.h> /* ditto */ +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" + +/* + * Routines and data used in processing the printcap file. + */ +static char *printcapdb[2] = { _PATH_PRINTCAP, 0 }; /* list for cget* */ + +static char *capdb_canonical_name(const char *_bp); +static int capdb_getaltlog(char *_bp, const char *_shrt, + const char *_lng); +static int capdb_getaltnum(char *_bp, const char *_shrt, + const char *_lng, long _dflt, long *_result); +static int capdb_getaltstr(char *_bp, const char *_shrt, + const char *lng, const char *_dflt, char **_result); +static int getprintcap_int(char *_bp, struct printer *_pp); + +/* + * Change the name of the printcap file. Used by chkprintcap(8), + * but could be used by other members of the suite with appropriate + * security measures. + */ +void +setprintcap(char *newfile) +{ + printcapdb[0] = newfile; +} + +/* + * Read the printcap database for printer `printer' into the + * struct printer pointed by `pp'. Return values are as for + * cgetent(3): -1 means we could not find what we wanted, -2 + * means a system error occurred (and errno is set), -3 if a + * reference (`tc=') loop was detected, and 0 means success. + * + * Copied from lpr; should add additional capabilities as they + * are required by the other programs in the suite so that + * printcap-reading is consistent across the entire family. + */ +int +getprintcap(const char *printer, struct printer *pp) +{ + int status; + char *XXX; + char *bp; + + /* + * A bug in the declaration of cgetent(3) means that we have + * to hide the constness of its third argument. + */ + XXX = (char *)printer; + if ((status = cgetent(&bp, printcapdb, XXX)) < 0) + return status; + status = getprintcap_int(bp, pp); + free(bp); + return status; +} + +/* + * Map the status values returned by cgetfirst/cgetnext into those + * used by cgetent, returning truth if there are more records to + * examine. This points out what is arguably a bug in the cget* + * interface (or at least a nasty wart). + */ +static int +firstnextmap(int *status) +{ + switch (*status) { + case 0: + return 0; + case 1: + *status = 0; + return 1; + case 2: + *status = 1; + return 1; + case -1: + *status = -2; + return 0; + case -2: + *status = -3; + return 1; + default: + return 0; + } +} + +/* + * Scan through the database of printers using cgetfirst/cgetnext. + * Return false of error or end-of-database; else true. + */ +int +firstprinter(struct printer *pp, int *error) +{ + int status; + char *bp; + + init_printer(pp); + status = cgetfirst(&bp, printcapdb); + if (firstnextmap(&status) == 0) { + if (error) + *error = status; + return 0; + } + if (error) + *error = status; + status = getprintcap_int(bp, pp); + free(bp); + if (error && status) + *error = status; + return 1; +} + +int +nextprinter(struct printer *pp, int *error) +{ + int status; + char *bp; + + free_printer(pp); + status = cgetnext(&bp, printcapdb); + if (firstnextmap(&status) == 0) { + if (error) + *error = status; + return 0; + } + if (error) + *error = status; + status = getprintcap_int(bp, pp); + free(bp); + if (error && status) + *error = status; + return 1; +} + +void +lastprinter(void) +{ + cgetclose(); +} + +/* + * This must match the order of declaration of enum filter in lp.h. + */ +static const char *filters[] = { + "cf", "df", "gf", "if", "nf", "of", "rf", "tf", "vf" +}; + +static const char *longfilters[] = { + "filt.cifplot", "filt.dvi", "filt.plot", "filt.input", "filt.ditroff", + "filt.output", "filt.fortran", "filt.troff", "filt.raster" +}; + +/* + * Internal routine for both getprintcap() and nextprinter(). + * Actually parse the printcap entry using cget* functions. + * Also attempt to figure out the canonical name of the printer + * and store a malloced copy of it in pp->printer. + */ +static int +getprintcap_int(char *bp, struct printer *pp) +{ + enum lpd_filters filt; + char *rp_name; + int error; + + if ((pp->printer = capdb_canonical_name(bp)) == 0) + return PCAPERR_OSERR; + +#define CHK(x) do {if ((x) == PCAPERR_OSERR) return PCAPERR_OSERR;}while(0) + CHK(capdb_getaltstr(bp, "af", "acct.file", 0, &pp->acct_file)); + CHK(capdb_getaltnum(bp, "br", "tty.rate", 0, &pp->baud_rate)); + CHK(capdb_getaltnum(bp, "ct", "remote.timeout", DEFTIMEOUT, + &pp->conn_timeout)); + CHK(capdb_getaltnum(bp, "du", "daemon.user", DEFUID, + &pp->daemon_user)); + CHK(capdb_getaltstr(bp, "ff", "job.formfeed", DEFFF, &pp->form_feed)); + CHK(capdb_getaltstr(bp, "lf", "spool.log", _PATH_CONSOLE, + &pp->log_file)); + CHK(capdb_getaltstr(bp, "lo", "spool.lock", DEFLOCK, &pp->lock_file)); + CHK(capdb_getaltstr(bp, "lp", "tty.device", _PATH_DEFDEVLP, &pp->lp)); + CHK(capdb_getaltnum(bp, "mc", "max.copies", DEFMAXCOPIES, + &pp->max_copies)); + CHK(capdb_getaltstr(bp, "ms", "tty.mode", 0, &pp->mode_set)); + CHK(capdb_getaltnum(bp, "mx", "max.blocks", DEFMX, &pp->max_blocks)); + CHK(capdb_getaltnum(bp, "pc", "acct.price", 0, &pp->price100)); + CHK(capdb_getaltnum(bp, "pl", "page.length", DEFLENGTH, + &pp->page_length)); + CHK(capdb_getaltnum(bp, "pw", "page.width", DEFWIDTH, + &pp->page_width)); + CHK(capdb_getaltnum(bp, "px", "page.pwidth", 0, &pp->page_pwidth)); + CHK(capdb_getaltnum(bp, "py", "page.plength", 0, &pp->page_plength)); + CHK(capdb_getaltstr(bp, "rg", "daemon.restrictgrp", 0, + &pp->restrict_grp)); + CHK(capdb_getaltstr(bp, "rm", "remote.host", 0, &pp->remote_host)); + CHK(capdb_getaltstr(bp, "rp", "remote.queue", DEFLP, + &pp->remote_queue)); + CHK(capdb_getaltstr(bp, "sd", "spool.dir", _PATH_DEFSPOOL, + &pp->spool_dir)); + CHK(capdb_getaltstr(bp, "sr", "stat.recv", 0, &pp->stat_recv)); + CHK(capdb_getaltstr(bp, "ss", "stat.send", 0, &pp->stat_send)); + CHK(capdb_getaltstr(bp, "st", "spool.status", DEFSTAT, + &pp->status_file)); + CHK(capdb_getaltstr(bp, "tr", "job.trailer", 0, &pp->trailer)); + + pp->resend_copies = capdb_getaltlog(bp, "rc", "remote.resend_copies"); + pp->restricted = capdb_getaltlog(bp, "rs", "daemon.restricted"); + pp->short_banner = capdb_getaltlog(bp, "sb", "banner.short"); + pp->no_copies = capdb_getaltlog(bp, "sc", "job.no_copies"); + pp->no_formfeed = capdb_getaltlog(bp, "sf", "job.no_formfeed"); + pp->no_header = capdb_getaltlog(bp, "sh", "banner.disable"); + pp->header_last = capdb_getaltlog(bp, "hl", "banner.last"); + pp->rw = capdb_getaltlog(bp, "rw", "tty.rw"); + pp->tof = !capdb_getaltlog(bp, "fo", "job.topofform"); + + /* + * Decide if the remote printer name matches the local printer name. + * If no name is given then we assume they mean them to match. + * If a name is given see if the rp_name is one of the names for + * this printer. + */ + pp->rp_matches_local = 1; + CHK((error = capdb_getaltstr(bp, "rp", "remote.queue", 0, &rp_name))); + if (error != PCAPERR_NOTFOUND && rp_name != NULL) { + if (cgetmatch(bp,rp_name) != 0) + pp->rp_matches_local = 0; + free(rp_name); + } + + /* + * Filters: + */ + for (filt = 0; filt < LPF_COUNT; filt++) { + CHK(capdb_getaltstr(bp, filters[filt], longfilters[filt], 0, + &pp->filters[filt])); + } + + return 0; +} + +/* + * Decode the error codes returned by cgetent() using the names we + * made up for them from "lp.h". + * This would have been much better done with Common Error, >sigh<. + * Perhaps this can be fixed in the next incarnation of cget*. + */ +const char * +pcaperr(int error) +{ + switch(error) { + case PCAPERR_TCOPEN: + return "unresolved tc= expansion"; + case PCAPERR_SUCCESS: + return "no error"; + case PCAPERR_NOTFOUND: + return "printer not found"; + case PCAPERR_OSERR: + return strerror(errno); + case PCAPERR_TCLOOP: + return "loop detected in tc= expansion"; + default: + return "unknown printcap error"; + } +} + +/* + * Initialize a `struct printer' to contain values harmless to + * the other routines in liblpr. + */ +void +init_printer(struct printer *pp) +{ + static struct printer zero; + *pp = zero; +} + +/* + * Free the dynamically-allocated strings in a `struct printer'. + * Idempotent. + */ +void +free_printer(struct printer *pp) +{ + enum lpd_filters filt; +#define cfree(x) do { if (x) free(x); } while(0) + cfree(pp->printer); + cfree(pp->acct_file); + for (filt = 0; filt < LPF_COUNT; filt++) + cfree(pp->filters[filt]); + cfree(pp->form_feed); + cfree(pp->log_file); + cfree(pp->lock_file); + cfree(pp->lp); + cfree(pp->restrict_grp); + cfree(pp->remote_host); + cfree(pp->remote_queue); + cfree(pp->spool_dir); + cfree(pp->stat_recv); + cfree(pp->stat_send); + cfree(pp->status_file); + cfree(pp->trailer); + cfree(pp->mode_set); + + init_printer(pp); +} + + +/* + * The following routines are part of what would be a sensible library + * interface to capability databases. Maybe someday this will become + * the default. + */ + +/* + * It provides similar functionality to cgetstr(), + * except that it provides for both a long and a short + * capability name and allows for a default to be specified. + */ +static int +capdb_getaltstr(char *bp, const char *shrt, const char *lng, + const char *dflt, char **result) +{ + int status; + + status = cgetstr(bp, (char *)/*XXX*/lng, result); + if (status >= 0 || status == PCAPERR_OSERR) + return status; + status = cgetstr(bp, (char *)/*XXX*/shrt, result); + if (status >= 0 || status == PCAPERR_OSERR) + return status; + if (dflt) { + *result = strdup(dflt); + if (*result == 0) + return PCAPERR_OSERR; + return strlen(*result); + } + return PCAPERR_NOTFOUND; +} + +/* + * The same, only for integers. + */ +static int +capdb_getaltnum(char *bp, const char *shrt, const char *lng, long dflt, + long *result) +{ + int status; + + status = cgetnum(bp, (char *)/*XXX*/lng, result); + if (status >= 0) + return status; + status = cgetnum(bp, (char *)/*XXX*/shrt, result); + if (status >= 0) + return status; + *result = dflt; + return 0; +} + +/* + * Likewise for logical values. There's no need for a default parameter + * because the default is always false. + */ +static int +capdb_getaltlog(char *bp, const char *shrt, const char *lng) +{ + if (cgetcap(bp, (char *)/*XXX*/lng, ':')) + return 1; + if (cgetcap(bp, (char *)/*XXX*/shrt, ':')) + return 1; + return 0; +} + +/* + * Also should be a part of a better cget* library. + * Given a capdb entry, attempt to figure out what its canonical name + * is, and return a malloced copy of it. The canonical name is + * considered to be the first one listed. + */ +static char * +capdb_canonical_name(const char *bp) +{ + char *retval; + const char *nameend; + + nameend = strpbrk(bp, "|:"); + if (nameend == 0) + nameend = bp + 1; + if ((retval = malloc(nameend - bp + 1)) != 0) { + retval[0] = '\0'; + strncat(retval, bp, nameend - bp); + } + return retval; +} + + diff --git a/usr.sbin/lpr/common_source/request.c b/usr.sbin/lpr/common_source/request.c new file mode 100644 index 0000000..b650e5c --- /dev/null +++ b/usr.sbin/lpr/common_source/request.c @@ -0,0 +1,81 @@ +/* + * Copyright 1997 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. + */ + +static const char copyright[] = + "Copyright (C) 1997, Massachusetts Institute of Technology\r\n"; + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <sys/param.h> /* needed for lp.h but not used here */ +#include <dirent.h> /* ditto */ +#include <stdio.h> /* ditto */ +#include "lp.h" +#include "lp.local.h" + +void +init_request(struct request *rp) +{ + static struct request zero; + + *rp = zero; + TAILQ_INIT(&rp->users); + TAILQ_INIT(&rp->jobids); +} + +void +free_request(struct request *rp) +{ + struct req_user *ru; + struct req_jobid *rj; + + if (rp->logname) + free(rp->logname); + if (rp->authname) + free(rp->authname); + if (rp->prettyname) + free(rp->prettyname); + if (rp->authinfo) + free(rp->authinfo); + while ((ru = TAILQ_FIRST(&rp->users)) != 0) { + TAILQ_REMOVE(&rp->users, ru, ru_link); + free(ru); + } + while ((rj = TAILQ_FIRST(&rp->jobids)) != 0) { + TAILQ_REMOVE(&rp->jobids, rj, rj_link); + free(rj); + } + init_request(rp); +} diff --git a/usr.sbin/lpr/common_source/rmjob.c b/usr.sbin/lpr/common_source/rmjob.c new file mode 100644 index 0000000..c89a6f0 --- /dev/null +++ b/usr.sbin/lpr/common_source/rmjob.c @@ -0,0 +1,392 @@ +/* + * 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. + * 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. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)rmjob.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ +#endif + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/uio.h> + +#include <ctype.h> +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#define psignal foil_gcc_psignal +#define sys_siglist foil_gcc_siglist +#include <unistd.h> +#undef psignal +#undef sys_siglist + +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" + +/* + * rmjob - remove the specified jobs from the queue. + */ + +/* + * Stuff for handling lprm specifications + */ +static char root[] = "root"; +static int all = 0; /* eliminate all files (root only) */ +static int cur_daemon; /* daemon's pid */ +static char current[7+MAXHOSTNAMELEN]; /* active control file name */ + +static void alarmhandler(int _signo); +static void do_unlink(char *_file); +static int isowner(char *_owner, char *_file, const char *_cfhost); + +void +rmjob(const char *printer) +{ + register int i, nitems; + int assassinated = 0; + struct dirent **files; + char *cp; + struct printer myprinter, *pp = &myprinter; + + init_printer(pp); + if ((i = getprintcap(printer, pp)) < 0) + fatal(pp, "getprintcap: %s", pcaperr(i)); + if ((cp = checkremote(pp))) { + printf("Warning: %s\n", cp); + free(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 == local_host) + fatal(pp, "The login name \"-all\" is reserved"); + all = 1; /* all those from 'from_host' */ + person = root; + } + + PRIV_START + if (chdir(pp->spool_dir) < 0) + fatal(pp, "cannot chdir to spool directory"); + if ((nitems = scandir(".", &files, iscf, NULL)) < 0) + fatal(pp, "cannot access spool directory"); + PRIV_END + + 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(pp, pp->lock_file) && chk(current)) { + PRIV_START + assassinated = kill(cur_daemon, SIGINT) == 0; + PRIV_END + if (!assassinated) + fatal(pp, "cannot kill printer daemon"); + } + /* + * process the files + */ + for (i = 0; i < nitems; i++) + process(pp, files[i]->d_name); + } + rmremote(pp); + /* + * Restart the printer daemon if it was killed + */ + if (assassinated && !startdaemon(pp)) + fatal(pp, "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(struct printer *pp, char *slockf) +{ + register FILE *fp; + register int i, n; + + PRIV_START + if ((fp = fopen(slockf, "r")) == NULL) { + if (errno == EACCES) + fatal(pp, "%s: %s", slockf, strerror(errno)); + else + return(0); + } + PRIV_END + if (!getline(fp)) { + (void) fclose(fp); + return(0); /* no daemon present */ + } + cur_daemon = atoi(line); + if (kill(cur_daemon, 0) < 0 && errno != EPERM) { + (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(const struct printer *pp, char *file) +{ + FILE *cfp; + + if (!chk(file)) + return; + PRIV_START + if ((cfp = fopen(file, "r")) == NULL) + fatal(pp, "cannot open %s", file); + PRIV_END + while (getline(cfp)) { + switch (line[0]) { + case 'U': /* unlink associated files */ + if (strchr(line+1, '/') || strncmp(line+1, "df", 2)) + break; + do_unlink(line+1); + } + } + (void) fclose(cfp); + do_unlink(file); +} + +static void +do_unlink(char *file) +{ + int ret; + + if (from_host != local_host) + printf("%s: ", local_host); + PRIV_START + ret = unlink(file); + PRIV_END + printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file); +} + +/* + * Do the dirty work in checking + */ +int +chk(char *file) +{ + int *r, jnum; + char **u; + const char *cfhost; + FILE *cfp; + + /* + * Check for valid cf file name (mostly checking current). + */ + if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f') + return(0); + + jnum = calc_jobnum(file, &cfhost); + if (all && (from_host == local_host || !strcmp(from_host, cfhost))) + return(1); + + /* + * get the owner's name from the control file. + */ + PRIV_START + if ((cfp = fopen(file, "r")) == NULL) + return(0); + PRIV_END + 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, cfhost)); + /* + * Check the request list + */ + for (r = requ; r < &requ[requests]; r++) + if (*r == jnum && isowner(line+1, file, cfhost)) + 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, cfhost)) + 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. + */ +static int +isowner(char *owner, char *file, const char *cfhost) +{ + if (!strcmp(person, root) && (from_host == local_host || + !strcmp(from_host, cfhost))) + return (1); + if (!strcmp(person, owner) && !strcmp(from_host, cfhost)) + return (1); + if (from_host != local_host) + printf("%s: ", local_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(const struct printer *pp) +{ + int i, elem, firstreq, niov, rem, totlen; + char buf[BUFSIZ]; + void (*savealrm)(int); + struct iovec *iov; + + if (!pp->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); + + /* + * Counting: + * 4 == "\5" + remote_queue + " " + person + * 2 * users == " " + user[i] for each user + * requests == asprintf results for each request + * 1 == "\n" + * Although laborious, doing it this way makes it possible for + * us to process requests of indeterminate length without + * applying an arbitrary limit. Arbitrary Limits Are Bad (tm). + */ + if (users > 0) + niov = 4 + 2 * users + requests + 1; + else + niov = 4 + requests + 1; + iov = malloc(niov * sizeof *iov); + if (iov == 0) + fatal(pp, "out of memory in rmremote()"); + iov[0].iov_base = "\5"; + iov[1].iov_base = pp->remote_queue; + iov[2].iov_base = " "; + iov[3].iov_base = all ? "-all" : person; + elem = 4; + for (i = 0; i < users; i++) { + iov[elem].iov_base = " "; + iov[elem + 1].iov_base = user[i]; + elem += 2; + } + firstreq = elem; + for (i = 0; i < requests; i++) { + asprintf((char **)&iov[elem].iov_base, " %d", requ[i]); + if (iov[elem].iov_base == 0) + fatal(pp, "out of memory in rmremote()"); + elem++; + } + iov[elem++].iov_base = "\n"; + for (totlen = i = 0; i < niov; i++) + totlen += (iov[i].iov_len = strlen(iov[i].iov_base)); + + savealrm = signal(SIGALRM, alarmhandler); + alarm(pp->conn_timeout); + rem = getport(pp, pp->remote_host, 0); + (void)signal(SIGALRM, savealrm); + if (rem < 0) { + if (from_host != local_host) + printf("%s: ", local_host); + printf("connection to %s is down\n", pp->remote_host); + } else { + if (writev(rem, iov, niov) != totlen) + fatal(pp, "Lost connection"); + while ((i = read(rem, buf, sizeof(buf))) > 0) + (void) fwrite(buf, 1, i, stdout); + (void) close(rem); + } + for (i = 0; i < requests; i++) + free(iov[firstreq + i].iov_base); + free(iov); +} + +/* + * Return 1 if the filename begins with 'cf' + */ +int +iscf(const struct dirent *d) +{ + return(d->d_name[0] == 'c' && d->d_name[1] == 'f'); +} + +void +alarmhandler(int signo __unused) +{ + /* the signal is ignored */ + /* (the '__unused' is just to avoid a compile-time warning) */ +} diff --git a/usr.sbin/lpr/common_source/startdaemon.c b/usr.sbin/lpr/common_source/startdaemon.c new file mode 100644 index 0000000..8e4f60a --- /dev/null +++ b/usr.sbin/lpr/common_source/startdaemon.c @@ -0,0 +1,105 @@ +/* + * 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. + * 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. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)startdaemon.c 8.2 (Berkeley) 4/17/94"; +#endif /* not lint */ +#endif + +#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/un.h> + +#include <dirent.h> +#include <err.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "lp.h" +#include "pathnames.h" + +/* + * Tell the printer daemon that there are new files in the spool directory. + */ + +int +startdaemon(const struct printer *pp) +{ + struct sockaddr_un un; + register int s, n; + int connectres; + char c; + + s = socket(PF_LOCAL, SOCK_STREAM, 0); + if (s < 0) { + warn("socket"); + return(0); + } + memset(&un, 0, sizeof(un)); + un.sun_family = AF_LOCAL; + strcpy(un.sun_path, _PATH_SOCKETNAME); +#ifndef SUN_LEN +#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) +#endif + PRIV_START + connectres = connect(s, (struct sockaddr *)&un, SUN_LEN(&un)); + PRIV_END + if (connectres < 0) { + warn("Unable to connect to %s", _PATH_SOCKETNAME); + warnx("Check to see if the master 'lpd' process is running."); + (void) close(s); + return(0); + } + + /* + * Avoid overruns without putting artificial limitations on + * the length. + */ + if (writel(s, "\1", pp->printer, "\n", (char *)0) <= 0) { + warn("write"); + (void) close(s); + return(0); + } + if (read(s, &c, 1) == 1) { + if (c == '\0') { /* everything is OK */ + (void) close(s); + return(1); + } + putchar(c); + } + while ((n = read(s, &c, 1)) > 0) + putchar(c); + (void) close(s); + return(0); +} |