1 /* $OpenBSD: displayq.c,v 1.26 2003/06/02 23:36:53 millert Exp $ */
2 /* $NetBSD: displayq.c,v 1.21 2001/08/30 00:51:50 itojun Exp $ */
3
4 /*
5 * Copyright (c) 1983, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #ifndef lint
34 #if 0
35 static const char sccsid[] = "@(#)displayq.c 8.4 (Berkeley) 4/28/95";
36 #else
37 static const char rcsid[] = "$OpenBSD: displayq.c,v 1.26 2003/06/02 23:36:53 millert Exp $";
38 #endif
39 #endif /* not lint */
40
41 #include <sys/param.h>
42 #include <sys/file.h>
43 #include <sys/ioctl.h>
44 #include <sys/stat.h>
45
46 #include <ctype.h>
47 #include <errno.h>
48 #include <dirent.h>
49 #include <fcntl.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include <vis.h>
56
57 #include "lp.h"
58 #include "lp.local.h"
59 #include "pathnames.h"
60
61 /*
62 * Routines to display the state of the queue.
63 */
64 #define JOBCOL 40 /* column for job # in -l format */
65 #define OWNCOL 7 /* start of Owner column in normal */
66 #define SIZCOL 62 /* start of Size column in normal */
67
68 /*
69 * Stuff for handling job specifications
70 */
71 extern int requ[]; /* job number of spool entries */
72 extern int requests; /* # of spool requests */
73 extern char *user[]; /* users to process */
74 extern int users; /* # of users in user array */
75
76 static int termwidth;
77 static int col; /* column on screen */
78 static char current[NAME_MAX]; /* current file being printed */
79 static char file[NAME_MAX]; /* print file name */
80 static int first; /* first file in ``files'' column? */
81 static int lflag; /* long output option */
82 static off_t 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 alarmer(int);
88 static void inform(char *, int);
89
90 /*
91 * Display the current state of the queue. Format = 1 if long format.
92 */
93 void
displayq(int format)94 displayq(int format)
95 {
96 struct queue *q;
97 int i, rank, nitems, fd, ret, len;
98 char *cp, *ecp, *p;
99 struct queue **queue;
100 struct winsize win;
101 struct stat statb;
102 FILE *fp;
103
104 termwidth = 80;
105 if (isatty(STDOUT_FILENO)) {
106 if ((p = getenv("COLUMNS")) != NULL)
107 termwidth = atoi(p);
108 else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
109 win.ws_col > 0)
110 termwidth = win.ws_col;
111 }
112 if (termwidth < 60)
113 termwidth = 60;
114
115 lflag = format;
116 totsize = 0;
117 if ((i = cgetent(&bp, printcapdb, printer)) == -2)
118 fatal("can't open printer description file");
119 else if (i == -1)
120 fatal("unknown printer");
121 else if (i == -3)
122 fatal("potential reference loop detected in printcap file");
123 if (cgetstr(bp, DEFLP, &LP) < 0)
124 LP = _PATH_DEFDEVLP;
125 if (cgetstr(bp, "rp", &RP) < 0)
126 RP = DEFLP;
127 if (cgetstr(bp, "sd", &SD) < 0)
128 SD = _PATH_DEFSPOOL;
129 if (cgetstr(bp,"lo", &LO) < 0)
130 LO = DEFLOCK;
131 if (cgetstr(bp, "st", &ST) < 0)
132 ST = DEFSTAT;
133 cgetstr(bp, "rm", &RM);
134 if ((cp = checkremote()) != NULL)
135 printf("Warning: %s\n", cp);
136
137 /*
138 * Print out local queue
139 * Find all the control files in the spooling directory
140 */
141 PRIV_START;
142 if (chdir(SD) < 0)
143 fatal("cannot chdir to spooling directory");
144 PRIV_END;
145 if ((nitems = getq(&queue)) < 0)
146 fatal("cannot examine spooling area\n");
147 PRIV_START;
148 ret = stat(LO, &statb);
149 PRIV_END;
150 if (ret >= 0) {
151 if (statb.st_mode & S_IXUSR) {
152 if (remote)
153 printf("%s: ", host);
154 printf("Warning: %s is down: ", printer);
155 PRIV_START;
156 fd = safe_open(ST, O_RDONLY|O_NOFOLLOW, 0);
157 PRIV_END;
158 if (fd >= 0 && flock(fd, LOCK_SH) == 0) {
159 while ((i = read(fd, line, sizeof(line))) > 0)
160 (void)fwrite(line, 1, i, stdout);
161 (void)close(fd); /* unlocks as well */
162 } else
163 putchar('\n');
164 }
165 if (statb.st_mode & S_IXGRP) {
166 if (remote)
167 printf("%s: ", host);
168 printf("Warning: %s queue is turned off\n", printer);
169 }
170 }
171
172 if (nitems) {
173 PRIV_START;
174 fd = safe_open(LO, O_RDONLY|O_NOFOLLOW, 0);
175 PRIV_END;
176 if (fd < 0 || (fp = fdopen(fd, "r")) == NULL) {
177 if (fd >= 0)
178 close(fd);
179 nodaemon();
180 } else {
181 /* get daemon pid */
182 cp = current;
183 ecp = cp + sizeof(current) - 1;
184 while ((i = getc(fp)) != EOF && i != '\n') {
185 if (cp < ecp)
186 *cp++ = i;
187 }
188 *cp = '\0';
189 i = atoi(current);
190 if (i <= 0) {
191 ret = -1;
192 } else {
193 PRIV_START;
194 ret = kill(i, 0);
195 PRIV_END;
196 }
197 if (ret < 0 && errno != EPERM) {
198 nodaemon();
199 } else {
200 /* read current file name */
201 cp = current;
202 ecp = cp + sizeof(current) - 1;
203 while ((i = getc(fp)) != EOF && i != '\n') {
204 if (cp < ecp)
205 *cp++ = i;
206 }
207 *cp = '\0';
208 /*
209 * Print the status file.
210 */
211 if (remote)
212 printf("%s: ", host);
213 PRIV_START;
214 fd = safe_open(ST, O_RDONLY|O_NOFOLLOW, 0);
215 PRIV_END;
216 if (fd >= 0 && flock(fd, LOCK_SH) == 0) {
217 while ((i = read(fd, line, sizeof(line))) > 0)
218 (void)fwrite(line, 1, i, stdout);
219 (void)close(fd); /* unlocks as well */
220 } else
221 putchar('\n');
222 }
223 (void)fclose(fp);
224 }
225 /*
226 * Now, examine the control files and print out the jobs to
227 * be done for each user.
228 */
229 if (!lflag)
230 header();
231 /* The currently printed job is treated specially. */
232 if (!remote && current[0] != '\0')
233 inform(current, 0);
234 for (i = 0, rank = 1; i < nitems; i++) {
235 q = queue[i];
236 if (remote || strcmp(current, q->q_name) != 0)
237 inform(q->q_name, rank++);
238 free(q);
239 }
240 free(queue);
241 }
242 if (!remote) {
243 if (nitems == 0)
244 puts("no entries");
245 return;
246 }
247
248 /*
249 * Print foreign queue
250 * Note that a file in transit may show up in either queue.
251 */
252 if (nitems)
253 putchar('\n');
254 (void)snprintf(line, sizeof(line), "%c%s", format + '\3', RP);
255 cp = line;
256 cp += strlen(cp);
257 for (i = 0; i < requests && cp - line < sizeof(line) - 1; i++) {
258 len = line + sizeof(line) - cp;
259 if (snprintf(cp, len, " %d", requ[i]) >= len) {
260 cp += strlen(cp);
261 break;
262 }
263 cp += strlen(cp);
264 }
265 for (i = 0; i < users && cp - line < sizeof(line) - 1; i++) {
266 len = line + sizeof(line) - cp;
267 if (snprintf(cp, len, " %s", user[i]) >= len) {
268 cp += strlen(cp);
269 break;
270 }
271 }
272 if (cp-line < sizeof(line) - 1)
273 strlcat(line, "\n", sizeof(line));
274 else
275 line[sizeof(line) - 2] = '\n';
276 fd = getport(RM, 0);
277 if (fd < 0) {
278 if (from != host)
279 printf("%s: ", host);
280 (void)printf("connection to %s is down\n", RM);
281 }
282 else {
283 struct sigaction osa, nsa;
284 char *visline;
285
286 i = strlen(line);
287 if (write(fd, line, i) != i)
288 fatal("Lost connection");
289 memset(&nsa, 0, sizeof(nsa));
290 nsa.sa_handler = alarmer;
291 sigemptyset(&nsa.sa_mask);
292 nsa.sa_flags = 0;
293 (void)sigaction(SIGALRM, &nsa, &osa);
294 alarm(wait_time);
295 if ((visline = (char *)malloc(4 * sizeof(line) + 1)) == NULL)
296 fatal("Out of memory");
297 while ((i = read(fd, line, sizeof(line))) > 0) {
298 i = strvisx(visline, line, i, VIS_SAFE|VIS_NOSLASH);
299 (void)fwrite(visline, 1, i, stdout);
300 alarm(wait_time);
301 }
302 alarm(0);
303 (void)sigaction(SIGALRM, &osa, NULL);
304 free(visline);
305 (void)close(fd);
306 }
307 }
308
309 static void
alarmer(int s)310 alarmer(int s)
311 {
312 /* nothing */
313 }
314
315 /*
316 * Print a warning message if there is no daemon present.
317 */
318 void
nodaemon(void)319 nodaemon(void)
320 {
321 if (remote)
322 printf("\n%s: ", host);
323 puts("Warning: no daemon present");
324 current[0] = '\0';
325 }
326
327 /*
328 * Print the header for the short listing format
329 */
330 void
header(void)331 header(void)
332 {
333 printf(head0);
334 col = strlen(head0)+1;
335 blankfill(termwidth - (80 - SIZCOL));
336 printf(head1);
337 }
338
339 static void
inform(char * cf,int rank)340 inform(char *cf, int rank)
341 {
342 int fd, j;
343 FILE *cfp = NULL;
344
345 /*
346 * There's a chance the control file has gone away
347 * in the meantime; if this is the case just keep going
348 */
349 PRIV_START;
350 fd = safe_open(cf, O_RDONLY|O_NOFOLLOW, 0);
351 PRIV_END;
352 if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) {
353 if (fd >= 0)
354 close(fd);
355 return;
356 }
357
358 j = 0;
359 while (getline(cfp)) {
360 switch (line[0]) {
361 case 'P': /* Was this file specified in the user's list? */
362 if (!inlist(line+1, cf)) {
363 fclose(cfp);
364 return;
365 }
366 if (lflag) {
367 printf("\n%s: ", line+1);
368 col = strlen(line+1) + 2;
369 prank(rank);
370 blankfill(JOBCOL);
371 printf(" [job %s]\n", cf+3);
372 } else {
373 col = 0;
374 prank(rank);
375 blankfill(OWNCOL);
376 printf("%-10s %-3d ", line+1, atoi(cf+3));
377 col += 16;
378 first = 1;
379 }
380 continue;
381 default: /* some format specifer and file name? */
382 if (line[0] < 'a' || line[0] > 'z')
383 continue;
384 if (j == 0 || strcmp(file, line+1) != 0)
385 (void)strlcpy(file, line+1, sizeof(file));
386 j++;
387 continue;
388 case 'N':
389 show(line+1, file, j);
390 file[0] = '\0';
391 j = 0;
392 }
393 }
394 fclose(cfp);
395 if (!lflag) {
396 blankfill(termwidth - (80 - SIZCOL));
397 printf("%lld bytes\n", (long long)totsize);
398 totsize = 0;
399 }
400 }
401
402 int
inlist(char * name,char * file)403 inlist(char *name, char *file)
404 {
405 int *r, n;
406 char **u, *cp;
407
408 if (users == 0 && requests == 0)
409 return(1);
410 /*
411 * Check to see if it's in the user list
412 */
413 for (u = user; u < &user[users]; u++)
414 if (!strcmp(*u, name))
415 return(1);
416 /*
417 * Check the request list
418 */
419 for (n = 0, cp = file+3; isdigit(*cp); )
420 n = n * 10 + (*cp++ - '0');
421 for (r = requ; r < &requ[requests]; r++)
422 if (*r == n && !strcmp(cp, from))
423 return(1);
424 return(0);
425 }
426
427 void
show(char * nfile,char * file,int copies)428 show(char *nfile, char *file, int copies)
429 {
430 if (strcmp(nfile, " ") == 0)
431 nfile = "(standard input)";
432 if (lflag)
433 ldump(nfile, file, copies);
434 else
435 dump(nfile, file, copies);
436 }
437
438 /*
439 * Fill the line with blanks to the specified column
440 */
441 void
blankfill(int n)442 blankfill(int n)
443 {
444 while (col++ < n)
445 putchar(' ');
446 }
447
448 /*
449 * Give the abbreviated dump of the file names
450 */
451 void
dump(char * nfile,char * file,int copies)452 dump(char *nfile, char *file, int copies)
453 {
454 int n, fill;
455 struct stat lbuf;
456
457 /*
458 * Print as many files as will fit
459 * (leaving room for the total size)
460 */
461 fill = first ? 0 : 2; /* fill space for ``, '' */
462 if (((n = strlen(nfile)) + col + fill) >=
463 (termwidth - (80 - SIZCOL)) - 4) {
464 if (col < (termwidth - (80 - SIZCOL))) {
465 printf(" ..."), col += 4;
466 blankfill(termwidth - (80 - SIZCOL));
467 }
468 } else {
469 if (first)
470 first = 0;
471 else
472 printf(", ");
473 printf("%s", nfile);
474 col += n+fill;
475 }
476 PRIV_START;
477 if (*file && !stat(file, &lbuf))
478 totsize += copies * lbuf.st_size;
479 PRIV_END;
480 }
481
482 /*
483 * Print the long info about the file
484 */
485 void
ldump(char * nfile,char * file,int copies)486 ldump(char *nfile, char *file, int copies)
487 {
488 struct stat lbuf;
489
490 putchar('\t');
491 if (copies > 1)
492 printf("%-2d copies of %-19s", copies, nfile);
493 else
494 printf("%-32s", nfile);
495 if (*file && !stat(file, &lbuf))
496 printf(" %lld bytes", (long long)lbuf.st_size);
497 else
498 printf(" ??? bytes");
499 putchar('\n');
500 }
501
502 /*
503 * Print the job's rank in the queue,
504 * update col for screen management
505 */
506 void
prank(int n)507 prank(int n)
508 {
509 char rline[100];
510 static char *r[] = {
511 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
512 };
513
514 if (n == 0) {
515 printf("active");
516 col += 6;
517 return;
518 }
519 if ((n/10)%10 == 1)
520 (void)snprintf(rline, sizeof(rline), "%dth", n);
521 else
522 (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
523 col += strlen(rline);
524 printf("%s", rline);
525 }
526