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