* Copyright (c) 1983 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
static char sccsid
[] = "@(#)lpd.c 5.12 (Berkeley) 3/7/91";
* lpd -- line printer daemon.
* Listen for a connection and perform the requested operation.
* check the queue for jobs and print any found.
* receive a job from another machine and queue it.
* \3printer [users ...] [jobs ...]\n
* return the current state of the queue (short form).
* \4printer [users ...] [jobs ...]\n
* return the current state of the queue (long form).
* \5printer person [users ...] [jobs ...]\n
* remove jobs from the queue.
* Strategy to maintain protected spooling area:
* 1. Spooling area is writable only by daemon and spooling group
* 2. lpr runs setuid root and setgrp spooling group; it uses
* root to access any file it wants (verifying things before
* with an access call) and group id to know how it should
* set up ownership of files in the spooling area.
* 3. Files in spooling area are owned by root, group spooling
* 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
* access files and printer. Users can't get to anything
* w/o help of lpq and lprm programs.
int lflag
; /* log requests flag */
int from_remote
; /* from remote socket */
void mcleanup(), reapchild();
int f
, funix
, finet
, options
= 0, defreadfds
, fromlen
;
struct sockaddr_un sun
, fromunix
;
struct sockaddr_in sin
, frominet
;
gethostname(host
, sizeof(host
));
* Set up standard environment by detaching from the parent.
openlog("lpd", LOG_PID
, LOG_LPR
);
lfd
= open(_PATH_MASTERLOCK
, O_WRONLY
|O_CREAT
, 0644);
syslog(LOG_ERR
, "%s: %m", _PATH_MASTERLOCK
);
if (flock(lfd
, LOCK_EX
|LOCK_NB
) < 0) {
if (errno
== EWOULDBLOCK
) /* active deamon present */
syslog(LOG_ERR
, "%s: %m", _PATH_MASTERLOCK
);
* write process id for others to know
sprintf(line
, "%u\n", getpid());
if (write(lfd
, line
, f
) != f
) {
syslog(LOG_ERR
, "%s: %m", _PATH_MASTERLOCK
);
signal(SIGCHLD
, reapchild
);
* Restart all the printers.
(void) unlink(_PATH_SOCKETNAME
);
funix
= socket(AF_UNIX
, SOCK_STREAM
, 0);
syslog(LOG_ERR
, "socket: %m");
#define mask(s) (1 << ((s) - 1))
omask
= sigblock(mask(SIGHUP
)|mask(SIGINT
)|mask(SIGQUIT
)|mask(SIGTERM
));
signal(SIGHUP
, mcleanup
);
signal(SIGINT
, mcleanup
);
signal(SIGQUIT
, mcleanup
);
signal(SIGTERM
, mcleanup
);
sun
.sun_family
= AF_UNIX
;
strcpy(sun
.sun_path
, _PATH_SOCKETNAME
);
(struct sockaddr
*)&sun
, strlen(sun
.sun_path
) + 2) < 0) {
syslog(LOG_ERR
, "ubind: %m");
finet
= socket(AF_INET
, SOCK_STREAM
, 0);
if (setsockopt(finet
, SOL_SOCKET
, SO_DEBUG
, 0, 0) < 0) {
syslog(LOG_ERR
, "setsockopt (SO_DEBUG): %m");
sp
= getservbyname("printer", "tcp");
syslog(LOG_ERR
, "printer/tcp: unknown service");
sin
.sin_addr
.s_addr
=INADDR_ANY
;
sin
.sin_family
= AF_INET
;
sin
.sin_port
= sp
->s_port
;
if (bind(finet
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0) {
syslog(LOG_ERR
, "bind: %m");
defreadfds
|= 1 << finet
;
* Main loop: accept, do a request, continue.
int domain
, nfds
, s
, readfds
= defreadfds
;
nfds
= select(20, &readfds
, 0, 0, 0);
if (nfds
< 0 && errno
!= EINTR
)
syslog(LOG_WARNING
, "select: %m");
if (readfds
& (1 << funix
)) {
domain
= AF_UNIX
, fromlen
= sizeof(fromunix
);
(struct sockaddr
*)&fromunix
, &fromlen
);
} else if (readfds
& (1 << finet
)) {
domain
= AF_INET
, fromlen
= sizeof(frominet
);
(struct sockaddr
*)&frominet
, &fromlen
);
syslog(LOG_WARNING
, "accept: %m");
signal(SIGCHLD
, SIG_IGN
);
signal(SIGQUIT
, SIG_IGN
);
signal(SIGTERM
, SIG_IGN
);
while (wait3((int *)&status
, WNOHANG
, 0) > 0)
syslog(LOG_INFO
, "exiting");
unlink(_PATH_SOCKETNAME
);
* Stuff for handling job specifications
char *user
[MAXUSERS
]; /* users to process */
int users
; /* # of users in user array */
int requ
[MAXREQUESTS
]; /* job number of spool entries */
int requests
; /* # of spool requests */
char *person
; /* name of person doing lprm */
char fromb
[32]; /* buffer for client's machine name */
char cbuf
[BUFSIZ
]; /* command line buffer */
if (cp
>= &cbuf
[sizeof(cbuf
) - 1])
fatal("Command line too long");
if ((n
= read(1, cp
, 1)) != 1) {
fatal("Lost connection");
if (*cp
>= '\1' && *cp
<= '\5')
syslog(LOG_INFO
, "%s requests %s %s",
from
, cmdnames
[*cp
], cp
+1);
syslog(LOG_INFO
, "bad request (%d) from %s",
case '\1': /* check the queue and print any jobs there */
case '\2': /* receive files to be queued */
syslog(LOG_INFO
, "illegal request (%d)", *cp
);
case '\3': /* display the queue (short form) */
case '\4': /* display the queue (long form) */
if (requests
>= MAXREQUESTS
)
fatal("Too many requests");
requ
[requests
++] = atoi(cp
);
displayq(cbuf
[0] - '\3');
case '\5': /* remove a job from the queue */
syslog(LOG_INFO
, "illegal request (%d)", *cp
);
while (*cp
&& *cp
!= ' ')
if (requests
>= MAXREQUESTS
)
fatal("Too many requests");
requ
[requests
++] = atoi(cp
);
fatal("Illegal service request");
* Make a pass through the printcap database and start printing any
* files left from the last time the machine went down.
while (getprent(buf
) > 0) {
for (cp
= buf
; *cp
; cp
++)
if (*cp
== '|' || *cp
== ':') {
if ((pid
= fork()) < 0) {
syslog(LOG_WARNING
, "startup: cannot fork");
#define DUMMY ":nobody::"
* Check to see if the from host has access to the line printer.
register struct hostent
*hp
;
extern char *inet_ntoa();
f
->sin_port
= ntohs(f
->sin_port
);
if (f
->sin_family
!= AF_INET
|| f
->sin_port
>= IPPORT_RESERVED
)
fatal("Malformed from address");
hp
= gethostbyaddr((char *)&f
->sin_addr
,
sizeof(struct in_addr
), f
->sin_family
);
fatal("Host name for your address (%s) unknown",
strcpy(fromb
, hp
->h_name
);
*cp
++ = isupper(*sp
) ? tolower(*sp
++) : *sp
++;
hostf
= fopen(_PATH_HOSTSEQUIV
, "r");
if (!_validuser(hostf
, ahost
, DUMMY
, DUMMY
, baselen
)) {
hostf
= fopen(_PATH_HOSTSLPD
, "r");
fatal("Your host does not have line printer access");