displayq.c revision 68401
1/*
2 * Copyright (c) 1983, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35/*
36static char sccsid[] = "@(#)displayq.c	8.4 (Berkeley) 4/28/95";
37*/
38static const char rcsid[] =
39  "$FreeBSD: head/usr.sbin/lpr/common_source/displayq.c 68401 2000-11-06 19:36:38Z gad $";
40#endif /* not lint */
41
42#include <sys/param.h>
43#include <sys/stat.h>
44
45#include <ctype.h>
46#include <dirent.h>
47#include <errno.h>
48#include <fcntl.h>
49#include <signal.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#define psignal foil_gcc_psignal
54#define	sys_siglist foil_gcc_siglist
55#include <unistd.h>
56#undef psignal
57#undef sys_siglist
58
59#include "lp.h"
60#include "lp.local.h"
61#include "pathnames.h"
62
63/*
64 * Routines to display the state of the queue.
65 */
66#define JOBCOL	40		/* column for job # in -l format */
67#define OWNCOL	7		/* start of Owner column in normal */
68#define SIZCOL	62		/* start of Size column in normal */
69
70/*
71 * Stuff for handling job specifications
72 */
73extern uid_t	uid, euid;
74
75static int	col;		/* column on screen */
76static char	current[40];	/* current file being printed */
77static char	file[132];	/* print file name */
78static int	first;		/* first file in ``files'' column? */
79static int	garbage;	/* # of garbage cf files */
80static int	lflag;		/* long output option */
81static int	rank;		/* order to be printed (-1=none, 0=active) */
82static long	totsize;	/* total print job size in bytes */
83
84static char	*head0 = "Rank   Owner      Job  Files";
85static char	*head1 = "Total Size\n";
86
87static void	alarmhandler __P((int));
88static void	warn __P((const struct printer *pp));
89
90/*
91 * Display the current state of the queue. Format = 1 if long format.
92 */
93void
94displayq(pp, format)
95	struct printer *pp;
96	int format;
97{
98	register struct jobqueue *q;
99	register int i, nitems, fd, ret;
100	register char	*cp;
101	struct jobqueue **queue;
102	struct stat statb;
103	FILE *fp;
104	void (*savealrm)(int);
105
106	lflag = format;
107	totsize = 0;
108	rank = -1;
109
110	if ((cp = checkremote(pp))) {
111		printf("Warning: %s\n", cp);
112		free(cp);
113	}
114
115	/*
116	 * Print out local queue
117	 * Find all the control files in the spooling directory
118	 */
119	seteuid(euid);
120	if (chdir(pp->spool_dir) < 0)
121		fatal(pp, "cannot chdir to spooling directory: %s",
122		      strerror(errno));
123	seteuid(uid);
124	if ((nitems = getq(pp, &queue)) < 0)
125		fatal(pp, "cannot examine spooling area\n");
126	seteuid(euid);
127	ret = stat(pp->lock_file, &statb);
128	seteuid(uid);
129	if (ret >= 0) {
130		if (statb.st_mode & LFM_PRINT_DIS) {
131			if (pp->remote)
132				printf("%s: ", host);
133			printf("Warning: %s is down: ", pp->printer);
134			seteuid(euid);
135			fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
136			seteuid(uid);
137			if (fd >= 0) {
138				while ((i = read(fd, line, sizeof(line))) > 0)
139					(void) fwrite(line, 1, i, stdout);
140				(void) close(fd);	/* unlocks as well */
141			} else
142				putchar('\n');
143		}
144		if (statb.st_mode & LFM_QUEUE_DIS) {
145			if (pp->remote)
146				printf("%s: ", host);
147			printf("Warning: %s queue is turned off\n",
148			       pp->printer);
149		}
150	}
151
152	if (nitems) {
153		seteuid(euid);
154		fp = fopen(pp->lock_file, "r");
155		seteuid(uid);
156		if (fp == NULL)
157			warn(pp);
158		else {
159			/* get daemon pid */
160			cp = current;
161			while ((i = getc(fp)) != EOF && i != '\n')
162				*cp++ = i;
163			*cp = '\0';
164			i = atoi(current);
165			if (i <= 0) {
166				ret = -1;
167			} else {
168				seteuid(euid);
169				ret = kill(i, 0);
170				seteuid(uid);
171			}
172			if (ret < 0) {
173				warn(pp);
174			} else {
175				/* read current file name */
176				cp = current;
177				while ((i = getc(fp)) != EOF && i != '\n')
178					*cp++ = i;
179				*cp = '\0';
180				/*
181				 * Print the status file.
182				 */
183				if (pp->remote)
184					printf("%s: ", host);
185				seteuid(euid);
186				fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
187				seteuid(uid);
188				if (fd >= 0) {
189					while ((i = read(fd, line,
190							 sizeof(line))) > 0)
191						fwrite(line, 1, i, stdout);
192					close(fd);	/* unlocks as well */
193				} else
194					putchar('\n');
195			}
196			(void) fclose(fp);
197		}
198		/*
199		 * Now, examine the control files and print out the jobs to
200		 * be done for each user.
201		 */
202		if (!lflag)
203			header();
204		for (i = 0; i < nitems; i++) {
205			q = queue[i];
206			inform(pp, q->job_cfname);
207			free(q);
208		}
209		free(queue);
210	}
211	if (!pp->remote) {
212		if (nitems == 0)
213			puts("no entries");
214		return;
215	}
216
217	/*
218	 * Print foreign queue
219	 * Note that a file in transit may show up in either queue.
220	 */
221	if (nitems)
222		putchar('\n');
223	(void) snprintf(line, sizeof(line), "%c%s", format ? '\4' : '\3',
224			pp->remote_queue);
225	cp = line;
226	for (i = 0; i < requests && cp-line+10 < sizeof(line) - 1; i++) {
227		cp += strlen(cp);
228		(void) sprintf(cp, " %d", requ[i]);
229	}
230	for (i = 0; i < users && cp - line + 1 + strlen(user[i]) <
231		sizeof(line) - 1; i++) {
232		cp += strlen(cp);
233		*cp++ = ' ';
234		(void) strcpy(cp, user[i]);
235	}
236	strcat(line, "\n");
237	savealrm = signal(SIGALRM, alarmhandler);
238	alarm(pp->conn_timeout);
239	fd = getport(pp, pp->remote_host, 0);
240	alarm(0);
241	(void)signal(SIGALRM, savealrm);
242	if (fd < 0) {
243		if (from != host)
244			printf("%s: ", host);
245		printf("connection to %s is down\n", pp->remote_host);
246	}
247	else {
248		i = strlen(line);
249		if (write(fd, line, i) != i)
250			fatal(pp, "Lost connection");
251		while ((i = read(fd, line, sizeof(line))) > 0)
252			(void) fwrite(line, 1, i, stdout);
253		(void) close(fd);
254	}
255}
256
257/*
258 * Print a warning message if there is no daemon present.
259 */
260static void
261warn(pp)
262	const struct printer *pp;
263{
264	if (pp->remote)
265		printf("%s: ", host);
266	puts("Warning: no daemon present");
267	current[0] = '\0';
268}
269
270/*
271 * Print the header for the short listing format
272 */
273void
274header()
275{
276	printf(head0);
277	col = strlen(head0)+1;
278	blankfill(SIZCOL);
279	printf(head1);
280}
281
282void
283inform(pp, cf)
284	const struct printer *pp;
285	char *cf;
286{
287	register int copycnt;
288	char	 savedname[MAXPATHLEN+1];
289	FILE	*cfp;
290
291	/*
292	 * There's a chance the control file has gone away
293	 * in the meantime; if this is the case just keep going
294	 */
295	seteuid(euid);
296	if ((cfp = fopen(cf, "r")) == NULL)
297		return;
298	seteuid(uid);
299
300	if (rank < 0)
301		rank = 0;
302	if (pp->remote || garbage || strcmp(cf, current))
303		rank++;
304
305	/*
306	 * The cf-file may include commands to print more than one datafile
307	 * from the user.  For each datafile, the cf-file contains at least
308	 * one line which starts with some format-specifier ('a'-'z'), and
309	 * a second line ('N'ame) which indicates the original name the user
310	 * specified for that file.  There can be multiple format-spec lines
311	 * for a single Name-line, if the user requested multiple copies of
312	 * that file.  Standard lpr puts the format-spec line(s) before the
313	 * Name-line, while lprNG puts the Name-line before the format-spec
314	 * line(s).  This section needs to handle the lines in either order.
315	 */
316	copycnt = 0;
317	file[0] = '\0';
318	savedname[0] = '\0';
319	while (getline(cfp)) {
320		switch (line[0]) {
321		case 'P': /* Was this file specified in the user's list? */
322			if (!inlist(line+1, cf)) {
323				fclose(cfp);
324				return;
325			}
326			if (lflag) {
327				printf("\n%s: ", line+1);
328				col = strlen(line+1) + 2;
329				prank(rank);
330				blankfill(JOBCOL);
331				printf(" [job %s]\n", cf+3);
332			} else {
333				col = 0;
334				prank(rank);
335				blankfill(OWNCOL);
336				printf("%-10s %-3d  ", line+1, atoi(cf+3));
337				col += 16;
338				first = 1;
339			}
340			continue;
341		default: /* some format specifer and file name? */
342			if (line[0] < 'a' || line[0] > 'z')
343				break;
344			if (copycnt == 0 || strcmp(file, line+1) != 0) {
345				strncpy(file, line + 1, sizeof(file) - 1);
346				file[sizeof(file) - 1] = '\0';
347			}
348			copycnt++;
349			/*
350			 * deliberately 'continue' to another getline(), so
351			 * all format-spec lines for this datafile are read
352			 * in and counted before calling show()
353			 */
354			continue;
355		case 'N':
356			strncpy(savedname, line + 1, sizeof(savedname) - 1);
357			savedname[sizeof(savedname) - 1] = '\0';
358			break;
359		}
360		if ((file[0] != '\0') && (savedname[0] != '\0')) {
361			show(savedname, file, copycnt);
362			copycnt = 0;
363			file[0] = '\0';
364			savedname[0] = '\0';
365		}
366	}
367	fclose(cfp);
368	/* check for a file which hasn't been shown yet */
369	if (file[0] != '\0') {
370		if (savedname[0] == '\0') {
371			/* a safeguard in case the N-ame line is missing */
372			strncpy(savedname, file, sizeof(savedname) - 1);
373			savedname[sizeof(savedname) - 1] = '\0';
374		}
375		show(savedname, file, copycnt);
376	}
377	if (!lflag) {
378		blankfill(SIZCOL);
379		printf("%ld bytes\n", totsize);
380		totsize = 0;
381	}
382}
383
384int
385inlist(name, file)
386	char *name, *file;
387{
388	register int *r, n;
389	register char **u, *cp;
390
391	if (users == 0 && requests == 0)
392		return(1);
393	/*
394	 * Check to see if it's in the user list
395	 */
396	for (u = user; u < &user[users]; u++)
397		if (!strcmp(*u, name))
398			return(1);
399	/*
400	 * Check the request list
401	 */
402	for (n = 0, cp = file+3; isdigit(*cp); )
403		n = n * 10 + (*cp++ - '0');
404	for (r = requ; r < &requ[requests]; r++)
405		if (*r == n && !strcmp(cp, from))
406			return(1);
407	return(0);
408}
409
410void
411show(nfile, file, copies)
412	register char *nfile, *file;
413	int copies;
414{
415	if (strcmp(nfile, " ") == 0)
416		nfile = "(standard input)";
417	if (lflag)
418		ldump(nfile, file, copies);
419	else
420		dump(nfile, file, copies);
421}
422
423/*
424 * Fill the line with blanks to the specified column
425 */
426void
427blankfill(n)
428	register int n;
429{
430	while (col++ < n)
431		putchar(' ');
432}
433
434/*
435 * Give the abbreviated dump of the file names
436 */
437void
438dump(nfile, file, copies)
439	char *nfile, *file;
440	int copies;
441{
442	struct stat lbuf;
443	const char etctmpl[] = ", ...";
444	char	 etc[sizeof(etctmpl)];
445	char	*lastsep;
446	short	 fill, nlen;
447	short	 rem, remetc;
448
449	/*
450	 * Print as many filenames as will fit
451	 *      (leaving room for the 'total size' field)
452	 */
453	fill = first ? 0 : 2;	/* fill space for ``, '' */
454	nlen = strlen(nfile);
455	rem = SIZCOL - 1 - col;
456	if (nlen + fill > rem) {
457		if (first) {
458			/* print the right-most part of the name */
459			printf("...%s ", &nfile[3+nlen-rem]);
460			col = SIZCOL;
461		} else if (rem > 0) {
462			/* fit as much of the etc-string as we can */
463			remetc = rem;
464			if (rem > strlen(etctmpl))
465				remetc = strlen(etctmpl);
466			etc[0] = '\0';
467			strncat(etc, etctmpl, remetc);
468			printf(etc);
469			col += remetc;
470			rem -= remetc;
471			/* room for the last segment of this filename? */
472			lastsep = strrchr(nfile, '/');
473			if ((lastsep != NULL) && (rem > strlen(lastsep))) {
474				/* print the right-most part of this name */
475				printf("%s", lastsep);
476				col += strlen(lastsep);
477			} else {
478				/* do not pack any more names in here */
479				blankfill(SIZCOL);
480			}
481		}
482	} else {
483		if (!first)
484			printf(", ");
485		printf("%s", nfile);
486		col += nlen + fill;
487	}
488	first = 0;
489
490	seteuid(euid);
491	if (*file && !stat(file, &lbuf))
492		totsize += copies * lbuf.st_size;
493	seteuid(uid);
494}
495
496/*
497 * Print the long info about the file
498 */
499void
500ldump(nfile, file, copies)
501	char *nfile, *file;
502	int copies;
503{
504	struct stat lbuf;
505
506	putchar('\t');
507	if (copies > 1)
508		printf("%-2d copies of %-19s", copies, nfile);
509	else
510		printf("%-32s", nfile);
511	if (*file && !stat(file, &lbuf))
512		printf(" %qd bytes", (long long) lbuf.st_size);
513	else
514		printf(" ??? bytes");
515	putchar('\n');
516}
517
518/*
519 * Print the job's rank in the queue,
520 *   update col for screen management
521 */
522void
523prank(n)
524	int n;
525{
526	char rline[100];
527	static char *r[] = {
528		"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
529	};
530
531	if (n == 0) {
532		printf("active");
533		col += 6;
534		return;
535	}
536	if ((n/10)%10 == 1)
537		(void)snprintf(rline, sizeof(rline), "%dth", n);
538	else
539		(void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
540	col += strlen(rline);
541	printf("%s", rline);
542}
543
544void
545alarmhandler(signo)
546	int signo;
547{
548	/* ignored */
549}
550