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