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