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