Style(9) cleanup.
[dragonfly.git] / usr.sbin / lpr / lpc / lpc.c
CommitLineData
984263bc
MD
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.
1de703da
MD
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 $
be6b9a2e 37 * $DragonFly: src/usr.sbin/lpr/lpc/lpc.c,v 1.4 2004/03/22 22:32:50 cpressey Exp $
984263bc
MD
38 */
39
984263bc
MD
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
69static int fromatty;
70
71static char cmdline[MAX_CMDLINE];
72static int margc;
73static char *margv[MAX_MARGV];
74uid_t uid, euid;
75
be6b9a2e 76int main(int _argc, char **_argv);
984263bc
MD
77static void cmdscanner(void);
78static struct cmd *getcmd(const char *_name);
79static void intr(int _signo);
80static void makeargv(void);
81static int ingroup(const char *_grname);
82
83int
be6b9a2e 84main(int argc, char **argv)
984263bc 85{
be6b9a2e 86 struct cmd *c;
984263bc
MD
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
124static void
125intr(int signo __unused)
126{
127 /* (the '__unused' is just to avoid a compile-time warning) */
128 exit(0);
129}
130
131static const char *
132lpc_prompt(void)
133{
134 return ("lpc> ");
135}
136
137/*
138 * Command parser.
139 */
140static void
141cmdscanner(void)
142{
be6b9a2e 143 struct cmd *c;
984263bc
MD
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
d2db1b77 178 len = (num > MAX_CMDLINE -1) ? MAX_CMDLINE -1 : num;
984263bc
MD
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
226static struct cmd *
227getcmd(const char *name)
228{
be6b9a2e
CP
229 const char *p, *q;
230 struct cmd *c, *found;
231 int nmatches, longest;
984263bc
MD
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 */
257static void
258makeargv(void)
259{
be6b9a2e
CP
260 char *cp;
261 char **argp = margv;
262 int n = 0;
984263bc
MD
263
264 margc = 0;
265 for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) &&
d2db1b77 266 n < MAX_MARGV -1; n++) {
984263bc
MD
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 */
287void
be6b9a2e 288help(int argc, char **argv)
984263bc 289{
be6b9a2e 290 struct cmd *c;
984263bc
MD
291
292 if (argc == 1) {
be6b9a2e 293 int i, j, w;
984263bc
MD
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) {
be6b9a2e
CP
327 char *arg;
328
984263bc
MD
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 */
344static int
345ingroup(const char *grname)
346{
347 static struct group *gptr=NULL;
348 static int ngroups = 0;
349 static gid_t groups[NGROUPS];
be6b9a2e
CP
350 gid_t gid;
351 int i;
984263bc
MD
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 */
374struct printer *
375setup_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}