source: trunk/minix/commands/simple/lpd.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 6.6 KB
Line 
1/* lpd 1.6 - Printer daemon Author: Kees J. Bot
2 * 3 Dec 1989
3 */
4#define nil 0
5#include <stdio.h>
6#include <stdlib.h>
7#include <limits.h>
8#include <string.h>
9#include <errno.h>
10#include <sys/types.h>
11#include <sys/stat.h>
12#include <dirent.h>
13#include <fcntl.h>
14#include <unistd.h>
15#include <termcap.h>
16
17char PRINTER[] = "/dev/lp";
18char SPOOL[] = "/usr/spool/lpd";
19char LOG[] = "/dev/log";
20
21void report(char *mess)
22{
23 fprintf(stderr, "lpd: %s: %s\n", mess, strerror(errno));
24}
25
26void fatal(char *mess)
27{
28 report(mess);
29 exit(1);
30}
31
32char jobX[] = "jobXXXXXX";
33char tmpX[] = "tmpXXXXXX";
34
35void spoolerr(char *file)
36{
37 int e= errno;
38
39 unlink(jobX);
40 unlink(tmpX);
41 fatal(file);
42}
43
44void spool(char *path)
45/* Place a file into the spool directory, either by copying it, or by leaving
46 * a reference.
47 */
48{
49 char *file;
50 int j, u;
51
52 mktemp(jobX);
53 file= mktemp(tmpX);
54
55 if (path[0] == '/') {
56 int f;
57
58 if ((f= open(path, O_RDONLY)) >= 0) {
59 close(f);
60 file= path;
61 }
62 }
63 if (file != path) {
64 int c;
65 FILE *t;
66
67 if ((t= fopen(tmpX, "w")) == nil) spoolerr(tmpX);
68
69 while ((c= getchar()) != EOF && putc(c, t) != EOF) {}
70
71 if (ferror(stdin)) spoolerr(path);
72
73 if (ferror(t) || fclose(t) == EOF) spoolerr(tmpX);
74
75 fclose(stdin);
76 }
77
78 if ((j= open(jobX, O_WRONLY|O_CREAT|O_EXCL, 0000)) < 0) spoolerr(jobX);
79
80 u= getuid();
81 if (write(j, file, strlen(file)+1) < 0
82 || write(j, &u, sizeof(u)) < 0
83 || write(j, path, strlen(path)+1) < 0
84 || close(j) < 0
85 || chmod(jobX, 0600) < 0
86 ) spoolerr(jobX);
87}
88
89struct job {
90 struct job *next;
91 time_t age;
92 char name[sizeof(jobX)];
93} *jobs = nil;
94
95int job(void)
96/* Look for print jobs in the spool directory. Make a list of them sorted
97 * by age. Return true iff the list is nonempty.
98 */
99{
100 DIR *spool;
101 struct dirent *entry;
102 struct job *newjob, **ajob;
103 struct stat st;
104
105 if (jobs != nil) return 1;
106
107 if ((spool= opendir(".")) == nil) fatal(SPOOL);
108
109 while ((entry= readdir(spool)) != nil) {
110 if (strncmp(entry->d_name, "job", 3) != 0) continue;
111
112 if (stat(entry->d_name, &st) < 0
113 || (st.st_mode & 0777) == 0000) continue;
114
115 if ((newjob= malloc(sizeof(*newjob))) == nil) fatal("malloc()");
116 newjob->age = st.st_mtime;
117 strcpy(newjob->name, entry->d_name);
118
119 ajob= &jobs;
120 while (*ajob != nil && (*ajob)->age < newjob->age)
121 ajob= &(*ajob)->next;
122
123 newjob->next= *ajob;
124 *ajob= newjob;
125 }
126 closedir(spool);
127
128 return jobs != nil;
129}
130
131/* What to do with control-X:
132 * 0 ignore,
133 * 1 give up on controlling the printer, assume user knows how printer works,
134 * 2 print.
135 */
136char control[] = {
137 0, 1, 1, 1, 1, 1, 1, 0, /* \0, \a don't show. */
138 2, 2, 2, 1, 2, 2, 1, 1, /* \b, \t, \n, \f, \r */
139 1, 1, 1, 1, 1, 1, 1, 1,
140 1, 1, 1, 1, 1, 1, 1, 1
141};
142
143int lp;
144char buf[BUFSIZ];
145int count, column, line, ncols = 80, nlines = 66;
146
147int flush(void)
148/* Copy the characters in the output buffer to the printer, with retries if
149 * out of paper.
150 */
151{
152 char *bp= buf;
153
154 while (count > 0) {
155 int retry = 0, complain = 0;
156 int r;
157
158 while ((r= write(lp, bp, count)) < 0) {
159 if (errno != EAGAIN) fatal(PRINTER);
160 if (retry == complain) {
161 fprintf(stderr,
162 "lpd: %s: Printer out of paper\n",
163 PRINTER);
164 complain= retry + 60;
165 }
166 sleep(1);
167 retry++;
168 }
169 bp+= r;
170 count-= r;
171 }
172 count = 0;
173}
174
175int put(int c)
176/* Send characters to the output buffer to be printed and do so if the buffer
177 * is full. Track the position of the write-head in `column' and `line'.
178 */
179{
180 buf[count++] = c;
181
182 switch (c) {
183 case '\f':
184 column = 0;
185 line = 0;
186 break;
187 case '\r':
188 column = 0;
189 break;
190 case '\n':
191 line++;
192 break;
193 case '\b':
194 column--;
195 break;
196 default:
197 if (++column > ncols) { line++; column= 1; }
198 }
199 if (line == nlines) line= 0;
200
201 if (count == BUFSIZ) flush();
202}
203
204void print(FILE *f)
205/* Send the contents of an open file to the printer. Expand tabs and change
206 * linefeed to a carriage-return linefeed sequence. Print a formfeed at the
207 * end if needed to reach the top of the next page. If a control character
208 * is printed that we do not know about, then the user is assumed to know
209 * what they are doing, so output processing is disabled.
210 */
211{
212 int c;
213
214 count= column= line= 0;
215
216 while ((c= getc(f)) != EOF) {
217 if (c < ' ') {
218 switch (control[c]) {
219 case 0: continue; /* Ignore this one. */
220 case 1:
221 /* Can't handle this junk, assume smart user. */
222 do {
223 buf[count++] = c;
224 if (count == BUFSIZ) flush();
225 } while ((c= getc(f)) != EOF);
226
227 flush();
228 return;
229 case 2: /* fine */;
230 }
231 }
232
233 switch (c) {
234 case '\n':
235 put('\r');
236 put('\n');
237 break;
238 case '\t':
239 do {
240 put(' ');
241 } while (column & 07);
242 break;
243 case '\b':
244 if (column > 0) put(c);
245 break;
246 default:
247 put(c);
248 }
249 }
250 if (column > 0) { put('\r'); put('\n'); }
251 if (line > 0) put('\f');
252 flush();
253 return;
254}
255
256void joberr(char *job)
257{
258 fprintf(stderr, "lpd: something is wrong with %s\n", job);
259
260 if (unlink(job) < 0) fatal("can't remove it");
261}
262
263void work(void)
264/* Print all the jobs in the job list. */
265{
266 FILE *j, *f;
267 char file[PATH_MAX+1], *pf=file;
268 int c;
269 struct job *job;
270
271 job= jobs;
272 jobs= jobs->next;
273
274 if ((j= fopen(job->name, "r")) == nil) {
275 joberr(job->name);
276 return;
277 }
278
279 do {
280 if (pf == file + sizeof(file) || (c= getc(j)) == EOF) {
281 fclose(j);
282 joberr(job->name);
283 return;
284 }
285 *pf++ = c;
286 } while (c != 0);
287
288 fclose(j);
289
290 if ((f= fopen(file, "r")) == nil)
291 fprintf(stderr, "lpd: can't read %s\n", file);
292 else {
293 print(f);
294 fclose(f);
295 }
296 if (file[0] != '/' && unlink(file) < 0) report(file);
297
298 if (unlink(job->name) < 0) fatal(job->name);
299 free(job);
300}
301
302void getcap(void)
303/* Find the line printer dimensions in the termcap database under "lp". */
304{
305 char printcap[1024];
306 int n;
307
308 if (tgetent(printcap, "lp") == 1) {
309 if ((n= tgetnum("co")) > 0) ncols= n;
310 if ((n= tgetnum("li")) > 0) nlines= n;
311 }
312}
313
314void haunt(void)
315/* Become a daemon, print jobs while there are any, exit. */
316{
317 int fd;
318
319 if ((fd= open("/dev/tty", O_RDONLY)) != -1) {
320 /* We have a controlling tty! Disconnect. */
321 close(fd);
322
323 switch(fork()) {
324 case -1: fatal("can't fork");
325 case 0: break;
326 default: exit(0);
327 }
328
329 if ((fd= open("/dev/null", O_RDONLY)) < 0) fatal("/dev/null");
330 dup2(fd, 0);
331 close(fd);
332 if ((fd= open(LOG, O_WRONLY)) < 0) fatal(LOG);
333 dup2(fd, 1);
334 dup2(fd, 2);
335 close(fd);
336 setsid();
337 }
338
339 getcap();
340
341 do {
342 if ((lp= open(PRINTER, O_WRONLY)) < 0) {
343 /* Another lpd? */
344 if (errno == EBUSY) exit(0);
345 fatal(PRINTER);
346 }
347
348 while (job()) work();
349
350 close(lp);
351 } while (job());
352}
353
354int main(int argc, char **argv)
355{
356 if (argc > 2) {
357 fprintf(stderr, "Usage: %s [path | stdin < path]\n", argv[0]);
358 exit(1);
359 }
360
361 umask(0077);
362
363 if (chdir(SPOOL) < 0) fatal(SPOOL);
364
365 if (argc == 2) spool(argv[1]);
366
367 haunt();
368}
Note: See TracBrowser for help on using the repository browser.