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