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