1/* $NetBSD: lpc.c,v 1.25 2009/01/18 09:57:26 lukem Exp $ */ 2 3/* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include <sys/cdefs.h> 34#ifndef lint 35__COPYRIGHT("@(#) Copyright (c) 1983, 1993\ 36 The Regents of the University of California. All rights reserved."); 37#if 0 38static char sccsid[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95"; 39#else 40__RCSID("$NetBSD: lpc.c,v 1.25 2009/01/18 09:57:26 lukem Exp $"); 41#endif 42#endif /* not lint */ 43 44#include <sys/param.h> 45 46#include <dirent.h> 47#include <signal.h> 48#include <setjmp.h> 49#include <syslog.h> 50#include <histedit.h> 51#include <unistd.h> 52#include <stdlib.h> 53#include <stdio.h> 54#include <ctype.h> 55#include <string.h> 56#include <grp.h> 57#include <err.h> 58#include "lp.h" 59#include "lpc.h" 60#include "extern.h" 61 62#ifndef LPR_OPER 63#define LPR_OPER "operator" /* group name of lpr operators */ 64#endif 65 66/* 67 * lpc -- line printer control program 68 */ 69 70#define MAX_MARGV 20 71int fromatty; 72 73char *cmdline; 74int margc; 75char *margv[MAX_MARGV]; 76int top; 77uid_t uid, euid; 78 79jmp_buf toplevel; 80 81History *hist; 82HistEvent he; 83EditLine *elptr; 84 85__dead static void cmdscanner(int); 86static struct cmd *getcmd(const char *); 87__dead static void intr(int); 88static void makeargv(void); 89static int ingroup(const char *); 90int main(int, char *p[]); 91const char *prompt(void); 92static int parse(char *, char *p[], int); 93 94int 95main(int argc, char *argv[]) 96{ 97 euid = geteuid(); 98 uid = getuid(); 99 seteuid(uid); 100 setprogname(argv[0]); 101 openlog("lpd", 0, LOG_LPR); 102 103 if (--argc > 0) { 104 argv++; 105 exit(!parse(*argv, argv, argc)); 106 } 107 fromatty = isatty(fileno(stdin)); 108 top = setjmp(toplevel) == 0; 109 if (top) 110 signal(SIGINT, intr); 111 for (;;) { 112 cmdscanner(top); 113 top = 1; 114 } 115} 116 117static int 118parse(char *arg, char **pargv, int pargc) 119{ 120 struct cmd *c; 121 122 c = getcmd(arg); 123 if (c == (struct cmd *)-1) { 124 printf("?Ambiguous command\n"); 125 return(0); 126 } 127 if (c == 0) { 128 printf("?Invalid command\n"); 129 return(0); 130 } 131 if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) { 132 printf("?Privileged command\n"); 133 return(0); 134 } 135 (*c->c_handler)(pargc, pargv); 136 return(1); 137} 138 139static void 140intr(int signo) 141{ 142 el_end(elptr); 143 history_end(hist); 144 if (!fromatty) 145 exit(0); 146 longjmp(toplevel, 1); 147} 148 149const char * 150prompt(void) 151{ 152 return ("lpc> "); 153} 154 155/* 156 * Command parser. 157 */ 158static void 159cmdscanner(int tp) 160{ 161 int scratch; 162 const char *elline; 163 164 if (!tp) 165 putchar('\n'); 166 hist = history_init(); 167 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ 168 169 elptr = el_init(getprogname(), stdin, stdout, stderr); 170 el_set(elptr, EL_EDITOR, "emacs"); 171 el_set(elptr, EL_PROMPT, prompt); 172 el_set(elptr, EL_HIST, history, hist); 173 el_source(elptr, NULL); 174 175 for (;;) { 176 cmdline = NULL; 177 do { 178 if (((elline = el_gets(elptr, &scratch)) != NULL) 179 && (scratch != 0)) { 180 history(hist, &he, H_ENTER, elline); 181 cmdline = strdup(elline); 182 makeargv(); 183 } else { 184 margc = 0; 185 break; 186 } 187 } while (margc == 0); 188 if (margc == 0) 189 quit(0, NULL); 190 if (!parse(cmdline, margv, margc)) { 191 if (cmdline != NULL) 192 free(cmdline); 193 continue; 194 } 195 fflush(stdout); 196 if (cmdline != NULL) 197 free(cmdline); 198 } 199 longjmp(toplevel, 0); 200} 201 202static struct cmd * 203getcmd(const char *name) 204{ 205 const char *p, *q; 206 struct cmd *c, *found; 207 int nmatches, longest; 208 209 longest = 0; 210 nmatches = 0; 211 found = 0; 212 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 213 for (q = name; *q == *p++; q++) 214 if (*q == 0) /* exact match? */ 215 return(c); 216 if (!*q) { /* the name was a prefix */ 217 if (q - name > longest) { 218 longest = q - name; 219 nmatches = 1; 220 found = c; 221 } else if (q - name == longest) 222 nmatches++; 223 } 224 } 225 if (nmatches > 1) 226 return((struct cmd *)-1); 227 return(found); 228} 229 230/* 231 * Slice a string up into argc/argv. 232 */ 233static void 234makeargv(void) 235{ 236 char *cp; 237 char **argp = margv; 238 int n = 0; 239 size_t s; 240 241 s = strlen(cmdline) + 1; 242 margc = 0; 243 for (cp = cmdline; *cp && (size_t)(cp - cmdline) < s && n < MAX_MARGV; n++) { 244 while (isspace((unsigned char)*cp)) 245 cp++; 246 if (*cp == '\0') 247 break; 248 *argp++ = cp; 249 margc += 1; 250 while (*cp != '\0' && !isspace((unsigned char)*cp)) 251 cp++; 252 if (*cp == '\0') 253 break; 254 *cp++ = '\0'; 255 } 256 *argp++ = 0; 257} 258 259#define HELPINDENT (sizeof ("directory")) 260 261/* 262 * Help command. 263 */ 264void 265help(int argc, char *argv[]) 266{ 267 struct cmd *c; 268 269 if (argc == 1) { 270 size_t i, j, w; 271 size_t columns, width = 0, lines; 272 273 printf("Commands may be abbreviated. Commands are:\n\n"); 274 for (c = cmdtab; c->c_name; c++) { 275 size_t len = strlen(c->c_name); 276 277 if (len > width) 278 width = len; 279 } 280 width = (width + 8) &~ 7; 281 columns = 80 / width; 282 if (columns == 0) 283 columns = 1; 284 lines = (NCMDS + columns - 1) / columns; 285 for (i = 0; i < lines; i++) { 286 for (j = 0; j < columns; j++) { 287 c = cmdtab + j * lines + i; 288 printf("%s", c->c_name); 289 if (c + lines >= &cmdtab[NCMDS - 1]) { 290 printf("\n"); 291 break; 292 } 293 w = strlen(c->c_name); 294 while (w < width) { 295 w = (w + 8) &~ 7; 296 putchar('\t'); 297 } 298 } 299 } 300 return; 301 } 302 while (--argc > 0) { 303 char *arg; 304 arg = *++argv; 305 c = getcmd(arg); 306 if (c == (struct cmd *)-1) 307 printf("?Ambiguous help command %s\n", arg); 308 else if (c == (struct cmd *)0) 309 printf("?Invalid help command %s\n", arg); 310 else 311 printf("%-*s\t%s\n", (int)HELPINDENT, 312 c->c_name, c->c_help); 313 } 314} 315 316/* 317 * return non-zero if the user is a member of the given group 318 */ 319static int 320ingroup(const char *grname) 321{ 322 static struct group *gptr = NULL; 323 static gid_t groups[NGROUPS]; 324 static int ngroups; 325 gid_t gid; 326 int i; 327 328 if (gptr == NULL) { 329 if ((gptr = getgrnam(grname)) == NULL) { 330 warnx("Warning: unknown group `%s'", 331 grname); 332 return(0); 333 } 334 ngroups = getgroups(NGROUPS, groups); 335 if (ngroups < 0) 336 err(1, "getgroups"); 337 } 338 gid = gptr->gr_gid; 339 for (i = 0; i < ngroups; i++) 340 if (gid == groups[i]) 341 return(1); 342 return(0); 343} 344