lpc.c revision 83563
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 411553Srgrimes#ifndef lint 4229780Scharnier#if 0 4315637Sjoergstatic char sccsid[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95"; 4429780Scharnier#endif 4529780Scharnierstatic const char rcsid[] = 4650479Speter "$FreeBSD: head/usr.sbin/lpr/lpc/lpc.c 83563 2001-09-17 02:35:34Z gad $"; 471553Srgrimes#endif /* not lint */ 481553Srgrimes 491553Srgrimes#include <sys/param.h> 501553Srgrimes 5129780Scharnier#include <ctype.h> 521553Srgrimes#include <dirent.h> 5331492Swollman#include <err.h> 5429780Scharnier#include <grp.h> 5529780Scharnier#include <setjmp.h> 561553Srgrimes#include <signal.h> 5729780Scharnier#include <stdio.h> 5829780Scharnier#include <stdlib.h> 591553Srgrimes#include <syslog.h> 6029780Scharnier#include <string.h> 611553Srgrimes#include <unistd.h> 6250039Smdodd#include <histedit.h> 6331492Swollman 641553Srgrimes#include "lp.h" 651553Srgrimes#include "lpc.h" 661553Srgrimes#include "extern.h" 671553Srgrimes 6815637Sjoerg#ifndef LPR_OPER 6915637Sjoerg#define LPR_OPER "operator" /* group name of lpr operators */ 7015637Sjoerg#endif 7115637Sjoerg 721553Srgrimes/* 731553Srgrimes * lpc -- line printer control program 741553Srgrimes */ 751553Srgrimes 7627618Simp#define MAX_CMDLINE 200 7727618Simp#define MAX_MARGV 20 7839084Swollmanstatic int fromatty; 791553Srgrimes 8039084Swollmanstatic char cmdline[MAX_CMDLINE]; 8139084Swollmanstatic int margc; 8239084Swollmanstatic char *margv[MAX_MARGV]; 8339084Swollmanuid_t uid, euid; 841553Srgrimes 8578146Sgadint main(int _argc, char *_argv[]); 8678146Sgadstatic void cmdscanner(void); 8778146Sgadstatic struct cmd *getcmd(const char *_name); 8878146Sgadstatic void intr(int _signo); 8978146Sgadstatic void makeargv(void); 9078146Sgadstatic int ingroup(const char *_grname); 911553Srgrimes 921553Srgrimesint 9378146Sgadmain(int argc, char *argv[]) 941553Srgrimes{ 951553Srgrimes register struct cmd *c; 961553Srgrimes 9727618Simp euid = geteuid(); 9827618Simp uid = getuid(); 9927618Simp seteuid(uid); 10078280Sgad progname = argv[0]; 1011553Srgrimes openlog("lpd", 0, LOG_LPR); 1021553Srgrimes 1031553Srgrimes if (--argc > 0) { 1041553Srgrimes c = getcmd(*++argv); 1051553Srgrimes if (c == (struct cmd *)-1) { 1061553Srgrimes printf("?Ambiguous command\n"); 1071553Srgrimes exit(1); 1081553Srgrimes } 1091553Srgrimes if (c == 0) { 1101553Srgrimes printf("?Invalid command\n"); 1111553Srgrimes exit(1); 1121553Srgrimes } 11315637Sjoerg if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) { 1141553Srgrimes printf("?Privileged command\n"); 1151553Srgrimes exit(1); 1161553Srgrimes } 11731492Swollman if (c->c_generic != 0) 11878750Sgad generic(c->c_generic, c->c_handler, argc, argv); 11931492Swollman else 12031492Swollman (*c->c_handler)(argc, argv); 1211553Srgrimes exit(0); 1221553Srgrimes } 1231553Srgrimes fromatty = isatty(fileno(stdin)); 12450039Smdodd if (!fromatty) 1251553Srgrimes signal(SIGINT, intr); 1261553Srgrimes for (;;) { 12750039Smdodd cmdscanner(); 1281553Srgrimes } 1291553Srgrimes} 1301553Srgrimes 1311553Srgrimesstatic void 13278146Sgadintr(int signo __unused) 1331553Srgrimes{ 13478146Sgad /* (the '__unused' is just to avoid a compile-time warning) */ 13550039Smdodd exit(0); 1361553Srgrimes} 1371553Srgrimes 13878146Sgadstatic const char * 13968400Sgadlpc_prompt(void) 14050039Smdodd{ 14168400Sgad return ("lpc> "); 14250039Smdodd} 14350039Smdodd 1441553Srgrimes/* 1451553Srgrimes * Command parser. 1461553Srgrimes */ 1471553Srgrimesstatic void 14878146Sgadcmdscanner(void) 1491553Srgrimes{ 1501553Srgrimes register struct cmd *c; 15179742Sgad static EditLine *el; 15279742Sgad static History *hist; 15379742Sgad size_t len; 15479742Sgad int num; 15579742Sgad const char *bp; 1561553Srgrimes 15779742Sgad num = 0; 15879742Sgad bp = NULL; 15979742Sgad el = NULL; 16079742Sgad hist = NULL; 1611553Srgrimes for (;;) { 1621553Srgrimes if (fromatty) { 16350039Smdodd if (!el) { 16450039Smdodd el = el_init("lpc", stdin, stdout); 16550039Smdodd hist = history_init(); 16650039Smdodd history(hist, H_EVENT, 100); 16750039Smdodd el_set(el, EL_HIST, history, hist); 16850039Smdodd el_set(el, EL_EDITOR, "emacs"); 16950039Smdodd el_set(el, EL_PROMPT, lpc_prompt); 17050039Smdodd el_set(el, EL_SIGNAL, 1); 17150042Smdodd el_source(el, NULL); 17283563Sgad /* 17383563Sgad * EditLine init may call 'cgetset()' to set a 17483563Sgad * capability-db meant for termcap (eg: to set 17583563Sgad * terminal type 'xterm'). Reset that now, or 17683563Sgad * that same db-information will be used for 17783563Sgad * printcap (giving us an "xterm" printer, with 17883563Sgad * all kinds of invalid capabilities...). 17983563Sgad */ 18083563Sgad cgetset(NULL); 18150039Smdodd } 18250039Smdodd if ((bp = el_gets(el, &num)) == NULL || num == 0) 18362294Smph quit(0, NULL); 18450039Smdodd 18550077Smdodd len = (num > MAX_CMDLINE) ? MAX_CMDLINE : num; 18650071Smdodd memcpy(cmdline, bp, len); 18750071Smdodd cmdline[len] = 0; 18850039Smdodd history(hist, H_ENTER, bp); 18950039Smdodd 19050039Smdodd } else { 19150039Smdodd if (fgets(cmdline, MAX_CMDLINE, stdin) == 0) 19250039Smdodd quit(0, NULL); 19350039Smdodd if (cmdline[0] == 0 || cmdline[0] == '\n') 19450039Smdodd break; 1951553Srgrimes } 19650039Smdodd 1971553Srgrimes makeargv(); 19850039Smdodd if (margc == 0) 19950039Smdodd continue; 20050039Smdodd if (el_parse(el, margc, margv) != -1) 20150039Smdodd continue; 20250039Smdodd 2031553Srgrimes c = getcmd(margv[0]); 2041553Srgrimes if (c == (struct cmd *)-1) { 2051553Srgrimes printf("?Ambiguous command\n"); 2061553Srgrimes continue; 2071553Srgrimes } 2081553Srgrimes if (c == 0) { 2091553Srgrimes printf("?Invalid command\n"); 2101553Srgrimes continue; 2111553Srgrimes } 21215637Sjoerg if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) { 2131553Srgrimes printf("?Privileged command\n"); 2141553Srgrimes continue; 2151553Srgrimes } 21678750Sgad 21778750Sgad /* 21878750Sgad * Two different commands might have the same generic rtn 21978750Sgad * (eg: "clean" and "tclean"), and just use different 22078750Sgad * handler routines for distinct command-setup. The handler 22178750Sgad * routine might also be set on a generic routine for 22278750Sgad * initial parameter processing. 22378750Sgad */ 22431492Swollman if (c->c_generic != 0) 22578750Sgad generic(c->c_generic, c->c_handler, margc, margv); 22631492Swollman else 22731492Swollman (*c->c_handler)(margc, margv); 2281553Srgrimes } 2291553Srgrimes} 2301553Srgrimes 23115637Sjoergstatic struct cmd * 23278146Sgadgetcmd(const char *name) 2331553Srgrimes{ 23478146Sgad register const char *p, *q; 2351553Srgrimes register struct cmd *c, *found; 2361553Srgrimes register int nmatches, longest; 2371553Srgrimes 2381553Srgrimes longest = 0; 2391553Srgrimes nmatches = 0; 2401553Srgrimes found = 0; 24119202Simp for (c = cmdtab; (p = c->c_name); c++) { 2421553Srgrimes for (q = name; *q == *p++; q++) 2431553Srgrimes if (*q == 0) /* exact match? */ 2441553Srgrimes return(c); 2451553Srgrimes if (!*q) { /* the name was a prefix */ 2461553Srgrimes if (q - name > longest) { 2471553Srgrimes longest = q - name; 2481553Srgrimes nmatches = 1; 2491553Srgrimes found = c; 2501553Srgrimes } else if (q - name == longest) 2511553Srgrimes nmatches++; 2521553Srgrimes } 2531553Srgrimes } 2541553Srgrimes if (nmatches > 1) 2551553Srgrimes return((struct cmd *)-1); 2561553Srgrimes return(found); 2571553Srgrimes} 2581553Srgrimes 2591553Srgrimes/* 2601553Srgrimes * Slice a string up into argc/argv. 2611553Srgrimes */ 2621553Srgrimesstatic void 26378146Sgadmakeargv(void) 2641553Srgrimes{ 2651553Srgrimes register char *cp; 2661553Srgrimes register char **argp = margv; 26727618Simp register int n = 0; 2681553Srgrimes 2691553Srgrimes margc = 0; 27080173Sgad for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) && 27127618Simp n < MAX_MARGV; n++) { 2721553Srgrimes while (isspace(*cp)) 2731553Srgrimes cp++; 2741553Srgrimes if (*cp == '\0') 2751553Srgrimes break; 2761553Srgrimes *argp++ = cp; 2771553Srgrimes margc += 1; 2781553Srgrimes while (*cp != '\0' && !isspace(*cp)) 2791553Srgrimes cp++; 2801553Srgrimes if (*cp == '\0') 2811553Srgrimes break; 2821553Srgrimes *cp++ = '\0'; 2831553Srgrimes } 2841553Srgrimes *argp++ = 0; 2851553Srgrimes} 2861553Srgrimes 2871553Srgrimes#define HELPINDENT (sizeof ("directory")) 2881553Srgrimes 2891553Srgrimes/* 2901553Srgrimes * Help command. 2911553Srgrimes */ 2921553Srgrimesvoid 29378146Sgadhelp(int argc, char *argv[]) 2941553Srgrimes{ 2951553Srgrimes register struct cmd *c; 2961553Srgrimes 2971553Srgrimes if (argc == 1) { 2981553Srgrimes register int i, j, w; 2991553Srgrimes int columns, width = 0, lines; 3001553Srgrimes 3011553Srgrimes printf("Commands may be abbreviated. Commands are:\n\n"); 3021553Srgrimes for (c = cmdtab; c->c_name; c++) { 3031553Srgrimes int len = strlen(c->c_name); 3041553Srgrimes 3051553Srgrimes if (len > width) 3061553Srgrimes width = len; 3071553Srgrimes } 3081553Srgrimes width = (width + 8) &~ 7; 3091553Srgrimes columns = 80 / width; 3101553Srgrimes if (columns == 0) 3111553Srgrimes columns = 1; 3121553Srgrimes lines = (NCMDS + columns - 1) / columns; 3131553Srgrimes for (i = 0; i < lines; i++) { 3141553Srgrimes for (j = 0; j < columns; j++) { 3151553Srgrimes c = cmdtab + j * lines + i; 3161553Srgrimes if (c->c_name) 3171553Srgrimes printf("%s", c->c_name); 3181553Srgrimes if (c + lines >= &cmdtab[NCMDS]) { 3191553Srgrimes printf("\n"); 3201553Srgrimes break; 3211553Srgrimes } 3221553Srgrimes w = strlen(c->c_name); 3231553Srgrimes while (w < width) { 3241553Srgrimes w = (w + 8) &~ 7; 3251553Srgrimes putchar('\t'); 3261553Srgrimes } 3271553Srgrimes } 3281553Srgrimes } 3291553Srgrimes return; 3301553Srgrimes } 3311553Srgrimes while (--argc > 0) { 3321553Srgrimes register char *arg; 3331553Srgrimes arg = *++argv; 3341553Srgrimes c = getcmd(arg); 3351553Srgrimes if (c == (struct cmd *)-1) 3361553Srgrimes printf("?Ambiguous help command %s\n", arg); 3371553Srgrimes else if (c == (struct cmd *)0) 3381553Srgrimes printf("?Invalid help command %s\n", arg); 3391553Srgrimes else 34034784Sjb printf("%-*s\t%s\n", (int) HELPINDENT, 3411553Srgrimes c->c_name, c->c_help); 3421553Srgrimes } 3431553Srgrimes} 34415637Sjoerg 34515637Sjoerg/* 34615637Sjoerg * return non-zero if the user is a member of the given group 34715637Sjoerg */ 34815637Sjoergstatic int 34978146Sgadingroup(const char *grname) 35015637Sjoerg{ 35115637Sjoerg static struct group *gptr=NULL; 35265035Salfred static int ngroups = 0; 35315637Sjoerg static gid_t groups[NGROUPS]; 35415637Sjoerg register gid_t gid; 35515637Sjoerg register int i; 35615637Sjoerg 35715637Sjoerg if (gptr == NULL) { 35815637Sjoerg if ((gptr = getgrnam(grname)) == NULL) { 35929780Scharnier warnx("warning: unknown group '%s'", grname); 36015637Sjoerg return(0); 36115637Sjoerg } 36265035Salfred ngroups = getgroups(NGROUPS, groups); 36365035Salfred if (ngroups < 0) 36429780Scharnier err(1, "getgroups"); 36515637Sjoerg } 36615637Sjoerg gid = gptr->gr_gid; 36765035Salfred for (i = 0; i < ngroups; i++) 36815637Sjoerg if (gid == groups[i]) 36915637Sjoerg return(1); 37015637Sjoerg return(0); 37115637Sjoerg} 372