displayq.c revision 139464
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#if 0
35#ifndef lint
36static char sccsid[] = "@(#)displayq.c	8.4 (Berkeley) 4/28/95";
37#endif /* not lint */
38#endif
39
40#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
41__FBSDID("$FreeBSD: head/usr.sbin/lpr/common_source/displayq.c 139464 2004-12-31 00:36:28Z gad $");
42
43#include <sys/param.h>
44#include <sys/stat.h>
45
46#include <ctype.h>
47#include <dirent.h>
48#include <errno.h>
49#include <fcntl.h>
50#include <signal.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#define psignal foil_gcc_psignal
55#define	sys_siglist foil_gcc_siglist
56#include <unistd.h>
57#undef psignal
58#undef sys_siglist
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 uid_t	uid, euid;
75
76static int	col;		/* column on screen */
77static char	current[MAXNAMLEN+1];	/* current file being printed */
78static char	file[MAXNAMLEN+1];	/* print file name */
79static int	first;		/* first file in ``files'' column? */
80static int	garbage;	/* # of garbage cf files */
81static int	lflag;		/* long output option */
82static int	rank;		/* order to be printed (-1=none, 0=active) */
83static long	totsize;	/* total print job size in bytes */
84
85static const char  *head0 = "Rank   Owner      Job  Files";
86static const char  *head1 = "Total Size\n";
87
88static void	alarmhandler(int _signo);
89static void	warn(const struct printer *_pp);
90
91/*
92 * Display the current state of the queue. Format = 1 if long format.
93 */
94void
95displayq(struct printer *pp, int format)
96{
97	register struct jobqueue *q;
98	register int i, nitems, fd, ret;
99	char *cp, *endp;
100	struct jobqueue **queue;
101	struct stat statb;
102	FILE *fp;
103	void (*savealrm)(int);
104
105	lflag = format;
106	totsize = 0;
107	rank = -1;
108
109	if ((cp = checkremote(pp))) {
110		printf("Warning: %s\n", cp);
111		free(cp);
112	}
113
114	/*
115	 * Print out local queue
116	 * Find all the control files in the spooling directory
117	 */
118	seteuid(euid);
119	if (chdir(pp->spool_dir) < 0)
120		fatal(pp, "cannot chdir to spooling directory: %s",
121		      strerror(errno));
122	seteuid(uid);
123	if ((nitems = getq(pp, &queue)) < 0)
124		fatal(pp, "cannot examine spooling area\n");
125	seteuid(euid);
126	ret = stat(pp->lock_file, &statb);
127	seteuid(uid);
128	if (ret >= 0) {
129		if (statb.st_mode & LFM_PRINT_DIS) {
130			if (pp->remote)
131				printf("%s: ", local_host);
132			printf("Warning: %s is down: ", pp->printer);
133			seteuid(euid);
134			fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
135			seteuid(uid);
136			if (fd >= 0) {
137				while ((i = read(fd, line, sizeof(line))) > 0)
138					(void) fwrite(line, 1, i, stdout);
139				(void) close(fd);	/* unlocks as well */
140			} else
141				putchar('\n');
142		}
143		if (statb.st_mode & LFM_QUEUE_DIS) {
144			if (pp->remote)
145				printf("%s: ", local_host);
146			printf("Warning: %s queue is turned off\n",
147			       pp->printer);
148		}
149	}
150
151	if (nitems) {
152		seteuid(euid);
153		fp = fopen(pp->lock_file, "r");
154		seteuid(uid);
155		if (fp == NULL)
156			warn(pp);
157		else {
158			/* get daemon pid */
159			cp = current;
160			endp = cp + sizeof(current) - 1;
161			while ((i = getc(fp)) != EOF && i != '\n') {
162				if (cp < endp)
163					*cp++ = i;
164			}
165			*cp = '\0';
166			i = atoi(current);
167			if (i <= 0) {
168				ret = -1;
169			} else {
170				seteuid(euid);
171				ret = kill(i, 0);
172				seteuid(uid);
173			}
174			if (ret < 0) {
175				warn(pp);
176			} else {
177				/* read current file name */
178				cp = current;
179				endp = cp + sizeof(current) - 1;
180				while ((i = getc(fp)) != EOF && i != '\n') {
181					if (cp < endp)
182						*cp++ = i;
183				}
184				*cp = '\0';
185				/*
186				 * Print the status file.
187				 */
188				if (pp->remote)
189					printf("%s: ", local_host);
190				seteuid(euid);
191				fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
192				seteuid(uid);
193				if (fd >= 0) {
194					while ((i = read(fd, line,
195							 sizeof(line))) > 0)
196						fwrite(line, 1, i, stdout);
197					close(fd);	/* unlocks as well */
198				} else
199					putchar('\n');
200			}
201			(void) fclose(fp);
202		}
203		/*
204		 * Now, examine the control files and print out the jobs to
205		 * be done for each user.
206		 */
207		if (!lflag)
208			header();
209		for (i = 0; i < nitems; i++) {
210			q = queue[i];
211			inform(pp, q->job_cfname);
212			free(q);
213		}
214		free(queue);
215	}
216	if (!pp->remote) {
217		if (nitems == 0)
218			puts("no entries");
219		return;
220	}
221
222	/*
223	 * Print foreign queue
224	 * Note that a file in transit may show up in either queue.
225	 */
226	if (nitems)
227		putchar('\n');
228	(void) snprintf(line, sizeof(line), "%c%s", format ? '\4' : '\3',
229			pp->remote_queue);
230	cp = line;
231	for (i = 0; i < requests && cp-line+10 < sizeof(line) - 1; i++) {
232		cp += strlen(cp);
233		(void) sprintf(cp, " %d", requ[i]);
234	}
235	for (i = 0; i < users && cp - line + 1 + strlen(user[i]) <
236		sizeof(line) - 1; i++) {
237		cp += strlen(cp);
238		*cp++ = ' ';
239		(void) strcpy(cp, user[i]);
240	}
241	strcat(line, "\n");
242	savealrm = signal(SIGALRM, alarmhandler);
243	alarm(pp->conn_timeout);
244	fd = getport(pp, pp->remote_host, 0);
245	alarm(0);
246	(void)signal(SIGALRM, savealrm);
247	if (fd < 0) {
248		if (from_host != local_host)
249			printf("%s: ", local_host);
250		printf("connection to %s is down\n", pp->remote_host);
251	}
252	else {
253		i = strlen(line);
254		if (write(fd, line, i) != i)
255			fatal(pp, "Lost connection");
256		while ((i = read(fd, line, sizeof(line))) > 0)
257			(void) fwrite(line, 1, i, stdout);
258		(void) close(fd);
259	}
260}
261
262/*
263 * Print a warning message if there is no daemon present.
264 */
265static void
266warn(const struct printer *pp)
267{
268	if (pp->remote)
269		printf("%s: ", local_host);
270	puts("Warning: no daemon present");
271	current[0] = '\0';
272}
273
274/*
275 * Print the header for the short listing format
276 */
277void
278header(void)
279{
280	printf("%s", head0);
281	col = strlen(head0)+1;
282	blankfill(SIZCOL);
283	printf("%s", head1);
284}
285
286void
287inform(const struct printer *pp, char *cf)
288{
289	int copycnt, jnum;
290	char	 savedname[MAXPATHLEN+1];
291	FILE	*cfp;
292
293	/*
294	 * There's a chance the control file has gone away
295	 * in the meantime; if this is the case just keep going
296	 */
297	seteuid(euid);
298	if ((cfp = fopen(cf, "r")) == NULL)
299		return;
300	seteuid(uid);
301
302	if (rank < 0)
303		rank = 0;
304	if (pp->remote || garbage || strcmp(cf, current))
305		rank++;
306
307	/*
308	 * The cf-file may include commands to print more than one datafile
309	 * from the user.  For each datafile, the cf-file contains at least
310	 * one line which starts with some format-specifier ('a'-'z'), and
311	 * a second line ('N'ame) which indicates the original name the user
312	 * specified for that file.  There can be multiple format-spec lines
313	 * for a single Name-line, if the user requested multiple copies of
314	 * that file.  Standard lpr puts the format-spec line(s) before the
315	 * Name-line, while lprNG puts the Name-line before the format-spec
316	 * line(s).  This section needs to handle the lines in either order.
317	 */
318	copycnt = 0;
319	file[0] = '\0';
320	savedname[0] = '\0';
321	jnum = calc_jobnum(cf, NULL);
322	while (getline(cfp)) {
323		switch (line[0]) {
324		case 'P': /* Was this file specified in the user's list? */
325			if (!inlist(line+1, cf)) {
326				fclose(cfp);
327				return;
328			}
329			if (lflag) {
330				printf("\n%s: ", line+1);
331				col = strlen(line+1) + 2;
332				prank(rank);
333				blankfill(JOBCOL);
334				printf(" [job %s]\n", cf+3);
335			} else {
336				col = 0;
337				prank(rank);
338				blankfill(OWNCOL);
339				printf("%-10s %-3d  ", line+1, jnum);
340				col += 16;
341				first = 1;
342			}
343			continue;
344		default: /* some format specifer and file name? */
345			if (line[0] < 'a' || line[0] > 'z')
346				break;
347			if (copycnt == 0 || strcmp(file, line+1) != 0) {
348				strlcpy(file, line + 1, sizeof(file));
349			}
350			copycnt++;
351			/*
352			 * deliberately 'continue' to another getline(), so
353			 * all format-spec lines for this datafile are read
354			 * in and counted before calling show()
355			 */
356			continue;
357		case 'N':
358			strlcpy(savedname, line + 1, sizeof(savedname));
359			break;
360		}
361		if ((file[0] != '\0') && (savedname[0] != '\0')) {
362			show(savedname, file, copycnt);
363			copycnt = 0;
364			file[0] = '\0';
365			savedname[0] = '\0';
366		}
367	}
368	fclose(cfp);
369	/* check for a file which hasn't been shown yet */
370	if (file[0] != '\0') {
371		if (savedname[0] == '\0') {
372			/* a safeguard in case the N-ame line is missing */
373			strlcpy(savedname, file, sizeof(savedname));
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(char *uname, char *cfile)
386{
387	int *r, jnum;
388	char **u;
389	const char *cfhost;
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, uname))
398			return(1);
399	/*
400	 * Check the request list
401	 */
402	jnum = calc_jobnum(cfile, &cfhost);
403	for (r = requ; r < &requ[requests]; r++)
404		if (*r == jnum && !strcmp(cfhost, from_host))
405			return(1);
406	return(0);
407}
408
409void
410show(const char *nfile, const char *datafile, int copies)
411{
412	if (strcmp(nfile, " ") == 0)
413		nfile = "(standard input)";
414	if (lflag)
415		ldump(nfile, datafile, copies);
416	else
417		dump(nfile, datafile, copies);
418}
419
420/*
421 * Fill the line with blanks to the specified column
422 */
423void
424blankfill(int tocol)
425{
426	while (col++ < tocol)
427		putchar(' ');
428}
429
430/*
431 * Give the abbreviated dump of the file names
432 */
433void
434dump(const char *nfile, const char *datafile, int copies)
435{
436	struct stat lbuf;
437	const char etctmpl[] = ", ...";
438	char	 etc[sizeof(etctmpl)];
439	char	*lastsep;
440	short	 fill, nlen;
441	short	 rem, remetc;
442
443	/*
444	 * Print as many filenames as will fit
445	 *      (leaving room for the 'total size' field)
446	 */
447	fill = first ? 0 : 2;	/* fill space for ``, '' */
448	nlen = strlen(nfile);
449	rem = SIZCOL - 1 - col;
450	if (nlen + fill > rem) {
451		if (first) {
452			/* print the right-most part of the name */
453			printf("...%s ", &nfile[3+nlen-rem]);
454			col = SIZCOL;
455		} else if (rem > 0) {
456			/* fit as much of the etc-string as we can */
457			remetc = rem;
458			if (rem > strlen(etctmpl))
459				remetc = strlen(etctmpl);
460			etc[0] = '\0';
461			strncat(etc, etctmpl, remetc);
462			printf("%s", etc);
463			col += remetc;
464			rem -= remetc;
465			/* room for the last segment of this filename? */
466			lastsep = strrchr(nfile, '/');
467			if ((lastsep != NULL) && (rem > strlen(lastsep))) {
468				/* print the right-most part of this name */
469				printf("%s", lastsep);
470				col += strlen(lastsep);
471			} else {
472				/* do not pack any more names in here */
473				blankfill(SIZCOL);
474			}
475		}
476	} else {
477		if (!first)
478			printf(", ");
479		printf("%s", nfile);
480		col += nlen + fill;
481	}
482	first = 0;
483
484	seteuid(euid);
485	if (*datafile && !stat(datafile, &lbuf))
486		totsize += copies * lbuf.st_size;
487	seteuid(uid);
488}
489
490/*
491 * Print the long info about the file
492 */
493void
494ldump(const char *nfile, const char *datafile, int copies)
495{
496	struct stat lbuf;
497
498	putchar('\t');
499	if (copies > 1)
500		printf("%-2d copies of %-19s", copies, nfile);
501	else
502		printf("%-32s", nfile);
503	if (*datafile && !stat(datafile, &lbuf))
504		printf(" %qd 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 */
514void
515prank(int n)
516{
517	char rline[100];
518	static const 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
535void
536alarmhandler(int signo __unused)
537{
538	/* the signal is ignored */
539	/* (the '__unused' is just to avoid a compile-time warning) */
540}
541