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