libc/net: Rename dprintf() to not conflict with dprintf(3).
[dragonfly.git] / usr.sbin / lpr / common_source / displayq.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)displayq.c       8.4 (Berkeley) 4/28/95
30  * $FreeBSD: src/usr.sbin/lpr/common_source/displayq.c,v 1.15.2.8 2001/08/30 09:27:41 kris Exp $
31  * $DragonFly: src/usr.sbin/lpr/common_source/displayq.c,v 1.5 2005/08/08 18:58:56 joerg Exp $
32  */
33
34 #include <sys/param.h>
35 #include <sys/stat.h>
36
37 #include <ctype.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #define psignal foil_gcc_psignal
46 #define sys_siglist foil_gcc_siglist
47 #include <unistd.h>
48 #undef psignal
49 #undef sys_siglist
50
51 #include "lp.h"
52 #include "lp.local.h"
53 #include "pathnames.h"
54
55 /*
56  * Routines to display the state of the queue.
57  */
58 #define JOBCOL  40              /* column for job # in -l format */
59 #define OWNCOL  7               /* start of Owner column in normal */
60 #define SIZCOL  62              /* start of Size column in normal */
61
62 /*
63  * Stuff for handling job specifications
64  */
65 extern uid_t    uid, euid;
66
67 static int      col;            /* column on screen */
68 static char     current[NAME_MAX+1];    /* current file being printed */
69 static char     file[NAME_MAX+1];       /* print file name */
70 static int      first;          /* first file in ``files'' column? */
71 static int      garbage;        /* # of garbage cf files */
72 static int      lflag;          /* long output option */
73 static int      rank;           /* order to be printed (-1=none, 0=active) */
74 static long     totsize;        /* total print job size in bytes */
75
76 static const char  *head0 = "Rank   Owner      Job  Files";
77 static const char  *head1 = "Total Size\n";
78
79 static void     alarmhandler(int _signo);
80 static void     warn(const struct printer *_pp);
81
82 /*
83  * Display the current state of the queue. Format = 1 if long format.
84  */
85 void
86 displayq(struct printer *pp, int format)
87 {
88         struct jobqueue *q;
89         int i, nitems, fd, ret;
90         char *cp, *endp;
91         struct jobqueue **queue;
92         struct stat statb;
93         FILE *fp;
94         void (*savealrm)(int);
95
96         lflag = format;
97         totsize = 0;
98         rank = -1;
99
100         if ((cp = checkremote(pp))) {
101                 printf("Warning: %s\n", cp);
102                 free(cp);
103         }
104
105         /*
106          * Print out local queue
107          * Find all the control files in the spooling directory
108          */
109         seteuid(euid);
110         if (chdir(pp->spool_dir) < 0)
111                 fatal(pp, "cannot chdir to spooling directory: %s",
112                       strerror(errno));
113         seteuid(uid);
114         if ((nitems = getq(pp, &queue)) < 0)
115                 fatal(pp, "cannot examine spooling area\n");
116         seteuid(euid);
117         ret = stat(pp->lock_file, &statb);
118         seteuid(uid);
119         if (ret >= 0) {
120                 if (statb.st_mode & LFM_PRINT_DIS) {
121                         if (pp->remote)
122                                 printf("%s: ", local_host);
123                         printf("Warning: %s is down: ", pp->printer);
124                         seteuid(euid);
125                         fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
126                         seteuid(uid);
127                         if (fd >= 0) {
128                                 while ((i = read(fd, line, sizeof(line))) > 0)
129                                         fwrite(line, 1, i, stdout);
130                                 close(fd);      /* unlocks as well */
131                         } else
132                                 putchar('\n');
133                 }
134                 if (statb.st_mode & LFM_QUEUE_DIS) {
135                         if (pp->remote)
136                                 printf("%s: ", local_host);
137                         printf("Warning: %s queue is turned off\n", 
138                                pp->printer);
139                 }
140         }
141
142         if (nitems) {
143                 seteuid(euid);
144                 fp = fopen(pp->lock_file, "r");
145                 seteuid(uid);
146                 if (fp == NULL)
147                         warn(pp);
148                 else {
149                         /* get daemon pid */
150                         cp = current;
151                         endp = cp + sizeof(current) - 1;
152                         while ((i = getc(fp)) != EOF && i != '\n') {
153                                 if (cp < endp)
154                                         *cp++ = i;
155                         }
156                         *cp = '\0';
157                         i = atoi(current);
158                         if (i <= 0) {
159                                 ret = -1;
160                         } else {
161                                 seteuid(euid);
162                                 ret = kill(i, 0);
163                                 seteuid(uid);
164                         }
165                         if (ret < 0) {
166                                 warn(pp);
167                         } else {
168                                 /* read current file name */
169                                 cp = current;
170                                 endp = cp + sizeof(current) - 1;
171                                 while ((i = getc(fp)) != EOF && i != '\n') {
172                                         if (cp < endp)
173                                                 *cp++ = i;
174                                 }
175                                 *cp = '\0';
176                                 /*
177                                  * Print the status file.
178                                  */
179                                 if (pp->remote)
180                                         printf("%s: ", local_host);
181                                 seteuid(euid);
182                                 fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
183                                 seteuid(uid);
184                                 if (fd >= 0) {
185                                         while ((i = read(fd, line,
186                                                          sizeof(line))) > 0)
187                                                 fwrite(line, 1, i, stdout);
188                                         close(fd);      /* unlocks as well */
189                                 } else
190                                         putchar('\n');
191                         }
192                         fclose(fp);
193                 }
194                 /*
195                  * Now, examine the control files and print out the jobs to
196                  * be done for each user.
197                  */
198                 if (!lflag)
199                         header();
200                 for (i = 0; i < nitems; i++) {
201                         q = queue[i];
202                         inform(pp, q->job_cfname);
203                         free(q);
204                 }
205                 free(queue);
206         }
207         if (!pp->remote) {
208                 if (nitems == 0)
209                         puts("no entries");
210                 return;
211         }
212
213         /*
214          * Print foreign queue
215          * Note that a file in transit may show up in either queue.
216          */
217         if (nitems)
218                 putchar('\n');
219         snprintf(line, sizeof(line), "%c%s", format ? '\4' : '\3',
220                  pp->remote_queue);
221         cp = line;
222         for (i = 0; i < requests && cp-line+10 < sizeof(line) - 1; i++) {
223                 cp += strlen(cp);
224                 sprintf(cp, " %d", requ[i]);
225         }
226         for (i = 0; i < users && cp - line + 1 + strlen(user[i]) < 
227                 sizeof(line) - 1; i++) {
228                 cp += strlen(cp);
229                 *cp++ = ' ';
230                 strcpy(cp, user[i]);
231         }
232         strcat(line, "\n");
233         savealrm = signal(SIGALRM, alarmhandler);
234         alarm(pp->conn_timeout);
235         fd = getport(pp, pp->remote_host, 0);
236         alarm(0);
237         signal(SIGALRM, savealrm);
238         if (fd < 0) {
239                 if (from_host != local_host)
240                         printf("%s: ", local_host);
241                 printf("connection to %s is down\n", pp->remote_host);
242         }
243         else {
244                 i = strlen(line);
245                 if (write(fd, line, i) != i)
246                         fatal(pp, "Lost connection");
247                 while ((i = read(fd, line, sizeof(line))) > 0)
248                         fwrite(line, 1, i, stdout);
249                 close(fd);
250         }
251 }
252
253 /*
254  * Print a warning message if there is no daemon present.
255  */
256 static void
257 warn(const struct printer *pp)
258 {
259         if (pp->remote)
260                 printf("%s: ", local_host);
261         puts("Warning: no daemon present");
262         current[0] = '\0';
263 }
264
265 /*
266  * Print the header for the short listing format
267  */
268 void
269 header(void)
270 {
271         printf("%s", head0);
272         col = strlen(head0)+1;
273         blankfill(SIZCOL);
274         printf("%s", head1);
275 }
276
277 void
278 inform(const struct printer *pp, char *cf)
279 {
280         int copycnt;
281         char savedname[MAXPATHLEN+1];
282         FILE *cfp;
283
284         /*
285          * There's a chance the control file has gone away
286          * in the meantime; if this is the case just keep going
287          */
288         seteuid(euid);
289         if ((cfp = fopen(cf, "r")) == NULL)
290                 return;
291         seteuid(uid);
292
293         if (rank < 0)
294                 rank = 0;
295         if (pp->remote || garbage || strcmp(cf, current))
296                 rank++;
297
298         /*
299          * The cf-file may include commands to print more than one datafile
300          * from the user.  For each datafile, the cf-file contains at least
301          * one line which starts with some format-specifier ('a'-'z'), and
302          * a second line ('N'ame) which indicates the original name the user
303          * specified for that file.  There can be multiple format-spec lines
304          * for a single Name-line, if the user requested multiple copies of
305          * that file.  Standard lpr puts the format-spec line(s) before the
306          * Name-line, while lprNG puts the Name-line before the format-spec
307          * line(s).  This section needs to handle the lines in either order.
308          */
309         copycnt = 0;
310         file[0] = '\0';
311         savedname[0] = '\0';
312         while (getline(cfp)) {
313                 switch (line[0]) {
314                 case 'P': /* Was this file specified in the user's list? */
315                         if (!inlist(line+1, cf)) {
316                                 fclose(cfp);
317                                 return;
318                         }
319                         if (lflag) {
320                                 printf("\n%s: ", line+1);
321                                 col = strlen(line+1) + 2;
322                                 prank(rank);
323                                 blankfill(JOBCOL);
324                                 printf(" [job %s]\n", cf+3);
325                         } else {
326                                 col = 0;
327                                 prank(rank);
328                                 blankfill(OWNCOL);
329                                 printf("%-10s %-3d  ", line+1, atoi(cf+3));
330                                 col += 16;
331                                 first = 1;
332                         }
333                         continue;
334                 default: /* some format specifer and file name? */
335                         if (line[0] < 'a' || line[0] > 'z')
336                                 break;
337                         if (copycnt == 0 || strcmp(file, line+1) != 0) {
338                                 strlcpy(file, line + 1, sizeof(file));
339                         }
340                         copycnt++;
341                         /*
342                          * deliberately 'continue' to another getline(), so
343                          * all format-spec lines for this datafile are read
344                          * in and counted before calling show()
345                          */
346                         continue;
347                 case 'N':
348                         strlcpy(savedname, line + 1, sizeof(savedname));
349                         break;
350                 }
351                 if ((file[0] != '\0') && (savedname[0] != '\0')) {
352                         show(savedname, file, copycnt);
353                         copycnt = 0;
354                         file[0] = '\0';
355                         savedname[0] = '\0';
356                 }
357         }
358         fclose(cfp);
359         /* check for a file which hasn't been shown yet */
360         if (file[0] != '\0') {
361                 if (savedname[0] == '\0') {
362                         /* a safeguard in case the N-ame line is missing */
363                         strlcpy(savedname, file, sizeof(savedname));
364                 }
365                 show(savedname, file, copycnt);
366         }
367         if (!lflag) {
368                 blankfill(SIZCOL);
369                 printf("%ld bytes\n", totsize);
370                 totsize = 0;
371         }
372 }
373
374 int
375 inlist(char *uname, char *cfile)
376 {
377         int *r, n;
378         char **u, *cp;
379
380         if (users == 0 && requests == 0)
381                 return(1);
382         /*
383          * Check to see if it's in the user list
384          */
385         for (u = user; u < &user[users]; u++)
386                 if (!strcmp(*u, uname))
387                         return(1);
388         /*
389          * Check the request list
390          */
391         for (n = 0, cp = cfile+3; isdigit(*cp); )
392                 n = n * 10 + (*cp++ - '0');
393         for (r = requ; r < &requ[requests]; r++)
394                 if (*r == n && !strcmp(cp, from_host))
395                         return(1);
396         return(0);
397 }
398
399 void
400 show(const char *nfile, const char *datafile, int copies)
401 {
402         if (strcmp(nfile, " ") == 0)
403                 nfile = "(standard input)";
404         if (lflag)
405                 ldump(nfile, datafile, copies);
406         else
407                 dump(nfile, datafile, copies);
408 }
409
410 /*
411  * Fill the line with blanks to the specified column
412  */
413 void
414 blankfill(int tocol)
415 {
416         while (col++ < tocol)
417                 putchar(' ');
418 }
419
420 /*
421  * Give the abbreviated dump of the file names
422  */
423 void
424 dump(const char *nfile, const char *datafile, int copies)
425 {
426         struct stat lbuf;
427         const char etctmpl[] = ", ...";
428         char     etc[sizeof(etctmpl)];
429         char    *lastsep;
430         short    fill, nlen;
431         short    rem, remetc;
432
433         /*
434          * Print as many filenames as will fit
435          *      (leaving room for the 'total size' field)
436          */
437         fill = first ? 0 : 2;   /* fill space for ``, '' */
438         nlen = strlen(nfile);
439         rem = SIZCOL - 1 - col;
440         if (nlen + fill > rem) {
441                 if (first) {
442                         /* print the right-most part of the name */
443                         printf("...%s ", &nfile[3+nlen-rem]);
444                         col = SIZCOL;
445                 } else if (rem > 0) {
446                         /* fit as much of the etc-string as we can */
447                         remetc = rem;
448                         if (rem > strlen(etctmpl))
449                                 remetc = strlen(etctmpl);
450                         etc[0] = '\0';
451                         strncat(etc, etctmpl, remetc);
452                         printf("%s", etc);
453                         col += remetc;
454                         rem -= remetc;
455                         /* room for the last segment of this filename? */
456                         lastsep = strrchr(nfile, '/');
457                         if ((lastsep != NULL) && (rem > strlen(lastsep))) {
458                                 /* print the right-most part of this name */
459                                 printf("%s", lastsep);
460                                 col += strlen(lastsep);
461                         } else {
462                                 /* do not pack any more names in here */
463                                 blankfill(SIZCOL);
464                         }
465                 }
466         } else {
467                 if (!first)
468                         printf(", ");
469                 printf("%s", nfile);
470                 col += nlen + fill;
471         }
472         first = 0;
473
474         seteuid(euid);
475         if (*datafile && !stat(datafile, &lbuf))
476                 totsize += copies * lbuf.st_size;
477         seteuid(uid);
478 }
479
480 /*
481  * Print the long info about the file
482  */
483 void
484 ldump(const char *nfile, const char *datafile, int copies)
485 {
486         struct stat lbuf;
487
488         putchar('\t');
489         if (copies > 1)
490                 printf("%-2d copies of %-19s", copies, nfile);
491         else
492                 printf("%-32s", nfile);
493         if (*datafile && !stat(datafile, &lbuf))
494                 printf(" %qd bytes", (long long) lbuf.st_size);
495         else
496                 printf(" ??? bytes");
497         putchar('\n');
498 }
499
500 /*
501  * Print the job's rank in the queue,
502  *   update col for screen management
503  */
504 void
505 prank(int n)
506 {
507         char rline[100];
508         static const char *r[] = {
509                 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
510         };
511
512         if (n == 0) {
513                 printf("active");
514                 col += 6;
515                 return;
516         }
517         if ((n/10)%10 == 1)
518                 snprintf(rline, sizeof(rline), "%dth", n);
519         else
520                 snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
521         col += strlen(rline);
522         printf("%s", rline);
523 }
524
525 void
526 alarmhandler(int signo __unused)
527 {
528         /* the signal is ignored */
529         /* (the '__unused' is just to avoid a compile-time warning) */
530 }