1/*	$NetBSD: lpc.c,v 1.27 2017/05/04 16:26:09 sevan Exp $	*/
2
3/*
4 * Copyright (c) 1983, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34#ifndef lint
35__COPYRIGHT("@(#) Copyright (c) 1983, 1993\
36 The Regents of the University of California.  All rights reserved.");
37#if 0
38static char sccsid[] = "@(#)lpc.c	8.3 (Berkeley) 4/28/95";
39#else
40__RCSID("$NetBSD: lpc.c,v 1.27 2017/05/04 16:26:09 sevan Exp $");
41#endif
42#endif /* not lint */
43
44#include <sys/param.h>
45
46#include <dirent.h>
47#include <signal.h>
48#include <setjmp.h>
49#include <syslog.h>
50#include <histedit.h>
51#include <unistd.h>
52#include <stdlib.h>
53#include <stdio.h>
54#include <ctype.h>
55#include <string.h>
56#include <grp.h>
57#include <err.h>
58#include "lp.h"
59#include "lpc.h"
60#include "extern.h"
61
62#ifndef LPR_OPER
63#define LPR_OPER	"operator"	/* group name of lpr operators */
64#endif
65
66/*
67 * lpc -- line printer control program
68 */
69
70#define MAX_MARGV	20
71int	fromatty;
72
73char	*cmdline;
74int	margc;
75char	*margv[MAX_MARGV];
76int	top;
77uid_t	uid, euid;
78
79jmp_buf	toplevel;
80
81History	*hist;
82HistEvent he;
83EditLine *elptr;
84
85__dead static void	 cmdscanner(int);
86static struct cmd	*getcmd(const char *);
87__dead static void	 intr(int);
88static void		 makeargv(void);
89static int		 ingroup(const char *);
90const char		*prompt(void);
91static int		 parse(char *, char *p[], int);
92
93int
94main(int argc, char *argv[])
95{
96	euid = geteuid();
97	uid = getuid();
98	seteuid(uid);
99	setprogname(argv[0]);
100	openlog("lpd", 0, LOG_LPR);
101
102	if (--argc > 0) {
103		argv++;
104		exit(!parse(*argv, argv, argc));
105	}
106	fromatty = isatty(fileno(stdin));
107	top = setjmp(toplevel) == 0;
108	if (top)
109		signal(SIGINT, intr);
110	for (;;) {
111		cmdscanner(top);
112		top = 1;
113	}
114}
115
116static int
117parse(char *arg, char **pargv, int pargc)
118{
119	struct cmd *c;
120
121	c = getcmd(arg);
122	if (c == (struct cmd *)-1) {
123		printf("?Ambiguous command\n");
124		return(0);
125	}
126	if (c == 0) {
127		printf("?Invalid command\n");
128		return(0);
129	}
130	if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) {
131		printf("?Privileged command\n");
132		return(0);
133	}
134	(*c->c_handler)(pargc, pargv);
135	return(1);
136}
137
138static void
139intr(int signo)
140{
141	el_end(elptr);
142	history_end(hist);
143	if (!fromatty)
144		exit(0);
145	longjmp(toplevel, 1);
146}
147
148const char *
149prompt(void)
150{
151	return ("lpc> ");
152}
153
154/*
155 * Command parser.
156 */
157static void
158cmdscanner(int tp)
159{
160	int scratch;
161	const char *elline;
162
163	if (!tp)
164		putchar('\n');
165	hist = history_init();
166	history(hist, &he, H_SETSIZE, 100);	/* 100 elt history buffer */
167
168	elptr = el_init(getprogname(), stdin, stdout, stderr);
169	el_set(elptr, EL_EDITOR, "emacs");
170	el_set(elptr, EL_PROMPT, prompt);
171	el_set(elptr, EL_HIST, history, hist);
172	el_source(elptr, NULL);
173
174	for (;;) {
175		cmdline = NULL;
176		do {
177			if (((elline = el_gets(elptr, &scratch)) != NULL)
178			    && (scratch != 0)) {
179				history(hist, &he, H_ENTER, elline);
180				cmdline = strdup(elline);
181				makeargv();
182			} else {
183				margc = 0;
184				break;
185			}
186		} while (margc == 0);
187		if (margc == 0)
188			quit(0, NULL);
189		if (!parse(cmdline, margv, margc)) {
190			if (cmdline != NULL)
191				free(cmdline);
192			continue;
193		}
194		fflush(stdout);
195		if (cmdline != NULL)
196			free(cmdline);
197	}
198	longjmp(toplevel, 0);
199}
200
201static struct cmd *
202getcmd(const char *name)
203{
204	const char *p, *q;
205	struct cmd *c, *found;
206	int nmatches, longest;
207
208	longest = 0;
209	nmatches = 0;
210	found = 0;
211	for (c = cmdtab; (p = c->c_name) != NULL; c++) {
212		for (q = name; *q == *p++; q++)
213			if (*q == 0)		/* exact match? */
214				return(c);
215		if (!*q) {			/* the name was a prefix */
216			if (q - name > longest) {
217				longest = q - name;
218				nmatches = 1;
219				found = c;
220			} else if (q - name == longest)
221				nmatches++;
222		}
223	}
224	if (nmatches > 1)
225		return((struct cmd *)-1);
226	return(found);
227}
228
229/*
230 * Slice a string up into argc/argv.
231 */
232static void
233makeargv(void)
234{
235	char *cp;
236	char **argp = margv;
237	int n = 0;
238	size_t s;
239
240	s = strlen(cmdline) + 1;
241	margc = 0;
242	for (cp = cmdline; *cp && (size_t)(cp - cmdline) < s && n < MAX_MARGV; n++) {
243		while (isspace((unsigned char)*cp))
244			cp++;
245		if (*cp == '\0')
246			break;
247		*argp++ = cp;
248		margc += 1;
249		while (*cp != '\0' && !isspace((unsigned char)*cp))
250			cp++;
251		if (*cp == '\0')
252			break;
253		*cp++ = '\0';
254	}
255	*argp++ = 0;
256}
257
258#define HELPINDENT (sizeof ("directory"))
259
260/*
261 * Help command.
262 */
263void
264help(int argc, char *argv[])
265{
266	struct cmd *c;
267
268	if (argc == 1) {
269		size_t i, j, w;
270		size_t columns, width = 0, lines;
271
272		printf("Commands may be abbreviated.  Commands are:\n\n");
273		for (c = cmdtab; c->c_name; c++) {
274			size_t len = strlen(c->c_name);
275
276			if (len > width)
277				width = len;
278		}
279		width = (width + 8) &~ 7;
280		columns = 80 / width;
281		if (columns == 0)
282			columns = 1;
283		lines = (NCMDS + columns - 1) / columns;
284		for (i = 0; i < lines; i++) {
285			for (j = 0; j < columns; j++) {
286				c = cmdtab + j * lines + i;
287				printf("%s", c->c_name);
288				if (c + lines >= &cmdtab[NCMDS - 1]) {
289					printf("\n");
290					break;
291				}
292				w = strlen(c->c_name);
293				while (w < width) {
294					w = (w + 8) &~ 7;
295					putchar('\t');
296				}
297			}
298		}
299		return;
300	}
301	while (--argc > 0) {
302		char *arg;
303		arg = *++argv;
304		c = getcmd(arg);
305		if (c == (struct cmd *)-1)
306			printf("?Ambiguous help command %s\n", arg);
307		else if (c == (struct cmd *)0)
308			printf("?Invalid help command %s\n", arg);
309		else
310			printf("%-*s\t%s\n", (int)HELPINDENT,
311				c->c_name, c->c_help);
312	}
313}
314
315/*
316 * return non-zero if the user is a member of the given group
317 */
318static int
319ingroup(const char *grname)
320{
321	static struct group *gptr = NULL;
322	static gid_t groups[NGROUPS];
323	static int ngroups;
324	gid_t gid;
325	int i;
326
327	if (gptr == NULL) {
328		if ((gptr = getgrnam(grname)) == NULL) {
329			warnx("Warning: unknown group `%s'",
330				grname);
331			return(0);
332		}
333		ngroups = getgroups(NGROUPS, groups);
334		if (ngroups < 0)
335			err(1, "getgroups");
336	}
337	gid = gptr->gr_gid;
338	for (i = 0; i < ngroups; i++)
339		if (gid == groups[i])
340			return(1);
341	return(0);
342}
343