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