lpc.c revision 194494
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1983, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes *
61553Srgrimes * Redistribution and use in source and binary forms, with or without
71553Srgrimes * modification, are permitted provided that the following conditions
81553Srgrimes * are met:
91553Srgrimes * 1. Redistributions of source code must retain the above copyright
101553Srgrimes *    notice, this list of conditions and the following disclaimer.
111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121553Srgrimes *    notice, this list of conditions and the following disclaimer in the
131553Srgrimes *    documentation and/or other materials provided with the distribution.
141553Srgrimes * 3. All advertising materials mentioning features or use of this software
151553Srgrimes *    must display the following acknowledgement:
161553Srgrimes *	This product includes software developed by the University of
171553Srgrimes *	California, Berkeley and its contributors.
181553Srgrimes * 4. Neither the name of the University nor the names of its contributors
191553Srgrimes *    may be used to endorse or promote products derived from this software
201553Srgrimes *    without specific prior written permission.
211553Srgrimes *
221553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321553Srgrimes * SUCH DAMAGE.
331553Srgrimes */
341553Srgrimes
351553Srgrimes#ifndef lint
3629780Scharnierstatic const char copyright[] =
371553Srgrimes"@(#) Copyright (c) 1983, 1993\n\
381553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
391553Srgrimes#endif /* not lint */
401553Srgrimes
41117599Sgad#if 0
421553Srgrimes#ifndef lint
4315637Sjoergstatic char sccsid[] = "@(#)lpc.c	8.3 (Berkeley) 4/28/95";
44117599Sgad#endif /* not lint */
4529780Scharnier#endif
461553Srgrimes
47117599Sgad#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
48117599Sgad__FBSDID("$FreeBSD: head/usr.sbin/lpr/lpc/lpc.c 194494 2009-06-19 15:58:24Z brooks $");
49117599Sgad
501553Srgrimes#include <sys/param.h>
511553Srgrimes
5229780Scharnier#include <ctype.h>
531553Srgrimes#include <dirent.h>
5431492Swollman#include <err.h>
5529780Scharnier#include <grp.h>
5629780Scharnier#include <setjmp.h>
571553Srgrimes#include <signal.h>
5829780Scharnier#include <stdio.h>
5929780Scharnier#include <stdlib.h>
601553Srgrimes#include <syslog.h>
6129780Scharnier#include <string.h>
621553Srgrimes#include <unistd.h>
6350039Smdodd#include <histedit.h>
6431492Swollman
651553Srgrimes#include "lp.h"
661553Srgrimes#include "lpc.h"
671553Srgrimes#include "extern.h"
681553Srgrimes
6915637Sjoerg#ifndef LPR_OPER
7015637Sjoerg#define LPR_OPER	"operator"	/* group name of lpr operators */
7115637Sjoerg#endif
7215637Sjoerg
731553Srgrimes/*
741553Srgrimes * lpc -- line printer control program
751553Srgrimes */
761553Srgrimes
7727618Simp#define MAX_CMDLINE	200
7827618Simp#define MAX_MARGV	20
7939084Swollmanstatic int	fromatty;
801553Srgrimes
8139084Swollmanstatic char	cmdline[MAX_CMDLINE];
8239084Swollmanstatic int	margc;
8339084Swollmanstatic char	*margv[MAX_MARGV];
8439084Swollmanuid_t		uid, euid;
851553Srgrimes
8678146Sgadint			 main(int _argc, char *_argv[]);
8778146Sgadstatic void		 cmdscanner(void);
8878146Sgadstatic struct cmd	*getcmd(const char *_name);
8978146Sgadstatic void		 intr(int _signo);
9078146Sgadstatic void		 makeargv(void);
9178146Sgadstatic int		 ingroup(const char *_grname);
921553Srgrimes
931553Srgrimesint
9478146Sgadmain(int argc, char *argv[])
951553Srgrimes{
961553Srgrimes	register struct cmd *c;
971553Srgrimes
9827618Simp	euid = geteuid();
9927618Simp	uid = getuid();
10027618Simp	seteuid(uid);
10178280Sgad	progname = argv[0];
1021553Srgrimes	openlog("lpd", 0, LOG_LPR);
1031553Srgrimes
1041553Srgrimes	if (--argc > 0) {
1051553Srgrimes		c = getcmd(*++argv);
1061553Srgrimes		if (c == (struct cmd *)-1) {
1071553Srgrimes			printf("?Ambiguous command\n");
1081553Srgrimes			exit(1);
1091553Srgrimes		}
1101553Srgrimes		if (c == 0) {
1111553Srgrimes			printf("?Invalid command\n");
1121553Srgrimes			exit(1);
1131553Srgrimes		}
11498267Sgad		if ((c->c_opts & LPC_PRIVCMD) && getuid() &&
11598267Sgad		    ingroup(LPR_OPER) == 0) {
1161553Srgrimes			printf("?Privileged command\n");
1171553Srgrimes			exit(1);
1181553Srgrimes		}
11931492Swollman		if (c->c_generic != 0)
12098267Sgad			generic(c->c_generic, c->c_opts, c->c_handler,
12198267Sgad			    argc, argv);
12231492Swollman		else
12331492Swollman			(*c->c_handler)(argc, argv);
1241553Srgrimes		exit(0);
1251553Srgrimes	}
1261553Srgrimes	fromatty = isatty(fileno(stdin));
12750039Smdodd	if (!fromatty)
1281553Srgrimes		signal(SIGINT, intr);
1291553Srgrimes	for (;;) {
13050039Smdodd		cmdscanner();
1311553Srgrimes	}
1321553Srgrimes}
1331553Srgrimes
1341553Srgrimesstatic void
13578146Sgadintr(int signo __unused)
1361553Srgrimes{
13778146Sgad	/* (the '__unused' is just to avoid a compile-time warning) */
13850039Smdodd	exit(0);
1391553Srgrimes}
1401553Srgrimes
14178146Sgadstatic const char *
14268400Sgadlpc_prompt(void)
14350039Smdodd{
14468400Sgad	return ("lpc> ");
14550039Smdodd}
14650039Smdodd
1471553Srgrimes/*
1481553Srgrimes * Command parser.
1491553Srgrimes */
1501553Srgrimesstatic void
15178146Sgadcmdscanner(void)
1521553Srgrimes{
1531553Srgrimes	register struct cmd *c;
15479742Sgad	static EditLine *el;
15579742Sgad	static History *hist;
15684261Sobrien	HistEvent he;
15779742Sgad	size_t len;
15879742Sgad	int num;
15979742Sgad	const char *bp;
1601553Srgrimes
16179742Sgad	num = 0;
16279742Sgad	bp = NULL;
16379742Sgad	el = NULL;
16479742Sgad	hist = NULL;
1651553Srgrimes	for (;;) {
1661553Srgrimes		if (fromatty) {
16750039Smdodd			if (!el) {
16884261Sobrien				el = el_init("lpc", stdin, stdout, stderr);
16950039Smdodd				hist = history_init();
170151476Sstefanf				history(hist, &he, H_SETSIZE, 100);
17150039Smdodd				el_set(el, EL_HIST, history, hist);
17250039Smdodd				el_set(el, EL_EDITOR, "emacs");
17350039Smdodd				el_set(el, EL_PROMPT, lpc_prompt);
17450039Smdodd				el_set(el, EL_SIGNAL, 1);
17550042Smdodd				el_source(el, NULL);
17683563Sgad				/*
17783563Sgad				 * EditLine init may call 'cgetset()' to set a
17883563Sgad				 * capability-db meant for termcap (eg: to set
17983563Sgad				 * terminal type 'xterm').  Reset that now, or
18083563Sgad				 * that same db-information will be used for
18183563Sgad				 * printcap (giving us an "xterm" printer, with
18283563Sgad				 * all kinds of invalid capabilities...).
18383563Sgad				 */
18483563Sgad				cgetset(NULL);
18550039Smdodd			}
18650039Smdodd			if ((bp = el_gets(el, &num)) == NULL || num == 0)
18762294Smph				quit(0, NULL);
18850039Smdodd
189121065Stjr			len = (num > MAX_CMDLINE - 1) ? MAX_CMDLINE - 1 : num;
19050071Smdodd			memcpy(cmdline, bp, len);
19150071Smdodd			cmdline[len] = 0;
19284261Sobrien			history(hist, &he, H_ENTER, bp);
19350039Smdodd
19450039Smdodd		} else {
19550039Smdodd			if (fgets(cmdline, MAX_CMDLINE, stdin) == 0)
19650039Smdodd				quit(0, NULL);
19750039Smdodd			if (cmdline[0] == 0 || cmdline[0] == '\n')
19850039Smdodd				break;
1991553Srgrimes		}
20050039Smdodd
2011553Srgrimes		makeargv();
20250039Smdodd		if (margc == 0)
20350039Smdodd			continue;
204142199Sdelphij		if (el != NULL && el_parse(el, margc, margv) != -1)
20550039Smdodd			continue;
20650039Smdodd
2071553Srgrimes		c = getcmd(margv[0]);
2081553Srgrimes		if (c == (struct cmd *)-1) {
2091553Srgrimes			printf("?Ambiguous command\n");
2101553Srgrimes			continue;
2111553Srgrimes		}
2121553Srgrimes		if (c == 0) {
2131553Srgrimes			printf("?Invalid command\n");
2141553Srgrimes			continue;
2151553Srgrimes		}
21698267Sgad		if ((c->c_opts & LPC_PRIVCMD) && getuid() &&
21798267Sgad		    ingroup(LPR_OPER) == 0) {
2181553Srgrimes			printf("?Privileged command\n");
2191553Srgrimes			continue;
2201553Srgrimes		}
22178750Sgad
22278750Sgad		/*
22378750Sgad		 * Two different commands might have the same generic rtn
22478750Sgad		 * (eg: "clean" and "tclean"), and just use different
22578750Sgad		 * handler routines for distinct command-setup.  The handler
22678750Sgad		 * routine might also be set on a generic routine for
22778750Sgad		 * initial parameter processing.
22878750Sgad		 */
22931492Swollman		if (c->c_generic != 0)
23098267Sgad			generic(c->c_generic, c->c_opts, c->c_handler,
23198267Sgad			    margc, margv);
23231492Swollman		else
23331492Swollman			(*c->c_handler)(margc, margv);
2341553Srgrimes	}
2351553Srgrimes}
2361553Srgrimes
23715637Sjoergstatic struct cmd *
23878146Sgadgetcmd(const char *name)
2391553Srgrimes{
24078146Sgad	register const char *p, *q;
2411553Srgrimes	register struct cmd *c, *found;
2421553Srgrimes	register int nmatches, longest;
2431553Srgrimes
2441553Srgrimes	longest = 0;
2451553Srgrimes	nmatches = 0;
2461553Srgrimes	found = 0;
24719202Simp	for (c = cmdtab; (p = c->c_name); c++) {
2481553Srgrimes		for (q = name; *q == *p++; q++)
2491553Srgrimes			if (*q == 0)		/* exact match? */
2501553Srgrimes				return(c);
2511553Srgrimes		if (!*q) {			/* the name was a prefix */
2521553Srgrimes			if (q - name > longest) {
2531553Srgrimes				longest = q - name;
2541553Srgrimes				nmatches = 1;
2551553Srgrimes				found = c;
2561553Srgrimes			} else if (q - name == longest)
2571553Srgrimes				nmatches++;
2581553Srgrimes		}
2591553Srgrimes	}
2601553Srgrimes	if (nmatches > 1)
2611553Srgrimes		return((struct cmd *)-1);
2621553Srgrimes	return(found);
2631553Srgrimes}
2641553Srgrimes
2651553Srgrimes/*
2661553Srgrimes * Slice a string up into argc/argv.
2671553Srgrimes */
2681553Srgrimesstatic void
26978146Sgadmakeargv(void)
2701553Srgrimes{
2711553Srgrimes	register char *cp;
2721553Srgrimes	register char **argp = margv;
27327618Simp	register int n = 0;
2741553Srgrimes
2751553Srgrimes	margc = 0;
27680173Sgad	for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) &&
277121065Stjr	    n < MAX_MARGV - 1; n++) {
2781553Srgrimes		while (isspace(*cp))
2791553Srgrimes			cp++;
2801553Srgrimes		if (*cp == '\0')
2811553Srgrimes			break;
2821553Srgrimes		*argp++ = cp;
2831553Srgrimes		margc += 1;
2841553Srgrimes		while (*cp != '\0' && !isspace(*cp))
2851553Srgrimes			cp++;
2861553Srgrimes		if (*cp == '\0')
2871553Srgrimes			break;
2881553Srgrimes		*cp++ = '\0';
2891553Srgrimes	}
2901553Srgrimes	*argp++ = 0;
2911553Srgrimes}
2921553Srgrimes
2931553Srgrimes#define HELPINDENT (sizeof ("directory"))
2941553Srgrimes
2951553Srgrimes/*
2961553Srgrimes * Help command.
2971553Srgrimes */
2981553Srgrimesvoid
29978146Sgadhelp(int argc, char *argv[])
3001553Srgrimes{
3011553Srgrimes	register struct cmd *c;
3021553Srgrimes
3031553Srgrimes	if (argc == 1) {
3041553Srgrimes		register int i, j, w;
3051553Srgrimes		int columns, width = 0, lines;
3061553Srgrimes
3071553Srgrimes		printf("Commands may be abbreviated.  Commands are:\n\n");
3081553Srgrimes		for (c = cmdtab; c->c_name; c++) {
3091553Srgrimes			int len = strlen(c->c_name);
3101553Srgrimes
3111553Srgrimes			if (len > width)
3121553Srgrimes				width = len;
3131553Srgrimes		}
3141553Srgrimes		width = (width + 8) &~ 7;
3151553Srgrimes		columns = 80 / width;
3161553Srgrimes		if (columns == 0)
3171553Srgrimes			columns = 1;
3181553Srgrimes		lines = (NCMDS + columns - 1) / columns;
3191553Srgrimes		for (i = 0; i < lines; i++) {
3201553Srgrimes			for (j = 0; j < columns; j++) {
3211553Srgrimes				c = cmdtab + j * lines + i;
3221553Srgrimes				if (c->c_name)
3231553Srgrimes					printf("%s", c->c_name);
3241553Srgrimes				if (c + lines >= &cmdtab[NCMDS]) {
3251553Srgrimes					printf("\n");
3261553Srgrimes					break;
3271553Srgrimes				}
3281553Srgrimes				w = strlen(c->c_name);
3291553Srgrimes				while (w < width) {
3301553Srgrimes					w = (w + 8) &~ 7;
3311553Srgrimes					putchar('\t');
3321553Srgrimes				}
3331553Srgrimes			}
3341553Srgrimes		}
3351553Srgrimes		return;
3361553Srgrimes	}
3371553Srgrimes	while (--argc > 0) {
3381553Srgrimes		register char *arg;
3391553Srgrimes		arg = *++argv;
3401553Srgrimes		c = getcmd(arg);
3411553Srgrimes		if (c == (struct cmd *)-1)
3421553Srgrimes			printf("?Ambiguous help command %s\n", arg);
3431553Srgrimes		else if (c == (struct cmd *)0)
3441553Srgrimes			printf("?Invalid help command %s\n", arg);
3451553Srgrimes		else
34634784Sjb			printf("%-*s\t%s\n", (int) HELPINDENT,
3471553Srgrimes				c->c_name, c->c_help);
3481553Srgrimes	}
3491553Srgrimes}
35015637Sjoerg
35115637Sjoerg/*
35215637Sjoerg * return non-zero if the user is a member of the given group
35315637Sjoerg */
35415637Sjoergstatic int
35578146Sgadingroup(const char *grname)
35615637Sjoerg{
35715637Sjoerg	static struct group *gptr=NULL;
35865035Salfred	static int ngroups = 0;
359194494Sbrooks	static long ngroups_max;
360194494Sbrooks	static gid_t *groups;
36115637Sjoerg	register gid_t gid;
36215637Sjoerg	register int i;
36315637Sjoerg
36415637Sjoerg	if (gptr == NULL) {
36515637Sjoerg		if ((gptr = getgrnam(grname)) == NULL) {
36629780Scharnier			warnx("warning: unknown group '%s'", grname);
36715637Sjoerg			return(0);
36815637Sjoerg		}
369194494Sbrooks		ngroups_max = sysconf(_SC_NGROUPS_MAX);
370194494Sbrooks		if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
371194494Sbrooks			err(1, "malloc");
372194494Sbrooks		ngroups = getgroups(ngroups_max, groups);
37365035Salfred		if (ngroups < 0)
37429780Scharnier			err(1, "getgroups");
37515637Sjoerg	}
37615637Sjoerg	gid = gptr->gr_gid;
37765035Salfred	for (i = 0; i < ngroups; i++)
37815637Sjoerg		if (gid == groups[i])
37915637Sjoerg			return(1);
38015637Sjoerg	return(0);
38115637Sjoerg}
382100203Sgad
383100203Sgad/*
384100203Sgad * Routine to get the information for a single printer (which will be
385100203Sgad * called by the routines which implement individual commands).
386100203Sgad * Note: This is for commands operating on a *single* printer.
387100203Sgad */
388100203Sgadstruct printer *
389100203Sgadsetup_myprinter(char *pwanted, struct printer *pp, int sump_opts)
390100203Sgad{
391100203Sgad	int cdres, cmdstatus;
392100203Sgad
393100203Sgad	init_printer(pp);
394100203Sgad	cmdstatus = getprintcap(pwanted, pp);
395100203Sgad	switch (cmdstatus) {
396100203Sgad	default:
397100203Sgad		fatal(pp, "%s", pcaperr(cmdstatus));
398100203Sgad		/* NOTREACHED */
399100203Sgad	case PCAPERR_NOTFOUND:
400100203Sgad		printf("unknown printer %s\n", pwanted);
401100203Sgad		return (NULL);
402100203Sgad	case PCAPERR_TCOPEN:
403100203Sgad		printf("warning: %s: unresolved tc= reference(s)", pwanted);
404100203Sgad		break;
405100203Sgad	case PCAPERR_SUCCESS:
406100203Sgad		break;
407100203Sgad	}
408100203Sgad	if ((sump_opts & SUMP_NOHEADER) == 0)
409100203Sgad		printf("%s:\n", pp->printer);
410100203Sgad
411100203Sgad	if (sump_opts & SUMP_CHDIR_SD) {
412100203Sgad		seteuid(euid);
413100203Sgad		cdres = chdir(pp->spool_dir);
414100203Sgad		seteuid(uid);
415100203Sgad		if (cdres < 0) {
416100203Sgad			printf("\tcannot chdir to %s\n", pp->spool_dir);
417100203Sgad			free_printer(pp);
418100203Sgad			return (NULL);
419100203Sgad		}
420100203Sgad	}
421100203Sgad
422100203Sgad	return (pp);
423100203Sgad}
424