xref: /openbsd-src/usr.sbin/lpr/common_source/displayq.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: displayq.c,v 1.39 2016/03/17 05:27:10 bentley 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/file.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36 
37 #include <ctype.h>
38 #include <errno.h>
39 #include <dirent.h>
40 #include <fcntl.h>
41 #include <limits.h>
42 #include <signal.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <vis.h>
48 
49 #include "lp.h"
50 #include "lp.local.h"
51 #include "pathnames.h"
52 
53 /*
54  * Routines to display the state of the queue.
55  */
56 #define JOBCOL	40		/* column for job # in -l format */
57 #define OWNCOL	7		/* start of Owner column in normal */
58 #define SIZCOL	62		/* start of Size column in normal */
59 
60 /*
61  * Stuff for handling job specifications
62  */
63 extern int	requ[];		/* job number of spool entries */
64 extern int	requests;	/* # of spool requests */
65 extern char    *user[];	        /* users to process */
66 extern int	users;		/* # of users in user array */
67 
68 static int	termwidth;
69 static int	col;		/* column on screen */
70 static char	current[NAME_MAX]; /* current file being printed */
71 static char	file[NAME_MAX];	/* print file name */
72 static int	first;		/* first file in ``files'' column? */
73 static int	lflag;		/* long output option */
74 static off_t	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	alarmer(int);
80 static void	blankfill(int);
81 static void	dump(char *, char *, int);
82 static void	header(void);
83 static void	inform(char *, int);
84 static int	inlist(char *, char *);
85 static void	ldump(char *, char *, int);
86 static void	nodaemon(void);
87 static void	prank(int);
88 static void	show(char *, char *, int);
89 
90 /*
91  * Display the current state of the queue. Format = 1 if long format.
92  */
93 void
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 = 0;
105 	if ((p = getenv("COLUMNS")) != NULL)
106 		termwidth = strtonum(p, 1, INT_MAX, NULL);
107 	if (termwidth == 0 && ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
108 	    win.ws_col > 0)
109 		termwidth = win.ws_col;
110 	if (termwidth == 0)
111 		termwidth = 80;
112 
113 	if (termwidth < 60)
114 		termwidth = 60;
115 
116 	lflag = format;
117 	totsize = 0;
118 	if ((i = cgetent(&bp, printcapdb, printer)) == -2)
119 		fatal("can't open printer description file");
120 	else if (i == -1)
121 		fatal("unknown printer");
122 	else if (i == -3)
123 		fatal("potential reference loop detected in printcap file");
124 	if (cgetstr(bp, DEFLP, &LP) < 0)
125 		LP = _PATH_DEFDEVLP;
126 	if (cgetstr(bp, "rp", &RP) < 0)
127 		RP = DEFLP;
128 	if (cgetstr(bp, "sd", &SD) < 0)
129 		SD = _PATH_DEFSPOOL;
130 	if (cgetstr(bp, "lo", &LO) < 0)
131 		LO = DEFLOCK;
132 	if (cgetstr(bp, "st", &ST) < 0)
133 		ST = DEFSTAT;
134 	cgetstr(bp, "rm", &RM);
135 	if ((cp = checkremote()) != NULL)
136 		printf("Warning: %s\n", cp);
137 
138 	/*
139 	 * Print out local queue
140 	 * Find all the control files in the spooling directory
141 	 */
142 	PRIV_START;
143 	if (chdir(SD) < 0)
144 		fatal("cannot chdir to spooling directory");
145 	PRIV_END;
146 	if ((nitems = getq(&queue)) < 0)
147 		fatal("cannot examine spooling area");
148 	PRIV_START;
149 	ret = stat(LO, &statb);
150 	PRIV_END;
151 	if (ret >= 0) {
152 		if (statb.st_mode & S_IXUSR) {
153 			if (remote)
154 				printf("%s: ", host);
155 			printf("Warning: %s is down: ", printer);
156 			PRIV_START;
157 			fd = safe_open(ST, O_RDONLY|O_NOFOLLOW, 0);
158 			PRIV_END;
159 			if (fd >= 0 && flock(fd, LOCK_SH) == 0) {
160 				while ((i = read(fd, line, sizeof(line))) > 0)
161 					(void)fwrite(line, 1, i, stdout);
162 				(void)close(fd);	/* unlocks as well */
163 			} else
164 				putchar('\n');
165 		}
166 		if (statb.st_mode & S_IXGRP) {
167 			if (remote)
168 				printf("%s: ", host);
169 			printf("Warning: %s queue is turned off\n", printer);
170 		}
171 	}
172 
173 	if (nitems) {
174 		PRIV_START;
175 		fd = safe_open(LO, O_RDONLY|O_NOFOLLOW, 0);
176 		PRIV_END;
177 		if (fd < 0 || (fp = fdopen(fd, "r")) == NULL) {
178 			if (fd >= 0)
179 				close(fd);
180 			nodaemon();
181 		} else {
182 			/* get daemon pid */
183 			cp = current;
184 			ecp = cp + sizeof(current) - 1;
185 			while ((i = getc(fp)) != EOF && i != '\n') {
186 				if (cp < ecp)
187 					*cp++ = i;
188 			}
189 			*cp = '\0';
190 			i = atoi(current);
191 			if (i <= 0) {
192 				ret = -1;
193 			} else {
194 				PRIV_START;
195 				ret = kill(i, 0);
196 				PRIV_END;
197 			}
198 			if (ret < 0 && errno != EPERM) {
199 				nodaemon();
200 			} else {
201 				/* read current file name */
202 				cp = current;
203 		    		ecp = cp + sizeof(current) - 1;
204 				while ((i = getc(fp)) != EOF && i != '\n') {
205 					if (cp < ecp)
206 						*cp++ = i;
207 				}
208 				*cp = '\0';
209 				/*
210 				 * Print the status file.
211 				 */
212 				if (remote)
213 					printf("%s: ", host);
214 				PRIV_START;
215 				fd = safe_open(ST, O_RDONLY|O_NOFOLLOW, 0);
216 				PRIV_END;
217 				if (fd >= 0 && flock(fd, LOCK_SH) == 0) {
218 					while ((i = read(fd, line, sizeof(line))) > 0)
219 						(void)fwrite(line, 1, i, stdout);
220 					(void)close(fd);	/* unlocks as well */
221 				} else
222 					putchar('\n');
223 			}
224 			(void)fclose(fp);
225 		}
226 		/*
227 		 * Now, examine the control files and print out the jobs to
228 		 * be done for each user.
229 		 */
230 		if (!lflag)
231 			header();
232 		/* The currently printed job is treated specially. */
233 		if (!remote && current[0] != '\0')
234 			inform(current, 0);
235 		for (i = 0, rank = 1; i < nitems; i++) {
236 			q = queue[i];
237 			if (remote || strcmp(current, q->q_name) != 0)
238 				inform(q->q_name, rank++);
239 			free(q);
240 		}
241 	}
242 	free(queue);
243 	if (!remote) {
244 		if (nitems == 0)
245 			puts("no entries");
246 		return;
247 	}
248 
249 	/*
250 	 * Print foreign queue
251 	 * Note that a file in transit may show up in either queue.
252 	 */
253 	if (nitems)
254 		putchar('\n');
255 	(void)snprintf(line, sizeof(line), "%c%s", format + '\3', RP);
256 	cp = line;
257 	cp += strlen(cp);
258 	for (i = 0; i < requests && cp - line < sizeof(line) - 1; i++) {
259 		len = line + sizeof(line) - cp;
260 		if (snprintf(cp, len, " %d", requ[i]) >= len) {
261 			cp += strlen(cp);
262 			break;
263 		}
264 		cp += strlen(cp);
265 	}
266 	for (i = 0; i < users && cp - line < sizeof(line) - 1; i++) {
267 		len = line + sizeof(line) - cp;
268 		if (snprintf(cp, len, " %s", user[i]) >= len) {
269 			cp += strlen(cp);
270 			break;
271 		}
272 	}
273 	if (cp-line < sizeof(line) - 1)
274 		strlcat(line, "\n", sizeof(line));
275 	else
276 		line[sizeof(line) - 2] = '\n';
277 	fd = getport(RM, 0);
278 	if (fd < 0) {
279 		if (from != host)
280 			printf("%s: ", host);
281 		(void)printf("connection to %s is down\n", RM);
282 	}
283 	else {
284 		struct sigaction osa, nsa;
285 		char *visline;
286 		int n = 0;
287 
288 		i = strlen(line);
289 		if (write(fd, line, i) != i)
290 			fatal("Lost connection");
291 		memset(&nsa, 0, sizeof(nsa));
292 		nsa.sa_handler = alarmer;
293 		sigemptyset(&nsa.sa_mask);
294 		nsa.sa_flags = 0;
295 		(void)sigaction(SIGALRM, &nsa, &osa);
296 		alarm(wait_time);
297 		if ((visline = malloc(4 * sizeof(line) + 1)) == NULL)
298 			fatal("Out of memory");
299 		while ((i = read(fd, line, sizeof(line))) > 0) {
300 			n = strvisx(visline, line, i, VIS_SAFE|VIS_NOSLASH);
301 			(void)fwrite(visline, 1, n, stdout);
302 			alarm(wait_time);
303 		}
304 		/* XXX some LPR implementations may not end stream with '\n' */
305 		if (n > 0 && visline[n-1] != '\n')
306 			putchar('\n');
307 		alarm(0);
308 		(void)sigaction(SIGALRM, &osa, NULL);
309 		free(visline);
310 		(void)close(fd);
311 	}
312 }
313 
314 static void
315 alarmer(int s)
316 {
317 	/* nothing */
318 }
319 
320 /*
321  * Print a warning message if there is no daemon present.
322  */
323 static void
324 nodaemon(void)
325 {
326 	if (remote)
327 		printf("\n%s: ", host);
328 	puts("Warning: no daemon present");
329 	current[0] = '\0';
330 }
331 
332 /*
333  * Print the header for the short listing format
334  */
335 static void
336 header(void)
337 {
338 	printf(head0);
339 	col = strlen(head0)+1;
340 	blankfill(termwidth - (80 - SIZCOL));
341 	printf(head1);
342 }
343 
344 static void
345 inform(char *cf, int rank)
346 {
347 	int fd, j;
348 	FILE *cfp = NULL;
349 
350 	/*
351 	 * There's a chance the control file has gone away
352 	 * in the meantime; if this is the case just keep going
353 	 */
354 	PRIV_START;
355 	fd = safe_open(cf, O_RDONLY|O_NOFOLLOW, 0);
356 	PRIV_END;
357 	if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) {
358 		if (fd >= 0)
359 			close(fd);
360 		return;
361 	}
362 
363 	j = 0;
364 	while (get_line(cfp)) {
365 		switch (line[0]) {
366 		case 'P': /* Was this file specified in the user's list? */
367 			if (!inlist(line+1, cf)) {
368 				fclose(cfp);
369 				return;
370 			}
371 			if (lflag) {
372 				printf("\n%s: ", line+1);
373 				col = strlen(line+1) + 2;
374 				prank(rank);
375 				blankfill(JOBCOL);
376 				printf(" [job %s]\n", cf+3);
377 			} else {
378 				col = 0;
379 				prank(rank);
380 				blankfill(OWNCOL);
381 				printf("%-10s %-3d  ", line+1, atoi(cf+3));
382 				col += 16;
383 				first = 1;
384 			}
385 			continue;
386 		default: /* some format specifer and file name? */
387 			if (line[0] < 'a' || line[0] > 'z')
388 				continue;
389 			if (j == 0 || strcmp(file, line+1) != 0)
390 				(void)strlcpy(file, line+1, sizeof(file));
391 			j++;
392 			continue;
393 		case 'N':
394 			show(line+1, file, j);
395 			file[0] = '\0';
396 			j = 0;
397 		}
398 	}
399 	fclose(cfp);
400 	if (!lflag) {
401 		blankfill(termwidth - (80 - SIZCOL));
402 		printf("%lld bytes\n", (long long)totsize);
403 		totsize = 0;
404 	}
405 }
406 
407 static int
408 inlist(char *name, char *file)
409 {
410 	int *r, n;
411 	char **u, *cp;
412 
413 	if (users == 0 && requests == 0)
414 		return(1);
415 	/*
416 	 * Check to see if it's in the user list
417 	 */
418 	for (u = user; u < &user[users]; u++)
419 		if (!strcmp(*u, name))
420 			return(1);
421 	/*
422 	 * Check the request list
423 	 */
424 	for (n = 0, cp = file+3; isdigit((unsigned char)*cp); )
425 		n = n * 10 + (*cp++ - '0');
426 	for (r = requ; r < &requ[requests]; r++)
427 		if (*r == n && !strcmp(cp, from))
428 			return(1);
429 	return(0);
430 }
431 
432 static void
433 show(char *nfile, char *file, int copies)
434 {
435 	if (strcmp(nfile, " ") == 0)
436 		nfile = "(standard input)";
437 	if (lflag)
438 		ldump(nfile, file, copies);
439 	else
440 		dump(nfile, file, copies);
441 }
442 
443 /*
444  * Fill the line with blanks to the specified column
445  */
446 static void
447 blankfill(int n)
448 {
449 	while (col++ < n)
450 		putchar(' ');
451 }
452 
453 /*
454  * Give the abbreviated dump of the file names
455  */
456 static void
457 dump(char *nfile, char *file, int copies)
458 {
459 	int n, fill;
460 	struct stat lbuf;
461 
462 	/*
463 	 * Print as many files as will fit
464 	 *  (leaving room for the total size)
465 	 */
466 	 fill = first ? 0 : 2;	/* fill space for ``, '' */
467 	 if (((n = strlen(nfile)) + col + fill) >=
468 	     (termwidth - (80 - SIZCOL)) - 4) {
469 		if (col < (termwidth - (80 - SIZCOL))) {
470 			printf(" ..."), col += 4;
471 			blankfill(termwidth - (80 - SIZCOL));
472 		}
473 	} else {
474 		if (first)
475 			first = 0;
476 		else
477 			printf(", ");
478 		printf("%s", nfile);
479 		col += n+fill;
480 	}
481 	PRIV_START;
482 	if (*file && !stat(file, &lbuf))
483 		totsize += copies * lbuf.st_size;
484 	PRIV_END;
485 }
486 
487 /*
488  * Print the long info about the file
489  */
490 static void
491 ldump(char *nfile, char *file, int copies)
492 {
493 	struct stat lbuf;
494 	int ret;
495 
496 	putchar('\t');
497 	if (copies > 1)
498 		printf("%-2d copies of %-19s", copies, nfile);
499 	else
500 		printf("%-32s", nfile);
501 	PRIV_START;
502 	ret = stat(file, &lbuf);
503 	PRIV_END;
504 	if (*file && !ret)
505 		printf(" %lld bytes", (long long)lbuf.st_size);
506 	else
507 		printf(" ??? bytes");
508 	putchar('\n');
509 }
510 
511 /*
512  * Print the job's rank in the queue,
513  *   update col for screen management
514  */
515 static void
516 prank(int n)
517 {
518 	char rline[100];
519 	static char *r[] = {
520 		"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
521 	};
522 
523 	if (n == 0) {
524 		printf("active");
525 		col += 6;
526 		return;
527 	}
528 	if ((n/10)%10 == 1)
529 		(void)snprintf(rline, sizeof(rline), "%dth", n);
530 	else
531 		(void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
532 	col += strlen(rline);
533 	printf("%s", rline);
534 }
535