1331722Seadler/*
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 * 4. Neither the name of the University nor the names of its contributors
151553Srgrimes *    may be used to endorse or promote products derived from this software
161553Srgrimes *    without specific prior written permission.
171553Srgrimes *
181553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
191553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
201553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
211553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
221553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
231553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
241553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
251553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
261553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
271553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
281553Srgrimes * SUCH DAMAGE.
291553Srgrimes */
301553Srgrimes
311553Srgrimes#ifndef lint
3229780Scharnierstatic const char copyright[] =
331553Srgrimes"@(#) Copyright (c) 1983, 1993\n\
341553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
351553Srgrimes#endif /* not lint */
361553Srgrimes
37117599Sgad#if 0
381553Srgrimes#ifndef lint
3915637Sjoergstatic char sccsid[] = "@(#)lpc.c	8.3 (Berkeley) 4/28/95";
40117599Sgad#endif /* not lint */
4129780Scharnier#endif
421553Srgrimes
43117599Sgad#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
44117599Sgad__FBSDID("$FreeBSD: stable/11/usr.sbin/lpr/lpc/lpc.c 347610 2019-05-15 07:51:30Z ngie $");
45117599Sgad
461553Srgrimes#include <sys/param.h>
471553Srgrimes
4829780Scharnier#include <ctype.h>
491553Srgrimes#include <dirent.h>
5031492Swollman#include <err.h>
5129780Scharnier#include <grp.h>
5229780Scharnier#include <setjmp.h>
531553Srgrimes#include <signal.h>
5429780Scharnier#include <stdio.h>
5529780Scharnier#include <stdlib.h>
561553Srgrimes#include <syslog.h>
5729780Scharnier#include <string.h>
581553Srgrimes#include <unistd.h>
5950039Smdodd#include <histedit.h>
6031492Swollman
611553Srgrimes#include "lp.h"
621553Srgrimes#include "lpc.h"
631553Srgrimes#include "extern.h"
641553Srgrimes
6515637Sjoerg#ifndef LPR_OPER
6615637Sjoerg#define LPR_OPER	"operator"	/* group name of lpr operators */
6715637Sjoerg#endif
6815637Sjoerg
691553Srgrimes/*
701553Srgrimes * lpc -- line printer control program
711553Srgrimes */
721553Srgrimes
7327618Simp#define MAX_CMDLINE	200
7427618Simp#define MAX_MARGV	20
7539084Swollmanstatic int	fromatty;
761553Srgrimes
7739084Swollmanstatic char	cmdline[MAX_CMDLINE];
7839084Swollmanstatic int	margc;
7939084Swollmanstatic char	*margv[MAX_MARGV];
8039084Swollmanuid_t		uid, euid;
811553Srgrimes
8278146Sgadint			 main(int _argc, char *_argv[]);
8378146Sgadstatic void		 cmdscanner(void);
8478146Sgadstatic struct cmd	*getcmd(const char *_name);
8578146Sgadstatic void		 intr(int _signo);
8678146Sgadstatic void		 makeargv(void);
8778146Sgadstatic int		 ingroup(const char *_grname);
881553Srgrimes
891553Srgrimesint
9078146Sgadmain(int argc, char *argv[])
911553Srgrimes{
921553Srgrimes	register struct cmd *c;
931553Srgrimes
9427618Simp	euid = geteuid();
9527618Simp	uid = getuid();
96241852Seadler	PRIV_END
9778280Sgad	progname = argv[0];
981553Srgrimes	openlog("lpd", 0, LOG_LPR);
991553Srgrimes
1001553Srgrimes	if (--argc > 0) {
1011553Srgrimes		c = getcmd(*++argv);
1021553Srgrimes		if (c == (struct cmd *)-1) {
1031553Srgrimes			printf("?Ambiguous command\n");
1041553Srgrimes			exit(1);
1051553Srgrimes		}
106297795Spfg		if (c == NULL) {
1071553Srgrimes			printf("?Invalid command\n");
1081553Srgrimes			exit(1);
1091553Srgrimes		}
11098267Sgad		if ((c->c_opts & LPC_PRIVCMD) && getuid() &&
11198267Sgad		    ingroup(LPR_OPER) == 0) {
1121553Srgrimes			printf("?Privileged command\n");
1131553Srgrimes			exit(1);
1141553Srgrimes		}
115297795Spfg		if (c->c_generic != NULL)
11698267Sgad			generic(c->c_generic, c->c_opts, c->c_handler,
11798267Sgad			    argc, argv);
11831492Swollman		else
11931492Swollman			(*c->c_handler)(argc, argv);
1201553Srgrimes		exit(0);
1211553Srgrimes	}
1221553Srgrimes	fromatty = isatty(fileno(stdin));
12350039Smdodd	if (!fromatty)
1241553Srgrimes		signal(SIGINT, intr);
1251553Srgrimes	for (;;) {
12650039Smdodd		cmdscanner();
1271553Srgrimes	}
1281553Srgrimes}
1291553Srgrimes
1301553Srgrimesstatic void
13178146Sgadintr(int signo __unused)
1321553Srgrimes{
13378146Sgad	/* (the '__unused' is just to avoid a compile-time warning) */
13450039Smdodd	exit(0);
1351553Srgrimes}
1361553Srgrimes
13778146Sgadstatic const char *
13868400Sgadlpc_prompt(void)
13950039Smdodd{
14068400Sgad	return ("lpc> ");
14150039Smdodd}
14250039Smdodd
1431553Srgrimes/*
1441553Srgrimes * Command parser.
1451553Srgrimes */
1461553Srgrimesstatic void
14778146Sgadcmdscanner(void)
1481553Srgrimes{
1491553Srgrimes	register struct cmd *c;
15079742Sgad	static EditLine *el;
15179742Sgad	static History *hist;
15284261Sobrien	HistEvent he;
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) {
16484261Sobrien				el = el_init("lpc", stdin, stdout, stderr);
16550039Smdodd				hist = history_init();
166151476Sstefanf				history(hist, &he, H_SETSIZE, 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
185298910Saraujo			len = MIN(MAX_CMDLINE - 1, num);
18650071Smdodd			memcpy(cmdline, bp, len);
18750071Smdodd			cmdline[len] = 0;
18884261Sobrien			history(hist, &he, H_ENTER, bp);
18950039Smdodd
19050039Smdodd		} else {
191230044Skevlo			if (fgets(cmdline, MAX_CMDLINE, stdin) == NULL)
19250039Smdodd				quit(0, NULL);
19350039Smdodd			if (cmdline[0] == 0 || cmdline[0] == '\n')
19450039Smdodd				break;
1951553Srgrimes		}
19650039Smdodd
1971553Srgrimes		makeargv();
19850039Smdodd		if (margc == 0)
19950039Smdodd			continue;
200347610Sngie		if (el != NULL && el_parse(el, margc, (const char **)margv) != -1)
20150039Smdodd			continue;
20250039Smdodd
2031553Srgrimes		c = getcmd(margv[0]);
2041553Srgrimes		if (c == (struct cmd *)-1) {
2051553Srgrimes			printf("?Ambiguous command\n");
2061553Srgrimes			continue;
2071553Srgrimes		}
208297795Spfg		if (c == NULL) {
2091553Srgrimes			printf("?Invalid command\n");
2101553Srgrimes			continue;
2111553Srgrimes		}
21298267Sgad		if ((c->c_opts & LPC_PRIVCMD) && getuid() &&
21398267Sgad		    ingroup(LPR_OPER) == 0) {
2141553Srgrimes			printf("?Privileged command\n");
2151553Srgrimes			continue;
2161553Srgrimes		}
21778750Sgad
21878750Sgad		/*
21978750Sgad		 * Two different commands might have the same generic rtn
22078750Sgad		 * (eg: "clean" and "tclean"), and just use different
22178750Sgad		 * handler routines for distinct command-setup.  The handler
22278750Sgad		 * routine might also be set on a generic routine for
22378750Sgad		 * initial parameter processing.
22478750Sgad		 */
225297795Spfg		if (c->c_generic != NULL)
22698267Sgad			generic(c->c_generic, c->c_opts, c->c_handler,
22798267Sgad			    margc, margv);
22831492Swollman		else
22931492Swollman			(*c->c_handler)(margc, margv);
2301553Srgrimes	}
2311553Srgrimes}
2321553Srgrimes
23315637Sjoergstatic struct cmd *
23478146Sgadgetcmd(const char *name)
2351553Srgrimes{
23678146Sgad	register const char *p, *q;
2371553Srgrimes	register struct cmd *c, *found;
2381553Srgrimes	register int nmatches, longest;
2391553Srgrimes
2401553Srgrimes	longest = 0;
2411553Srgrimes	nmatches = 0;
242297795Spfg	found = NULL;
24319202Simp	for (c = cmdtab; (p = c->c_name); c++) {
2441553Srgrimes		for (q = name; *q == *p++; q++)
2451553Srgrimes			if (*q == 0)		/* exact match? */
2461553Srgrimes				return(c);
2471553Srgrimes		if (!*q) {			/* the name was a prefix */
2481553Srgrimes			if (q - name > longest) {
2491553Srgrimes				longest = q - name;
2501553Srgrimes				nmatches = 1;
2511553Srgrimes				found = c;
2521553Srgrimes			} else if (q - name == longest)
2531553Srgrimes				nmatches++;
2541553Srgrimes		}
2551553Srgrimes	}
2561553Srgrimes	if (nmatches > 1)
2571553Srgrimes		return((struct cmd *)-1);
2581553Srgrimes	return(found);
2591553Srgrimes}
2601553Srgrimes
2611553Srgrimes/*
2621553Srgrimes * Slice a string up into argc/argv.
2631553Srgrimes */
2641553Srgrimesstatic void
26578146Sgadmakeargv(void)
2661553Srgrimes{
2671553Srgrimes	register char *cp;
2681553Srgrimes	register char **argp = margv;
26927618Simp	register int n = 0;
2701553Srgrimes
2711553Srgrimes	margc = 0;
27280173Sgad	for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) &&
273121065Stjr	    n < MAX_MARGV - 1; n++) {
2741553Srgrimes		while (isspace(*cp))
2751553Srgrimes			cp++;
2761553Srgrimes		if (*cp == '\0')
2771553Srgrimes			break;
2781553Srgrimes		*argp++ = cp;
2791553Srgrimes		margc += 1;
2801553Srgrimes		while (*cp != '\0' && !isspace(*cp))
2811553Srgrimes			cp++;
2821553Srgrimes		if (*cp == '\0')
2831553Srgrimes			break;
2841553Srgrimes		*cp++ = '\0';
2851553Srgrimes	}
286297795Spfg	*argp++ = NULL;
2871553Srgrimes}
2881553Srgrimes
2891553Srgrimes#define HELPINDENT (sizeof ("directory"))
2901553Srgrimes
2911553Srgrimes/*
2921553Srgrimes * Help command.
2931553Srgrimes */
2941553Srgrimesvoid
29578146Sgadhelp(int argc, char *argv[])
2961553Srgrimes{
2971553Srgrimes	register struct cmd *c;
2981553Srgrimes
2991553Srgrimes	if (argc == 1) {
3001553Srgrimes		register int i, j, w;
3011553Srgrimes		int columns, width = 0, lines;
3021553Srgrimes
3031553Srgrimes		printf("Commands may be abbreviated.  Commands are:\n\n");
3041553Srgrimes		for (c = cmdtab; c->c_name; c++) {
3051553Srgrimes			int len = strlen(c->c_name);
3061553Srgrimes
3071553Srgrimes			if (len > width)
3081553Srgrimes				width = len;
3091553Srgrimes		}
3101553Srgrimes		width = (width + 8) &~ 7;
3111553Srgrimes		columns = 80 / width;
3121553Srgrimes		if (columns == 0)
3131553Srgrimes			columns = 1;
3141553Srgrimes		lines = (NCMDS + columns - 1) / columns;
3151553Srgrimes		for (i = 0; i < lines; i++) {
3161553Srgrimes			for (j = 0; j < columns; j++) {
3171553Srgrimes				c = cmdtab + j * lines + i;
3181553Srgrimes				if (c->c_name)
3191553Srgrimes					printf("%s", c->c_name);
3201553Srgrimes				if (c + lines >= &cmdtab[NCMDS]) {
3211553Srgrimes					printf("\n");
3221553Srgrimes					break;
3231553Srgrimes				}
3241553Srgrimes				w = strlen(c->c_name);
3251553Srgrimes				while (w < width) {
3261553Srgrimes					w = (w + 8) &~ 7;
3271553Srgrimes					putchar('\t');
3281553Srgrimes				}
3291553Srgrimes			}
3301553Srgrimes		}
3311553Srgrimes		return;
3321553Srgrimes	}
3331553Srgrimes	while (--argc > 0) {
3341553Srgrimes		register char *arg;
3351553Srgrimes		arg = *++argv;
3361553Srgrimes		c = getcmd(arg);
3371553Srgrimes		if (c == (struct cmd *)-1)
3381553Srgrimes			printf("?Ambiguous help command %s\n", arg);
3391553Srgrimes		else if (c == (struct cmd *)0)
3401553Srgrimes			printf("?Invalid help command %s\n", arg);
3411553Srgrimes		else
34234784Sjb			printf("%-*s\t%s\n", (int) HELPINDENT,
3431553Srgrimes				c->c_name, c->c_help);
3441553Srgrimes	}
3451553Srgrimes}
34615637Sjoerg
34715637Sjoerg/*
34815637Sjoerg * return non-zero if the user is a member of the given group
34915637Sjoerg */
35015637Sjoergstatic int
35178146Sgadingroup(const char *grname)
35215637Sjoerg{
35315637Sjoerg	static struct group *gptr=NULL;
35465035Salfred	static int ngroups = 0;
355194494Sbrooks	static long ngroups_max;
356194494Sbrooks	static gid_t *groups;
35715637Sjoerg	register gid_t gid;
35815637Sjoerg	register int i;
35915637Sjoerg
36015637Sjoerg	if (gptr == NULL) {
36115637Sjoerg		if ((gptr = getgrnam(grname)) == NULL) {
36229780Scharnier			warnx("warning: unknown group '%s'", grname);
36315637Sjoerg			return(0);
36415637Sjoerg		}
365194494Sbrooks		ngroups_max = sysconf(_SC_NGROUPS_MAX);
366194494Sbrooks		if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
367194494Sbrooks			err(1, "malloc");
368194494Sbrooks		ngroups = getgroups(ngroups_max, groups);
36965035Salfred		if (ngroups < 0)
37029780Scharnier			err(1, "getgroups");
37115637Sjoerg	}
37215637Sjoerg	gid = gptr->gr_gid;
37365035Salfred	for (i = 0; i < ngroups; i++)
37415637Sjoerg		if (gid == groups[i])
37515637Sjoerg			return(1);
37615637Sjoerg	return(0);
37715637Sjoerg}
378100203Sgad
379100203Sgad/*
380100203Sgad * Routine to get the information for a single printer (which will be
381100203Sgad * called by the routines which implement individual commands).
382100203Sgad * Note: This is for commands operating on a *single* printer.
383100203Sgad */
384100203Sgadstruct printer *
385100203Sgadsetup_myprinter(char *pwanted, struct printer *pp, int sump_opts)
386100203Sgad{
387100203Sgad	int cdres, cmdstatus;
388100203Sgad
389100203Sgad	init_printer(pp);
390100203Sgad	cmdstatus = getprintcap(pwanted, pp);
391100203Sgad	switch (cmdstatus) {
392100203Sgad	default:
393100203Sgad		fatal(pp, "%s", pcaperr(cmdstatus));
394100203Sgad		/* NOTREACHED */
395100203Sgad	case PCAPERR_NOTFOUND:
396100203Sgad		printf("unknown printer %s\n", pwanted);
397100203Sgad		return (NULL);
398100203Sgad	case PCAPERR_TCOPEN:
399100203Sgad		printf("warning: %s: unresolved tc= reference(s)", pwanted);
400100203Sgad		break;
401100203Sgad	case PCAPERR_SUCCESS:
402100203Sgad		break;
403100203Sgad	}
404100203Sgad	if ((sump_opts & SUMP_NOHEADER) == 0)
405100203Sgad		printf("%s:\n", pp->printer);
406100203Sgad
407100203Sgad	if (sump_opts & SUMP_CHDIR_SD) {
408241852Seadler		PRIV_START
409100203Sgad		cdres = chdir(pp->spool_dir);
410241852Seadler		PRIV_END
411100203Sgad		if (cdres < 0) {
412100203Sgad			printf("\tcannot chdir to %s\n", pp->spool_dir);
413100203Sgad			free_printer(pp);
414100203Sgad			return (NULL);
415100203Sgad		}
416100203Sgad	}
417100203Sgad
418100203Sgad	return (pp);
419100203Sgad}
420