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 int main(int _argc, char **_argv); 76 static void cmdscanner(void); 77 static struct cmd *getcmd(const char *_name); 78 static void intr(int _signo); 79 static void makeargv(void); 80 static int ingroup(const char *_grname); 81 82 int 83 main(int argc, char **argv) 84 { 85 struct cmd *c; 86 87 euid = geteuid(); 88 uid = getuid(); 89 seteuid(uid); 90 progname = argv[0]; 91 openlog("lpd", 0, LOG_LPR); 92 93 if (--argc > 0) { 94 c = getcmd(*++argv); 95 if (c == (struct cmd *)-1) { 96 printf("?Ambiguous command\n"); 97 exit(1); 98 } 99 if (c == NULL) { 100 printf("?Invalid command\n"); 101 exit(1); 102 } 103 if ((c->c_opts & LPC_PRIVCMD) && getuid() && 104 ingroup(LPR_OPER) == 0) { 105 printf("?Privileged command\n"); 106 exit(1); 107 } 108 if (c->c_generic != 0) 109 generic(c->c_generic, c->c_opts, c->c_handler, 110 argc, argv); 111 else 112 (*c->c_handler)(argc, argv); 113 exit(0); 114 } 115 fromatty = isatty(fileno(stdin)); 116 if (!fromatty) 117 signal(SIGINT, intr); 118 for (;;) { 119 cmdscanner(); 120 } 121 } 122 123 static void 124 intr(int signo __unused) 125 { 126 /* (the '__unused' is just to avoid a compile-time warning) */ 127 exit(0); 128 } 129 130 static const char * 131 lpc_prompt(void) 132 { 133 return ("lpc> "); 134 } 135 136 /* 137 * Command parser. 138 */ 139 static void 140 cmdscanner(void) 141 { 142 struct cmd *c; 143 static EditLine *el; 144 static History *hist; 145 static HistEvent he; 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, stderr); 158 hist = history_init(); 159 history(hist, &he, H_SETSIZE, 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 178 len = (num > MAX_CMDLINE -1) ? MAX_CMDLINE -1 : num; 179 memcpy(cmdline, bp, len); 180 cmdline[len] = 0; 181 history(hist, &he, 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 != NULL && el_parse(el, margc, (const char **)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 == NULL) { 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 226 static struct cmd * 227 getcmd(const char *name) 228 { 229 const char *p, *q; 230 struct cmd *c, *found; 231 int nmatches, longest; 232 233 longest = 0; 234 nmatches = 0; 235 found = NULL; 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 */ 257 static void 258 makeargv(void) 259 { 260 char *cp; 261 char **argp = margv; 262 int n = 0; 263 264 margc = 0; 265 for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) && 266 n < MAX_MARGV -1; n++) { 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++ = NULL; 280 } 281 282 #define HELPINDENT (sizeof ("directory")) 283 284 /* 285 * Help command. 286 */ 287 void 288 help(int argc, char **argv) 289 { 290 struct cmd *c; 291 292 if (argc == 1) { 293 int i, j, w; 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) { 327 char *arg; 328 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 == NULL) 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 */ 344 static int 345 ingroup(const char *grname) 346 { 347 static struct group *gptr=NULL; 348 static int ngroups = 0; 349 static gid_t groups[NGROUPS]; 350 gid_t gid; 351 int i; 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 */ 374 struct printer * 375 setup_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 } 410