displayq.c revision 1.23
1/*	$OpenBSD: displayq.c,v 1.23 2003/04/05 10:42:54 avsm 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. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static const char sccsid[] = "@(#)displayq.c	8.4 (Berkeley) 4/28/95";
40#else
41static const char rcsid[] = "$OpenBSD: displayq.c,v 1.23 2003/04/05 10:42:54 avsm Exp $";
42#endif
43#endif /* not lint */
44
45#include <sys/param.h>
46#include <sys/file.h>
47#include <sys/ioctl.h>
48#include <sys/stat.h>
49
50#include <ctype.h>
51#include <errno.h>
52#include <dirent.h>
53#include <fcntl.h>
54#include <signal.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <unistd.h>
59
60#include "lp.h"
61#include "lp.local.h"
62#include "pathnames.h"
63
64/*
65 * Routines to display the state of the queue.
66 */
67#define JOBCOL	40		/* column for job # in -l format */
68#define OWNCOL	7		/* start of Owner column in normal */
69#define SIZCOL	62		/* start of Size column in normal */
70
71/*
72 * Stuff for handling job specifications
73 */
74extern int	requ[];		/* job number of spool entries */
75extern int	requests;	/* # of spool requests */
76extern char    *user[];	        /* users to process */
77extern int	users;		/* # of users in user array */
78
79static int	termwidth;
80static int	col;		/* column on screen */
81static char	current[NAME_MAX]; /* current file being printed */
82static char	file[NAME_MAX];	/* print file name */
83static int	first;		/* first file in ``files'' column? */
84static int	lflag;		/* long output option */
85static off_t	totsize;	/* total print job size in bytes */
86
87static const char *head0 = "Rank   Owner      Job  Files";
88static const char *head1 = "Total Size\n";
89
90static void	alarmer(int);
91static void	inform(char *, int);
92
93/*
94 * Display the current state of the queue. Format = 1 if long format.
95 */
96void
97displayq(int format)
98{
99	struct queue *q;
100	int i, rank, nitems, fd, ret, len;
101	char *cp, *ecp, *p;
102	struct queue **queue;
103	struct winsize win;
104	struct stat statb;
105	FILE *fp;
106
107	termwidth = 80;
108	if (isatty(STDOUT_FILENO)) {
109		if ((p = getenv("COLUMNS")) != NULL)
110			termwidth = atoi(p);
111		else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
112		    win.ws_col > 0)
113			termwidth = win.ws_col;
114	}
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\n");
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		free(queue);
242	}
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
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		while ((i = read(fd, line, sizeof(line))) > 0) {
296			(void)fwrite(line, 1, i, stdout);
297			alarm(wait_time);
298		}
299		alarm(0);
300		(void)sigaction(SIGALRM, &osa, NULL);
301		(void)close(fd);
302	}
303}
304
305static void
306alarmer(int s)
307{
308	/* nothing */
309}
310
311/*
312 * Print a warning message if there is no daemon present.
313 */
314void
315nodaemon(void)
316{
317	if (remote)
318		printf("\n%s: ", host);
319	puts("Warning: no daemon present");
320	current[0] = '\0';
321}
322
323/*
324 * Print the header for the short listing format
325 */
326void
327header(void)
328{
329	printf(head0);
330	col = strlen(head0)+1;
331	blankfill(termwidth - (80 - SIZCOL));
332	printf(head1);
333}
334
335static void
336inform(char *cf, int rank)
337{
338	int fd, j;
339	FILE *cfp = NULL;
340
341	/*
342	 * There's a chance the control file has gone away
343	 * in the meantime; if this is the case just keep going
344	 */
345	PRIV_START;
346	fd = safe_open(cf, O_RDONLY|O_NOFOLLOW, 0);
347	PRIV_END;
348	if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) {
349		if (fd >= 0)
350			close(fd);
351		return;
352	}
353
354	j = 0;
355	while (getline(cfp)) {
356		switch (line[0]) {
357		case 'P': /* Was this file specified in the user's list? */
358			if (!inlist(line+1, cf)) {
359				fclose(cfp);
360				return;
361			}
362			if (lflag) {
363				printf("\n%s: ", line+1);
364				col = strlen(line+1) + 2;
365				prank(rank);
366				blankfill(JOBCOL);
367				printf(" [job %s]\n", cf+3);
368			} else {
369				col = 0;
370				prank(rank);
371				blankfill(OWNCOL);
372				printf("%-10s %-3d  ", line+1, atoi(cf+3));
373				col += 16;
374				first = 1;
375			}
376			continue;
377		default: /* some format specifer and file name? */
378			if (line[0] < 'a' || line[0] > 'z')
379				continue;
380			if (j == 0 || strcmp(file, line+1) != 0)
381				(void)strlcpy(file, line+1, sizeof(file));
382			j++;
383			continue;
384		case 'N':
385			show(line+1, file, j);
386			file[0] = '\0';
387			j = 0;
388		}
389	}
390	fclose(cfp);
391	if (!lflag) {
392		blankfill(termwidth - (80 - SIZCOL));
393		printf("%lld bytes\n", (long long)totsize);
394		totsize = 0;
395	}
396}
397
398int
399inlist(char *name, char *file)
400{
401	int *r, n;
402	char **u, *cp;
403
404	if (users == 0 && requests == 0)
405		return(1);
406	/*
407	 * Check to see if it's in the user list
408	 */
409	for (u = user; u < &user[users]; u++)
410		if (!strcmp(*u, name))
411			return(1);
412	/*
413	 * Check the request list
414	 */
415	for (n = 0, cp = file+3; isdigit(*cp); )
416		n = n * 10 + (*cp++ - '0');
417	for (r = requ; r < &requ[requests]; r++)
418		if (*r == n && !strcmp(cp, from))
419			return(1);
420	return(0);
421}
422
423void
424show(char *nfile, char *file, int copies)
425{
426	if (strcmp(nfile, " ") == 0)
427		nfile = "(standard input)";
428	if (lflag)
429		ldump(nfile, file, copies);
430	else
431		dump(nfile, file, copies);
432}
433
434/*
435 * Fill the line with blanks to the specified column
436 */
437void
438blankfill(int n)
439{
440	while (col++ < n)
441		putchar(' ');
442}
443
444/*
445 * Give the abbreviated dump of the file names
446 */
447void
448dump(char *nfile, char *file, int copies)
449{
450	int n, fill;
451	struct stat lbuf;
452
453	/*
454	 * Print as many files as will fit
455	 *  (leaving room for the total size)
456	 */
457	 fill = first ? 0 : 2;	/* fill space for ``, '' */
458	 if (((n = strlen(nfile)) + col + fill) >=
459	     (termwidth - (80 - SIZCOL)) - 4) {
460		if (col < (termwidth - (80 - SIZCOL))) {
461			printf(" ..."), col += 4;
462			blankfill(termwidth - (80 - SIZCOL));
463		}
464	} else {
465		if (first)
466			first = 0;
467		else
468			printf(", ");
469		printf("%s", nfile);
470		col += n+fill;
471	}
472	PRIV_START;
473	if (*file && !stat(file, &lbuf))
474		totsize += copies * lbuf.st_size;
475	PRIV_END;
476}
477
478/*
479 * Print the long info about the file
480 */
481void
482ldump(char *nfile, char *file, int copies)
483{
484	struct stat lbuf;
485
486	putchar('\t');
487	if (copies > 1)
488		printf("%-2d copies of %-19s", copies, nfile);
489	else
490		printf("%-32s", nfile);
491	if (*file && !stat(file, &lbuf))
492		printf(" %lld bytes", (long long)lbuf.st_size);
493	else
494		printf(" ??? bytes");
495	putchar('\n');
496}
497
498/*
499 * Print the job's rank in the queue,
500 *   update col for screen management
501 */
502void
503prank(int n)
504{
505	char rline[100];
506	static char *r[] = {
507		"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
508	};
509
510	if (n == 0) {
511		printf("active");
512		col += 6;
513		return;
514	}
515	if ((n/10)%10 == 1)
516		(void)snprintf(rline, sizeof(rline), "%dth", n);
517	else
518		(void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
519	col += strlen(rline);
520	printf("%s", rline);
521}
522