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