xref: /dragonfly/usr.sbin/lpr/lpc/lpc.c (revision b40e316c)
1 /*
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by the University of
17  *	California, Berkeley and its contributors.
18  * 4. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * @(#) Copyright (c) 1983, 1993 The Regents of the University of California.  All rights reserved.
35  * @(#)lpc.c	8.3 (Berkeley) 4/28/95
36  * $FreeBSD: src/usr.sbin/lpr/lpc/lpc.c,v 1.13.2.11 2002/07/26 03:12:07 gad Exp $
37  * $DragonFly: src/usr.sbin/lpr/lpc/lpc.c,v 1.4 2004/03/22 22:32:50 cpressey Exp $
38  */
39 
40 #include <sys/param.h>
41 
42 #include <ctype.h>
43 #include <dirent.h>
44 #include <err.h>
45 #include <grp.h>
46 #include <setjmp.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <syslog.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <histedit.h>
54 
55 #include "lp.h"
56 #include "lpc.h"
57 #include "extern.h"
58 
59 #ifndef LPR_OPER
60 #define LPR_OPER	"operator"	/* group name of lpr operators */
61 #endif
62 
63 /*
64  * lpc -- line printer control program
65  */
66 
67 #define MAX_CMDLINE	200
68 #define MAX_MARGV	20
69 static int	fromatty;
70 
71 static char	cmdline[MAX_CMDLINE];
72 static int	margc;
73 static char	*margv[MAX_MARGV];
74 uid_t		uid, euid;
75 
76 int			 main(int _argc, char **_argv);
77 static void		 cmdscanner(void);
78 static struct cmd	*getcmd(const char *_name);
79 static void		 intr(int _signo);
80 static void		 makeargv(void);
81 static int		 ingroup(const char *_grname);
82 
83 int
84 main(int argc, char **argv)
85 {
86 	struct cmd *c;
87 
88 	euid = geteuid();
89 	uid = getuid();
90 	seteuid(uid);
91 	progname = argv[0];
92 	openlog("lpd", 0, LOG_LPR);
93 
94 	if (--argc > 0) {
95 		c = getcmd(*++argv);
96 		if (c == (struct cmd *)-1) {
97 			printf("?Ambiguous command\n");
98 			exit(1);
99 		}
100 		if (c == 0) {
101 			printf("?Invalid command\n");
102 			exit(1);
103 		}
104 		if ((c->c_opts & LPC_PRIVCMD) && getuid() &&
105 		    ingroup(LPR_OPER) == 0) {
106 			printf("?Privileged command\n");
107 			exit(1);
108 		}
109 		if (c->c_generic != 0)
110 			generic(c->c_generic, c->c_opts, c->c_handler,
111 			    argc, argv);
112 		else
113 			(*c->c_handler)(argc, argv);
114 		exit(0);
115 	}
116 	fromatty = isatty(fileno(stdin));
117 	if (!fromatty)
118 		signal(SIGINT, intr);
119 	for (;;) {
120 		cmdscanner();
121 	}
122 }
123 
124 static void
125 intr(int signo __unused)
126 {
127 	/* (the '__unused' is just to avoid a compile-time warning) */
128 	exit(0);
129 }
130 
131 static const char *
132 lpc_prompt(void)
133 {
134 	return ("lpc> ");
135 }
136 
137 /*
138  * Command parser.
139  */
140 static void
141 cmdscanner(void)
142 {
143 	struct cmd *c;
144 	static EditLine *el;
145 	static History *hist;
146 	size_t len;
147 	int num;
148 	const char *bp;
149 
150 	num = 0;
151 	bp = NULL;
152 	el = NULL;
153 	hist = NULL;
154 	for (;;) {
155 		if (fromatty) {
156 			if (!el) {
157 				el = el_init("lpc", stdin, stdout);
158 				hist = history_init();
159 				history(hist, H_EVENT, 100);
160 				el_set(el, EL_HIST, history, hist);
161 				el_set(el, EL_EDITOR, "emacs");
162 				el_set(el, EL_PROMPT, lpc_prompt);
163 				el_set(el, EL_SIGNAL, 1);
164 				el_source(el, NULL);
165 				/*
166 				 * EditLine init may call 'cgetset()' to set a
167 				 * capability-db meant for termcap (eg: to set
168 				 * terminal type 'xterm').  Reset that now, or
169 				 * that same db-information will be used for
170 				 * printcap (giving us an "xterm" printer, with
171 				 * all kinds of invalid capabilities...).
172 				 */
173 				cgetset(NULL);
174 			}
175 			if ((bp = el_gets(el, &num)) == NULL || num == 0)
176 				quit(0, NULL);
177 
178 			len = (num > MAX_CMDLINE -1) ? MAX_CMDLINE -1 : num;
179 			memcpy(cmdline, bp, len);
180 			cmdline[len] = 0;
181 			history(hist, H_ENTER, bp);
182 
183 		} else {
184 			if (fgets(cmdline, MAX_CMDLINE, stdin) == 0)
185 				quit(0, NULL);
186 			if (cmdline[0] == 0 || cmdline[0] == '\n')
187 				break;
188 		}
189 
190 		makeargv();
191 		if (margc == 0)
192 			continue;
193 		if (el_parse(el, margc, margv) != -1)
194 			continue;
195 
196 		c = getcmd(margv[0]);
197 		if (c == (struct cmd *)-1) {
198 			printf("?Ambiguous command\n");
199 			continue;
200 		}
201 		if (c == 0) {
202 			printf("?Invalid command\n");
203 			continue;
204 		}
205 		if ((c->c_opts & LPC_PRIVCMD) && getuid() &&
206 		    ingroup(LPR_OPER) == 0) {
207 			printf("?Privileged command\n");
208 			continue;
209 		}
210 
211 		/*
212 		 * Two different commands might have the same generic rtn
213 		 * (eg: "clean" and "tclean"), and just use different
214 		 * handler routines for distinct command-setup.  The handler
215 		 * routine might also be set on a generic routine for
216 		 * initial parameter processing.
217 		 */
218 		if (c->c_generic != 0)
219 			generic(c->c_generic, c->c_opts, c->c_handler,
220 			    margc, margv);
221 		else
222 			(*c->c_handler)(margc, margv);
223 	}
224 }
225 
226 static struct cmd *
227 getcmd(const char *name)
228 {
229 	const char *p, *q;
230 	struct cmd *c, *found;
231 	int nmatches, longest;
232 
233 	longest = 0;
234 	nmatches = 0;
235 	found = 0;
236 	for (c = cmdtab; (p = c->c_name); c++) {
237 		for (q = name; *q == *p++; q++)
238 			if (*q == 0)		/* exact match? */
239 				return(c);
240 		if (!*q) {			/* the name was a prefix */
241 			if (q - name > longest) {
242 				longest = q - name;
243 				nmatches = 1;
244 				found = c;
245 			} else if (q - name == longest)
246 				nmatches++;
247 		}
248 	}
249 	if (nmatches > 1)
250 		return((struct cmd *)-1);
251 	return(found);
252 }
253 
254 /*
255  * Slice a string up into argc/argv.
256  */
257 static void
258 makeargv(void)
259 {
260 	char *cp;
261 	char **argp = margv;
262 	int n = 0;
263 
264 	margc = 0;
265 	for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) &&
266 	    n < MAX_MARGV -1; n++) {
267 		while (isspace(*cp))
268 			cp++;
269 		if (*cp == '\0')
270 			break;
271 		*argp++ = cp;
272 		margc += 1;
273 		while (*cp != '\0' && !isspace(*cp))
274 			cp++;
275 		if (*cp == '\0')
276 			break;
277 		*cp++ = '\0';
278 	}
279 	*argp++ = 0;
280 }
281 
282 #define HELPINDENT (sizeof ("directory"))
283 
284 /*
285  * Help command.
286  */
287 void
288 help(int argc, char **argv)
289 {
290 	struct cmd *c;
291 
292 	if (argc == 1) {
293 		int i, j, w;
294 		int columns, width = 0, lines;
295 
296 		printf("Commands may be abbreviated.  Commands are:\n\n");
297 		for (c = cmdtab; c->c_name; c++) {
298 			int len = strlen(c->c_name);
299 
300 			if (len > width)
301 				width = len;
302 		}
303 		width = (width + 8) &~ 7;
304 		columns = 80 / width;
305 		if (columns == 0)
306 			columns = 1;
307 		lines = (NCMDS + columns - 1) / columns;
308 		for (i = 0; i < lines; i++) {
309 			for (j = 0; j < columns; j++) {
310 				c = cmdtab + j * lines + i;
311 				if (c->c_name)
312 					printf("%s", c->c_name);
313 				if (c + lines >= &cmdtab[NCMDS]) {
314 					printf("\n");
315 					break;
316 				}
317 				w = strlen(c->c_name);
318 				while (w < width) {
319 					w = (w + 8) &~ 7;
320 					putchar('\t');
321 				}
322 			}
323 		}
324 		return;
325 	}
326 	while (--argc > 0) {
327 		char *arg;
328 
329 		arg = *++argv;
330 		c = getcmd(arg);
331 		if (c == (struct cmd *)-1)
332 			printf("?Ambiguous help command %s\n", arg);
333 		else if (c == (struct cmd *)0)
334 			printf("?Invalid help command %s\n", arg);
335 		else
336 			printf("%-*s\t%s\n", (int) HELPINDENT,
337 				c->c_name, c->c_help);
338 	}
339 }
340 
341 /*
342  * return non-zero if the user is a member of the given group
343  */
344 static int
345 ingroup(const char *grname)
346 {
347 	static struct group *gptr=NULL;
348 	static int ngroups = 0;
349 	static gid_t groups[NGROUPS];
350 	gid_t gid;
351 	int i;
352 
353 	if (gptr == NULL) {
354 		if ((gptr = getgrnam(grname)) == NULL) {
355 			warnx("warning: unknown group '%s'", grname);
356 			return(0);
357 		}
358 		ngroups = getgroups(NGROUPS, groups);
359 		if (ngroups < 0)
360 			err(1, "getgroups");
361 	}
362 	gid = gptr->gr_gid;
363 	for (i = 0; i < ngroups; i++)
364 		if (gid == groups[i])
365 			return(1);
366 	return(0);
367 }
368 
369 /*
370  * Routine to get the information for a single printer (which will be
371  * called by the routines which implement individual commands).
372  * Note: This is for commands operating on a *single* printer.
373  */
374 struct printer *
375 setup_myprinter(char *pwanted, struct printer *pp, int sump_opts)
376 {
377 	int cdres, cmdstatus;
378 
379 	init_printer(pp);
380 	cmdstatus = getprintcap(pwanted, pp);
381 	switch (cmdstatus) {
382 	default:
383 		fatal(pp, "%s", pcaperr(cmdstatus));
384 		/* NOTREACHED */
385 	case PCAPERR_NOTFOUND:
386 		printf("unknown printer %s\n", pwanted);
387 		return (NULL);
388 	case PCAPERR_TCOPEN:
389 		printf("warning: %s: unresolved tc= reference(s)", pwanted);
390 		break;
391 	case PCAPERR_SUCCESS:
392 		break;
393 	}
394 	if ((sump_opts & SUMP_NOHEADER) == 0)
395 		printf("%s:\n", pp->printer);
396 
397 	if (sump_opts & SUMP_CHDIR_SD) {
398 		seteuid(euid);
399 		cdres = chdir(pp->spool_dir);
400 		seteuid(uid);
401 		if (cdres < 0) {
402 			printf("\tcannot chdir to %s\n", pp->spool_dir);
403 			free_printer(pp);
404 			return (NULL);
405 		}
406 	}
407 
408 	return (pp);
409 }
410