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.7 2006/01/14 22:58:18 corecode 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 static HistEvent he; 147 size_t len; 148 int num; 149 const char *bp; 150 151 num = 0; 152 bp = NULL; 153 el = NULL; 154 hist = NULL; 155 for (;;) { 156 if (fromatty) { 157 if (!el) { 158 el = el_init("lpc", stdin, stdout, stderr); 159 hist = history_init(); 160 history(hist, &he, H_SETSIZE, 100); 161 el_set(el, EL_HIST, history, hist); 162 el_set(el, EL_EDITOR, "emacs"); 163 el_set(el, EL_PROMPT, lpc_prompt); 164 el_set(el, EL_SIGNAL, 1); 165 el_source(el, NULL); 166 /* 167 * EditLine init may call 'cgetset()' to set a 168 * capability-db meant for termcap (eg: to set 169 * terminal type 'xterm'). Reset that now, or 170 * that same db-information will be used for 171 * printcap (giving us an "xterm" printer, with 172 * all kinds of invalid capabilities...). 173 */ 174 cgetset(NULL); 175 } 176 if ((bp = el_gets(el, &num)) == NULL || num == 0) 177 quit(0, NULL); 178 179 len = (num > MAX_CMDLINE -1) ? MAX_CMDLINE -1 : num; 180 memcpy(cmdline, bp, len); 181 cmdline[len] = 0; 182 history(hist, &he, H_ENTER, bp); 183 184 } else { 185 if (fgets(cmdline, MAX_CMDLINE, stdin) == 0) 186 quit(0, NULL); 187 if (cmdline[0] == 0 || cmdline[0] == '\n') 188 break; 189 } 190 191 makeargv(); 192 if (margc == 0) 193 continue; 194 if (el != NULL && el_parse(el, margc, (const char **)margv) != -1) 195 continue; 196 197 c = getcmd(margv[0]); 198 if (c == (struct cmd *)-1) { 199 printf("?Ambiguous command\n"); 200 continue; 201 } 202 if (c == 0) { 203 printf("?Invalid command\n"); 204 continue; 205 } 206 if ((c->c_opts & LPC_PRIVCMD) && getuid() && 207 ingroup(LPR_OPER) == 0) { 208 printf("?Privileged command\n"); 209 continue; 210 } 211 212 /* 213 * Two different commands might have the same generic rtn 214 * (eg: "clean" and "tclean"), and just use different 215 * handler routines for distinct command-setup. The handler 216 * routine might also be set on a generic routine for 217 * initial parameter processing. 218 */ 219 if (c->c_generic != 0) 220 generic(c->c_generic, c->c_opts, c->c_handler, 221 margc, margv); 222 else 223 (*c->c_handler)(margc, margv); 224 } 225 } 226 227 static struct cmd * 228 getcmd(const char *name) 229 { 230 const char *p, *q; 231 struct cmd *c, *found; 232 int nmatches, longest; 233 234 longest = 0; 235 nmatches = 0; 236 found = 0; 237 for (c = cmdtab; (p = c->c_name); c++) { 238 for (q = name; *q == *p++; q++) 239 if (*q == 0) /* exact match? */ 240 return(c); 241 if (!*q) { /* the name was a prefix */ 242 if (q - name > longest) { 243 longest = q - name; 244 nmatches = 1; 245 found = c; 246 } else if (q - name == longest) 247 nmatches++; 248 } 249 } 250 if (nmatches > 1) 251 return((struct cmd *)-1); 252 return(found); 253 } 254 255 /* 256 * Slice a string up into argc/argv. 257 */ 258 static void 259 makeargv(void) 260 { 261 char *cp; 262 char **argp = margv; 263 int n = 0; 264 265 margc = 0; 266 for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) && 267 n < MAX_MARGV -1; n++) { 268 while (isspace(*cp)) 269 cp++; 270 if (*cp == '\0') 271 break; 272 *argp++ = cp; 273 margc += 1; 274 while (*cp != '\0' && !isspace(*cp)) 275 cp++; 276 if (*cp == '\0') 277 break; 278 *cp++ = '\0'; 279 } 280 *argp++ = 0; 281 } 282 283 #define HELPINDENT (sizeof ("directory")) 284 285 /* 286 * Help command. 287 */ 288 void 289 help(int argc, char **argv) 290 { 291 struct cmd *c; 292 293 if (argc == 1) { 294 int i, j, w; 295 int columns, width = 0, lines; 296 297 printf("Commands may be abbreviated. Commands are:\n\n"); 298 for (c = cmdtab; c->c_name; c++) { 299 int len = strlen(c->c_name); 300 301 if (len > width) 302 width = len; 303 } 304 width = (width + 8) &~ 7; 305 columns = 80 / width; 306 if (columns == 0) 307 columns = 1; 308 lines = (NCMDS + columns - 1) / columns; 309 for (i = 0; i < lines; i++) { 310 for (j = 0; j < columns; j++) { 311 c = cmdtab + j * lines + i; 312 if (c->c_name) 313 printf("%s", c->c_name); 314 if (c + lines >= &cmdtab[NCMDS]) { 315 printf("\n"); 316 break; 317 } 318 w = strlen(c->c_name); 319 while (w < width) { 320 w = (w + 8) &~ 7; 321 putchar('\t'); 322 } 323 } 324 } 325 return; 326 } 327 while (--argc > 0) { 328 char *arg; 329 330 arg = *++argv; 331 c = getcmd(arg); 332 if (c == (struct cmd *)-1) 333 printf("?Ambiguous help command %s\n", arg); 334 else if (c == NULL) 335 printf("?Invalid help command %s\n", arg); 336 else 337 printf("%-*s\t%s\n", (int) HELPINDENT, 338 c->c_name, c->c_help); 339 } 340 } 341 342 /* 343 * return non-zero if the user is a member of the given group 344 */ 345 static int 346 ingroup(const char *grname) 347 { 348 static struct group *gptr=NULL; 349 static int ngroups = 0; 350 static gid_t groups[NGROUPS]; 351 gid_t gid; 352 int i; 353 354 if (gptr == NULL) { 355 if ((gptr = getgrnam(grname)) == NULL) { 356 warnx("warning: unknown group '%s'", grname); 357 return(0); 358 } 359 ngroups = getgroups(NGROUPS, groups); 360 if (ngroups < 0) 361 err(1, "getgroups"); 362 } 363 gid = gptr->gr_gid; 364 for (i = 0; i < ngroups; i++) 365 if (gid == groups[i]) 366 return(1); 367 return(0); 368 } 369 370 /* 371 * Routine to get the information for a single printer (which will be 372 * called by the routines which implement individual commands). 373 * Note: This is for commands operating on a *single* printer. 374 */ 375 struct printer * 376 setup_myprinter(char *pwanted, struct printer *pp, int sump_opts) 377 { 378 int cdres, cmdstatus; 379 380 init_printer(pp); 381 cmdstatus = getprintcap(pwanted, pp); 382 switch (cmdstatus) { 383 default: 384 fatal(pp, "%s", pcaperr(cmdstatus)); 385 /* NOTREACHED */ 386 case PCAPERR_NOTFOUND: 387 printf("unknown printer %s\n", pwanted); 388 return (NULL); 389 case PCAPERR_TCOPEN: 390 printf("warning: %s: unresolved tc= reference(s)", pwanted); 391 break; 392 case PCAPERR_SUCCESS: 393 break; 394 } 395 if ((sump_opts & SUMP_NOHEADER) == 0) 396 printf("%s:\n", pp->printer); 397 398 if (sump_opts & SUMP_CHDIR_SD) { 399 seteuid(euid); 400 cdres = chdir(pp->spool_dir); 401 seteuid(uid); 402 if (cdres < 0) { 403 printf("\tcannot chdir to %s\n", pp->spool_dir); 404 free_printer(pp); 405 return (NULL); 406 } 407 } 408 409 return (pp); 410 } 411