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