lpc.c revision 194494
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * 61553Srgrimes * Redistribution and use in source and binary forms, with or without 71553Srgrimes * modification, are permitted provided that the following conditions 81553Srgrimes * are met: 91553Srgrimes * 1. Redistributions of source code must retain the above copyright 101553Srgrimes * notice, this list of conditions and the following disclaimer. 111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer in the 131553Srgrimes * documentation and/or other materials provided with the distribution. 141553Srgrimes * 3. All advertising materials mentioning features or use of this software 151553Srgrimes * must display the following acknowledgement: 161553Srgrimes * This product includes software developed by the University of 171553Srgrimes * California, Berkeley and its contributors. 181553Srgrimes * 4. Neither the name of the University nor the names of its contributors 191553Srgrimes * may be used to endorse or promote products derived from this software 201553Srgrimes * without specific prior written permission. 211553Srgrimes * 221553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 231553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 261553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 271553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321553Srgrimes * SUCH DAMAGE. 331553Srgrimes */ 341553Srgrimes 351553Srgrimes#ifndef lint 3629780Scharnierstatic const char copyright[] = 371553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 381553Srgrimes The Regents of the University of California. All rights reserved.\n"; 391553Srgrimes#endif /* not lint */ 401553Srgrimes 41117599Sgad#if 0 421553Srgrimes#ifndef lint 4315637Sjoergstatic char sccsid[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95"; 44117599Sgad#endif /* not lint */ 4529780Scharnier#endif 461553Srgrimes 47117599Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 48117599Sgad__FBSDID("$FreeBSD: head/usr.sbin/lpr/lpc/lpc.c 194494 2009-06-19 15:58:24Z brooks $"); 49117599Sgad 501553Srgrimes#include <sys/param.h> 511553Srgrimes 5229780Scharnier#include <ctype.h> 531553Srgrimes#include <dirent.h> 5431492Swollman#include <err.h> 5529780Scharnier#include <grp.h> 5629780Scharnier#include <setjmp.h> 571553Srgrimes#include <signal.h> 5829780Scharnier#include <stdio.h> 5929780Scharnier#include <stdlib.h> 601553Srgrimes#include <syslog.h> 6129780Scharnier#include <string.h> 621553Srgrimes#include <unistd.h> 6350039Smdodd#include <histedit.h> 6431492Swollman 651553Srgrimes#include "lp.h" 661553Srgrimes#include "lpc.h" 671553Srgrimes#include "extern.h" 681553Srgrimes 6915637Sjoerg#ifndef LPR_OPER 7015637Sjoerg#define LPR_OPER "operator" /* group name of lpr operators */ 7115637Sjoerg#endif 7215637Sjoerg 731553Srgrimes/* 741553Srgrimes * lpc -- line printer control program 751553Srgrimes */ 761553Srgrimes 7727618Simp#define MAX_CMDLINE 200 7827618Simp#define MAX_MARGV 20 7939084Swollmanstatic int fromatty; 801553Srgrimes 8139084Swollmanstatic char cmdline[MAX_CMDLINE]; 8239084Swollmanstatic int margc; 8339084Swollmanstatic char *margv[MAX_MARGV]; 8439084Swollmanuid_t uid, euid; 851553Srgrimes 8678146Sgadint main(int _argc, char *_argv[]); 8778146Sgadstatic void cmdscanner(void); 8878146Sgadstatic struct cmd *getcmd(const char *_name); 8978146Sgadstatic void intr(int _signo); 9078146Sgadstatic void makeargv(void); 9178146Sgadstatic int ingroup(const char *_grname); 921553Srgrimes 931553Srgrimesint 9478146Sgadmain(int argc, char *argv[]) 951553Srgrimes{ 961553Srgrimes register struct cmd *c; 971553Srgrimes 9827618Simp euid = geteuid(); 9927618Simp uid = getuid(); 10027618Simp seteuid(uid); 10178280Sgad progname = argv[0]; 1021553Srgrimes openlog("lpd", 0, LOG_LPR); 1031553Srgrimes 1041553Srgrimes if (--argc > 0) { 1051553Srgrimes c = getcmd(*++argv); 1061553Srgrimes if (c == (struct cmd *)-1) { 1071553Srgrimes printf("?Ambiguous command\n"); 1081553Srgrimes exit(1); 1091553Srgrimes } 1101553Srgrimes if (c == 0) { 1111553Srgrimes printf("?Invalid command\n"); 1121553Srgrimes exit(1); 1131553Srgrimes } 11498267Sgad if ((c->c_opts & LPC_PRIVCMD) && getuid() && 11598267Sgad ingroup(LPR_OPER) == 0) { 1161553Srgrimes printf("?Privileged command\n"); 1171553Srgrimes exit(1); 1181553Srgrimes } 11931492Swollman if (c->c_generic != 0) 12098267Sgad generic(c->c_generic, c->c_opts, c->c_handler, 12198267Sgad argc, argv); 12231492Swollman else 12331492Swollman (*c->c_handler)(argc, argv); 1241553Srgrimes exit(0); 1251553Srgrimes } 1261553Srgrimes fromatty = isatty(fileno(stdin)); 12750039Smdodd if (!fromatty) 1281553Srgrimes signal(SIGINT, intr); 1291553Srgrimes for (;;) { 13050039Smdodd cmdscanner(); 1311553Srgrimes } 1321553Srgrimes} 1331553Srgrimes 1341553Srgrimesstatic void 13578146Sgadintr(int signo __unused) 1361553Srgrimes{ 13778146Sgad /* (the '__unused' is just to avoid a compile-time warning) */ 13850039Smdodd exit(0); 1391553Srgrimes} 1401553Srgrimes 14178146Sgadstatic const char * 14268400Sgadlpc_prompt(void) 14350039Smdodd{ 14468400Sgad return ("lpc> "); 14550039Smdodd} 14650039Smdodd 1471553Srgrimes/* 1481553Srgrimes * Command parser. 1491553Srgrimes */ 1501553Srgrimesstatic void 15178146Sgadcmdscanner(void) 1521553Srgrimes{ 1531553Srgrimes register struct cmd *c; 15479742Sgad static EditLine *el; 15579742Sgad static History *hist; 15684261Sobrien HistEvent he; 15779742Sgad size_t len; 15879742Sgad int num; 15979742Sgad const char *bp; 1601553Srgrimes 16179742Sgad num = 0; 16279742Sgad bp = NULL; 16379742Sgad el = NULL; 16479742Sgad hist = NULL; 1651553Srgrimes for (;;) { 1661553Srgrimes if (fromatty) { 16750039Smdodd if (!el) { 16884261Sobrien el = el_init("lpc", stdin, stdout, stderr); 16950039Smdodd hist = history_init(); 170151476Sstefanf history(hist, &he, H_SETSIZE, 100); 17150039Smdodd el_set(el, EL_HIST, history, hist); 17250039Smdodd el_set(el, EL_EDITOR, "emacs"); 17350039Smdodd el_set(el, EL_PROMPT, lpc_prompt); 17450039Smdodd el_set(el, EL_SIGNAL, 1); 17550042Smdodd el_source(el, NULL); 17683563Sgad /* 17783563Sgad * EditLine init may call 'cgetset()' to set a 17883563Sgad * capability-db meant for termcap (eg: to set 17983563Sgad * terminal type 'xterm'). Reset that now, or 18083563Sgad * that same db-information will be used for 18183563Sgad * printcap (giving us an "xterm" printer, with 18283563Sgad * all kinds of invalid capabilities...). 18383563Sgad */ 18483563Sgad cgetset(NULL); 18550039Smdodd } 18650039Smdodd if ((bp = el_gets(el, &num)) == NULL || num == 0) 18762294Smph quit(0, NULL); 18850039Smdodd 189121065Stjr len = (num > MAX_CMDLINE - 1) ? MAX_CMDLINE - 1 : num; 19050071Smdodd memcpy(cmdline, bp, len); 19150071Smdodd cmdline[len] = 0; 19284261Sobrien history(hist, &he, H_ENTER, bp); 19350039Smdodd 19450039Smdodd } else { 19550039Smdodd if (fgets(cmdline, MAX_CMDLINE, stdin) == 0) 19650039Smdodd quit(0, NULL); 19750039Smdodd if (cmdline[0] == 0 || cmdline[0] == '\n') 19850039Smdodd break; 1991553Srgrimes } 20050039Smdodd 2011553Srgrimes makeargv(); 20250039Smdodd if (margc == 0) 20350039Smdodd continue; 204142199Sdelphij if (el != NULL && el_parse(el, margc, margv) != -1) 20550039Smdodd continue; 20650039Smdodd 2071553Srgrimes c = getcmd(margv[0]); 2081553Srgrimes if (c == (struct cmd *)-1) { 2091553Srgrimes printf("?Ambiguous command\n"); 2101553Srgrimes continue; 2111553Srgrimes } 2121553Srgrimes if (c == 0) { 2131553Srgrimes printf("?Invalid command\n"); 2141553Srgrimes continue; 2151553Srgrimes } 21698267Sgad if ((c->c_opts & LPC_PRIVCMD) && getuid() && 21798267Sgad ingroup(LPR_OPER) == 0) { 2181553Srgrimes printf("?Privileged command\n"); 2191553Srgrimes continue; 2201553Srgrimes } 22178750Sgad 22278750Sgad /* 22378750Sgad * Two different commands might have the same generic rtn 22478750Sgad * (eg: "clean" and "tclean"), and just use different 22578750Sgad * handler routines for distinct command-setup. The handler 22678750Sgad * routine might also be set on a generic routine for 22778750Sgad * initial parameter processing. 22878750Sgad */ 22931492Swollman if (c->c_generic != 0) 23098267Sgad generic(c->c_generic, c->c_opts, c->c_handler, 23198267Sgad margc, margv); 23231492Swollman else 23331492Swollman (*c->c_handler)(margc, margv); 2341553Srgrimes } 2351553Srgrimes} 2361553Srgrimes 23715637Sjoergstatic struct cmd * 23878146Sgadgetcmd(const char *name) 2391553Srgrimes{ 24078146Sgad register const char *p, *q; 2411553Srgrimes register struct cmd *c, *found; 2421553Srgrimes register int nmatches, longest; 2431553Srgrimes 2441553Srgrimes longest = 0; 2451553Srgrimes nmatches = 0; 2461553Srgrimes found = 0; 24719202Simp for (c = cmdtab; (p = c->c_name); c++) { 2481553Srgrimes for (q = name; *q == *p++; q++) 2491553Srgrimes if (*q == 0) /* exact match? */ 2501553Srgrimes return(c); 2511553Srgrimes if (!*q) { /* the name was a prefix */ 2521553Srgrimes if (q - name > longest) { 2531553Srgrimes longest = q - name; 2541553Srgrimes nmatches = 1; 2551553Srgrimes found = c; 2561553Srgrimes } else if (q - name == longest) 2571553Srgrimes nmatches++; 2581553Srgrimes } 2591553Srgrimes } 2601553Srgrimes if (nmatches > 1) 2611553Srgrimes return((struct cmd *)-1); 2621553Srgrimes return(found); 2631553Srgrimes} 2641553Srgrimes 2651553Srgrimes/* 2661553Srgrimes * Slice a string up into argc/argv. 2671553Srgrimes */ 2681553Srgrimesstatic void 26978146Sgadmakeargv(void) 2701553Srgrimes{ 2711553Srgrimes register char *cp; 2721553Srgrimes register char **argp = margv; 27327618Simp register int n = 0; 2741553Srgrimes 2751553Srgrimes margc = 0; 27680173Sgad for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) && 277121065Stjr n < MAX_MARGV - 1; n++) { 2781553Srgrimes while (isspace(*cp)) 2791553Srgrimes cp++; 2801553Srgrimes if (*cp == '\0') 2811553Srgrimes break; 2821553Srgrimes *argp++ = cp; 2831553Srgrimes margc += 1; 2841553Srgrimes while (*cp != '\0' && !isspace(*cp)) 2851553Srgrimes cp++; 2861553Srgrimes if (*cp == '\0') 2871553Srgrimes break; 2881553Srgrimes *cp++ = '\0'; 2891553Srgrimes } 2901553Srgrimes *argp++ = 0; 2911553Srgrimes} 2921553Srgrimes 2931553Srgrimes#define HELPINDENT (sizeof ("directory")) 2941553Srgrimes 2951553Srgrimes/* 2961553Srgrimes * Help command. 2971553Srgrimes */ 2981553Srgrimesvoid 29978146Sgadhelp(int argc, char *argv[]) 3001553Srgrimes{ 3011553Srgrimes register struct cmd *c; 3021553Srgrimes 3031553Srgrimes if (argc == 1) { 3041553Srgrimes register int i, j, w; 3051553Srgrimes int columns, width = 0, lines; 3061553Srgrimes 3071553Srgrimes printf("Commands may be abbreviated. Commands are:\n\n"); 3081553Srgrimes for (c = cmdtab; c->c_name; c++) { 3091553Srgrimes int len = strlen(c->c_name); 3101553Srgrimes 3111553Srgrimes if (len > width) 3121553Srgrimes width = len; 3131553Srgrimes } 3141553Srgrimes width = (width + 8) &~ 7; 3151553Srgrimes columns = 80 / width; 3161553Srgrimes if (columns == 0) 3171553Srgrimes columns = 1; 3181553Srgrimes lines = (NCMDS + columns - 1) / columns; 3191553Srgrimes for (i = 0; i < lines; i++) { 3201553Srgrimes for (j = 0; j < columns; j++) { 3211553Srgrimes c = cmdtab + j * lines + i; 3221553Srgrimes if (c->c_name) 3231553Srgrimes printf("%s", c->c_name); 3241553Srgrimes if (c + lines >= &cmdtab[NCMDS]) { 3251553Srgrimes printf("\n"); 3261553Srgrimes break; 3271553Srgrimes } 3281553Srgrimes w = strlen(c->c_name); 3291553Srgrimes while (w < width) { 3301553Srgrimes w = (w + 8) &~ 7; 3311553Srgrimes putchar('\t'); 3321553Srgrimes } 3331553Srgrimes } 3341553Srgrimes } 3351553Srgrimes return; 3361553Srgrimes } 3371553Srgrimes while (--argc > 0) { 3381553Srgrimes register char *arg; 3391553Srgrimes arg = *++argv; 3401553Srgrimes c = getcmd(arg); 3411553Srgrimes if (c == (struct cmd *)-1) 3421553Srgrimes printf("?Ambiguous help command %s\n", arg); 3431553Srgrimes else if (c == (struct cmd *)0) 3441553Srgrimes printf("?Invalid help command %s\n", arg); 3451553Srgrimes else 34634784Sjb printf("%-*s\t%s\n", (int) HELPINDENT, 3471553Srgrimes c->c_name, c->c_help); 3481553Srgrimes } 3491553Srgrimes} 35015637Sjoerg 35115637Sjoerg/* 35215637Sjoerg * return non-zero if the user is a member of the given group 35315637Sjoerg */ 35415637Sjoergstatic int 35578146Sgadingroup(const char *grname) 35615637Sjoerg{ 35715637Sjoerg static struct group *gptr=NULL; 35865035Salfred static int ngroups = 0; 359194494Sbrooks static long ngroups_max; 360194494Sbrooks static gid_t *groups; 36115637Sjoerg register gid_t gid; 36215637Sjoerg register int i; 36315637Sjoerg 36415637Sjoerg if (gptr == NULL) { 36515637Sjoerg if ((gptr = getgrnam(grname)) == NULL) { 36629780Scharnier warnx("warning: unknown group '%s'", grname); 36715637Sjoerg return(0); 36815637Sjoerg } 369194494Sbrooks ngroups_max = sysconf(_SC_NGROUPS_MAX); 370194494Sbrooks if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) 371194494Sbrooks err(1, "malloc"); 372194494Sbrooks ngroups = getgroups(ngroups_max, groups); 37365035Salfred if (ngroups < 0) 37429780Scharnier err(1, "getgroups"); 37515637Sjoerg } 37615637Sjoerg gid = gptr->gr_gid; 37765035Salfred for (i = 0; i < ngroups; i++) 37815637Sjoerg if (gid == groups[i]) 37915637Sjoerg return(1); 38015637Sjoerg return(0); 38115637Sjoerg} 382100203Sgad 383100203Sgad/* 384100203Sgad * Routine to get the information for a single printer (which will be 385100203Sgad * called by the routines which implement individual commands). 386100203Sgad * Note: This is for commands operating on a *single* printer. 387100203Sgad */ 388100203Sgadstruct printer * 389100203Sgadsetup_myprinter(char *pwanted, struct printer *pp, int sump_opts) 390100203Sgad{ 391100203Sgad int cdres, cmdstatus; 392100203Sgad 393100203Sgad init_printer(pp); 394100203Sgad cmdstatus = getprintcap(pwanted, pp); 395100203Sgad switch (cmdstatus) { 396100203Sgad default: 397100203Sgad fatal(pp, "%s", pcaperr(cmdstatus)); 398100203Sgad /* NOTREACHED */ 399100203Sgad case PCAPERR_NOTFOUND: 400100203Sgad printf("unknown printer %s\n", pwanted); 401100203Sgad return (NULL); 402100203Sgad case PCAPERR_TCOPEN: 403100203Sgad printf("warning: %s: unresolved tc= reference(s)", pwanted); 404100203Sgad break; 405100203Sgad case PCAPERR_SUCCESS: 406100203Sgad break; 407100203Sgad } 408100203Sgad if ((sump_opts & SUMP_NOHEADER) == 0) 409100203Sgad printf("%s:\n", pp->printer); 410100203Sgad 411100203Sgad if (sump_opts & SUMP_CHDIR_SD) { 412100203Sgad seteuid(euid); 413100203Sgad cdres = chdir(pp->spool_dir); 414100203Sgad seteuid(uid); 415100203Sgad if (cdres < 0) { 416100203Sgad printf("\tcannot chdir to %s\n", pp->spool_dir); 417100203Sgad free_printer(pp); 418100203Sgad return (NULL); 419100203Sgad } 420100203Sgad } 421100203Sgad 422100203Sgad return (pp); 423100203Sgad} 424