1 /* $OpenBSD: displayq.c,v 1.41 2024/04/23 13:34:51 jsg 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 #include <sys/ioctl.h>
34 #include <sys/stat.h>
35
36 #include <ctype.h>
37 #include <errno.h>
38 #include <dirent.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 #include <unistd.h>
46 #include <vis.h>
47
48 #include "lp.h"
49 #include "lp.local.h"
50 #include "pathnames.h"
51
52 /*
53 * Routines to display the state of the queue.
54 */
55 #define JOBCOL 40 /* column for job # in -l format */
56 #define OWNCOL 7 /* start of Owner column in normal */
57 #define SIZCOL 62 /* start of Size column in normal */
58
59 /*
60 * Stuff for handling job specifications
61 */
62 extern int requ[]; /* job number of spool entries */
63 extern int requests; /* # of spool requests */
64 extern char *user[]; /* users to process */
65 extern int users; /* # of users in user array */
66
67 static int termwidth;
68 static int col; /* column on screen */
69 static char current[NAME_MAX]; /* current file being printed */
70 static char file[NAME_MAX]; /* print file name */
71 static int first; /* first file in ``files'' column? */
72 static int lflag; /* long output option */
73 static off_t totsize; /* total print job size in bytes */
74
75 static const char head0[] = "Rank Owner Job Files";
76 static const char head1[] = "Total Size\n";
77
78 static void alarmer(int);
79 static void blankfill(int);
80 static void dump(char *, char *, int);
81 static void header(void);
82 static void inform(char *, int);
83 static int inlist(char *, char *);
84 static void ldump(char *, char *, int);
85 static void nodaemon(void);
86 static void prank(int);
87 static void show(char *, char *, int);
88
89 /*
90 * Display the current state of the queue. Format = 1 if long format.
91 */
92 void
displayq(int format)93 displayq(int format)
94 {
95 struct queue *q;
96 int i, rank, nitems, fd, ret, len;
97 char *cp, *ecp, *p;
98 struct queue **queue;
99 struct winsize win;
100 struct stat statb;
101 FILE *fp;
102
103 termwidth = 0;
104 if ((p = getenv("COLUMNS")) != NULL)
105 termwidth = strtonum(p, 1, INT_MAX, NULL);
106 if (termwidth == 0 && ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
107 win.ws_col > 0)
108 termwidth = win.ws_col;
109 if (termwidth == 0)
110 termwidth = 80;
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");
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 }
241 free(queue);
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 int n = 0;
286
287 i = strlen(line);
288 if (write(fd, line, i) != i)
289 fatal("Lost connection");
290 memset(&nsa, 0, sizeof(nsa));
291 nsa.sa_handler = alarmer;
292 sigemptyset(&nsa.sa_mask);
293 nsa.sa_flags = 0;
294 (void)sigaction(SIGALRM, &nsa, &osa);
295 alarm(wait_time);
296 if ((visline = malloc(4 * sizeof(line) + 1)) == NULL)
297 fatal("Out of memory");
298 while ((i = read(fd, line, sizeof(line))) > 0) {
299 n = strvisx(visline, line, i, VIS_SAFE|VIS_NOSLASH);
300 (void)fwrite(visline, 1, n, stdout);
301 alarm(wait_time);
302 }
303 /* XXX some LPR implementations may not end stream with '\n' */
304 if (n > 0 && visline[n-1] != '\n')
305 putchar('\n');
306 alarm(0);
307 (void)sigaction(SIGALRM, &osa, NULL);
308 free(visline);
309 (void)close(fd);
310 }
311 }
312
313 static void
alarmer(int s)314 alarmer(int s)
315 {
316 /* nothing */
317 }
318
319 /*
320 * Print a warning message if there is no daemon present.
321 */
322 static void
nodaemon(void)323 nodaemon(void)
324 {
325 if (remote)
326 printf("\n%s: ", host);
327 puts("Warning: no daemon present");
328 current[0] = '\0';
329 }
330
331 /*
332 * Print the header for the short listing format
333 */
334 static void
header(void)335 header(void)
336 {
337 printf(head0);
338 col = strlen(head0)+1;
339 blankfill(termwidth - (80 - SIZCOL));
340 printf(head1);
341 }
342
343 static void
inform(char * cf,int rank)344 inform(char *cf, int rank)
345 {
346 int fd, j;
347 FILE *cfp = NULL;
348
349 /*
350 * There's a chance the control file has gone away
351 * in the meantime; if this is the case just keep going
352 */
353 PRIV_START;
354 fd = safe_open(cf, O_RDONLY|O_NOFOLLOW, 0);
355 PRIV_END;
356 if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) {
357 if (fd >= 0)
358 close(fd);
359 return;
360 }
361
362 j = 0;
363 while (get_line(cfp)) {
364 switch (line[0]) {
365 case 'P': /* Was this file specified in the user's list? */
366 if (!inlist(line+1, cf)) {
367 fclose(cfp);
368 return;
369 }
370 if (lflag) {
371 printf("\n%s: ", line+1);
372 col = strlen(line+1) + 2;
373 prank(rank);
374 blankfill(JOBCOL);
375 printf(" [job %s]\n", cf+3);
376 } else {
377 col = 0;
378 prank(rank);
379 blankfill(OWNCOL);
380 printf("%-10s %-3d ", line+1, atoi(cf+3));
381 col += 16;
382 first = 1;
383 }
384 continue;
385 default: /* some format specifer and file name? */
386 if (line[0] < 'a' || line[0] > 'z')
387 continue;
388 if (j == 0 || strcmp(file, line+1) != 0)
389 (void)strlcpy(file, line+1, sizeof(file));
390 j++;
391 continue;
392 case 'N':
393 show(line+1, file, j);
394 file[0] = '\0';
395 j = 0;
396 }
397 }
398 fclose(cfp);
399 if (!lflag) {
400 blankfill(termwidth - (80 - SIZCOL));
401 printf("%lld bytes\n", (long long)totsize);
402 totsize = 0;
403 }
404 }
405
406 static int
inlist(char * name,char * file)407 inlist(char *name, char *file)
408 {
409 int *r, n;
410 char **u, *cp;
411
412 if (users == 0 && requests == 0)
413 return(1);
414 /*
415 * Check to see if it's in the user list
416 */
417 for (u = user; u < &user[users]; u++)
418 if (!strcmp(*u, name))
419 return(1);
420 /*
421 * Check the request list
422 */
423 for (n = 0, cp = file+3; isdigit((unsigned char)*cp); )
424 n = n * 10 + (*cp++ - '0');
425 for (r = requ; r < &requ[requests]; r++)
426 if (*r == n && !strcmp(cp, from))
427 return(1);
428 return(0);
429 }
430
431 static void
show(char * nfile,char * file,int copies)432 show(char *nfile, char *file, int copies)
433 {
434 if (strcmp(nfile, " ") == 0)
435 nfile = "(standard input)";
436 if (lflag)
437 ldump(nfile, file, copies);
438 else
439 dump(nfile, file, copies);
440 }
441
442 /*
443 * Fill the line with blanks to the specified column
444 */
445 static void
blankfill(int n)446 blankfill(int n)
447 {
448 while (col++ < n)
449 putchar(' ');
450 }
451
452 /*
453 * Give the abbreviated dump of the file names
454 */
455 static void
dump(char * nfile,char * file,int copies)456 dump(char *nfile, char *file, int copies)
457 {
458 int n, fill;
459 struct stat lbuf;
460
461 /*
462 * Print as many files as will fit
463 * (leaving room for the total size)
464 */
465 fill = first ? 0 : 2; /* fill space for ``, '' */
466 if (((n = strlen(nfile)) + col + fill) >=
467 (termwidth - (80 - SIZCOL)) - 4) {
468 if (col < (termwidth - (80 - SIZCOL))) {
469 printf(" ..."), col += 4;
470 blankfill(termwidth - (80 - SIZCOL));
471 }
472 } else {
473 if (first)
474 first = 0;
475 else
476 printf(", ");
477 printf("%s", nfile);
478 col += n+fill;
479 }
480 PRIV_START;
481 if (*file && !stat(file, &lbuf))
482 totsize += copies * lbuf.st_size;
483 PRIV_END;
484 }
485
486 /*
487 * Print the long info about the file
488 */
489 static void
ldump(char * nfile,char * file,int copies)490 ldump(char *nfile, char *file, int copies)
491 {
492 struct stat lbuf;
493 int ret;
494
495 putchar('\t');
496 if (copies > 1)
497 printf("%-2d copies of %-19s", copies, nfile);
498 else
499 printf("%-32s", nfile);
500 PRIV_START;
501 ret = stat(file, &lbuf);
502 PRIV_END;
503 if (*file && !ret)
504 printf(" %lld bytes", (long long)lbuf.st_size);
505 else
506 printf(" ??? bytes");
507 putchar('\n');
508 }
509
510 /*
511 * Print the job's rank in the queue,
512 * update col for screen management
513 */
514 static void
prank(int n)515 prank(int n)
516 {
517 char rline[100];
518 static char *r[] = {
519 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
520 };
521
522 if (n == 0) {
523 printf("active");
524 col += 6;
525 return;
526 }
527 if ((n/10)%10 == 1)
528 (void)snprintf(rline, sizeof(rline), "%dth", n);
529 else
530 (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
531 col += strlen(rline);
532 printf("%s", rline);
533 }
534