123291Sbde/*- 223291Sbde * SPDX-License-Identifier: BSD-3-Clause 323291Sbde * 423291Sbde * Copyright (c) 1983, 1993 523291Sbde * The Regents of the University of California. All rights reserved. 623291Sbde * 723291Sbde * 823291Sbde * Redistribution and use in source and binary forms, with or without 923291Sbde * modification, are permitted provided that the following conditions 1023291Sbde * are met: 1123291Sbde * 1. Redistributions of source code must retain the above copyright 1223291Sbde * notice, this list of conditions and the following disclaimer. 1323291Sbde * 2. Redistributions in binary form must reproduce the above copyright 1423291Sbde * notice, this list of conditions and the following disclaimer in the 1523291Sbde * documentation and/or other materials provided with the distribution. 1623291Sbde * 3. Neither the name of the University nor the names of its contributors 1723291Sbde * may be used to endorse or promote products derived from this software 1823291Sbde * without specific prior written permission. 1923291Sbde * 2023291Sbde * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2123291Sbde * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2223291Sbde * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2323291Sbde * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2423291Sbde * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2523291Sbde * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2623291Sbde * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2723291Sbde * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2823291Sbde * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2923291Sbde * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3090039Sobrien * SUCH DAMAGE. 3123291Sbde */ 3290039Sobrien 3390039Sobrien#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 3490039Sobrien#include <sys/param.h> 3523291Sbde 3623291Sbde#include <ctype.h> 3723291Sbde#include <dirent.h> 3823291Sbde#include <err.h> 3923291Sbde#include <grp.h> 40101651Smux#include <setjmp.h> 41101651Smux#include <signal.h> 4223291Sbde#include <stdio.h> 4323291Sbde#include <stdlib.h> 4423291Sbde#include <syslog.h> 45101651Smux#include <string.h> 4623291Sbde#include <unistd.h> 47101651Smux#include <histedit.h> 4823291Sbde 4923291Sbde#include "lp.h" 50101651Smux#include "lpc.h" 5123291Sbde#include "extern.h" 52101651Smux 5323291Sbde#ifndef LPR_OPER 54101651Smux#define LPR_OPER "operator" /* group name of lpr operators */ 5523291Sbde#endif 56101651Smux 5723291Sbde/* 58101651Smux * lpc -- line printer control program 59101651Smux */ 60101651Smux 61101651Smux#define MAX_CMDLINE 200 62101651Smux#define MAX_MARGV 20 63101651Smuxstatic int fromatty; 64101651Smux 65101651Smuxstatic char cmdline[MAX_CMDLINE]; 66101651Smuxstatic int margc; 67101651Smuxstatic char *margv[MAX_MARGV]; 68101651Smuxuid_t uid, euid; 69101651Smux 70101651Smuxint main(int _argc, char *_argv[]); 7123291Sbdestatic void cmdscanner(void); 7223291Sbdestatic struct cmd *getcmd(const char *_name); 73101651Smuxstatic void intr(int _signo); 7423291Sbdestatic void makeargv(void); 7523291Sbdestatic int ingroup(const char *_grname); 7623291Sbde 77int 78main(int argc, char *argv[]) 79{ 80 register struct cmd *c; 81 82 euid = geteuid(); 83 uid = getuid(); 84 PRIV_END 85 progname = argv[0]; 86 openlog("lpd", 0, LOG_LPR); 87 88 if (--argc > 0) { 89 c = getcmd(*++argv); 90 if (c == (struct cmd *)-1) { 91 printf("?Ambiguous command\n"); 92 exit(1); 93 } 94 if (c == NULL) { 95 printf("?Invalid command\n"); 96 exit(1); 97 } 98 if ((c->c_opts & LPC_PRIVCMD) && getuid() && 99 ingroup(LPR_OPER) == 0) { 100 printf("?Privileged command\n"); 101 exit(1); 102 } 103 if (c->c_generic != NULL) 104 generic(c->c_generic, c->c_opts, c->c_handler, 105 argc, argv); 106 else 107 (*c->c_handler)(argc, argv); 108 exit(0); 109 } 110 fromatty = isatty(fileno(stdin)); 111 if (!fromatty) 112 signal(SIGINT, intr); 113 for (;;) { 114 cmdscanner(); 115 } 116} 117 118static void 119intr(int signo __unused) 120{ 121 /* (the '__unused' is just to avoid a compile-time warning) */ 122 exit(0); 123} 124 125static const char * 126lpc_prompt(void) 127{ 128 return ("lpc> "); 129} 130 131/* 132 * Command parser. 133 */ 134static void 135cmdscanner(void) 136{ 137 register struct cmd *c; 138 static EditLine *el; 139 static History *hist; 140 HistEvent he; 141 size_t len; 142 int num; 143 const char *bp; 144 145 num = 0; 146 bp = NULL; 147 el = NULL; 148 hist = NULL; 149 for (;;) { 150 if (fromatty) { 151 if (!el) { 152 el = el_init("lpc", stdin, stdout, stderr); 153 hist = history_init(); 154 history(hist, &he, H_SETSIZE, 100); 155 el_set(el, EL_HIST, history, hist); 156 el_set(el, EL_EDITOR, "emacs"); 157 el_set(el, EL_PROMPT, lpc_prompt); 158 el_set(el, EL_SIGNAL, 1); 159 el_source(el, NULL); 160 /* 161 * EditLine init may call 'cgetset()' to set a 162 * capability-db meant for termcap (eg: to set 163 * terminal type 'xterm'). Reset that now, or 164 * that same db-information will be used for 165 * printcap (giving us an "xterm" printer, with 166 * all kinds of invalid capabilities...). 167 */ 168 cgetset(NULL); 169 } 170 if ((bp = el_gets(el, &num)) == NULL || num == 0) 171 quit(0, NULL); 172 173 len = MIN(MAX_CMDLINE - 1, num); 174 memcpy(cmdline, bp, len); 175 cmdline[len] = 0; 176 history(hist, &he, H_ENTER, bp); 177 178 } else { 179 if (fgets(cmdline, MAX_CMDLINE, stdin) == NULL) 180 quit(0, NULL); 181 if (cmdline[0] == 0 || cmdline[0] == '\n') 182 break; 183 } 184 185 makeargv(); 186 if (margc == 0) 187 continue; 188 if (el != NULL && el_parse(el, margc, (const char **)margv) != -1) 189 continue; 190 191 c = getcmd(margv[0]); 192 if (c == (struct cmd *)-1) { 193 printf("?Ambiguous command\n"); 194 continue; 195 } 196 if (c == NULL) { 197 printf("?Invalid command\n"); 198 continue; 199 } 200 if ((c->c_opts & LPC_PRIVCMD) && getuid() && 201 ingroup(LPR_OPER) == 0) { 202 printf("?Privileged command\n"); 203 continue; 204 } 205 206 /* 207 * Two different commands might have the same generic rtn 208 * (eg: "clean" and "tclean"), and just use different 209 * handler routines for distinct command-setup. The handler 210 * routine might also be set on a generic routine for 211 * initial parameter processing. 212 */ 213 if (c->c_generic != NULL) 214 generic(c->c_generic, c->c_opts, c->c_handler, 215 margc, margv); 216 else 217 (*c->c_handler)(margc, margv); 218 } 219} 220 221static struct cmd * 222getcmd(const char *name) 223{ 224 register const char *p, *q; 225 register struct cmd *c, *found; 226 register int nmatches, longest; 227 228 longest = 0; 229 nmatches = 0; 230 found = NULL; 231 for (c = cmdtab; (p = c->c_name); c++) { 232 for (q = name; *q == *p++; q++) 233 if (*q == 0) /* exact match? */ 234 return(c); 235 if (!*q) { /* the name was a prefix */ 236 if (q - name > longest) { 237 longest = q - name; 238 nmatches = 1; 239 found = c; 240 } else if (q - name == longest) 241 nmatches++; 242 } 243 } 244 if (nmatches > 1) 245 return((struct cmd *)-1); 246 return(found); 247} 248 249/* 250 * Slice a string up into argc/argv. 251 */ 252static void 253makeargv(void) 254{ 255 register char *cp; 256 register char **argp = margv; 257 register int n = 0; 258 259 margc = 0; 260 for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) && 261 n < MAX_MARGV - 1; n++) { 262 while (isspace(*cp)) 263 cp++; 264 if (*cp == '\0') 265 break; 266 *argp++ = cp; 267 margc += 1; 268 while (*cp != '\0' && !isspace(*cp)) 269 cp++; 270 if (*cp == '\0') 271 break; 272 *cp++ = '\0'; 273 } 274 *argp++ = NULL; 275} 276 277#define HELPINDENT (sizeof ("directory")) 278 279/* 280 * Help command. 281 */ 282void 283help(int argc, char *argv[]) 284{ 285 register struct cmd *c; 286 287 if (argc == 1) { 288 register int i, j, w; 289 int columns, width = 0, lines; 290 291 printf("Commands may be abbreviated. Commands are:\n\n"); 292 for (c = cmdtab; c->c_name; c++) { 293 int len = strlen(c->c_name); 294 295 if (len > width) 296 width = len; 297 } 298 width = (width + 8) &~ 7; 299 columns = 80 / width; 300 if (columns == 0) 301 columns = 1; 302 lines = (NCMDS + columns - 1) / columns; 303 for (i = 0; i < lines; i++) { 304 for (j = 0; j < columns; j++) { 305 c = cmdtab + j * lines + i; 306 if (c->c_name) 307 printf("%s", c->c_name); 308 if (c + lines >= &cmdtab[NCMDS]) { 309 printf("\n"); 310 break; 311 } 312 w = strlen(c->c_name); 313 while (w < width) { 314 w = (w + 8) &~ 7; 315 putchar('\t'); 316 } 317 } 318 } 319 return; 320 } 321 while (--argc > 0) { 322 register char *arg; 323 arg = *++argv; 324 c = getcmd(arg); 325 if (c == (struct cmd *)-1) 326 printf("?Ambiguous help command %s\n", arg); 327 else if (c == (struct cmd *)0) 328 printf("?Invalid help command %s\n", arg); 329 else 330 printf("%-*s\t%s\n", (int) HELPINDENT, 331 c->c_name, c->c_help); 332 } 333} 334 335/* 336 * return non-zero if the user is a member of the given group 337 */ 338static int 339ingroup(const char *grname) 340{ 341 static struct group *gptr=NULL; 342 static int ngroups = 0; 343 static long ngroups_max; 344 static gid_t *groups; 345 register gid_t gid; 346 register int i; 347 348 if (gptr == NULL) { 349 if ((gptr = getgrnam(grname)) == NULL) { 350 warnx("warning: unknown group '%s'", grname); 351 return(0); 352 } 353 ngroups_max = sysconf(_SC_NGROUPS_MAX); 354 if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) 355 err(1, "malloc"); 356 ngroups = getgroups(ngroups_max, groups); 357 if (ngroups < 0) 358 err(1, "getgroups"); 359 } 360 gid = gptr->gr_gid; 361 for (i = 0; i < ngroups; i++) 362 if (gid == groups[i]) 363 return(1); 364 return(0); 365} 366 367/* 368 * Routine to get the information for a single printer (which will be 369 * called by the routines which implement individual commands). 370 * Note: This is for commands operating on a *single* printer. 371 */ 372struct printer * 373setup_myprinter(char *pwanted, struct printer *pp, int sump_opts) 374{ 375 int cdres, cmdstatus; 376 377 init_printer(pp); 378 cmdstatus = getprintcap(pwanted, pp); 379 switch (cmdstatus) { 380 default: 381 fatal(pp, "%s", pcaperr(cmdstatus)); 382 /* NOTREACHED */ 383 case PCAPERR_NOTFOUND: 384 printf("unknown printer %s\n", pwanted); 385 return (NULL); 386 case PCAPERR_TCOPEN: 387 printf("warning: %s: unresolved tc= reference(s)", pwanted); 388 break; 389 case PCAPERR_SUCCESS: 390 break; 391 } 392 if ((sump_opts & SUMP_NOHEADER) == 0) 393 printf("%s:\n", pp->printer); 394 395 if (sump_opts & SUMP_CHDIR_SD) { 396 PRIV_START 397 cdres = chdir(pp->spool_dir); 398 PRIV_END 399 if (cdres < 0) { 400 printf("\tcannot chdir to %s\n", pp->spool_dir); 401 free_printer(pp); 402 return (NULL); 403 } 404 } 405 406 return (pp); 407} 408