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