1/* $NetBSD: lpc.c,v 1.27 2017/05/04 16:26:09 sevan 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.27 2017/05/04 16:26:09 sevan 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 *); 90const char *prompt(void); 91static int parse(char *, char *p[], int); 92 93int 94main(int argc, char *argv[]) 95{ 96 euid = geteuid(); 97 uid = getuid(); 98 seteuid(uid); 99 setprogname(argv[0]); 100 openlog("lpd", 0, LOG_LPR); 101 102 if (--argc > 0) { 103 argv++; 104 exit(!parse(*argv, argv, argc)); 105 } 106 fromatty = isatty(fileno(stdin)); 107 top = setjmp(toplevel) == 0; 108 if (top) 109 signal(SIGINT, intr); 110 for (;;) { 111 cmdscanner(top); 112 top = 1; 113 } 114} 115 116static int 117parse(char *arg, char **pargv, int pargc) 118{ 119 struct cmd *c; 120 121 c = getcmd(arg); 122 if (c == (struct cmd *)-1) { 123 printf("?Ambiguous command\n"); 124 return(0); 125 } 126 if (c == 0) { 127 printf("?Invalid command\n"); 128 return(0); 129 } 130 if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) { 131 printf("?Privileged command\n"); 132 return(0); 133 } 134 (*c->c_handler)(pargc, pargv); 135 return(1); 136} 137 138static void 139intr(int signo) 140{ 141 el_end(elptr); 142 history_end(hist); 143 if (!fromatty) 144 exit(0); 145 longjmp(toplevel, 1); 146} 147 148const char * 149prompt(void) 150{ 151 return ("lpc> "); 152} 153 154/* 155 * Command parser. 156 */ 157static void 158cmdscanner(int tp) 159{ 160 int scratch; 161 const char *elline; 162 163 if (!tp) 164 putchar('\n'); 165 hist = history_init(); 166 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ 167 168 elptr = el_init(getprogname(), stdin, stdout, stderr); 169 el_set(elptr, EL_EDITOR, "emacs"); 170 el_set(elptr, EL_PROMPT, prompt); 171 el_set(elptr, EL_HIST, history, hist); 172 el_source(elptr, NULL); 173 174 for (;;) { 175 cmdline = NULL; 176 do { 177 if (((elline = el_gets(elptr, &scratch)) != NULL) 178 && (scratch != 0)) { 179 history(hist, &he, H_ENTER, elline); 180 cmdline = strdup(elline); 181 makeargv(); 182 } else { 183 margc = 0; 184 break; 185 } 186 } while (margc == 0); 187 if (margc == 0) 188 quit(0, NULL); 189 if (!parse(cmdline, margv, margc)) { 190 if (cmdline != NULL) 191 free(cmdline); 192 continue; 193 } 194 fflush(stdout); 195 if (cmdline != NULL) 196 free(cmdline); 197 } 198 longjmp(toplevel, 0); 199} 200 201static struct cmd * 202getcmd(const char *name) 203{ 204 const char *p, *q; 205 struct cmd *c, *found; 206 int nmatches, longest; 207 208 longest = 0; 209 nmatches = 0; 210 found = 0; 211 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 212 for (q = name; *q == *p++; q++) 213 if (*q == 0) /* exact match? */ 214 return(c); 215 if (!*q) { /* the name was a prefix */ 216 if (q - name > longest) { 217 longest = q - name; 218 nmatches = 1; 219 found = c; 220 } else if (q - name == longest) 221 nmatches++; 222 } 223 } 224 if (nmatches > 1) 225 return((struct cmd *)-1); 226 return(found); 227} 228 229/* 230 * Slice a string up into argc/argv. 231 */ 232static void 233makeargv(void) 234{ 235 char *cp; 236 char **argp = margv; 237 int n = 0; 238 size_t s; 239 240 s = strlen(cmdline) + 1; 241 margc = 0; 242 for (cp = cmdline; *cp && (size_t)(cp - cmdline) < s && n < MAX_MARGV; n++) { 243 while (isspace((unsigned char)*cp)) 244 cp++; 245 if (*cp == '\0') 246 break; 247 *argp++ = cp; 248 margc += 1; 249 while (*cp != '\0' && !isspace((unsigned char)*cp)) 250 cp++; 251 if (*cp == '\0') 252 break; 253 *cp++ = '\0'; 254 } 255 *argp++ = 0; 256} 257 258#define HELPINDENT (sizeof ("directory")) 259 260/* 261 * Help command. 262 */ 263void 264help(int argc, char *argv[]) 265{ 266 struct cmd *c; 267 268 if (argc == 1) { 269 size_t i, j, w; 270 size_t columns, width = 0, lines; 271 272 printf("Commands may be abbreviated. Commands are:\n\n"); 273 for (c = cmdtab; c->c_name; c++) { 274 size_t len = strlen(c->c_name); 275 276 if (len > width) 277 width = len; 278 } 279 width = (width + 8) &~ 7; 280 columns = 80 / width; 281 if (columns == 0) 282 columns = 1; 283 lines = (NCMDS + columns - 1) / columns; 284 for (i = 0; i < lines; i++) { 285 for (j = 0; j < columns; j++) { 286 c = cmdtab + j * lines + i; 287 printf("%s", c->c_name); 288 if (c + lines >= &cmdtab[NCMDS - 1]) { 289 printf("\n"); 290 break; 291 } 292 w = strlen(c->c_name); 293 while (w < width) { 294 w = (w + 8) &~ 7; 295 putchar('\t'); 296 } 297 } 298 } 299 return; 300 } 301 while (--argc > 0) { 302 char *arg; 303 arg = *++argv; 304 c = getcmd(arg); 305 if (c == (struct cmd *)-1) 306 printf("?Ambiguous help command %s\n", arg); 307 else if (c == (struct cmd *)0) 308 printf("?Invalid help command %s\n", arg); 309 else 310 printf("%-*s\t%s\n", (int)HELPINDENT, 311 c->c_name, c->c_help); 312 } 313} 314 315/* 316 * return non-zero if the user is a member of the given group 317 */ 318static int 319ingroup(const char *grname) 320{ 321 static struct group *gptr = NULL; 322 static gid_t groups[NGROUPS]; 323 static int ngroups; 324 gid_t gid; 325 int i; 326 327 if (gptr == NULL) { 328 if ((gptr = getgrnam(grname)) == NULL) { 329 warnx("Warning: unknown group `%s'", 330 grname); 331 return(0); 332 } 333 ngroups = getgroups(NGROUPS, groups); 334 if (ngroups < 0) 335 err(1, "getgroups"); 336 } 337 gid = gptr->gr_gid; 338 for (i = 0; i < ngroups; i++) 339 if (gid == groups[i]) 340 return(1); 341 return(0); 342} 343