added 'hl' flag to printcap to print the banner last.
[unix-history] / usr / src / usr.sbin / lpr / lpd / lpd.c
CommitLineData
3a7f30af 1#ifndef lint
82bca8f8 2static char sccsid[] = "@(#)lpd.c 4.12 (Berkeley) %G%";
3a7f30af
SL
3#endif
4
7245937a
RC
5/*
6 * lpd -- line printer daemon.
7 *
8 * Listen for a connection and perform the requested operation.
9 * Operations are:
10 * \1printer\n
11 * check the queue for jobs and print any found.
12 * \2printer\n
13 * receive a job from another machine and queue it.
14 * \3printer [users ...] [jobs ...]\n
15 * return the current state of the queue (short form).
16 * \4printer [users ...] [jobs ...]\n
17 * return the current state of the queue (long form).
18 * \5printer person [users ...] [jobs ...]\n
19 * remove jobs from the queue.
20 *
21 * Strategy to maintain protected spooling area:
22 * 1. Spooling area is writable only by daemon and spooling group
23 * 2. lpr runs setuid root and setgrp spooling group; it uses
24 * root to access any file it wants (verifying things before
25 * with an access call) and group id to know how it should
26 * set up ownership of files in the spooling area.
4d4caa50 27 * 3. Files in spooling area are owned by root, group spooling
7245937a
RC
28 * group, with mode 660.
29 * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
30 * access files and printer. Users can't get to anything
31 * w/o help of lpq and lprm programs.
32 */
33
34#include "lp.h"
35
82bca8f8 36int lflag; /* log requests flag */
54266d1f 37
7245937a 38int reapchild();
82bca8f8 39int mcleanup();
7245937a
RC
40
41main(argc, argv)
42 int argc;
43 char **argv;
44{
8c4b7b45
SL
45 int f, funix, finet, options, defreadfds, fromlen;
46 struct sockaddr_un sun, fromunix;
47 struct sockaddr_in sin, frominet;
c0297f12 48 int omask, lfd;
7245937a
RC
49
50 gethostname(host, sizeof(host));
51 name = argv[0];
52
7245937a
RC
53 while (--argc > 0) {
54 argv++;
55 if (argv[0][0] == '-')
56 switch (argv[0][1]) {
57 case 'd':
58 options |= SO_DEBUG;
59 break;
60 case 'l':
4d4caa50
RC
61 lflag++;
62 break;
7245937a 63 }
7245937a 64 }
c0297f12 65
7245937a
RC
66#ifndef DEBUG
67 /*
68 * Set up standard environment by detaching from the parent.
69 */
70 if (fork())
71 exit(0);
82bca8f8 72 for (f = 0; f < 5; f++)
7245937a 73 (void) close(f);
adec4d9e
SL
74 (void) open("/dev/null", O_RDONLY);
75 (void) open("/dev/null", O_WRONLY);
82bca8f8 76 (void) dup(1);
adec4d9e 77 f = open("/dev/tty", O_RDWR);
7245937a
RC
78 if (f > 0) {
79 ioctl(f, TIOCNOTTY, 0);
80 (void) close(f);
81 }
82#endif
c0297f12 83
82bca8f8 84 openlog("lpd", LOG_PID, 0);
7245937a 85 (void) umask(0);
c0297f12
RC
86 lfd = open(MASTERLOCK, O_WRONLY|O_CREAT, 0644);
87 if (lfd < 0) {
82bca8f8 88 syslog(LOG_ERR, "%s: %m", MASTERLOCK);
c0297f12
RC
89 exit(1);
90 }
91 if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
92 if (errno == EWOULDBLOCK) /* active deamon present */
93 exit(0);
82bca8f8 94 syslog(LOG_ERR, "%s: %m", MASTERLOCK);
c0297f12
RC
95 exit(1);
96 }
97 ftruncate(lfd, 0);
98 /*
99 * write process id for others to know
100 */
101 sprintf(line, "%u\n", getpid());
102 f = strlen(line);
103 if (write(lfd, line, f) != f) {
82bca8f8 104 syslog(LOG_ERR, "%s: %m", MASTERLOCK);
c0297f12
RC
105 exit(1);
106 }
107 signal(SIGCHLD, reapchild);
54266d1f
RC
108 /*
109 * Restart all the printers.
110 */
111 startup();
c0297f12 112 (void) unlink(SOCKETNAME);
8c4b7b45
SL
113 funix = socket(AF_UNIX, SOCK_STREAM, 0);
114 if (funix < 0) {
82bca8f8 115 syslog(LOG_ERR, "socket: %m");
7245937a
RC
116 exit(1);
117 }
845f1693
RC
118#define mask(s) (1 << ((s) - 1))
119 omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
82bca8f8
RC
120 signal(SIGHUP, mcleanup);
121 signal(SIGINT, mcleanup);
122 signal(SIGQUIT, mcleanup);
123 signal(SIGTERM, mcleanup);
8c4b7b45
SL
124 sun.sun_family = AF_UNIX;
125 strcpy(sun.sun_path, SOCKETNAME);
126 if (bind(funix, &sun, strlen(sun.sun_path) + 2) < 0) {
82bca8f8 127 syslog(LOG_ERR, "ubind: %m");
7245937a
RC
128 exit(1);
129 }
8c4b7b45 130 sigsetmask(omask);
845f1693
RC
131 defreadfds = 1 << funix;
132 listen(funix, 5);
8c4b7b45
SL
133 finet = socket(AF_INET, SOCK_STREAM, 0);
134 if (finet >= 0) {
135 struct servent *sp;
136
137 if (options & SO_DEBUG)
138 if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
82bca8f8
RC
139 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
140 mcleanup();
8c4b7b45
SL
141 }
142 sp = getservbyname("printer", "tcp");
143 if (sp == NULL) {
82bca8f8
RC
144 syslog(LOG_ERR, "printer/tcp: unknown service");
145 mcleanup();
8c4b7b45
SL
146 }
147 sin.sin_family = AF_INET;
148 sin.sin_port = sp->s_port;
149 if (bind(finet, &sin, sizeof(sin), 0) < 0) {
82bca8f8
RC
150 syslog(LOG_ERR, "bind: %m");
151 mcleanup();
8c4b7b45 152 }
8c4b7b45
SL
153 defreadfds |= 1 << finet;
154 listen(finet, 5);
155 }
845f1693
RC
156 /*
157 * Main loop: accept, do a request, continue.
158 */
7245937a 159 for (;;) {
3a7f30af 160 int domain, nfds, s, readfds = defreadfds;
7245937a 161
3a7f30af
SL
162 nfds = select(20, &readfds, 0, 0, 0);
163 if (nfds <= 0) {
82bca8f8
RC
164 if (nfds < 0 && errno != EINTR)
165 syslog(LOG_WARNING, "select: %m");
3a7f30af
SL
166 continue;
167 }
8c4b7b45 168 if (readfds & (1 << funix)) {
3a7f30af 169 domain = AF_UNIX, fromlen = sizeof(fromunix);
8c4b7b45 170 s = accept(funix, &fromunix, &fromlen);
3a7f30af
SL
171 } else if (readfds & (1 << finet)) {
172 domain = AF_INET, fromlen = sizeof(frominet);
8c4b7b45
SL
173 s = accept(finet, &frominet, &fromlen);
174 }
7245937a 175 if (s < 0) {
82bca8f8
RC
176 if (errno != EINTR)
177 syslog(LOG_WARNING, "accept: %m");
178 continue;
7245937a
RC
179 }
180 if (fork() == 0) {
adec4d9e 181 signal(SIGCHLD, SIG_IGN);
f0b73bd5
RC
182 signal(SIGHUP, SIG_IGN);
183 signal(SIGINT, SIG_IGN);
184 signal(SIGQUIT, SIG_IGN);
185 signal(SIGTERM, SIG_IGN);
8c4b7b45
SL
186 (void) close(funix);
187 (void) close(finet);
188 dup2(s, 1);
189 (void) close(s);
190 if (domain == AF_INET)
191 chkhost(&frominet);
192 doit();
7245937a
RC
193 exit(0);
194 }
195 (void) close(s);
196 }
197}
198
199reapchild()
200{
201 union wait status;
202
203 while (wait3(&status, WNOHANG, 0) > 0)
204 ;
205}
206
82bca8f8 207mcleanup()
8c4b7b45 208{
845f1693 209 if (lflag)
82bca8f8 210 syslog(LOG_INFO, "exiting");
8c4b7b45
SL
211 unlink(SOCKETNAME);
212 exit(0);
213}
214
7245937a
RC
215/*
216 * Stuff for handling job specifications
217 */
218char *user[MAXUSERS]; /* users to process */
219int users; /* # of users in user array */
220int requ[MAXREQUESTS]; /* job number of spool entries */
221int requests; /* # of spool requests */
54266d1f 222char *person; /* name of person doing lprm */
7245937a 223
82bca8f8
RC
224char fromb[32]; /* buffer for client's machine name */
225char cbuf[BUFSIZ]; /* command line buffer */
226char *cmdnames[] = {
4d4caa50
RC
227 "null",
228 "printjob",
229 "recvjob",
230 "displayq short",
231 "displayq long",
232 "rmjob"
233};
7245937a 234
8c4b7b45 235doit()
7245937a
RC
236{
237 register char *cp;
7245937a 238 register int n;
7245937a 239
7245937a
RC
240 for (;;) {
241 cp = cbuf;
242 do {
8c4b7b45
SL
243 if (cp >= &cbuf[sizeof(cbuf) - 1])
244 fatal("Command line too long");
245 if ((n = read(1, cp, 1)) != 1) {
7245937a
RC
246 if (n < 0)
247 fatal("Lost connection");
248 return;
249 }
8c4b7b45 250 } while (*cp++ != '\n');
7245937a
RC
251 *--cp = '\0';
252 cp = cbuf;
82bca8f8
RC
253 if (lflag) {
254 if (*cp >= '\1' && *cp <= '\5')
255 syslog(LOG_INFO, "%s requests %s %s",
256 from, cmdnames[*cp], cp+1);
257 else
258 syslog(LOG_INFO, "bad request (%d) from %s",
259 *cp, from);
4d4caa50 260 }
7245937a
RC
261 switch (*cp++) {
262 case '\1': /* check the queue and print any jobs there */
263 printer = cp;
264 printjob();
265 break;
266 case '\2': /* receive files to be queued */
267 printer = cp;
268 recvjob();
269 break;
4d4caa50
RC
270 case '\3': /* display the queue (short form) */
271 case '\4': /* display the queue (long form) */
7245937a
RC
272 printer = cp;
273 while (*cp) {
274 if (*cp != ' ') {
275 cp++;
276 continue;
277 }
278 *cp++ = '\0';
279 while (isspace(*cp))
280 cp++;
281 if (*cp == '\0')
282 break;
283 if (isdigit(*cp)) {
284 if (requests >= MAXREQUESTS)
285 fatal("Too many requests");
286 requ[requests++] = atoi(cp);
287 } else {
288 if (users >= MAXUSERS)
289 fatal("Too many users");
290 user[users++] = cp;
291 }
292 }
293 displayq(cbuf[0] - '\3');
294 exit(0);
295 case '\5': /* remove a job from the queue */
296 printer = cp;
297 while (*cp && *cp != ' ')
298 cp++;
299 if (!*cp)
300 break;
301 *cp++ = '\0';
302 person = cp;
303 while (*cp) {
304 if (*cp != ' ') {
305 cp++;
306 continue;
307 }
308 *cp++ = '\0';
309 while (isspace(*cp))
310 cp++;
311 if (*cp == '\0')
312 break;
313 if (isdigit(*cp)) {
314 if (requests >= MAXREQUESTS)
315 fatal("Too many requests");
316 requ[requests++] = atoi(cp);
317 } else {
318 if (users >= MAXUSERS)
319 fatal("Too many users");
320 user[users++] = cp;
321 }
322 }
323 rmjob();
324 break;
325 }
326 fatal("Illegal service request");
7245937a
RC
327 }
328}
329
4d4caa50
RC
330/*
331 * Make a pass through the printcap database and start printing any
332 * files left from the last time the machine went down.
333 */
334startup()
335{
336 char buf[BUFSIZ];
337 register char *cp;
338 int pid;
339
340 printer = buf;
341
342 /*
343 * Restart the daemons.
344 */
345 while (getprent(buf) > 0) {
346 for (cp = buf; *cp; cp++)
347 if (*cp == '|' || *cp == ':') {
348 *cp = '\0';
349 break;
350 }
351 if ((pid = fork()) < 0) {
82bca8f8
RC
352 syslog(LOG_WARNING, "startup: cannot fork");
353 mcleanup();
4d4caa50
RC
354 }
355 if (!pid) {
356 endprent();
357 printjob();
358 }
359 }
360}
361
362/*
363 * Check to see if the from host has access to the line printer.
364 */
8c4b7b45
SL
365chkhost(f)
366 struct sockaddr_in *f;
4d4caa50 367{
8c4b7b45 368 register struct hostent *hp;
4d4caa50
RC
369 register FILE *hostf;
370 register char *cp;
371 char ahost[50];
24c04811 372 int first = 1;
8c4b7b45 373 extern char *inet_ntoa();
4d4caa50 374
8c4b7b45
SL
375 f->sin_port = ntohs(f->sin_port);
376 if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED)
377 fatal("Malformed from address");
378 hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family);
379 if (hp == 0)
380 fatal("Host name for your address (%s) unknown",
381 inet_ntoa(f->sin_addr));
382
383 strcpy(fromb, hp->h_name);
384 from = fromb;
219e8551 385 if (!strcmp(from, host))
8c4b7b45 386 return;
219e8551 387
4d4caa50 388 hostf = fopen("/etc/hosts.equiv", "r");
24c04811
RC
389again:
390 if (hostf) {
391 while (fgets(ahost, sizeof (ahost), hostf)) {
392 if (cp = index(ahost, '\n'))
393 *cp = '\0';
394 cp = index(ahost, ' ');
395 if (!strcmp(from, ahost) && cp == NULL) {
396 (void) fclose(hostf);
397 return;
398 }
4d4caa50 399 }
24c04811
RC
400 (void) fclose(hostf);
401 }
402 if (first == 1) {
403 first = 0;
404 hostf = fopen("/etc/hosts.lpd", "r");
405 goto again;
4d4caa50 406 }
8c4b7b45 407 fatal("Your host does not have line printer access");
4d4caa50 408}