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 35 #ifndef lint 36 static const char copyright[] = 37 "@(#) Copyright (c) 1983, 1993\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39 #endif /* not lint */ 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95"; 44 #endif 45 static const char rcsid[] = 46 "$FreeBSD$"; 47 #endif /* not lint */ 48 49 #include <sys/param.h> 50 51 #include <ctype.h> 52 #include <dirent.h> 53 #include <err.h> 54 #include <grp.h> 55 #include <setjmp.h> 56 #include <signal.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <syslog.h> 60 #include <string.h> 61 #include <unistd.h> 62 #include <histedit.h> 63 64 #include "lp.h" 65 #include "lpc.h" 66 #include "extern.h" 67 68 #ifndef LPR_OPER 69 #define LPR_OPER "operator" /* group name of lpr operators */ 70 #endif 71 72 /* 73 * lpc -- line printer control program 74 */ 75 76 #define MAX_CMDLINE 200 77 #define MAX_MARGV 20 78 static int fromatty; 79 80 static char cmdline[MAX_CMDLINE]; 81 static int margc; 82 static char *margv[MAX_MARGV]; 83 uid_t uid, euid; 84 85 int main(int _argc, char *_argv[]); 86 static void cmdscanner(void); 87 static struct cmd *getcmd(const char *_name); 88 static void intr(int _signo); 89 static void makeargv(void); 90 static int ingroup(const char *_grname); 91 92 int 93 main(int argc, char *argv[]) 94 { 95 register struct cmd *c; 96 97 euid = geteuid(); 98 uid = getuid(); 99 seteuid(uid); 100 progname = argv[0]; 101 openlog("lpd", 0, LOG_LPR); 102 103 if (--argc > 0) { 104 c = getcmd(*++argv); 105 if (c == (struct cmd *)-1) { 106 printf("?Ambiguous command\n"); 107 exit(1); 108 } 109 if (c == 0) { 110 printf("?Invalid command\n"); 111 exit(1); 112 } 113 if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) { 114 printf("?Privileged command\n"); 115 exit(1); 116 } 117 if (c->c_generic != 0) 118 generic(c->c_generic, c->c_handler, argc, argv); 119 else 120 (*c->c_handler)(argc, argv); 121 exit(0); 122 } 123 fromatty = isatty(fileno(stdin)); 124 if (!fromatty) 125 signal(SIGINT, intr); 126 for (;;) { 127 cmdscanner(); 128 } 129 } 130 131 static void 132 intr(int signo __unused) 133 { 134 /* (the '__unused' is just to avoid a compile-time warning) */ 135 exit(0); 136 } 137 138 static const char * 139 lpc_prompt(void) 140 { 141 return ("lpc> "); 142 } 143 144 /* 145 * Command parser. 146 */ 147 static void 148 cmdscanner(void) 149 { 150 register struct cmd *c; 151 static EditLine *el; 152 static History *hist; 153 HistEvent he; 154 size_t len; 155 int num; 156 const char *bp; 157 158 num = 0; 159 bp = NULL; 160 el = NULL; 161 hist = NULL; 162 for (;;) { 163 if (fromatty) { 164 if (!el) { 165 el = el_init("lpc", stdin, stdout, stderr); 166 hist = history_init(); 167 history(hist, &he, H_EVENT, 100); 168 el_set(el, EL_HIST, history, hist); 169 el_set(el, EL_EDITOR, "emacs"); 170 el_set(el, EL_PROMPT, lpc_prompt); 171 el_set(el, EL_SIGNAL, 1); 172 el_source(el, NULL); 173 /* 174 * EditLine init may call 'cgetset()' to set a 175 * capability-db meant for termcap (eg: to set 176 * terminal type 'xterm'). Reset that now, or 177 * that same db-information will be used for 178 * printcap (giving us an "xterm" printer, with 179 * all kinds of invalid capabilities...). 180 */ 181 cgetset(NULL); 182 } 183 if ((bp = el_gets(el, &num)) == NULL || num == 0) 184 quit(0, NULL); 185 186 len = (num > MAX_CMDLINE) ? MAX_CMDLINE : num; 187 memcpy(cmdline, bp, len); 188 cmdline[len] = 0; 189 history(hist, &he, H_ENTER, bp); 190 191 } else { 192 if (fgets(cmdline, MAX_CMDLINE, stdin) == 0) 193 quit(0, NULL); 194 if (cmdline[0] == 0 || cmdline[0] == '\n') 195 break; 196 } 197 198 makeargv(); 199 if (margc == 0) 200 continue; 201 if (el_parse(el, margc, margv) != -1) 202 continue; 203 204 c = getcmd(margv[0]); 205 if (c == (struct cmd *)-1) { 206 printf("?Ambiguous command\n"); 207 continue; 208 } 209 if (c == 0) { 210 printf("?Invalid command\n"); 211 continue; 212 } 213 if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) { 214 printf("?Privileged command\n"); 215 continue; 216 } 217 218 /* 219 * Two different commands might have the same generic rtn 220 * (eg: "clean" and "tclean"), and just use different 221 * handler routines for distinct command-setup. The handler 222 * routine might also be set on a generic routine for 223 * initial parameter processing. 224 */ 225 if (c->c_generic != 0) 226 generic(c->c_generic, c->c_handler, margc, margv); 227 else 228 (*c->c_handler)(margc, margv); 229 } 230 } 231 232 static struct cmd * 233 getcmd(const char *name) 234 { 235 register const char *p, *q; 236 register struct cmd *c, *found; 237 register int nmatches, longest; 238 239 longest = 0; 240 nmatches = 0; 241 found = 0; 242 for (c = cmdtab; (p = c->c_name); c++) { 243 for (q = name; *q == *p++; q++) 244 if (*q == 0) /* exact match? */ 245 return(c); 246 if (!*q) { /* the name was a prefix */ 247 if (q - name > longest) { 248 longest = q - name; 249 nmatches = 1; 250 found = c; 251 } else if (q - name == longest) 252 nmatches++; 253 } 254 } 255 if (nmatches > 1) 256 return((struct cmd *)-1); 257 return(found); 258 } 259 260 /* 261 * Slice a string up into argc/argv. 262 */ 263 static void 264 makeargv(void) 265 { 266 register char *cp; 267 register char **argp = margv; 268 register int n = 0; 269 270 margc = 0; 271 for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) && 272 n < MAX_MARGV; n++) { 273 while (isspace(*cp)) 274 cp++; 275 if (*cp == '\0') 276 break; 277 *argp++ = cp; 278 margc += 1; 279 while (*cp != '\0' && !isspace(*cp)) 280 cp++; 281 if (*cp == '\0') 282 break; 283 *cp++ = '\0'; 284 } 285 *argp++ = 0; 286 } 287 288 #define HELPINDENT (sizeof ("directory")) 289 290 /* 291 * Help command. 292 */ 293 void 294 help(int argc, char *argv[]) 295 { 296 register struct cmd *c; 297 298 if (argc == 1) { 299 register int i, j, w; 300 int columns, width = 0, lines; 301 302 printf("Commands may be abbreviated. Commands are:\n\n"); 303 for (c = cmdtab; c->c_name; c++) { 304 int len = strlen(c->c_name); 305 306 if (len > width) 307 width = len; 308 } 309 width = (width + 8) &~ 7; 310 columns = 80 / width; 311 if (columns == 0) 312 columns = 1; 313 lines = (NCMDS + columns - 1) / columns; 314 for (i = 0; i < lines; i++) { 315 for (j = 0; j < columns; j++) { 316 c = cmdtab + j * lines + i; 317 if (c->c_name) 318 printf("%s", c->c_name); 319 if (c + lines >= &cmdtab[NCMDS]) { 320 printf("\n"); 321 break; 322 } 323 w = strlen(c->c_name); 324 while (w < width) { 325 w = (w + 8) &~ 7; 326 putchar('\t'); 327 } 328 } 329 } 330 return; 331 } 332 while (--argc > 0) { 333 register char *arg; 334 arg = *++argv; 335 c = getcmd(arg); 336 if (c == (struct cmd *)-1) 337 printf("?Ambiguous help command %s\n", arg); 338 else if (c == (struct cmd *)0) 339 printf("?Invalid help command %s\n", arg); 340 else 341 printf("%-*s\t%s\n", (int) HELPINDENT, 342 c->c_name, c->c_help); 343 } 344 } 345 346 /* 347 * return non-zero if the user is a member of the given group 348 */ 349 static int 350 ingroup(const char *grname) 351 { 352 static struct group *gptr=NULL; 353 static int ngroups = 0; 354 static gid_t groups[NGROUPS]; 355 register gid_t gid; 356 register int i; 357 358 if (gptr == NULL) { 359 if ((gptr = getgrnam(grname)) == NULL) { 360 warnx("warning: unknown group '%s'", grname); 361 return(0); 362 } 363 ngroups = getgroups(NGROUPS, groups); 364 if (ngroups < 0) 365 err(1, "getgroups"); 366 } 367 gid = gptr->gr_gid; 368 for (i = 0; i < ngroups; i++) 369 if (gid == groups[i]) 370 return(1); 371 return(0); 372 } 373