lpd.c

Go to the documentation of this file.
00001 /*      lpd 1.6 - Printer daemon                        Author: Kees J. Bot
00002  *                                                              3 Dec 1989
00003  */
00004 #define nil 0
00005 #include <stdio.h>
00006 #include <stdlib.h>
00007 #include <limits.h>
00008 #include <string.h>
00009 #include <errno.h>
00010 #include <sys/types.h>
00011 #include <sys/stat.h>
00012 #include <dirent.h>
00013 #include <fcntl.h>
00014 #include <unistd.h>
00015 #include <termcap.h>
00016 
00017 char PRINTER[] =        "/dev/lp";
00018 char SPOOL[] =          "/usr/spool/lpd";
00019 char LOG[] =            "/dev/log";
00020 
00021 void report(char *mess)
00022 {
00023         fprintf(stderr, "lpd: %s: %s\n", mess, strerror(errno));
00024 }
00025 
00026 void fatal(char *mess)
00027 {
00028         report(mess);
00029         exit(1);
00030 }
00031 
00032 char jobX[] = "jobXXXXXX";
00033 char tmpX[] = "tmpXXXXXX";
00034 
00035 void spoolerr(char *file)
00036 {
00037         int e= errno;
00038 
00039         unlink(jobX);
00040         unlink(tmpX);
00041         fatal(file);
00042 }
00043 
00044 void spool(char *path)
00045 /* Place a file into the spool directory, either by copying it, or by leaving
00046  * a reference.
00047  */
00048 {
00049         char *file;
00050         int j, u;
00051 
00052         mktemp(jobX);
00053         file= mktemp(tmpX);
00054 
00055         if (path[0] == '/') {
00056                 int f;
00057 
00058                 if ((f= open(path, O_RDONLY)) >= 0) {
00059                         close(f);
00060                         file= path;
00061                 }
00062         }
00063         if (file != path) {
00064                 int c;
00065                 FILE *t;
00066 
00067                 if ((t= fopen(tmpX, "w")) == nil) spoolerr(tmpX);
00068 
00069                 while ((c= getchar()) != EOF && putc(c, t) != EOF) {}
00070 
00071                 if (ferror(stdin)) spoolerr(path);
00072 
00073                 if (ferror(t) || fclose(t) == EOF) spoolerr(tmpX);
00074 
00075                 fclose(stdin);
00076         }
00077 
00078         if ((j= open(jobX, O_WRONLY|O_CREAT|O_EXCL, 0000)) < 0) spoolerr(jobX);
00079 
00080         u= getuid();
00081         if (write(j, file, strlen(file)+1) < 0
00082                 || write(j, &u, sizeof(u)) < 0
00083                 || write(j, path, strlen(path)+1) < 0
00084                 || close(j) < 0
00085                 || chmod(jobX, 0600) < 0
00086         ) spoolerr(jobX);
00087 }
00088 
00089 struct job {
00090         struct job *next;
00091         time_t age;
00092         char name[sizeof(jobX)];
00093 } *jobs = nil;
00094 
00095 int job(void)
00096 /* Look for print jobs in the spool directory.  Make a list of them sorted
00097  * by age.  Return true iff the list is nonempty.
00098  */
00099 {
00100         DIR *spool;
00101         struct dirent *entry;
00102         struct job *newjob, **ajob;
00103         struct stat st;
00104 
00105         if (jobs != nil) return 1;
00106 
00107         if ((spool= opendir(".")) == nil) fatal(SPOOL);
00108 
00109         while ((entry= readdir(spool)) != nil) {
00110                 if (strncmp(entry->d_name, "job", 3) != 0) continue;
00111 
00112                 if (stat(entry->d_name, &st) < 0
00113                         || (st.st_mode & 0777) == 0000) continue;
00114 
00115                 if ((newjob= malloc(sizeof(*newjob))) == nil) fatal("malloc()");
00116                 newjob->age = st.st_mtime;
00117                 strcpy(newjob->name, entry->d_name);
00118 
00119                 ajob= &jobs;
00120                 while (*ajob != nil && (*ajob)->age < newjob->age)
00121                         ajob= &(*ajob)->next;
00122 
00123                 newjob->next= *ajob;
00124                 *ajob= newjob;
00125         }
00126         closedir(spool);
00127 
00128         return jobs != nil;
00129 }
00130 
00131 /* What to do with control-X:
00132  * 0 ignore,
00133  * 1 give up on controlling the printer, assume user knows how printer works,
00134  * 2 print.
00135  */
00136 char control[] = {
00137         0, 1, 1, 1, 1, 1, 1, 0,         /* \0, \a  don't show.  */
00138         2, 2, 2, 1, 2, 2, 1, 1,         /* \b, \t, \n, \f, \r   */
00139         1, 1, 1, 1, 1, 1, 1, 1,
00140         1, 1, 1, 1, 1, 1, 1, 1
00141 };
00142 
00143 int lp;
00144 char buf[BUFSIZ];
00145 int count, column, line, ncols = 80, nlines = 66;
00146 
00147 int flush(void)
00148 /* Copy the characters in the output buffer to the printer, with retries if
00149  * out of paper.
00150  */
00151 {
00152         char *bp= buf;
00153 
00154         while (count > 0) {
00155                 int retry = 0, complain = 0;
00156                 int r;
00157 
00158                 while ((r= write(lp, bp, count)) < 0) {
00159                         if (errno != EAGAIN) fatal(PRINTER);
00160                         if (retry == complain) {
00161                                 fprintf(stderr,
00162                                         "lpd: %s: Printer out of paper\n",
00163                                         PRINTER);
00164                                 complain= retry + 60;
00165                         }
00166                         sleep(1);
00167                         retry++;
00168                 }
00169                 bp+= r;
00170                 count-= r;
00171         }
00172         count = 0;
00173 }
00174 
00175 int put(int c)
00176 /* Send characters to the output buffer to be printed and do so if the buffer
00177  * is full.  Track the position of the write-head in `column' and `line'.
00178  */
00179 {
00180         buf[count++] = c;
00181 
00182         switch (c) {
00183         case '\f':
00184                 column = 0;
00185                 line = 0;
00186                 break;
00187         case '\r':
00188                 column = 0;
00189                 break;
00190         case '\n':
00191                 line++;
00192                 break;
00193         case '\b':
00194                 column--;
00195                 break;
00196         default:
00197                 if (++column > ncols) { line++; column= 1; }
00198         }
00199         if (line == nlines) line= 0;
00200 
00201         if (count == BUFSIZ) flush();
00202 }
00203 
00204 void print(FILE *f)
00205 /* Send the contents of an open file to the printer.  Expand tabs and change
00206  * linefeed to a carriage-return linefeed sequence.  Print a formfeed at the
00207  * end if needed to reach the top of the next page.  If a control character
00208  * is printed that we do not know about, then the user is assumed to know
00209  * what they are doing, so output processing is disabled.
00210  */
00211 {
00212         int c;
00213 
00214         count= column= line= 0;
00215 
00216         while ((c= getc(f)) != EOF) {
00217                 if (c < ' ') {
00218                         switch (control[c]) {
00219                         case 0: continue;       /* Ignore this one. */
00220                         case 1:
00221                                 /* Can't handle this junk, assume smart user. */
00222                                 do {
00223                                         buf[count++] = c;
00224                                         if (count == BUFSIZ) flush();
00225                                 } while ((c= getc(f)) != EOF);
00226 
00227                                 flush();
00228                                 return;
00229                         case 2: /* fine */;
00230                         }
00231                 }
00232 
00233                 switch (c) {
00234                 case '\n':
00235                         put('\r');
00236                         put('\n');
00237                         break;
00238                 case '\t':
00239                         do {
00240                                 put(' ');
00241                         } while (column & 07);
00242                         break;
00243                 case '\b':
00244                         if (column > 0) put(c);
00245                         break;
00246                 default:
00247                         put(c);
00248                 }
00249         }
00250         if (column > 0) { put('\r'); put('\n'); }
00251         if (line > 0) put('\f');
00252         flush();
00253         return;
00254 }
00255 
00256 void joberr(char *job)
00257 {
00258         fprintf(stderr, "lpd: something is wrong with %s\n", job);
00259 
00260         if (unlink(job) < 0) fatal("can't remove it");
00261 }
00262 
00263 void work(void)
00264 /* Print all the jobs in the job list. */
00265 {
00266         FILE *j, *f;
00267         char file[PATH_MAX+1], *pf=file;
00268         int c;
00269         struct job *job;
00270 
00271         job= jobs;
00272         jobs= jobs->next;
00273 
00274         if ((j= fopen(job->name, "r")) == nil) {
00275                 joberr(job->name);
00276                 return;
00277         }
00278 
00279         do {
00280                 if (pf == file + sizeof(file) || (c= getc(j)) == EOF) {
00281                         fclose(j);
00282                         joberr(job->name);
00283                         return;
00284                 }
00285                 *pf++ = c;
00286         } while (c != 0);
00287 
00288         fclose(j);
00289 
00290         if ((f= fopen(file, "r")) == nil)
00291                 fprintf(stderr, "lpd: can't read %s\n", file);
00292         else {
00293                 print(f);
00294                 fclose(f);
00295         }
00296         if (file[0] != '/' && unlink(file) < 0) report(file);
00297 
00298         if (unlink(job->name) < 0) fatal(job->name);
00299         free(job);
00300 }
00301 
00302 void getcap(void)
00303 /* Find the line printer dimensions in the termcap database under "lp". */
00304 {
00305         char printcap[1024];
00306         int n;
00307 
00308         if (tgetent(printcap, "lp") == 1) {
00309                 if ((n= tgetnum("co")) > 0) ncols= n;
00310                 if ((n= tgetnum("li")) > 0) nlines= n;
00311         }
00312 }
00313 
00314 void haunt(void)
00315 /* Become a daemon, print jobs while there are any, exit. */
00316 {
00317         int fd;
00318 
00319         if ((fd= open("/dev/tty", O_RDONLY)) != -1) {
00320                 /* We have a controlling tty!  Disconnect. */
00321                 close(fd);
00322 
00323                 switch(fork()) {
00324                 case -1:        fatal("can't fork");
00325                 case  0:        break;
00326                 default:        exit(0);
00327                 }
00328 
00329                 if ((fd= open("/dev/null", O_RDONLY)) < 0) fatal("/dev/null");
00330                 dup2(fd, 0);
00331                 close(fd);
00332                 if ((fd= open(LOG, O_WRONLY)) < 0) fatal(LOG);
00333                 dup2(fd, 1);
00334                 dup2(fd, 2);
00335                 close(fd);
00336                 setsid();
00337         }
00338 
00339         getcap();
00340 
00341         do {
00342                 if ((lp= open(PRINTER, O_WRONLY)) < 0) {
00343                         /* Another lpd? */
00344                         if (errno == EBUSY) exit(0);
00345                         fatal(PRINTER);
00346                 }
00347 
00348                 while (job()) work();
00349 
00350                 close(lp);
00351         } while (job());
00352 }
00353 
00354 int main(int argc, char **argv)
00355 {
00356         if (argc > 2) {
00357                 fprintf(stderr, "Usage: %s [path | stdin < path]\n", argv[0]);
00358                 exit(1);
00359         }
00360 
00361         umask(0077);
00362 
00363         if (chdir(SPOOL) < 0) fatal(SPOOL);
00364 
00365         if (argc == 2) spool(argv[1]);
00366 
00367         haunt();
00368 }

Generated on Fri Apr 14 22:57:08 2006 for minix by  doxygen 1.4.6