lpc.c revision 83563
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
411553Srgrimes#ifndef lint
4229780Scharnier#if 0
4315637Sjoergstatic char sccsid[] = "@(#)lpc.c	8.3 (Berkeley) 4/28/95";
4429780Scharnier#endif
4529780Scharnierstatic const char rcsid[] =
4650479Speter  "$FreeBSD: head/usr.sbin/lpr/lpc/lpc.c 83563 2001-09-17 02:35:34Z gad $";
471553Srgrimes#endif /* not lint */
481553Srgrimes
491553Srgrimes#include <sys/param.h>
501553Srgrimes
5129780Scharnier#include <ctype.h>
521553Srgrimes#include <dirent.h>
5331492Swollman#include <err.h>
5429780Scharnier#include <grp.h>
5529780Scharnier#include <setjmp.h>
561553Srgrimes#include <signal.h>
5729780Scharnier#include <stdio.h>
5829780Scharnier#include <stdlib.h>
591553Srgrimes#include <syslog.h>
6029780Scharnier#include <string.h>
611553Srgrimes#include <unistd.h>
6250039Smdodd#include <histedit.h>
6331492Swollman
641553Srgrimes#include "lp.h"
651553Srgrimes#include "lpc.h"
661553Srgrimes#include "extern.h"
671553Srgrimes
6815637Sjoerg#ifndef LPR_OPER
6915637Sjoerg#define LPR_OPER	"operator"	/* group name of lpr operators */
7015637Sjoerg#endif
7115637Sjoerg
721553Srgrimes/*
731553Srgrimes * lpc -- line printer control program
741553Srgrimes */
751553Srgrimes
7627618Simp#define MAX_CMDLINE	200
7727618Simp#define MAX_MARGV	20
7839084Swollmanstatic int	fromatty;
791553Srgrimes
8039084Swollmanstatic char	cmdline[MAX_CMDLINE];
8139084Swollmanstatic int	margc;
8239084Swollmanstatic char	*margv[MAX_MARGV];
8339084Swollmanuid_t		uid, euid;
841553Srgrimes
8578146Sgadint			 main(int _argc, char *_argv[]);
8678146Sgadstatic void		 cmdscanner(void);
8778146Sgadstatic struct cmd	*getcmd(const char *_name);
8878146Sgadstatic void		 intr(int _signo);
8978146Sgadstatic void		 makeargv(void);
9078146Sgadstatic int		 ingroup(const char *_grname);
911553Srgrimes
921553Srgrimesint
9378146Sgadmain(int argc, char *argv[])
941553Srgrimes{
951553Srgrimes	register struct cmd *c;
961553Srgrimes
9727618Simp	euid = geteuid();
9827618Simp	uid = getuid();
9927618Simp	seteuid(uid);
10078280Sgad	progname = argv[0];
1011553Srgrimes	openlog("lpd", 0, LOG_LPR);
1021553Srgrimes
1031553Srgrimes	if (--argc > 0) {
1041553Srgrimes		c = getcmd(*++argv);
1051553Srgrimes		if (c == (struct cmd *)-1) {
1061553Srgrimes			printf("?Ambiguous command\n");
1071553Srgrimes			exit(1);
1081553Srgrimes		}
1091553Srgrimes		if (c == 0) {
1101553Srgrimes			printf("?Invalid command\n");
1111553Srgrimes			exit(1);
1121553Srgrimes		}
11315637Sjoerg		if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) {
1141553Srgrimes			printf("?Privileged command\n");
1151553Srgrimes			exit(1);
1161553Srgrimes		}
11731492Swollman		if (c->c_generic != 0)
11878750Sgad			generic(c->c_generic, c->c_handler, argc, argv);
11931492Swollman		else
12031492Swollman			(*c->c_handler)(argc, argv);
1211553Srgrimes		exit(0);
1221553Srgrimes	}
1231553Srgrimes	fromatty = isatty(fileno(stdin));
12450039Smdodd	if (!fromatty)
1251553Srgrimes		signal(SIGINT, intr);
1261553Srgrimes	for (;;) {
12750039Smdodd		cmdscanner();
1281553Srgrimes	}
1291553Srgrimes}
1301553Srgrimes
1311553Srgrimesstatic void
13278146Sgadintr(int signo __unused)
1331553Srgrimes{
13478146Sgad	/* (the '__unused' is just to avoid a compile-time warning) */
13550039Smdodd	exit(0);
1361553Srgrimes}
1371553Srgrimes
13878146Sgadstatic const char *
13968400Sgadlpc_prompt(void)
14050039Smdodd{
14168400Sgad	return ("lpc> ");
14250039Smdodd}
14350039Smdodd
1441553Srgrimes/*
1451553Srgrimes * Command parser.
1461553Srgrimes */
1471553Srgrimesstatic void
14878146Sgadcmdscanner(void)
1491553Srgrimes{
1501553Srgrimes	register struct cmd *c;
15179742Sgad	static EditLine *el;
15279742Sgad	static History *hist;
15379742Sgad	size_t len;
15479742Sgad	int num;
15579742Sgad	const char *bp;
1561553Srgrimes
15779742Sgad	num = 0;
15879742Sgad	bp = NULL;
15979742Sgad	el = NULL;
16079742Sgad	hist = NULL;
1611553Srgrimes	for (;;) {
1621553Srgrimes		if (fromatty) {
16350039Smdodd			if (!el) {
16450039Smdodd				el = el_init("lpc", stdin, stdout);
16550039Smdodd				hist = history_init();
16650039Smdodd				history(hist, H_EVENT, 100);
16750039Smdodd				el_set(el, EL_HIST, history, hist);
16850039Smdodd				el_set(el, EL_EDITOR, "emacs");
16950039Smdodd				el_set(el, EL_PROMPT, lpc_prompt);
17050039Smdodd				el_set(el, EL_SIGNAL, 1);
17150042Smdodd				el_source(el, NULL);
17283563Sgad				/*
17383563Sgad				 * EditLine init may call 'cgetset()' to set a
17483563Sgad				 * capability-db meant for termcap (eg: to set
17583563Sgad				 * terminal type 'xterm').  Reset that now, or
17683563Sgad				 * that same db-information will be used for
17783563Sgad				 * printcap (giving us an "xterm" printer, with
17883563Sgad				 * all kinds of invalid capabilities...).
17983563Sgad				 */
18083563Sgad				cgetset(NULL);
18150039Smdodd			}
18250039Smdodd			if ((bp = el_gets(el, &num)) == NULL || num == 0)
18362294Smph				quit(0, NULL);
18450039Smdodd
18550077Smdodd			len = (num > MAX_CMDLINE) ? MAX_CMDLINE : num;
18650071Smdodd			memcpy(cmdline, bp, len);
18750071Smdodd			cmdline[len] = 0;
18850039Smdodd			history(hist, H_ENTER, bp);
18950039Smdodd
19050039Smdodd		} else {
19150039Smdodd			if (fgets(cmdline, MAX_CMDLINE, stdin) == 0)
19250039Smdodd				quit(0, NULL);
19350039Smdodd			if (cmdline[0] == 0 || cmdline[0] == '\n')
19450039Smdodd				break;
1951553Srgrimes		}
19650039Smdodd
1971553Srgrimes		makeargv();
19850039Smdodd		if (margc == 0)
19950039Smdodd			continue;
20050039Smdodd		if (el_parse(el, margc, margv) != -1)
20150039Smdodd			continue;
20250039Smdodd
2031553Srgrimes		c = getcmd(margv[0]);
2041553Srgrimes		if (c == (struct cmd *)-1) {
2051553Srgrimes			printf("?Ambiguous command\n");
2061553Srgrimes			continue;
2071553Srgrimes		}
2081553Srgrimes		if (c == 0) {
2091553Srgrimes			printf("?Invalid command\n");
2101553Srgrimes			continue;
2111553Srgrimes		}
21215637Sjoerg		if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) {
2131553Srgrimes			printf("?Privileged command\n");
2141553Srgrimes			continue;
2151553Srgrimes		}
21678750Sgad
21778750Sgad		/*
21878750Sgad		 * Two different commands might have the same generic rtn
21978750Sgad		 * (eg: "clean" and "tclean"), and just use different
22078750Sgad		 * handler routines for distinct command-setup.  The handler
22178750Sgad		 * routine might also be set on a generic routine for
22278750Sgad		 * initial parameter processing.
22378750Sgad		 */
22431492Swollman		if (c->c_generic != 0)
22578750Sgad			generic(c->c_generic, c->c_handler, margc, margv);
22631492Swollman		else
22731492Swollman			(*c->c_handler)(margc, margv);
2281553Srgrimes	}
2291553Srgrimes}
2301553Srgrimes
23115637Sjoergstatic struct cmd *
23278146Sgadgetcmd(const char *name)
2331553Srgrimes{
23478146Sgad	register const char *p, *q;
2351553Srgrimes	register struct cmd *c, *found;
2361553Srgrimes	register int nmatches, longest;
2371553Srgrimes
2381553Srgrimes	longest = 0;
2391553Srgrimes	nmatches = 0;
2401553Srgrimes	found = 0;
24119202Simp	for (c = cmdtab; (p = c->c_name); c++) {
2421553Srgrimes		for (q = name; *q == *p++; q++)
2431553Srgrimes			if (*q == 0)		/* exact match? */
2441553Srgrimes				return(c);
2451553Srgrimes		if (!*q) {			/* the name was a prefix */
2461553Srgrimes			if (q - name > longest) {
2471553Srgrimes				longest = q - name;
2481553Srgrimes				nmatches = 1;
2491553Srgrimes				found = c;
2501553Srgrimes			} else if (q - name == longest)
2511553Srgrimes				nmatches++;
2521553Srgrimes		}
2531553Srgrimes	}
2541553Srgrimes	if (nmatches > 1)
2551553Srgrimes		return((struct cmd *)-1);
2561553Srgrimes	return(found);
2571553Srgrimes}
2581553Srgrimes
2591553Srgrimes/*
2601553Srgrimes * Slice a string up into argc/argv.
2611553Srgrimes */
2621553Srgrimesstatic void
26378146Sgadmakeargv(void)
2641553Srgrimes{
2651553Srgrimes	register char *cp;
2661553Srgrimes	register char **argp = margv;
26727618Simp	register int n = 0;
2681553Srgrimes
2691553Srgrimes	margc = 0;
27080173Sgad	for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) &&
27127618Simp	    n < MAX_MARGV; n++) {
2721553Srgrimes		while (isspace(*cp))
2731553Srgrimes			cp++;
2741553Srgrimes		if (*cp == '\0')
2751553Srgrimes			break;
2761553Srgrimes		*argp++ = cp;
2771553Srgrimes		margc += 1;
2781553Srgrimes		while (*cp != '\0' && !isspace(*cp))
2791553Srgrimes			cp++;
2801553Srgrimes		if (*cp == '\0')
2811553Srgrimes			break;
2821553Srgrimes		*cp++ = '\0';
2831553Srgrimes	}
2841553Srgrimes	*argp++ = 0;
2851553Srgrimes}
2861553Srgrimes
2871553Srgrimes#define HELPINDENT (sizeof ("directory"))
2881553Srgrimes
2891553Srgrimes/*
2901553Srgrimes * Help command.
2911553Srgrimes */
2921553Srgrimesvoid
29378146Sgadhelp(int argc, char *argv[])
2941553Srgrimes{
2951553Srgrimes	register struct cmd *c;
2961553Srgrimes
2971553Srgrimes	if (argc == 1) {
2981553Srgrimes		register int i, j, w;
2991553Srgrimes		int columns, width = 0, lines;
3001553Srgrimes
3011553Srgrimes		printf("Commands may be abbreviated.  Commands are:\n\n");
3021553Srgrimes		for (c = cmdtab; c->c_name; c++) {
3031553Srgrimes			int len = strlen(c->c_name);
3041553Srgrimes
3051553Srgrimes			if (len > width)
3061553Srgrimes				width = len;
3071553Srgrimes		}
3081553Srgrimes		width = (width + 8) &~ 7;
3091553Srgrimes		columns = 80 / width;
3101553Srgrimes		if (columns == 0)
3111553Srgrimes			columns = 1;
3121553Srgrimes		lines = (NCMDS + columns - 1) / columns;
3131553Srgrimes		for (i = 0; i < lines; i++) {
3141553Srgrimes			for (j = 0; j < columns; j++) {
3151553Srgrimes				c = cmdtab + j * lines + i;
3161553Srgrimes				if (c->c_name)
3171553Srgrimes					printf("%s", c->c_name);
3181553Srgrimes				if (c + lines >= &cmdtab[NCMDS]) {
3191553Srgrimes					printf("\n");
3201553Srgrimes					break;
3211553Srgrimes				}
3221553Srgrimes				w = strlen(c->c_name);
3231553Srgrimes				while (w < width) {
3241553Srgrimes					w = (w + 8) &~ 7;
3251553Srgrimes					putchar('\t');
3261553Srgrimes				}
3271553Srgrimes			}
3281553Srgrimes		}
3291553Srgrimes		return;
3301553Srgrimes	}
3311553Srgrimes	while (--argc > 0) {
3321553Srgrimes		register char *arg;
3331553Srgrimes		arg = *++argv;
3341553Srgrimes		c = getcmd(arg);
3351553Srgrimes		if (c == (struct cmd *)-1)
3361553Srgrimes			printf("?Ambiguous help command %s\n", arg);
3371553Srgrimes		else if (c == (struct cmd *)0)
3381553Srgrimes			printf("?Invalid help command %s\n", arg);
3391553Srgrimes		else
34034784Sjb			printf("%-*s\t%s\n", (int) HELPINDENT,
3411553Srgrimes				c->c_name, c->c_help);
3421553Srgrimes	}
3431553Srgrimes}
34415637Sjoerg
34515637Sjoerg/*
34615637Sjoerg * return non-zero if the user is a member of the given group
34715637Sjoerg */
34815637Sjoergstatic int
34978146Sgadingroup(const char *grname)
35015637Sjoerg{
35115637Sjoerg	static struct group *gptr=NULL;
35265035Salfred	static int ngroups = 0;
35315637Sjoerg	static gid_t groups[NGROUPS];
35415637Sjoerg	register gid_t gid;
35515637Sjoerg	register int i;
35615637Sjoerg
35715637Sjoerg	if (gptr == NULL) {
35815637Sjoerg		if ((gptr = getgrnam(grname)) == NULL) {
35929780Scharnier			warnx("warning: unknown group '%s'", grname);
36015637Sjoerg			return(0);
36115637Sjoerg		}
36265035Salfred		ngroups = getgroups(NGROUPS, groups);
36365035Salfred		if (ngroups < 0)
36429780Scharnier			err(1, "getgroups");
36515637Sjoerg	}
36615637Sjoerg	gid = gptr->gr_gid;
36765035Salfred	for (i = 0; i < ngroups; i++)
36815637Sjoerg		if (gid == groups[i])
36915637Sjoerg			return(1);
37015637Sjoerg	return(0);
37115637Sjoerg}
372