xref: /openbsd-src/usr.sbin/lpd/lp.c (revision 3a50f0a93a2072911d0ba6ababa815fb04bf9a71)
1*3a50f0a9Sjmc /*	$OpenBSD: lp.c,v 1.4 2022/12/28 21:30:17 jmc Exp $	*/
23b188dabSeric 
33b188dabSeric /*
43b188dabSeric  * Copyright (c) 2017 Eric Faurot <eric@openbsd.org>
53b188dabSeric  *
63b188dabSeric  * Permission to use, copy, modify, and distribute this software for any
73b188dabSeric  * purpose with or without fee is hereby granted, provided that the above
83b188dabSeric  * copyright notice and this permission notice appear in all copies.
93b188dabSeric  *
103b188dabSeric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113b188dabSeric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123b188dabSeric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133b188dabSeric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143b188dabSeric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153b188dabSeric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163b188dabSeric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173b188dabSeric  */
183b188dabSeric 
193b188dabSeric #include <sys/types.h>
203b188dabSeric #include <sys/mount.h>
213b188dabSeric #include <sys/stat.h>
223b188dabSeric 
233b188dabSeric #include <ctype.h>
243b188dabSeric #include <dirent.h>
253b188dabSeric #include <errno.h>
263b188dabSeric #include <fcntl.h>
273b188dabSeric #include <limits.h>
283b188dabSeric #include <signal.h>
293b188dabSeric #include <stdarg.h>
303b188dabSeric #include <stdlib.h>
313b188dabSeric #include <string.h>
323b188dabSeric #include <unistd.h>
333b188dabSeric 
343b188dabSeric #include "lp.h"
353b188dabSeric 
363b188dabSeric #include "log.h"
373b188dabSeric 
383b188dabSeric #define XXX_PID_MAX 99999
393b188dabSeric 
403b188dabSeric struct qentry {
413b188dabSeric 	char *name;
423b188dabSeric 	time_t mtime;
433b188dabSeric };
443b188dabSeric 
453b188dabSeric static int readent(struct lp_printer *, char *);
463b188dabSeric static int qentrycmp(const void *, const void *);
473b188dabSeric static int fullpath(struct lp_printer *, const char *, char *, size_t);
483b188dabSeric static int checksize(struct lp_printer *, size_t);
493b188dabSeric 
503b188dabSeric static int scanning;
513b188dabSeric static char *db[] = {
523b188dabSeric 	_PATH_PRINTCAP,
533b188dabSeric 	NULL,
543b188dabSeric };
553b188dabSeric 
563b188dabSeric /*
573b188dabSeric  * Fill a printer structure from its /etc/printcap entry.
583b188dabSeric  * Return 0 on success, or -1 on error.
593b188dabSeric  */
603b188dabSeric int
lp_getprinter(struct lp_printer * lp,const char * name)613b188dabSeric lp_getprinter(struct lp_printer *lp, const char *name)
623b188dabSeric {
633b188dabSeric 	char *buf = NULL;
643b188dabSeric 	int r;
653b188dabSeric 
663b188dabSeric 	memset(lp, 0, sizeof(*lp));
673b188dabSeric 
683b188dabSeric 	r = cgetent(&buf, db, name);
693b188dabSeric 	if (r == 0)
703b188dabSeric 		r = readent(lp, buf);
713b188dabSeric 	free(buf);
723b188dabSeric 
733b188dabSeric 	switch (r) {
743b188dabSeric 	case -3:
753b188dabSeric 		log_warnx("%s: potential reference loop", name);
763b188dabSeric 		break;
773b188dabSeric 	case -2:
783b188dabSeric 		log_warn("%s", name);
793b188dabSeric 		break;
803b188dabSeric 	case -1:
813b188dabSeric 		log_warnx("%s: unknown printer", name);
823b188dabSeric 		break;
833b188dabSeric 	case 0:
843b188dabSeric 		return 0;
853b188dabSeric 	case 1:
863b188dabSeric 		log_warnx("%s: unresolved tc expansion", name);
873b188dabSeric 		break;
883b188dabSeric 	default:
893b188dabSeric 		log_warnx("%s: unexpected return value %d", name, r);
903b188dabSeric 	}
913b188dabSeric 
923b188dabSeric 	lp_clearprinter(lp);
933b188dabSeric 	return -1;
943b188dabSeric }
953b188dabSeric 
963b188dabSeric /*
973b188dabSeric  * Iterate over /etc/printcap and fill the printer structure from the next
983b188dabSeric  * entry, if any.
993b188dabSeric  *
1003b188dabSeric  * Return 0 if no entry is found.
1013b188dabSeric  *        1 if a printer is filled.
1023b188dabSeric  *       -1 on error, and set errno.
1033b188dabSeric  */
1043b188dabSeric int
lp_scanprinters(struct lp_printer * lp)1053b188dabSeric lp_scanprinters(struct lp_printer *lp)
1063b188dabSeric {
1073b188dabSeric 	char *buf;
1083b188dabSeric 	int r, saved_errno;
1093b188dabSeric 
1103b188dabSeric 	if (scanning++ == 0)
1113b188dabSeric 		r = cgetfirst(&buf, db);
1123b188dabSeric 	else
1133b188dabSeric 		r = cgetnext(&buf, db);
1143b188dabSeric 
1153b188dabSeric 	if (r == 0) {
1163b188dabSeric 		cgetclose();
1173b188dabSeric 		scanning = 0;
1183b188dabSeric 		return 0;
119b00bff5eSmestre 	} else if (r == 1) {
1203b188dabSeric 		memset(lp, 0, sizeof(*lp));
1213b188dabSeric 		r = readent(lp, buf);
1223b188dabSeric 		free(buf);
1233b188dabSeric 		if (r == -2)
1243b188dabSeric 			goto fail;
1253b188dabSeric 		return 1;
126b00bff5eSmestre 	} else if (r == -1)
127b00bff5eSmestre 		fatal("cannot open %s", _PATH_PRINTCAP);
1283b188dabSeric 	else if (r == -2)
1293b188dabSeric 		errno = ELOOP; /* potential reference loop */
1303b188dabSeric 
1313b188dabSeric     fail:
1323b188dabSeric 	saved_errno = errno;
1333b188dabSeric 	lp_clearprinter(lp);
1343b188dabSeric 	cgetclose();
1353b188dabSeric 	scanning = 0;
1363b188dabSeric 	errno = saved_errno;
1373b188dabSeric 	return -1;
1383b188dabSeric }
1393b188dabSeric 
1403b188dabSeric static int
readent(struct lp_printer * lp,char * buf)1413b188dabSeric readent(struct lp_printer *lp, char *buf)
1423b188dabSeric {
1433b188dabSeric 	char *s;
1443b188dabSeric 
1453b188dabSeric 	s = strchr(buf, ':');
1463b188dabSeric 	if (s)
1473b188dabSeric 		*s = '\0';
1483b188dabSeric 	lp->lp_name = strdup(buf);
1493b188dabSeric 	if (s)
1503b188dabSeric 		*s = ':';
1513b188dabSeric 	if (lp->lp_name == NULL)
1523b188dabSeric 		return -2;
1533b188dabSeric 
1543b188dabSeric 	s = lp->lp_name;
1553b188dabSeric 	while ((s = strchr(s, '|'))) {
1563b188dabSeric 		*s++ = '\0';
1573b188dabSeric 		if (lp->lp_aliascount < LP_MAXALIASES)
1583b188dabSeric 			lp->lp_aliases[lp->lp_aliascount++] = s;
1593b188dabSeric 	}
1603b188dabSeric 
1613b188dabSeric #define CGETSTR(x)	if (cgetstr(buf, #x, &(lp->lp_ ## x)) == -2) \
1623b188dabSeric 				goto fail
1633b188dabSeric #define CGETNUM(x, d)	if (cgetnum(buf, #x, &(lp->lp_ ## x)) == -1) \
1643b188dabSeric 				(lp->lp_ ## x) = d;
1653b188dabSeric #define CGETBOOL(x) lp->lp_ ## x = cgetcap(buf, #x, ':') ? 1 : 0
1663b188dabSeric 
1673b188dabSeric 	CGETSTR(af);
1683b188dabSeric 	CGETNUM(br, 0);
1693b188dabSeric 	CGETSTR(cf);
1703b188dabSeric 	CGETSTR(df);
1713b188dabSeric 	CGETSTR(ff);
1723b188dabSeric 	CGETBOOL(fo);
1733b188dabSeric 	CGETSTR(gf);
1743b188dabSeric 	CGETBOOL(hl);
1753b188dabSeric 	CGETBOOL(ic);
1763b188dabSeric 	CGETSTR(if);
1773b188dabSeric 	CGETSTR(lf);
1783b188dabSeric 	CGETSTR(lo);
1793b188dabSeric 	CGETSTR(lp);
1803b188dabSeric 	CGETNUM(mc, 0);
1813b188dabSeric 	CGETSTR(ms);
1823b188dabSeric 	CGETNUM(mx, 0);
1833b188dabSeric 	CGETSTR(nd);
1843b188dabSeric 	CGETSTR(nf);
1853b188dabSeric 	CGETSTR(of);
1863b188dabSeric 	CGETNUM(pc, DEFAULT_PC);
1873b188dabSeric 	CGETNUM(pl, DEFAULT_PL);
1883b188dabSeric 	CGETNUM(pw, DEFAULT_PW);
1893b188dabSeric 	CGETNUM(px, 0);
1903b188dabSeric 	CGETNUM(py, 0);
1913b188dabSeric 	CGETSTR(rf);
1923b188dabSeric 	CGETSTR(rg);
1933b188dabSeric 	CGETSTR(rm);
1943b188dabSeric 	CGETSTR(rp);
1953b188dabSeric 	CGETBOOL(rs);
1963b188dabSeric 	CGETBOOL(rw);
1973b188dabSeric 	CGETBOOL(sb);
1983b188dabSeric 	CGETBOOL(sc);
1993b188dabSeric 	CGETSTR(sd);
2003b188dabSeric 	CGETBOOL(sf);
2013b188dabSeric 	CGETBOOL(sh);
2023b188dabSeric 	CGETSTR(st);
2033b188dabSeric 	CGETSTR(tf);
2043b188dabSeric 	CGETSTR(tr);
2053b188dabSeric 	CGETSTR(vf);
2063b188dabSeric 
2073b188dabSeric 	if (lp->lp_rm && lp->lp_rm[0]) {
2083b188dabSeric 		lp->lp_type = PRN_LPR;
2093b188dabSeric 		lp->lp_host = lp->lp_rm;
2103b188dabSeric 		lp->lp_port = NULL;
2113b188dabSeric 	}
2123b188dabSeric 	else if (strchr(LP_LP(lp), '@')) {
2133b188dabSeric 		lp->lp_type = PRN_NET;
2143b188dabSeric 		lp->lp_port = strdup(LP_LP(lp));
2153b188dabSeric 		lp->lp_host = strchr(lp->lp_port, '@');
2163b188dabSeric 		*lp->lp_host++ = '\0';
2173b188dabSeric 	}
2183b188dabSeric 	else {
2193b188dabSeric 		lp->lp_type = PRN_LOCAL;
2203b188dabSeric 	}
2213b188dabSeric 
2223b188dabSeric 	return 0;
2233b188dabSeric     fail:
2243b188dabSeric 	return -2;
2253b188dabSeric }
2263b188dabSeric 
2273b188dabSeric void
lp_clearprinter(struct lp_printer * lp)2283b188dabSeric lp_clearprinter(struct lp_printer *lp)
2293b188dabSeric {
2303b188dabSeric 	free(lp->lp_name);
2313b188dabSeric 	free(lp->lp_port);
2323b188dabSeric 	if (lp->lp_lock)
2333b188dabSeric 		fclose(lp->lp_lock);
2343b188dabSeric 	free(lp->lp_af);
2353b188dabSeric 	free(lp->lp_cf);
2363b188dabSeric 	free(lp->lp_df);
2373b188dabSeric 	free(lp->lp_ff);
2383b188dabSeric 	free(lp->lp_gf);
2393b188dabSeric 	free(lp->lp_if);
2403b188dabSeric 	free(lp->lp_lf);
2413b188dabSeric 	free(lp->lp_lo);
2423b188dabSeric 	free(lp->lp_lp);
2433b188dabSeric 	free(lp->lp_ms);
2443b188dabSeric 	free(lp->lp_nd);
2453b188dabSeric 	free(lp->lp_nf);
2463b188dabSeric 	free(lp->lp_of);
2473b188dabSeric 	free(lp->lp_rf);
2483b188dabSeric 	free(lp->lp_rg);
2493b188dabSeric 	free(lp->lp_rm);
2503b188dabSeric 	free(lp->lp_rp);
2513b188dabSeric 	free(lp->lp_sd);
2523b188dabSeric 	free(lp->lp_st);
2533b188dabSeric 	free(lp->lp_tf);
2543b188dabSeric 	free(lp->lp_tr);
2553b188dabSeric 	free(lp->lp_vf);
2563b188dabSeric 	memset(lp, 0, sizeof(*lp));
2573b188dabSeric }
2583b188dabSeric 
2593b188dabSeric static int
qentrycmp(const void * aa,const void * bb)2603b188dabSeric qentrycmp(const void *aa, const void *bb)
2613b188dabSeric {
2623b188dabSeric 	const struct qentry *a = aa, *b = bb;
2633b188dabSeric 
2643b188dabSeric 	if (a->mtime < b->mtime)
2653b188dabSeric 		return -1;
2663b188dabSeric 	if (a->mtime > b->mtime)
2673b188dabSeric 		return 1;
2683b188dabSeric 
2693b188dabSeric 	return strcmp(a->name, b->name);
2703b188dabSeric }
2713b188dabSeric 
2723b188dabSeric /*
2733b188dabSeric  * Read the printer queue content.
2743b188dabSeric  * Return the task count, or -1 on error.
2753b188dabSeric  */
2763b188dabSeric int
lp_readqueue(struct lp_printer * lp,struct lp_queue * q)2773b188dabSeric lp_readqueue(struct lp_printer *lp, struct lp_queue *q)
2783b188dabSeric {
2793b188dabSeric 	struct qentry *qe = NULL, *tqe;
2803b188dabSeric 	struct dirent *d;
2813b188dabSeric 	struct stat st;
2823b188dabSeric 	size_t len, sz, nqi, nqe = 0, nqa = 0, n, i;
2833b188dabSeric 	char path[PATH_MAX], *end;
2843b188dabSeric 	DIR *dp= NULL;
2853b188dabSeric 
2863b188dabSeric 	len = strlcpy(path, LP_SD(lp), sizeof(path));
2873b188dabSeric 	if (len == 0 || len >= sizeof(path)) {
2883b188dabSeric 		log_warn("%s: %s: invalid spool directory name",
2893b188dabSeric 		    __func__, LP_SD(lp));
2903b188dabSeric 		goto fail;
2913b188dabSeric 	}
2923b188dabSeric 
2933b188dabSeric 	if ((dp = opendir(path)) == NULL) {
2943b188dabSeric 		log_warn("%s: opendir", __func__);
2953b188dabSeric 		goto fail;
2963b188dabSeric 	}
2973b188dabSeric 
2983b188dabSeric 	if (fstat(dirfd(dp), &st) == -1) {
2993b188dabSeric 		log_warn("%s: fstat", __func__);
3003b188dabSeric 		goto fail;
3013b188dabSeric 	}
3023b188dabSeric 	/* Assume half the files are cf files. */
3033b188dabSeric 	nqi = st.st_nlink / 2;
3043b188dabSeric 
3053b188dabSeric 	if (path[len-1] != '/') {
3063b188dabSeric 		len = strlcat(path, "/", sizeof(path));
3073b188dabSeric 		if (len >= sizeof(path)) {
3083b188dabSeric 			errno = ENAMETOOLONG;
3093b188dabSeric 			log_warn("%s: strlcat", __func__);
3103b188dabSeric 			goto fail;
3113b188dabSeric 		}
3123b188dabSeric 	}
3133b188dabSeric 	end = path + len;
3143b188dabSeric 	sz = sizeof(path) - len;
3153b188dabSeric 
3163b188dabSeric 	errno = 0;
3173b188dabSeric 	while ((d = readdir(dp))) {
3183b188dabSeric 		if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
3193b188dabSeric 			continue;
3203b188dabSeric 
3213b188dabSeric 		if (strlen(d->d_name) < 7)
3223b188dabSeric 			continue;
3233b188dabSeric 
324895f7d11Sbenno 		if (!isdigit((unsigned char)d->d_name[3]) ||
325895f7d11Sbenno 		    !isdigit((unsigned char)d->d_name[4]) ||
326895f7d11Sbenno 		    !isdigit((unsigned char)d->d_name[5]))
3273b188dabSeric 			continue;
3283b188dabSeric 
3293b188dabSeric 		if (strlcpy(end, d->d_name, sz) >= sz) {
3303b188dabSeric 			errno = ENAMETOOLONG;
3313b188dabSeric 			log_warn("%s: strlcat", __func__);
3323b188dabSeric 			goto fail;
3333b188dabSeric 		}
3343b188dabSeric 
3353b188dabSeric 		if (stat(path, &st) == -1) {
3363b188dabSeric 			log_warn("%s: stat", __func__);
3373b188dabSeric 			goto fail;
3383b188dabSeric 		}
3393b188dabSeric 
3403b188dabSeric 		if (nqe == nqa) {
3413b188dabSeric 			n = nqa ? (nqa + 5) : nqi;
3423b188dabSeric 			tqe = recallocarray(qe, nqa, n, sizeof(*qe));
3433b188dabSeric 			if (tqe == NULL) {
3443b188dabSeric 				log_warn("%s: recallocarray", __func__);
3453b188dabSeric 				goto fail;
3463b188dabSeric 			}
3473b188dabSeric 			qe = tqe;
3483b188dabSeric 			nqa = n;
3493b188dabSeric 		}
3503b188dabSeric 
3513b188dabSeric 		if ((qe[nqe].name = strdup(d->d_name)) == NULL) {
3523b188dabSeric 			log_warn("%s: strdup", __func__);
3533b188dabSeric 			goto fail;
3543b188dabSeric 		}
3553b188dabSeric 		qe[nqe].mtime = st.st_mtime;
3563b188dabSeric 		nqe++;
3573b188dabSeric 	}
3583b188dabSeric 	if (errno) {
3593b188dabSeric 		log_warn("%s: readdir", __func__);
3603b188dabSeric 		goto fail;
3613b188dabSeric 	}
3623b188dabSeric 	closedir(dp);
3633b188dabSeric 	dp = NULL;
3643b188dabSeric 
3653b188dabSeric 	qsort(qe, nqe, sizeof(*qe), qentrycmp);
3663b188dabSeric 
3673b188dabSeric 	q->count = nqe;
3683b188dabSeric 	q->cfname = calloc(nqe, sizeof(*q->cfname));
3693b188dabSeric 	if (q->cfname == NULL) {
3703b188dabSeric 		log_warn("%s: calloc", __func__);
3713b188dabSeric 		goto fail;
3723b188dabSeric 	}
3733b188dabSeric 	for (i = 0; i < nqe; i++)
3743b188dabSeric 		q->cfname[i] = qe[i].name;
3753b188dabSeric 
3763b188dabSeric 	free(qe);
3773b188dabSeric 	return nqe;
3783b188dabSeric 
3793b188dabSeric     fail:
3803b188dabSeric 	if (dp)
3813b188dabSeric 		closedir(dp);
3823b188dabSeric 	for (i = 0; i < nqe; i++)
3833b188dabSeric 		free(qe[i].name);
3843b188dabSeric 	free(qe);
3853b188dabSeric 	lp_clearqueue(q);
3863b188dabSeric 	return -1;
3873b188dabSeric }
3883b188dabSeric 
3893b188dabSeric void
lp_clearqueue(struct lp_queue * q)3903b188dabSeric lp_clearqueue(struct lp_queue *q)
3913b188dabSeric {
3923b188dabSeric 	int i;
3933b188dabSeric 
3943b188dabSeric 	for (i = 0; i < q->count; i++)
3953b188dabSeric 		free(q->cfname[i]);
3963b188dabSeric 	free(q->cfname);
3973b188dabSeric }
3983b188dabSeric 
3993b188dabSeric static int
fullpath(struct lp_printer * lp,const char * fname,char * dst,size_t sz)4003b188dabSeric fullpath(struct lp_printer *lp, const char *fname, char *dst, size_t sz)
4013b188dabSeric {
4023b188dabSeric 	int r;
4033b188dabSeric 
4043b188dabSeric 	r = snprintf(dst, sz, "%s/%s", LP_SD(lp), fname);
4053b188dabSeric 	if (r < 0 || (size_t)r >= sz) {
4063b188dabSeric 		errno = ENAMETOOLONG;
4073b188dabSeric 		return -1;
4083b188dabSeric 	}
4093b188dabSeric 
4103b188dabSeric 	return 0;
4113b188dabSeric }
4123b188dabSeric 
4133b188dabSeric /*
4143b188dabSeric  * fopen(3) a file in the printer spooldir for reading.
4153b188dabSeric  */
4163b188dabSeric FILE *
lp_fopen(struct lp_printer * lp,const char * fname)4173b188dabSeric lp_fopen(struct lp_printer *lp, const char *fname)
4183b188dabSeric {
4193b188dabSeric 	char path[PATH_MAX];
4203b188dabSeric 
4213b188dabSeric 	if (fullpath(lp, fname, path, sizeof(path)) == -1)
4223b188dabSeric 		return NULL;
4233b188dabSeric 
4243b188dabSeric 	return fopen(path, "r");
4253b188dabSeric }
4263b188dabSeric 
4273b188dabSeric /*
4283b188dabSeric  * unlink(2) a file in the printer spooldir.
4293b188dabSeric  */
4303b188dabSeric int
lp_unlink(struct lp_printer * lp,const char * fname)4313b188dabSeric lp_unlink(struct lp_printer *lp, const char *fname)
4323b188dabSeric {
4333b188dabSeric 	char path[PATH_MAX];
4343b188dabSeric 
4353b188dabSeric 	if (fullpath(lp, fname, path, sizeof(path)) == -1)
4363b188dabSeric 		return -1;
4373b188dabSeric 
4383b188dabSeric 	return unlink(path);
4393b188dabSeric }
4403b188dabSeric 
4413b188dabSeric /*
4423b188dabSeric  * stat(2) a file in the printer spooldir.
4433b188dabSeric  */
4443b188dabSeric int
lp_stat(struct lp_printer * lp,const char * fname,struct stat * st)4453b188dabSeric lp_stat(struct lp_printer *lp, const char *fname, struct stat *st)
4463b188dabSeric {
4473b188dabSeric 	char path[PATH_MAX];
4483b188dabSeric 
4493b188dabSeric 	if (fullpath(lp, fname, path, sizeof(path)) == -1)
4503b188dabSeric 		return -1;
4513b188dabSeric 
4523b188dabSeric 	return stat(path, st);
4533b188dabSeric }
4543b188dabSeric 
4553b188dabSeric /*
4563b188dabSeric  * Grab the lockfile for this printer, and associate it to the printer.
4573b188dabSeric  * Return -1 on error or 0 otherwise.
4583b188dabSeric  */
4593b188dabSeric int
lp_lock(struct lp_printer * lp)4603b188dabSeric lp_lock(struct lp_printer *lp)
4613b188dabSeric {
4623b188dabSeric 	char path[PATH_MAX];
4633b188dabSeric 	struct stat st;
4643b188dabSeric         int fd, saved_errno;
4653b188dabSeric 
4663b188dabSeric 	if (lp->lp_lock) {
4673b188dabSeric 		errno = EWOULDBLOCK;
4683b188dabSeric 		return -1;
4693b188dabSeric 	}
4703b188dabSeric 
4713b188dabSeric 	if (fullpath(lp, LP_LO(lp), path, sizeof(path)) == -1) {
4723b188dabSeric 		log_warn("%s: %s", __func__, LP_LO(lp));
4733b188dabSeric 		return -1;
4743b188dabSeric 	}
4753b188dabSeric 
4763b188dabSeric         fd = open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_NONBLOCK|O_EXLOCK|O_TRUNC,
4773b188dabSeric 	    0640);
4783b188dabSeric 	if (fd == -1) {
4793b188dabSeric 		if (errno != EWOULDBLOCK)
4803b188dabSeric 			log_warn("%s: open", __func__);
4813b188dabSeric 		return -1;
4823b188dabSeric 	}
4833b188dabSeric 
4843b188dabSeric 	if (fstat(fd, &st) == -1) {
4853b188dabSeric 		log_warn("%s: fstat", __func__);
4863b188dabSeric 		saved_errno = errno;
4873b188dabSeric 		(void)close(fd);
4883b188dabSeric 		errno = saved_errno;
4893b188dabSeric 		return -1;
4903b188dabSeric         }
4913b188dabSeric 
4923b188dabSeric 	if (!S_ISREG(st.st_mode)) {
4933b188dabSeric 		errno = EFTYPE;
4943b188dabSeric 		log_warnx("%s: %s: Not a regular file", __func__, path);
4953b188dabSeric 		(void)close(fd);
4963b188dabSeric 		return -1;
4973b188dabSeric 	}
4983b188dabSeric 
4993b188dabSeric 	if ((lp->lp_lock = fdopen(fd, "w")) == NULL) {
5003b188dabSeric 		log_warn("%s: fdopen", __func__);
5013b188dabSeric 		saved_errno = errno;
5023b188dabSeric 		(void)close(fd);
5033b188dabSeric 		errno = saved_errno;
5043b188dabSeric 		return -1;
5053b188dabSeric 	}
5063b188dabSeric 
5073b188dabSeric 	lp_setcurrtask(lp, NULL);
5083b188dabSeric 
5093b188dabSeric 	return 0;
5103b188dabSeric }
5113b188dabSeric 
5123b188dabSeric /*
5133b188dabSeric  * Truncate the lock file and close it.
5143b188dabSeric  */
5153b188dabSeric void
lp_unlock(struct lp_printer * lp)5163b188dabSeric lp_unlock(struct lp_printer *lp)
5173b188dabSeric {
5183b188dabSeric 	if (ftruncate(fileno(lp->lp_lock), 0) == -1)
5193b188dabSeric 		log_warn("%s: ftruncate", __func__);
5203b188dabSeric 	if (fclose(lp->lp_lock) == EOF)
5213b188dabSeric 		log_warn("%s: fclose", __func__);
5223b188dabSeric 	lp->lp_lock = NULL;
5233b188dabSeric }
5243b188dabSeric 
5253b188dabSeric int
lp_getqueuestate(struct lp_printer * lp,int reset,int * qstate)5263b188dabSeric lp_getqueuestate(struct lp_printer *lp, int reset, int *qstate)
5273b188dabSeric {
5283b188dabSeric 	FILE *fp;
5293b188dabSeric 	struct stat st;
5303b188dabSeric 	int saved_errno;
5313b188dabSeric 
5323b188dabSeric 	*qstate = 0;
5333b188dabSeric 
5343b188dabSeric 	fp = lp->lp_lock;
5353b188dabSeric 	if (lp->lp_lock == NULL) {
5363b188dabSeric 		fp = lp_fopen(lp, LP_LO(lp));
5373b188dabSeric 		if (fp == NULL) {
5383b188dabSeric 			if (errno == ENOENT)
5393b188dabSeric 				return 0;
5403b188dabSeric 			log_warn("%s: lp_fopen", __func__);
5413b188dabSeric 			return -1;
5423b188dabSeric 		}
5433b188dabSeric 	}
5443b188dabSeric 
5453b188dabSeric 	if (fstat(fileno(fp), &st) == -1) {
5463b188dabSeric 		log_warn("%s: fstat", __func__);
5473b188dabSeric 		if (lp->lp_lock == NULL) {
5483b188dabSeric 			saved_errno = errno;
5493b188dabSeric 			(void)fclose(fp);
5503b188dabSeric 			errno = saved_errno;
5513b188dabSeric 		}
5523b188dabSeric 		return -1;
5533b188dabSeric 	}
5543b188dabSeric 
5553b188dabSeric 	if (st.st_mode & S_IXUSR)
5563b188dabSeric 		*qstate |= LPQ_PRINTER_DOWN;
5573b188dabSeric 	if (st.st_mode & S_IXGRP)
5583b188dabSeric 		*qstate |= LPQ_QUEUE_OFF;
5593b188dabSeric 	if (st.st_mode & S_IXOTH) {
5603b188dabSeric 		*qstate |= LPQ_QUEUE_UPDATED;
5613b188dabSeric 		if (reset) {
5623b188dabSeric 			st.st_mode &= ~S_IXOTH & 0777;
5633b188dabSeric 			if (fchmod(fileno(lp->lp_lock), st.st_mode) == -1)
5643b188dabSeric 				log_warn("%s: fchmod", __func__);
5653b188dabSeric 		}
5663b188dabSeric 	}
5673b188dabSeric 
5683b188dabSeric 	if (lp->lp_lock == NULL)
5693b188dabSeric 		fclose(fp);
5703b188dabSeric 
5713b188dabSeric 	return 0;
5723b188dabSeric }
5733b188dabSeric 
5743b188dabSeric /*
5753b188dabSeric  * Update the current task description in the lock file.
5763b188dabSeric  * The lock file must be opened.
5773b188dabSeric  */
5783b188dabSeric void
lp_setcurrtask(struct lp_printer * lp,const char * cfile)5793b188dabSeric lp_setcurrtask(struct lp_printer *lp, const char *cfile)
5803b188dabSeric {
5813b188dabSeric 	int r;
5823b188dabSeric 
5833b188dabSeric 	if (ftruncate(fileno(lp->lp_lock), 0) == -1)
5843b188dabSeric 		log_warn("%s: ftruncate", __func__);
5853b188dabSeric 
5863b188dabSeric 	if (cfile)
5873b188dabSeric 		r = fprintf(lp->lp_lock, "%d\n%s\n", (int)getpid(), cfile);
5883b188dabSeric 	else
5893b188dabSeric 		r = fprintf(lp->lp_lock, "%d\n", (int)getpid());
5903b188dabSeric 	if (r < 0)
5913b188dabSeric 		log_warn("%s: fprintf", __func__);
5923b188dabSeric 
5933b188dabSeric 	if (fflush(lp->lp_lock) != 0)
5943b188dabSeric 		log_warn("%s: fflush", __func__);
5953b188dabSeric }
5963b188dabSeric 
5973b188dabSeric /*
5983b188dabSeric  * Find the pid of the running process if any, and the task being printed.
5993b188dabSeric  *
6003b188dabSeric  * Returns -1 on error (errno set).
6013b188dabSeric  *          0 if no process is running.
6023b188dabSeric  *          1 if a printer process is up.
6033b188dabSeric  *          2 if a printer process is up and a file is being printed.
6043b188dabSeric  */
6053b188dabSeric int
lp_getcurrtask(struct lp_printer * lp,pid_t * ppid,char * dst,size_t dstsz)6063b188dabSeric lp_getcurrtask(struct lp_printer *lp, pid_t *ppid, char *dst, size_t dstsz)
6073b188dabSeric {
6083b188dabSeric 	FILE *fp;
6093b188dabSeric 	const char *errstr;
6103b188dabSeric 	char *line = NULL;
6113b188dabSeric 	size_t linesz = 0;
6123b188dabSeric 	ssize_t len;
6133b188dabSeric 	pid_t pid;
6143b188dabSeric 	int r, saved_errno;
6153b188dabSeric 
6163b188dabSeric 	pid = *ppid = 0;
6173b188dabSeric 	dst[0] = '\0';
6183b188dabSeric 	r = -1;
6193b188dabSeric 
6203b188dabSeric 	fp = lp_fopen(lp, LP_LO(lp));
6213b188dabSeric 	if (fp == NULL) {
6223b188dabSeric 		if (errno == ENOENT)
6233b188dabSeric 			return 0;
6243b188dabSeric 		log_warn("%s: lp_fopen", __func__);
6253b188dabSeric 		return -1;
6263b188dabSeric 	}
6273b188dabSeric 
6283b188dabSeric 	while ((len = getline(&line, &linesz, fp)) != -1) {
6293b188dabSeric 		if (line[len-1] == '\n')
6303b188dabSeric 			line[len-1] = '\0';
6313b188dabSeric 
6323b188dabSeric 		/* Read filename. */
6333b188dabSeric 		if (pid) {
6343b188dabSeric 			(void)strlcpy(dst, line, dstsz);
6353b188dabSeric 			break;
6363b188dabSeric 		}
6373b188dabSeric 
6383b188dabSeric 		pid = strtonum(line, 2, XXX_PID_MAX, &errstr);
6393b188dabSeric 		if (errstr) {
6403b188dabSeric 			log_warn("%s: strtonum", __func__);
6413b188dabSeric 			goto done;
6423b188dabSeric 		}
6433b188dabSeric 	}
6443b188dabSeric 
6453b188dabSeric 	if ((errno = ferror(fp))) {
6463b188dabSeric 		log_warn("%s: getline", __func__);
6473b188dabSeric 		goto done;
6483b188dabSeric 	}
6493b188dabSeric 
6503b188dabSeric 	r = 0;
6513b188dabSeric 	if (pid == 0)
6523b188dabSeric 		goto done;
6533b188dabSeric 
6543b188dabSeric 	if (kill(pid, 0) == -1 && errno != EPERM) {
6553b188dabSeric 		if (errno != ESRCH)
6563b188dabSeric 			log_warn("%s: kill", __func__);
6573b188dabSeric 		goto done;
6583b188dabSeric 	}
6593b188dabSeric 
6603b188dabSeric 	*ppid = pid;
6613b188dabSeric 	r = dst[0] ? 2 : 1;
6623b188dabSeric 
6633b188dabSeric     done:
6643b188dabSeric 	free(line);
6653b188dabSeric 	saved_errno = errno;
6663b188dabSeric 	(void)fclose(fp);
6673b188dabSeric 	errno = saved_errno;
6683b188dabSeric 	return r;
6693b188dabSeric }
6703b188dabSeric 
6713b188dabSeric /*
6723b188dabSeric  * Read the current printer status file.
6733b188dabSeric  * Return -1 on error, 0 on success.
6743b188dabSeric  */
6753b188dabSeric int
lp_getstatus(struct lp_printer * lp,char * buf,size_t bufsz)6763b188dabSeric lp_getstatus(struct lp_printer *lp, char *buf, size_t bufsz)
6773b188dabSeric {
6783b188dabSeric 	size_t len;
6793b188dabSeric 	char *line;
6803b188dabSeric 	FILE *fp;
6813b188dabSeric 	int saved_errno;
6823b188dabSeric 
6833b188dabSeric 	buf[0] = '\0';
6843b188dabSeric 
6853b188dabSeric 	fp = lp_fopen(lp, LP_ST(lp));
6863b188dabSeric 	if (fp == NULL) {
6873b188dabSeric 		if (errno == ENOENT)
6883b188dabSeric 			return 0;
6893b188dabSeric 		log_warn("%s: lp_fopen", __func__);
6903b188dabSeric 		return -1;
6913b188dabSeric 	}
6923b188dabSeric 
6933b188dabSeric 	line = fgetln(fp, &len);
6943b188dabSeric 	if (line) {
6953b188dabSeric 		if (len >= bufsz)
6963b188dabSeric 			len = bufsz - 1;
6973b188dabSeric 		else if (line[len - 1] == '\n')
6983b188dabSeric 			len--;
6993b188dabSeric 		memmove(buf, line, len);
7003b188dabSeric 		buf[len] = '\0';
7013b188dabSeric 	}
7023b188dabSeric 	else if ((errno = ferror(fp))) {
7033b188dabSeric 		log_warn("%s: fgetln", __func__);
7043b188dabSeric 		saved_errno = errno;
7053b188dabSeric 		(void)fclose(fp);
7063b188dabSeric 		errno = saved_errno;
7073b188dabSeric 		return -1;
7083b188dabSeric 	}
7093b188dabSeric 
7103b188dabSeric 	(void)fclose(fp);
7113b188dabSeric 	return 0;
7123b188dabSeric }
7133b188dabSeric 
7143b188dabSeric /*
7153b188dabSeric  * Update the current printer status.
7163b188dabSeric  */
7173b188dabSeric void
lp_setstatus(struct lp_printer * lp,const char * fmt,...)7183b188dabSeric lp_setstatus(struct lp_printer *lp, const char *fmt, ...)
7193b188dabSeric {
7203b188dabSeric 	va_list ap;
7213b188dabSeric 	FILE *fp;
7223b188dabSeric 	char path[PATH_MAX], *s;
7233b188dabSeric 	int fd = -1, r, saved_errno;
7243b188dabSeric 
7253b188dabSeric 	va_start(ap, fmt);
7263b188dabSeric 	r = vasprintf(&s, fmt, ap);
7273b188dabSeric 	va_end(ap);
7283b188dabSeric 
7293b188dabSeric 	if (r == -1) {
7303b188dabSeric 		log_warn("%s: vasprintf", __func__);
7313b188dabSeric 		return;
7323b188dabSeric 	}
7333b188dabSeric 
7343b188dabSeric 	if (fullpath(lp, LP_ST(lp), path, sizeof(path)) == -1) {
7353b188dabSeric 		log_warn("%s: fullpath", __func__);
7363b188dabSeric 		free(s);
7373b188dabSeric 		return;
7383b188dabSeric 	}
7393b188dabSeric 
7403b188dabSeric 	fd = open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_EXLOCK|O_TRUNC, 0660);
7413b188dabSeric 	if (fd == -1) {
7423b188dabSeric 		log_warn("%s: open", __func__);
7433b188dabSeric 		free(s);
7443b188dabSeric 		return;
7453b188dabSeric 	}
7463b188dabSeric 
7473b188dabSeric 	fp = fdopen(fd, "w");
7483b188dabSeric 	if (fp == NULL) {
7493b188dabSeric 		log_warn("%s: fdopen", __func__);
7503b188dabSeric 		saved_errno = errno;
7513b188dabSeric 		(void)close(fd);
7523b188dabSeric 		free(s);
7533b188dabSeric 		errno = saved_errno;
7543b188dabSeric 		return;
7553b188dabSeric 	}
7563b188dabSeric 
7573b188dabSeric 	r = fprintf(fp, "%s\n", s);
7583b188dabSeric 
7593b188dabSeric 	if (r <= 0) {
7603b188dabSeric 		log_warn("%s: fprintf", __func__);
7613b188dabSeric 		saved_errno = errno;
7623b188dabSeric 		(void)fclose(fp);
7633b188dabSeric 		free(s);
7643b188dabSeric 		errno = saved_errno;
7653b188dabSeric 		return;
7663b188dabSeric 	}
7673b188dabSeric 
7683b188dabSeric 	(void)fclose(fp);
7693b188dabSeric 	free(s);
7703b188dabSeric }
7713b188dabSeric 
7723b188dabSeric /*
7733b188dabSeric  * Check that the given string is a valid filename for the spooler.
7743b188dabSeric  */
7753b188dabSeric int
lp_validfilename(const char * fname,int cf)7763b188dabSeric lp_validfilename(const char *fname, int cf)
7773b188dabSeric {
7783b188dabSeric 	size_t len, i;
7793b188dabSeric 
7803b188dabSeric 	len = strlen(fname);
7813b188dabSeric 	if (len <= 6) /* strlen("cfA000") */
7823b188dabSeric 		return 0;
7833b188dabSeric 
7843b188dabSeric 	if (fname[0] != (cf ? 'c' : 'd') ||
7853b188dabSeric 	    fname[1] != 'f' ||
7863b188dabSeric 	    fname[2] < 'A' ||
7873b188dabSeric 	    fname[2] > 'Z' ||
7883b188dabSeric 	    !isdigit((unsigned char)fname[3]) ||
7893b188dabSeric 	    !isdigit((unsigned char)fname[4]) ||
7903b188dabSeric 	    !isdigit((unsigned char)fname[5]))
7913b188dabSeric 		return 0;
7923b188dabSeric 
7933b188dabSeric 	for (i = 6; i < len; i++)
7943b188dabSeric 		if (!isalpha((unsigned char)fname[i]) &&
7953b188dabSeric 		    !isdigit((unsigned char)fname[i]) &&
7963b188dabSeric 		    strchr("-_.", (unsigned char)fname[i]) == NULL)
7973b188dabSeric 			return 0;
7983b188dabSeric 
7993b188dabSeric 	return 1;
8003b188dabSeric }
8013b188dabSeric 
8023b188dabSeric /*
8033b188dabSeric  * Create a new control or data file in the printer spooldir.
8043b188dabSeric  * Control files have there name changed to a temporary name. They are renamed
8053b188dabSeric  * to their final name by lp_commit().
8063b188dabSeric  */
8073b188dabSeric int
lp_create(struct lp_printer * lp,int cf,size_t sz,const char * fname)8083b188dabSeric lp_create(struct lp_printer *lp, int cf, size_t sz, const char *fname)
8093b188dabSeric {
8103b188dabSeric 	struct stat st;
8113b188dabSeric 	char path[PATH_MAX];
8123b188dabSeric 	int fd;
8133b188dabSeric 
8143b188dabSeric 	/* Make sure the file size is acceptable. */
8153b188dabSeric 	if (checksize(lp, sz) == -1)
8163b188dabSeric 		return -1;
8173b188dabSeric 
8183b188dabSeric 	if (fullpath(lp, fname, path, sizeof(path)) == -1) {
8193b188dabSeric 		log_warn("%s: %s", __func__, fname);
8203b188dabSeric 		return -1;
8213b188dabSeric 	}
8223b188dabSeric 
8233b188dabSeric 	if (cf) {
8243b188dabSeric 		/*
825*3a50f0a9Sjmc 		 * Create a temporary file, but we want to avoid
8263b188dabSeric 		 * a collision with the final cf filename.
8273b188dabSeric 		 */
8283b188dabSeric 		/* XXX this would require a lock on .seq */
8293b188dabSeric 		path[strlen(LP_SD(lp)) + 1] = 'c';
8303b188dabSeric 		if (stat(path, &st) == 0) {
8313b188dabSeric 			errno = EEXIST;
8323b188dabSeric 			log_warn("%s: %s", __func__, fname);
8333b188dabSeric 			return -1;
8343b188dabSeric 		}
8353b188dabSeric 		path[strlen(LP_SD(lp)) + 1] = 't';
8363b188dabSeric 	}
8373b188dabSeric 
8383b188dabSeric 	fd = open(path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, 0660);
8393b188dabSeric 	if (fd == -1)
8403b188dabSeric 		log_warn("%s: open", __func__);
8413b188dabSeric 
8423b188dabSeric 	return fd;
8433b188dabSeric }
8443b188dabSeric 
8453b188dabSeric /*
8463b188dabSeric  * Commit the job given by its temporary CF name.
847*3a50f0a9Sjmc  * This is done by renaming the temporary CF file name to its final name.
8483b188dabSeric  * The functions return 0 on success, or -1 on error and set errno.
8493b188dabSeric  */
8503b188dabSeric int
lp_commit(struct lp_printer * lp,const char * cf)8513b188dabSeric lp_commit(struct lp_printer *lp, const char *cf)
8523b188dabSeric {
8533b188dabSeric 	char ipath[PATH_MAX], opath[PATH_MAX];
8543b188dabSeric 
8553b188dabSeric 	if (fullpath(lp, cf, ipath, sizeof(ipath)) == -1 ||
8563b188dabSeric 	    fullpath(lp, cf, opath, sizeof(opath)) == -1)
8573b188dabSeric 		return -1;
8583b188dabSeric 
8593b188dabSeric 	ipath[strlen(LP_SD(lp)) + 1] = 't';
8603b188dabSeric 	opath[strlen(LP_SD(lp)) + 1] = 'c';
8613b188dabSeric 
8623b188dabSeric 	return rename(ipath, opath);
8633b188dabSeric }
8643b188dabSeric 
8653b188dabSeric /*
8663b188dabSeric  * Check if a file size is acceptable.
8673b188dabSeric  * Return -1 on error or if false (EFBIG or ENOSPC), 0 otherwise.
8683b188dabSeric  */
8693b188dabSeric static int
checksize(struct lp_printer * lp,size_t sz)8703b188dabSeric checksize(struct lp_printer *lp, size_t sz)
8713b188dabSeric {
8723b188dabSeric 	struct statfs st;
8733b188dabSeric 	ssize_t len;
8743b188dabSeric 	size_t linesz = 0;
8753b188dabSeric 	off_t req, avail, minfree;
8763b188dabSeric 	char *line = NULL;
8773b188dabSeric 	const char *errstr;
8783b188dabSeric 	FILE *fp;
8793b188dabSeric 	int saved_errno;
8803b188dabSeric 
8813b188dabSeric 	if (sz == 0) {
8823b188dabSeric 		errno = EINVAL;
8833b188dabSeric 		return -1;
8843b188dabSeric 	}
8853b188dabSeric 
8863b188dabSeric 	/* Check printer limit. */
8873b188dabSeric 	if (lp->lp_mx && sz > (size_t)lp->lp_mx) {
8883b188dabSeric 		errno = EFBIG;
8893b188dabSeric 		return -1;
8903b188dabSeric 	}
8913b188dabSeric 
8923b188dabSeric 	/*
8933b188dabSeric 	 * Check for minfree.  Note that it does not really guarantee the
8943b188dabSeric 	 * directory will not be filled up anyway, since it's not taking
8953b188dabSeric 	 * other incoming files into account.
8963b188dabSeric 	 */
8973b188dabSeric 	fp = lp_fopen(lp, "minfree");
8983b188dabSeric 	if (fp == NULL) {
8993b188dabSeric 		if (errno == ENOENT)
9003b188dabSeric 			return 0;
9013b188dabSeric 		log_warn("%s: lp_fopen", __func__);
9023b188dabSeric 		return -1;
9033b188dabSeric 	}
9043b188dabSeric 
9053b188dabSeric 	len = getline(&line, &linesz, fp);
9063b188dabSeric 	saved_errno = errno;
9073b188dabSeric 	(void)fclose(fp);
9083b188dabSeric 	if (len == -1) {
9093b188dabSeric 		errno = saved_errno;
9103b188dabSeric 		return 0;
9113b188dabSeric 	}
9123b188dabSeric 
9133b188dabSeric 	if (line[len - 1] == '\n')
9143b188dabSeric 		line[len - 1] = '\0';
9153b188dabSeric 	minfree = strtonum(line, 0, INT_MAX, &errstr);
9163b188dabSeric 	free(line);
9173b188dabSeric 
9183b188dabSeric 	if (errstr)
9193b188dabSeric 		return 0;
9203b188dabSeric 
9213b188dabSeric 	if (statfs(LP_SD(lp), &st) == -1)
9223b188dabSeric 		return 0;
9233b188dabSeric 
9243b188dabSeric 	req = sz / 512 + (sz % 512) ? 1 : 0;
9253b188dabSeric 	avail = st.f_bavail * (st.f_bsize / 512);
9263b188dabSeric 	if (avail < minfree || (avail - minfree) < req) {
9273b188dabSeric 		errno = ENOSPC;
9283b188dabSeric 		return -1;
9293b188dabSeric 	}
9303b188dabSeric 
9313b188dabSeric 	return 0;
9323b188dabSeric }
933