xref: /openbsd-src/usr.sbin/lpr/lpc/lpc.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: lpc.c,v 1.17 2004/09/30 18:20:06 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.17 2004/09/30 18:20:06 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 		makeargv();
163 		if (margc == 0)
164 			break;
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 = cmdline;
217 	char **ap = margv;
218 
219 	margc = 0;
220 	while (margc < MAX_MARGV - 1 && (*ap = strsep(&cp, " \t\n")) != NULL) {
221 		if (**ap != '\0') {
222 			ap++;
223 			margc++;
224 		}
225 	}
226 	*ap = NULL;
227 }
228 
229 #define HELPINDENT ((int)sizeof("directory"))
230 
231 /*
232  * Help command.
233  */
234 void
235 help(int argc, char **argv)
236 {
237 	struct cmd *c;
238 
239 	if (argc == 1) {
240 		int i, j, w;
241 		int columns, width = 0, lines;
242 
243 		printf("Commands may be abbreviated.  Commands are:\n\n");
244 		for (c = cmdtab; c->c_name; c++) {
245 			int len = strlen(c->c_name);
246 
247 			if (len > width)
248 				width = len;
249 		}
250 		width = (width + 8) &~ 7;
251 		columns = 80 / width;
252 		if (columns == 0)
253 			columns = 1;
254 		lines = (NCMDS + columns - 1) / columns;
255 		for (i = 0; i < lines; i++) {
256 			for (j = 0; j < columns; j++) {
257 				c = cmdtab + j * lines + i;
258 				if (c->c_name)
259 					printf("%s", c->c_name);
260 				if (c + lines >= &cmdtab[NCMDS]) {
261 					printf("\n");
262 					break;
263 				}
264 				w = strlen(c->c_name);
265 				while (w < width) {
266 					w = (w + 8) &~ 7;
267 					putchar('\t');
268 				}
269 			}
270 		}
271 		return;
272 	}
273 	while (--argc > 0) {
274 		char *arg;
275 
276 		arg = *++argv;
277 		c = getcmd(arg);
278 		if (c == (struct cmd *)-1)
279 			printf("?Ambiguous help command %s\n", arg);
280 		else if (c == (struct cmd *)0)
281 			printf("?Invalid help command %s\n", arg);
282 		else
283 			printf("%-*s\t%s\n", HELPINDENT,
284 				c->c_name, c->c_help);
285 	}
286 }
287 
288 /*
289  * return non-zero if the user is a member of the given group
290  */
291 static int
292 ingroup(char *grname)
293 {
294 	static struct group *gptr = NULL;
295 	static gid_t groups[NGROUPS];
296 	static int ngroups;
297 	gid_t gid;
298 	int i;
299 
300 	if (gptr == NULL) {
301 		if ((gptr = getgrnam(grname)) == NULL) {
302 			warnx("Warning: unknown group `%s'", grname);
303 			return(0);
304 		}
305 		if ((ngroups = getgroups(NGROUPS, groups)) < 0)
306 			err(1, "getgroups");
307 	}
308 	gid = gptr->gr_gid;
309 	for (i = 0; i < ngroups; i++)
310 		if (gid == groups[i])
311 			return(1);
312 	return(0);
313 }
314