131492Swollman/*
231492Swollman * Copyright 1997 Massachusetts Institute of Technology
331492Swollman *
431492Swollman * Permission to use, copy, modify, and distribute this software and
531492Swollman * its documentation for any purpose and without fee is hereby
631492Swollman * granted, provided that both the above copyright notice and this
731492Swollman * permission notice appear in all copies, that both the above
831492Swollman * copyright notice and this permission notice appear in all
931492Swollman * supporting documentation, and that the name of M.I.T. not be used
1031492Swollman * in advertising or publicity pertaining to distribution of the
1131492Swollman * software without specific, written prior permission.  M.I.T. makes
1231492Swollman * no representations about the suitability of this software for any
1331492Swollman * purpose.  It is provided "as is" without express or implied
1431492Swollman * warranty.
1531492Swollman *
1631492Swollman * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1731492Swollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
1831492Swollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1931492Swollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2031492Swollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2131492Swollman * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2231492Swollman * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2331492Swollman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2431492Swollman * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2531492Swollman * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2631492Swollman * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2731492Swollman * SUCH DAMAGE.
2831492Swollman */
2931492Swollman
3031492Swollmanstatic const char copyright[] =
3131492Swollman	"Copyright (C) 1997, Massachusetts Institute of Technology\r\n";
3231492Swollman
33117623Sgad#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
34117623Sgad__FBSDID("$FreeBSD$");
35117623Sgad
3631492Swollman#include <sys/types.h>
3731492Swollman#include <sys/queue.h>
3831492Swollman#include <sys/stat.h>
3931492Swollman
4031492Swollman#include <err.h>
4131492Swollman#include <errno.h>
4231492Swollman#include <grp.h>
4331492Swollman#include <stdio.h>
4431492Swollman#include <string.h>
4531492Swollman#include <stdlib.h>
4631492Swollman#include <unistd.h>
4731492Swollman
4831492Swollman#include <sys/param.h>		/* needed for lp.h but not used here */
4931492Swollman#include <dirent.h>		/* ditto */
5031492Swollman#include "lp.h"
5131492Swollman#include "lp.local.h"
5288004Sgad#include "pathnames.h"
5388004Sgad#include "skimprintcap.h"
5431492Swollman
5531492Swollmanstatic	void check_spool_dirs(void);
5631492Swollmanstatic	int interpret_error(const struct printer *pp, int error);
5731492Swollmanstatic	void make_spool_dir(const struct printer *pp);
5831492Swollmanstatic	void note_spool_dir(const struct printer *pp, const struct stat *st);
5931492Swollmanstatic	void usage(void) __dead2;
6031492Swollman
6131492Swollmanstatic	int problems;		/* number of problems encountered */
6231492Swollman
6331492Swollman/*
6431492Swollman * chkprintcap - check the printcap file for syntactic and semantic errors
6531492Swollman * Returns the number of problems found.
6631492Swollman */
6731492Swollmanint
6831492Swollmanmain(int argc, char **argv)
6931492Swollman{
7088004Sgad	struct skiminfo *skres;
7188004Sgad	char *pcap_fname;
7288004Sgad	int c, error, makedirs, more, queuecnt, verbosity;
7331492Swollman	struct printer myprinter, *pp;
7431492Swollman
7531492Swollman	makedirs = 0;
7688004Sgad	queuecnt = 0;
7788004Sgad	verbosity = 0;
7888004Sgad	pcap_fname = NULL;
7931492Swollman	pp = &myprinter;
8031492Swollman
8188004Sgad	while ((c = getopt(argc, argv, "df:v")) != -1) {
8231492Swollman		switch (c) {
8331492Swollman		case 'd':
8431492Swollman			makedirs = 1;
8531492Swollman			break;
8631492Swollman
8731492Swollman		case 'f':
8888004Sgad			pcap_fname = strdup(optarg);
8988004Sgad			setprintcap(pcap_fname);
9031492Swollman			break;
9131492Swollman
9288004Sgad		case 'v':
9388004Sgad			verbosity++;
9488004Sgad			break;
9588004Sgad
9631492Swollman		default:
9731492Swollman			usage();
9831492Swollman		}
9931492Swollman	}
10031492Swollman
10131492Swollman	if (optind != argc)
10231492Swollman		usage();
10331492Swollman
10488004Sgad	if (pcap_fname == NULL)
10588004Sgad		pcap_fname = strdup(_PATH_PRINTCAP);
10688004Sgad
10788004Sgad	/*
10888004Sgad	 * Skim through the printcap file looking for simple user-mistakes
10988004Sgad	 * which will produce the wrong result for the user, but which may
11088004Sgad	 * be pretty hard for the user to notice.  Such user-mistakes will
11188004Sgad	 * only generate warning messages.  The (fatal-) problem count will
11288004Sgad	 * only be incremented if there is a system problem trying to read
11388004Sgad	 * the printcap file.
11488004Sgad	*/
11588004Sgad	skres = skim_printcap(pcap_fname, verbosity);
11688004Sgad	if (skres->fatalerr)
11788004Sgad		return (skres->fatalerr);
11888004Sgad
11988004Sgad	/*
12088004Sgad	 * Now use the standard capability-db routines to check the values
12188004Sgad	 * in each of the queues defined in the printcap file.
12288004Sgad	*/
12331492Swollman	more = firstprinter(pp, &error);
12431492Swollman	if (interpret_error(pp, error) && more)
12531492Swollman		goto next;
12631492Swollman
12731492Swollman	while (more) {
12831492Swollman		struct stat stab;
12931492Swollman
13088004Sgad		queuecnt++;
13131492Swollman		errno = 0;
13231492Swollman		if (stat(pp->spool_dir, &stab) < 0) {
13331492Swollman			if (errno == ENOENT && makedirs) {
13431492Swollman				make_spool_dir(pp);
13531492Swollman			} else {
13631492Swollman				problems++;
13731492Swollman				warn("%s: %s", pp->printer, pp->spool_dir);
13831492Swollman			}
13931492Swollman		} else {
14031492Swollman			note_spool_dir(pp, &stab);
14131492Swollman		}
14231492Swollman
14388004Sgad		/* Make other queue-specific validity checks here... */
14431492Swollman
14531492Swollmannext:
14631492Swollman		more = nextprinter(pp, &error);
14731492Swollman		if (interpret_error(pp, error) && more)
14831492Swollman			goto next;
14931492Swollman	}
15088004Sgad
15131492Swollman	check_spool_dirs();
15288004Sgad
15388004Sgad	if (queuecnt != skres->entries) {
15488004Sgad		warnx("WARNING: found %d entries when skimming %s,",
15588004Sgad		    skres->entries, pcap_fname);
15688004Sgad		warnx("WARNING:  but only found %d queues to process!",
15788004Sgad		    queuecnt);
15888004Sgad	}
15988004Sgad	return (problems);
16031492Swollman}
16131492Swollman
16231492Swollman/*
16331492Swollman * Interpret the error code.  Returns 1 if we should skip to the next
16431492Swollman * record (as this record is unlikely to make sense).  If the problem
16531492Swollman * is very severe, exit.  Otherwise, return zero.
16631492Swollman */
16731492Swollmanstatic int
16831492Swollmaninterpret_error(const struct printer *pp, int error)
16931492Swollman{
17031492Swollman	switch(error) {
17131492Swollman	case PCAPERR_OSERR:
17231492Swollman		err(++problems, "reading printer database");
17331492Swollman	case PCAPERR_TCLOOP:
17431492Swollman		++problems;
17531492Swollman		warnx("%s: loop detected in tc= expansion", pp->printer);
17631492Swollman		return 1;
17731492Swollman	case PCAPERR_TCOPEN:
17831492Swollman		warnx("%s: unresolved tc= expansion", pp->printer);
17931492Swollman		return 1;
18031492Swollman	case PCAPERR_SUCCESS:
18131492Swollman		break;
18231492Swollman	default:
18331492Swollman		errx(++problems, "unknown printcap library error %d", error);
18431492Swollman	}
18531492Swollman	return 0;
18631492Swollman}
18731492Swollman
18831492Swollman/*
18931492Swollman * Keep the list of spool directories.  Note that we don't whine
19031492Swollman * until all spool directories are noted, so that all of the more serious
19131492Swollman * problems are noted first.  We keep the list sorted by st_dev and
19231492Swollman * st_ino, so that the problem spool directories can be noted in
19331492Swollman * a single loop.
19431492Swollman */
19531492Swollmanstruct	dirlist {
19660938Sjake	LIST_ENTRY(dirlist) link;
19731492Swollman	struct stat stab;
19831492Swollman	char *path;
19931492Swollman	char *printer;
20031492Swollman};
20131492Swollman
20260938Sjakestatic	LIST_HEAD(, dirlist) dirlist;
20331492Swollman
20431492Swollmanstatic int
20531492Swollmanlessp(const struct dirlist *a, const struct dirlist *b)
20631492Swollman{
20731492Swollman	if (a->stab.st_dev == b->stab.st_dev)
20831492Swollman		return a->stab.st_ino < b->stab.st_ino;
20931492Swollman	return a->stab.st_dev < b->stab.st_dev;
21031492Swollman}
21131492Swollman
21231492Swollmanstatic int
21331492Swollmanequal(const struct dirlist *a, const struct dirlist *b)
21431492Swollman{
21531492Swollman	return ((a->stab.st_dev == b->stab.st_dev)
21631492Swollman		&& (a->stab.st_ino == b->stab.st_ino));
21731492Swollman}
21831492Swollman
21931492Swollmanstatic void
22031492Swollmannote_spool_dir(const struct printer *pp, const struct stat *st)
22131492Swollman{
22231492Swollman	struct dirlist *dp, *dp2, *last;
22331492Swollman
22431492Swollman	dp = malloc(sizeof *dp);
225297795Spfg	if (dp == NULL)
22631492Swollman		err(++problems, "malloc(%lu)", (u_long)sizeof *dp);
22731492Swollman
22831492Swollman	dp->stab = *st;
22931492Swollman	dp->printer = strdup(pp->printer);
23031492Swollman	if (dp->printer == 0)
23131492Swollman		err(++problems, "malloc(%lu)", strlen(pp->printer) + 1UL);
23231492Swollman	dp->path = strdup(pp->spool_dir);
23331492Swollman	if (dp->path == 0)
23431492Swollman		err(++problems, "malloc(%lu)", strlen(pp->spool_dir) + 1UL);
23531492Swollman
236297795Spfg	last = NULL;
23770520Sphk	LIST_FOREACH(dp2, &dirlist, link) {
23870520Sphk		if(!lessp(dp, dp2))
23970520Sphk			break;
24031492Swollman		last = dp2;
24131492Swollman	}
24231492Swollman
24331492Swollman	if (last) {
24431492Swollman		LIST_INSERT_AFTER(last, dp, link);
24531492Swollman	} else {
24631492Swollman		LIST_INSERT_HEAD(&dirlist, dp, link);
24731492Swollman	}
24831492Swollman}
24931492Swollman
25031492Swollmanstatic void
25131492Swollmancheck_spool_dirs(void)
25231492Swollman{
25331492Swollman	struct dirlist *dp, *dp2;
25431492Swollman
25570520Sphk	for (dp = LIST_FIRST(&dirlist); dp; dp = dp2) {
25670520Sphk		dp2 = LIST_NEXT(dp, link);
25731492Swollman
258297795Spfg		if (dp2 != NULL && equal(dp, dp2)) {
25931492Swollman			++problems;
26031492Swollman			if (strcmp(dp->path, dp2->path) == 0) {
26131492Swollman				warnx("%s and %s share the same spool, %s",
26231492Swollman				      dp->printer, dp2->printer, dp->path);
26331492Swollman			} else {
26431492Swollman				warnx("%s (%s) and %s (%s) are the same "
26531492Swollman				      "directory", dp->path, dp->printer,
26631492Swollman				      dp2->path, dp2->printer);
26731492Swollman			}
26831492Swollman			continue;
26931492Swollman		}
27031492Swollman		/* Should probably check owners and modes here. */
27131492Swollman	}
27231492Swollman}
27331492Swollman
27431492Swollman#ifndef SPOOL_DIR_MODE
27531492Swollman#define	SPOOL_DIR_MODE	(S_IRUSR | S_IWUSR | S_IXUSR \
27631492Swollman			 | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
27731492Swollman#endif
27831492Swollman
27931492Swollmanstatic void
28031492Swollmanmake_spool_dir(const struct printer *pp)
28131492Swollman{
28231492Swollman	char *sd = pp->spool_dir;
28331492Swollman	struct group *gr;
28431492Swollman	struct stat stab;
28531492Swollman
28631492Swollman	if (mkdir(sd, S_IRUSR | S_IXUSR) < 0) {
28731492Swollman		problems++;
28831492Swollman		warn("%s: mkdir %s", pp->printer, sd);
28931492Swollman		return;
29031492Swollman	}
29131492Swollman	gr = getgrnam("daemon");
292297795Spfg	if (gr == NULL)
29331492Swollman		errx(++problems, "cannot locate daemon group");
29431492Swollman
29531492Swollman	if (chown(sd, pp->daemon_user, gr->gr_gid) < 0) {
29631492Swollman		++problems;
29731492Swollman		warn("%s: cannot change ownership to %ld:%ld", sd,
29831492Swollman		     (long)pp->daemon_user, (long)gr->gr_gid);
29931492Swollman		return;
30031492Swollman	}
30131492Swollman
30231492Swollman	if (chmod(sd, SPOOL_DIR_MODE) < 0) {
30331492Swollman		++problems;
30431569Sjdp		warn("%s: cannot change mode to %lo", sd, (long)SPOOL_DIR_MODE);
30531492Swollman		return;
30631492Swollman	}
30731492Swollman	if (stat(sd, &stab) < 0)
30831492Swollman		err(++problems, "stat: %s", sd);
30931492Swollman
31031492Swollman	note_spool_dir(pp, &stab);
31131492Swollman}
31231492Swollman
31331492Swollmanstatic void
31431492Swollmanusage(void)
31531492Swollman{
31688004Sgad	fprintf(stderr, "usage:\n\tchkprintcap [-dv] [-f printcapfile]\n");
31731492Swollman	exit(1);
31831492Swollman}
319