Home | History | Annotate | Line # | Download | only in lpd
lpd.c revision 1.2
      1 /*
      2  * Copyright (c) 1983 Regents of the University of California.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. All advertising materials mentioning features or use of this software
     14  *    must display the following acknowledgement:
     15  *	This product includes software developed by the University of
     16  *	California, Berkeley and its contributors.
     17  * 4. Neither the name of the University nor the names of its contributors
     18  *    may be used to endorse or promote products derived from this software
     19  *    without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 #ifndef lint
     35 char copyright[] =
     36 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
     37  All rights reserved.\n";
     38 #endif /* not lint */
     39 
     40 #ifndef lint
     41 /*static char sccsid[] = "from: @(#)lpd.c	5.12 (Berkeley) 3/7/91";*/
     42 static char rcsid[] = "$Id: lpd.c,v 1.2 1993/08/01 17:58:54 mycroft Exp $";
     43 #endif /* not lint */
     44 
     45 /*
     46  * lpd -- line printer daemon.
     47  *
     48  * Listen for a connection and perform the requested operation.
     49  * Operations are:
     50  *	\1printer\n
     51  *		check the queue for jobs and print any found.
     52  *	\2printer\n
     53  *		receive a job from another machine and queue it.
     54  *	\3printer [users ...] [jobs ...]\n
     55  *		return the current state of the queue (short form).
     56  *	\4printer [users ...] [jobs ...]\n
     57  *		return the current state of the queue (long form).
     58  *	\5printer person [users ...] [jobs ...]\n
     59  *		remove jobs from the queue.
     60  *
     61  * Strategy to maintain protected spooling area:
     62  *	1. Spooling area is writable only by daemon and spooling group
     63  *	2. lpr runs setuid root and setgrp spooling group; it uses
     64  *	   root to access any file it wants (verifying things before
     65  *	   with an access call) and group id to know how it should
     66  *	   set up ownership of files in the spooling area.
     67  *	3. Files in spooling area are owned by root, group spooling
     68  *	   group, with mode 660.
     69  *	4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
     70  *	   access files and printer.  Users can't get to anything
     71  *	   w/o help of lpq and lprm programs.
     72  */
     73 
     74 #include "lp.h"
     75 #include "pathnames.h"
     76 
     77 int	lflag;				/* log requests flag */
     78 int	from_remote;			/* from remote socket */
     79 
     80 void mcleanup(), reapchild();
     81 
     82 main(argc, argv)
     83 	int argc;
     84 	char **argv;
     85 {
     86 	int f, funix, finet, options = 0, defreadfds, fromlen;
     87 	struct sockaddr_un sun, fromunix;
     88 	struct sockaddr_in sin, frominet;
     89 	int omask, lfd;
     90 
     91 	gethostname(host, sizeof(host));
     92 	name = argv[0];
     93 
     94 	while (--argc > 0) {
     95 		argv++;
     96 		if (argv[0][0] == '-')
     97 			switch (argv[0][1]) {
     98 			case 'd':
     99 				options |= SO_DEBUG;
    100 				break;
    101 			case 'l':
    102 				lflag++;
    103 				break;
    104 			}
    105 	}
    106 
    107 #ifndef DEBUG
    108 	/*
    109 	 * Set up standard environment by detaching from the parent.
    110 	 */
    111 	daemon(0, 0);
    112 #endif
    113 
    114 	openlog("lpd", LOG_PID, LOG_LPR);
    115 	(void) umask(0);
    116 	lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT, 0644);
    117 	if (lfd < 0) {
    118 		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
    119 		exit(1);
    120 	}
    121 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
    122 		if (errno == EWOULDBLOCK)	/* active deamon present */
    123 			exit(0);
    124 		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
    125 		exit(1);
    126 	}
    127 	ftruncate(lfd, 0);
    128 	/*
    129 	 * write process id for others to know
    130 	 */
    131 	sprintf(line, "%u\n", getpid());
    132 	f = strlen(line);
    133 	if (write(lfd, line, f) != f) {
    134 		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
    135 		exit(1);
    136 	}
    137 	signal(SIGCHLD, reapchild);
    138 	/*
    139 	 * Restart all the printers.
    140 	 */
    141 	startup();
    142 	(void) unlink(_PATH_SOCKETNAME);
    143 	funix = socket(AF_UNIX, SOCK_STREAM, 0);
    144 	if (funix < 0) {
    145 		syslog(LOG_ERR, "socket: %m");
    146 		exit(1);
    147 	}
    148 #define	mask(s)	(1 << ((s) - 1))
    149 	omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
    150 	signal(SIGHUP, mcleanup);
    151 	signal(SIGINT, mcleanup);
    152 	signal(SIGQUIT, mcleanup);
    153 	signal(SIGTERM, mcleanup);
    154 	sun.sun_family = AF_UNIX;
    155 	strcpy(sun.sun_path, _PATH_SOCKETNAME);
    156 	if (bind(funix,
    157 	     (struct sockaddr *)&sun, strlen(sun.sun_path) + 2) < 0) {
    158 		syslog(LOG_ERR, "ubind: %m");
    159 		exit(1);
    160 	}
    161 	sigsetmask(omask);
    162 	defreadfds = 1 << funix;
    163 	listen(funix, 5);
    164 	finet = socket(AF_INET, SOCK_STREAM, 0);
    165 	if (finet >= 0) {
    166 		struct servent *sp;
    167 
    168 		if (options & SO_DEBUG)
    169 			if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
    170 				syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
    171 				mcleanup();
    172 			}
    173 		sp = getservbyname("printer", "tcp");
    174 		if (sp == NULL) {
    175 			syslog(LOG_ERR, "printer/tcp: unknown service");
    176 			mcleanup();
    177 		}
    178 		sin.sin_family = AF_INET;
    179 		sin.sin_port = sp->s_port;
    180 		if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
    181 			syslog(LOG_ERR, "bind: %m");
    182 			mcleanup();
    183 		}
    184 		defreadfds |= 1 << finet;
    185 		listen(finet, 5);
    186 	}
    187 	/*
    188 	 * Main loop: accept, do a request, continue.
    189 	 */
    190 	for (;;) {
    191 		int domain, nfds, s, readfds = defreadfds;
    192 
    193 		nfds = select(20, &readfds, 0, 0, 0);
    194 		if (nfds <= 0) {
    195 			if (nfds < 0 && errno != EINTR)
    196 				syslog(LOG_WARNING, "select: %m");
    197 			continue;
    198 		}
    199 		if (readfds & (1 << funix)) {
    200 			domain = AF_UNIX, fromlen = sizeof(fromunix);
    201 			s = accept(funix,
    202 			    (struct sockaddr *)&fromunix, &fromlen);
    203 		} else if (readfds & (1 << finet)) {
    204 			domain = AF_INET, fromlen = sizeof(frominet);
    205 			s = accept(finet,
    206 			    (struct sockaddr *)&frominet, &fromlen);
    207 		}
    208 		if (s < 0) {
    209 			if (errno != EINTR)
    210 				syslog(LOG_WARNING, "accept: %m");
    211 			continue;
    212 		}
    213 		if (fork() == 0) {
    214 			signal(SIGCHLD, SIG_IGN);
    215 			signal(SIGHUP, SIG_IGN);
    216 			signal(SIGINT, SIG_IGN);
    217 			signal(SIGQUIT, SIG_IGN);
    218 			signal(SIGTERM, SIG_IGN);
    219 			(void) close(funix);
    220 			(void) close(finet);
    221 			dup2(s, 1);
    222 			(void) close(s);
    223 			if (domain == AF_INET) {
    224 				from_remote = 1;
    225 				chkhost(&frominet);
    226 			} else
    227 				from_remote = 0;
    228 			doit();
    229 			exit(0);
    230 		}
    231 		(void) close(s);
    232 	}
    233 }
    234 
    235 void
    236 reapchild()
    237 {
    238 	union wait status;
    239 
    240 	while (wait3((int *)&status, WNOHANG, 0) > 0)
    241 		;
    242 }
    243 
    244 void
    245 mcleanup()
    246 {
    247 	if (lflag)
    248 		syslog(LOG_INFO, "exiting");
    249 	unlink(_PATH_SOCKETNAME);
    250 	exit(0);
    251 }
    252 
    253 /*
    254  * Stuff for handling job specifications
    255  */
    256 char	*user[MAXUSERS];	/* users to process */
    257 int	users;			/* # of users in user array */
    258 int	requ[MAXREQUESTS];	/* job number of spool entries */
    259 int	requests;		/* # of spool requests */
    260 char	*person;		/* name of person doing lprm */
    261 
    262 char	fromb[32];	/* buffer for client's machine name */
    263 char	cbuf[BUFSIZ];	/* command line buffer */
    264 char	*cmdnames[] = {
    265 	"null",
    266 	"printjob",
    267 	"recvjob",
    268 	"displayq short",
    269 	"displayq long",
    270 	"rmjob"
    271 };
    272 
    273 doit()
    274 {
    275 	register char *cp;
    276 	register int n;
    277 
    278 	for (;;) {
    279 		cp = cbuf;
    280 		do {
    281 			if (cp >= &cbuf[sizeof(cbuf) - 1])
    282 				fatal("Command line too long");
    283 			if ((n = read(1, cp, 1)) != 1) {
    284 				if (n < 0)
    285 					fatal("Lost connection");
    286 				return;
    287 			}
    288 		} while (*cp++ != '\n');
    289 		*--cp = '\0';
    290 		cp = cbuf;
    291 		if (lflag) {
    292 			if (*cp >= '\1' && *cp <= '\5')
    293 				syslog(LOG_INFO, "%s requests %s %s",
    294 					from, cmdnames[*cp], cp+1);
    295 			else
    296 				syslog(LOG_INFO, "bad request (%d) from %s",
    297 					*cp, from);
    298 		}
    299 		switch (*cp++) {
    300 		case '\1':	/* check the queue and print any jobs there */
    301 			printer = cp;
    302 			printjob();
    303 			break;
    304 		case '\2':	/* receive files to be queued */
    305 			if (!from_remote) {
    306 				syslog(LOG_INFO, "illegal request (%d)", *cp);
    307 				exit(1);
    308 			}
    309 			printer = cp;
    310 			recvjob();
    311 			break;
    312 		case '\3':	/* display the queue (short form) */
    313 		case '\4':	/* display the queue (long form) */
    314 			printer = cp;
    315 			while (*cp) {
    316 				if (*cp != ' ') {
    317 					cp++;
    318 					continue;
    319 				}
    320 				*cp++ = '\0';
    321 				while (isspace(*cp))
    322 					cp++;
    323 				if (*cp == '\0')
    324 					break;
    325 				if (isdigit(*cp)) {
    326 					if (requests >= MAXREQUESTS)
    327 						fatal("Too many requests");
    328 					requ[requests++] = atoi(cp);
    329 				} else {
    330 					if (users >= MAXUSERS)
    331 						fatal("Too many users");
    332 					user[users++] = cp;
    333 				}
    334 			}
    335 			displayq(cbuf[0] - '\3');
    336 			exit(0);
    337 		case '\5':	/* remove a job from the queue */
    338 			if (!from_remote) {
    339 				syslog(LOG_INFO, "illegal request (%d)", *cp);
    340 				exit(1);
    341 			}
    342 			printer = cp;
    343 			while (*cp && *cp != ' ')
    344 				cp++;
    345 			if (!*cp)
    346 				break;
    347 			*cp++ = '\0';
    348 			person = cp;
    349 			while (*cp) {
    350 				if (*cp != ' ') {
    351 					cp++;
    352 					continue;
    353 				}
    354 				*cp++ = '\0';
    355 				while (isspace(*cp))
    356 					cp++;
    357 				if (*cp == '\0')
    358 					break;
    359 				if (isdigit(*cp)) {
    360 					if (requests >= MAXREQUESTS)
    361 						fatal("Too many requests");
    362 					requ[requests++] = atoi(cp);
    363 				} else {
    364 					if (users >= MAXUSERS)
    365 						fatal("Too many users");
    366 					user[users++] = cp;
    367 				}
    368 			}
    369 			rmjob();
    370 			break;
    371 		}
    372 		fatal("Illegal service request");
    373 	}
    374 }
    375 
    376 /*
    377  * Make a pass through the printcap database and start printing any
    378  * files left from the last time the machine went down.
    379  */
    380 startup()
    381 {
    382 	char buf[BUFSIZ];
    383 	register char *cp;
    384 	int pid;
    385 
    386 	printer = buf;
    387 
    388 	/*
    389 	 * Restart the daemons.
    390 	 */
    391 	while (getprent(buf) > 0) {
    392 		for (cp = buf; *cp; cp++)
    393 			if (*cp == '|' || *cp == ':') {
    394 				*cp = '\0';
    395 				break;
    396 			}
    397 		if ((pid = fork()) < 0) {
    398 			syslog(LOG_WARNING, "startup: cannot fork");
    399 			mcleanup();
    400 		}
    401 		if (!pid) {
    402 			endprent();
    403 			printjob();
    404 		}
    405 	}
    406 }
    407 
    408 #define DUMMY ":nobody::"
    409 
    410 /*
    411  * Check to see if the from host has access to the line printer.
    412  */
    413 chkhost(f)
    414 	struct sockaddr_in *f;
    415 {
    416 	register struct hostent *hp;
    417 	register FILE *hostf;
    418 	register char *cp, *sp;
    419 	char ahost[50];
    420 	int first = 1;
    421 	extern char *inet_ntoa();
    422 	int baselen = -1;
    423 
    424 	f->sin_port = ntohs(f->sin_port);
    425 	if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED)
    426 		fatal("Malformed from address");
    427 	hp = gethostbyaddr((char *)&f->sin_addr,
    428 	    sizeof(struct in_addr), f->sin_family);
    429 	if (hp == 0)
    430 		fatal("Host name for your address (%s) unknown",
    431 			inet_ntoa(f->sin_addr));
    432 
    433 	strcpy(fromb, hp->h_name);
    434 	from = fromb;
    435 	if (!strcmp(from, host))
    436 		return;
    437 
    438 	sp = fromb;
    439 	cp = ahost;
    440 	while (*sp) {
    441 		if (*sp == '.') {
    442 			if (baselen == -1)
    443 				baselen = sp - fromb;
    444 			*cp++ = *sp++;
    445 		} else {
    446 			*cp++ = isupper(*sp) ? tolower(*sp++) : *sp++;
    447 		}
    448 	}
    449 	*cp = '\0';
    450 	hostf = fopen(_PATH_HOSTSEQUIV, "r");
    451 again:
    452 	if (hostf) {
    453 		if (!_validuser(hostf, ahost, DUMMY, DUMMY, baselen)) {
    454 			(void) fclose(hostf);
    455 			return;
    456 		}
    457 		(void) fclose(hostf);
    458 	}
    459 	if (first == 1) {
    460 		first = 0;
    461 		hostf = fopen(_PATH_HOSTSLPD, "r");
    462 		goto again;
    463 	}
    464 	fatal("Your host does not have line printer access");
    465 }
    466