summaryrefslogtreecommitdiffstats
path: root/usr.sbin/lpr/common_source
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/lpr/common_source')
-rw-r--r--usr.sbin/lpr/common_source/Makefile15
-rw-r--r--usr.sbin/lpr/common_source/Makefile.depend14
-rw-r--r--usr.sbin/lpr/common_source/common.c778
-rw-r--r--usr.sbin/lpr/common_source/ctlinfo.c914
-rw-r--r--usr.sbin/lpr/common_source/ctlinfo.h73
-rw-r--r--usr.sbin/lpr/common_source/displayq.c636
-rw-r--r--usr.sbin/lpr/common_source/lp.cdefs.h122
-rw-r--r--usr.sbin/lpr/common_source/lp.h317
-rw-r--r--usr.sbin/lpr/common_source/lp.local.h78
-rw-r--r--usr.sbin/lpr/common_source/matchjobs.c568
-rw-r--r--usr.sbin/lpr/common_source/matchjobs.h102
-rw-r--r--usr.sbin/lpr/common_source/net.c298
-rw-r--r--usr.sbin/lpr/common_source/pathnames.h49
-rw-r--r--usr.sbin/lpr/common_source/printcap.c451
-rw-r--r--usr.sbin/lpr/common_source/request.c81
-rw-r--r--usr.sbin/lpr/common_source/rmjob.c392
-rw-r--r--usr.sbin/lpr/common_source/startdaemon.c105
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);
+}
OpenPOWER on IntegriCloud