displayq.c revision 1.26
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
35static const char sccsid[] = "@(#)displayq.c	8.4 (Berkeley) 4/28/95";
36#else
37static 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 */
71extern int	requ[];		/* job number of spool entries */
72extern int	requests;	/* # of spool requests */
73extern char    *user[];	        /* users to process */
74extern int	users;		/* # of users in user array */
75
76static int	termwidth;
77static int	col;		/* column on screen */
78static char	current[NAME_MAX]; /* current file being printed */
79static char	file[NAME_MAX];	/* print file name */
80static int	first;		/* first file in ``files'' column? */
81static int	lflag;		/* long output option */
82static off_t	totsize;	/* total print job size in bytes */
83
84static const char *head0 = "Rank   Owner      Job  Files";
85static const char *head1 = "Total Size\n";
86
87static void	alarmer(int);
88static void	inform(char *, int);
89
90/*
91 * Display the current state of the queue. Format = 1 if long format.
92 */
93void
94displayq(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
309static void
310alarmer(int s)
311{
312	/* nothing */
313}
314
315/*
316 * Print a warning message if there is no daemon present.
317 */
318void
319nodaemon(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 */
330void
331header(void)
332{
333	printf(head0);
334	col = strlen(head0)+1;
335	blankfill(termwidth - (80 - SIZCOL));
336	printf(head1);
337}
338
339static void
340inform(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
402int
403inlist(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
427void
428show(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 */
441void
442blankfill(int n)
443{
444	while (col++ < n)
445		putchar(' ');
446}
447
448/*
449 * Give the abbreviated dump of the file names
450 */
451void
452dump(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 */
485void
486ldump(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 */
506void
507prank(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