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 35 #ifndef lint 36 static const char copyright[] = 37 "@(#) Copyright (c) 1983, 1993\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39 #endif /* not lint */ 40 41 #if 0 42 #ifndef lint 43 static char sccsid[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95"; 44 #endif /* not lint */ 45 #endif 46 47 #include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 48 __FBSDID("$FreeBSD$"); 49 50 #include <sys/param.h> 51 52 #include <ctype.h> 53 #include <dirent.h> 54 #include <err.h> 55 #include <grp.h> 56 #include <setjmp.h> 57 #include <signal.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <syslog.h> 61 #include <string.h> 62 #include <unistd.h> 63 #include <histedit.h> 64 65 #include "lp.h" 66 #include "lpc.h" 67 #include "extern.h" 68 69 #ifndef LPR_OPER 70 #define LPR_OPER "operator" /* group name of lpr operators */ 71 #endif 72 73 /* 74 * lpc -- line printer control program 75 */ 76 77 #define MAX_CMDLINE 200 78 #define MAX_MARGV 20 79 static int fromatty; 80 81 static char cmdline[MAX_CMDLINE]; 82 static int margc; 83 static char *margv[MAX_MARGV]; 84 uid_t uid, euid; 85 86 int main(int _argc, char *_argv[]); 87 static void cmdscanner(void); 88 static struct cmd *getcmd(const char *_name); 89 static void intr(int _signo); 90 static void makeargv(void); 91 static int ingroup(const char *_grname); 92 93 int 94 main(int argc, char *argv[]) 95 { 96 register struct cmd *c; 97 98 euid = geteuid(); 99 uid = getuid(); 100 seteuid(uid); 101 progname = argv[0]; 102 openlog("lpd", 0, LOG_LPR); 103 104 if (--argc > 0) { 105 c = getcmd(*++argv); 106 if (c == (struct cmd *)-1) { 107 printf("?Ambiguous command\n"); 108 exit(1); 109 } 110 if (c == 0) { 111 printf("?Invalid command\n"); 112 exit(1); 113 } 114 if ((c->c_opts & LPC_PRIVCMD) && getuid() && 115 ingroup(LPR_OPER) == 0) { 116 printf("?Privileged command\n"); 117 exit(1); 118 } 119 if (c->c_generic != 0) 120 generic(c->c_generic, c->c_opts, c->c_handler, 121 argc, argv); 122 else 123 (*c->c_handler)(argc, argv); 124 exit(0); 125 } 126 fromatty = isatty(fileno(stdin)); 127 if (!fromatty) 128 signal(SIGINT, intr); 129 for (;;) { 130 cmdscanner(); 131 } 132 } 133 134 static void 135 intr(int signo __unused) 136 { 137 /* (the '__unused' is just to avoid a compile-time warning) */ 138 exit(0); 139 } 140 141 static const char * 142 lpc_prompt(void) 143 { 144 return ("lpc> "); 145 } 146 147 /* 148 * Command parser. 149 */ 150 static void 151 cmdscanner(void) 152 { 153 register struct cmd *c; 154 static EditLine *el; 155 static History *hist; 156 HistEvent he; 157 size_t len; 158 int num; 159 const char *bp; 160 161 num = 0; 162 bp = NULL; 163 el = NULL; 164 hist = NULL; 165 for (;;) { 166 if (fromatty) { 167 if (!el) { 168 el = el_init("lpc", stdin, stdout, stderr); 169 hist = history_init(); 170 history(hist, &he, H_SETSIZE, 100); 171 el_set(el, EL_HIST, history, hist); 172 el_set(el, EL_EDITOR, "emacs"); 173 el_set(el, EL_PROMPT, lpc_prompt); 174 el_set(el, EL_SIGNAL, 1); 175 el_source(el, NULL); 176 /* 177 * EditLine init may call 'cgetset()' to set a 178 * capability-db meant for termcap (eg: to set 179 * terminal type 'xterm'). Reset that now, or 180 * that same db-information will be used for 181 * printcap (giving us an "xterm" printer, with 182 * all kinds of invalid capabilities...). 183 */ 184 cgetset(NULL); 185 } 186 if ((bp = el_gets(el, &num)) == NULL || num == 0) 187 quit(0, NULL); 188 189 len = (num > MAX_CMDLINE - 1) ? MAX_CMDLINE - 1 : num; 190 memcpy(cmdline, bp, len); 191 cmdline[len] = 0; 192 history(hist, &he, H_ENTER, bp); 193 194 } else { 195 if (fgets(cmdline, MAX_CMDLINE, stdin) == 0) 196 quit(0, NULL); 197 if (cmdline[0] == 0 || cmdline[0] == '\n') 198 break; 199 } 200 201 makeargv(); 202 if (margc == 0) 203 continue; 204 if (el != NULL && el_parse(el, margc, margv) != -1) 205 continue; 206 207 c = getcmd(margv[0]); 208 if (c == (struct cmd *)-1) { 209 printf("?Ambiguous command\n"); 210 continue; 211 } 212 if (c == 0) { 213 printf("?Invalid command\n"); 214 continue; 215 } 216 if ((c->c_opts & LPC_PRIVCMD) && getuid() && 217 ingroup(LPR_OPER) == 0) { 218 printf("?Privileged command\n"); 219 continue; 220 } 221 222 /* 223 * Two different commands might have the same generic rtn 224 * (eg: "clean" and "tclean"), and just use different 225 * handler routines for distinct command-setup. The handler 226 * routine might also be set on a generic routine for 227 * initial parameter processing. 228 */ 229 if (c->c_generic != 0) 230 generic(c->c_generic, c->c_opts, c->c_handler, 231 margc, margv); 232 else 233 (*c->c_handler)(margc, margv); 234 } 235 } 236 237 static struct cmd * 238 getcmd(const char *name) 239 { 240 register const char *p, *q; 241 register struct cmd *c, *found; 242 register int nmatches, longest; 243 244 longest = 0; 245 nmatches = 0; 246 found = 0; 247 for (c = cmdtab; (p = c->c_name); c++) { 248 for (q = name; *q == *p++; q++) 249 if (*q == 0) /* exact match? */ 250 return(c); 251 if (!*q) { /* the name was a prefix */ 252 if (q - name > longest) { 253 longest = q - name; 254 nmatches = 1; 255 found = c; 256 } else if (q - name == longest) 257 nmatches++; 258 } 259 } 260 if (nmatches > 1) 261 return((struct cmd *)-1); 262 return(found); 263 } 264 265 /* 266 * Slice a string up into argc/argv. 267 */ 268 static void 269 makeargv(void) 270 { 271 register char *cp; 272 register char **argp = margv; 273 register int n = 0; 274 275 margc = 0; 276 for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) && 277 n < MAX_MARGV - 1; n++) { 278 while (isspace(*cp)) 279 cp++; 280 if (*cp == '\0') 281 break; 282 *argp++ = cp; 283 margc += 1; 284 while (*cp != '\0' && !isspace(*cp)) 285 cp++; 286 if (*cp == '\0') 287 break; 288 *cp++ = '\0'; 289 } 290 *argp++ = 0; 291 } 292 293 #define HELPINDENT (sizeof ("directory")) 294 295 /* 296 * Help command. 297 */ 298 void 299 help(int argc, char *argv[]) 300 { 301 register struct cmd *c; 302 303 if (argc == 1) { 304 register int i, j, w; 305 int columns, width = 0, lines; 306 307 printf("Commands may be abbreviated. Commands are:\n\n"); 308 for (c = cmdtab; c->c_name; c++) { 309 int len = strlen(c->c_name); 310 311 if (len > width) 312 width = len; 313 } 314 width = (width + 8) &~ 7; 315 columns = 80 / width; 316 if (columns == 0) 317 columns = 1; 318 lines = (NCMDS + columns - 1) / columns; 319 for (i = 0; i < lines; i++) { 320 for (j = 0; j < columns; j++) { 321 c = cmdtab + j * lines + i; 322 if (c->c_name) 323 printf("%s", c->c_name); 324 if (c + lines >= &cmdtab[NCMDS]) { 325 printf("\n"); 326 break; 327 } 328 w = strlen(c->c_name); 329 while (w < width) { 330 w = (w + 8) &~ 7; 331 putchar('\t'); 332 } 333 } 334 } 335 return; 336 } 337 while (--argc > 0) { 338 register char *arg; 339 arg = *++argv; 340 c = getcmd(arg); 341 if (c == (struct cmd *)-1) 342 printf("?Ambiguous help command %s\n", arg); 343 else if (c == (struct cmd *)0) 344 printf("?Invalid help command %s\n", arg); 345 else 346 printf("%-*s\t%s\n", (int) HELPINDENT, 347 c->c_name, c->c_help); 348 } 349 } 350 351 /* 352 * return non-zero if the user is a member of the given group 353 */ 354 static int 355 ingroup(const char *grname) 356 { 357 static struct group *gptr=NULL; 358 static int ngroups = 0; 359 static gid_t groups[NGROUPS]; 360 register gid_t gid; 361 register int i; 362 363 if (gptr == NULL) { 364 if ((gptr = getgrnam(grname)) == NULL) { 365 warnx("warning: unknown group '%s'", grname); 366 return(0); 367 } 368 ngroups = getgroups(NGROUPS, 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 seteuid(euid); 409 cdres = chdir(pp->spool_dir); 410 seteuid(uid); 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