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 * $DragonFly: src/usr.sbin/lpr/lpc/lpc.c,v 1.4 2004/03/22 22:32:50 cpressey Exp $ 38 */ 39 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 69 static int fromatty; 70 71 static char cmdline[MAX_CMDLINE]; 72 static int margc; 73 static char *margv[MAX_MARGV]; 74 uid_t uid, euid; 75 76 int main(int _argc, char **_argv); 77 static void cmdscanner(void); 78 static struct cmd *getcmd(const char *_name); 79 static void intr(int _signo); 80 static void makeargv(void); 81 static int ingroup(const char *_grname); 82 83 int 84 main(int argc, char **argv) 85 { 86 struct cmd *c; 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 124 static void 125 intr(int signo __unused) 126 { 127 /* (the '__unused' is just to avoid a compile-time warning) */ 128 exit(0); 129 } 130 131 static const char * 132 lpc_prompt(void) 133 { 134 return ("lpc> "); 135 } 136 137 /* 138 * Command parser. 139 */ 140 static void 141 cmdscanner(void) 142 { 143 struct cmd *c; 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 178 len = (num > MAX_CMDLINE -1) ? MAX_CMDLINE -1 : num; 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 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 = 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 */ 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++ = 0; 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 == (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 */ 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