cmds.c revision 78146
1/*
2 * Copyright (c) 1983, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by the University of
17 *	California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36static const char copyright[] =
37"@(#) Copyright (c) 1983, 1993\n\
38	The Regents of the University of California.  All rights reserved.\n";
39#endif /* not lint */
40
41#ifndef lint
42/*
43static char sccsid[] = "@(#)cmds.c	8.2 (Berkeley) 4/28/95";
44*/
45static const char rcsid[] =
46  "$FreeBSD: head/usr.sbin/lpr/lpc/cmds.c 78146 2001-06-12 16:38:20Z gad $";
47#endif /* not lint */
48
49/*
50 * lpc -- line printer control program -- commands:
51 */
52
53#include <sys/param.h>
54#include <sys/time.h>
55#include <sys/stat.h>
56#include <sys/file.h>
57
58#include <signal.h>
59#include <fcntl.h>
60#include <errno.h>
61#include <dirent.h>
62#include <unistd.h>
63#include <stdlib.h>
64#include <stdio.h>
65#include <ctype.h>
66#include <string.h>
67#include "lp.h"
68#include "lp.local.h"
69#include "lpc.h"
70#include "extern.h"
71#include "pathnames.h"
72
73static void	 abortpr(struct printer *_pp, int _dis);
74static int	 doarg(char *_job);
75static int	 doselect(struct dirent *_d);
76static void	 putmsg(struct printer *_pp, int _argc, char **_argv);
77static int	 sortq(const void *_a, const void *_b);
78static void	 startpr(struct printer *_pp, int _chgenable);
79static int	 touch(struct jobqueue *_jq);
80static void	 unlinkf(char *_name);
81static void	 upstat(struct printer *_pp, const char *_msg);
82
83/*
84 * generic framework for commands which operate on all or a specified
85 * set of printers
86 */
87void
88generic(void (*specificrtn)(struct printer *_pp), int argc, char *argv[])
89{
90	int cmdstatus, more;
91	struct printer myprinter, *pp = &myprinter;
92
93	if (argc == 1) {
94		printf("Usage: %s {all | printer ...}\n", argv[0]);
95		return;
96	}
97	if (argc == 2 && strcmp(argv[1], "all") == 0) {
98		more = firstprinter(pp, &cmdstatus);
99		if (cmdstatus)
100			goto looperr;
101		while (more) {
102			(*specificrtn)(pp);
103			do {
104				more = nextprinter(pp, &cmdstatus);
105looperr:
106				switch (cmdstatus) {
107				case PCAPERR_TCOPEN:
108					printf("warning: %s: unresolved "
109					       "tc= reference(s) ",
110					       pp->printer);
111				case PCAPERR_SUCCESS:
112					break;
113				default:
114					fatal(pp, pcaperr(cmdstatus));
115				}
116			} while (more && cmdstatus);
117		}
118		return;
119	}
120	while (--argc) {
121		++argv;
122		init_printer(pp);
123		cmdstatus = getprintcap(*argv, pp);
124		switch (cmdstatus) {
125		default:
126			fatal(pp, pcaperr(cmdstatus));
127		case PCAPERR_NOTFOUND:
128			printf("unknown printer %s\n", *argv);
129			continue;
130		case PCAPERR_TCOPEN:
131			printf("warning: %s: unresolved tc= reference(s)\n",
132			       *argv);
133			break;
134		case PCAPERR_SUCCESS:
135			break;
136		}
137		(*specificrtn)(pp);
138	}
139}
140
141/*
142 * kill an existing daemon and disable printing.
143 */
144void
145doabort(struct printer *pp)
146{
147	abortpr(pp, 1);
148}
149
150static void
151abortpr(struct printer *pp, int dis)
152{
153	register FILE *fp;
154	struct stat stbuf;
155	int pid, fd;
156	char lf[MAXPATHLEN];
157
158	lock_file_name(pp, lf, sizeof lf);
159	printf("%s:\n", pp->printer);
160
161	/*
162	 * Turn on the owner execute bit of the lock file to disable printing.
163	 */
164	if (dis) {
165		seteuid(euid);
166		if (stat(lf, &stbuf) >= 0) {
167			if (chmod(lf, stbuf.st_mode | LFM_PRINT_DIS) < 0)
168				printf("\tcannot disable printing: %s\n",
169				       strerror(errno));
170			else {
171				upstat(pp, "printing disabled\n");
172				printf("\tprinting disabled\n");
173			}
174		} else if (errno == ENOENT) {
175			if ((fd = open(lf, O_WRONLY|O_CREAT,
176				       LOCK_FILE_MODE | LFM_PRINT_DIS)) < 0)
177				printf("\tcannot create lock file: %s\n",
178				       strerror(errno));
179			else {
180				(void) close(fd);
181				upstat(pp, "printing disabled\n");
182				printf("\tprinting disabled\n");
183				printf("\tno daemon to abort\n");
184			}
185			goto out;
186		} else {
187			printf("\tcannot stat lock file\n");
188			goto out;
189		}
190	}
191	/*
192	 * Kill the current daemon to stop printing now.
193	 */
194	if ((fp = fopen(lf, "r")) == NULL) {
195		printf("\tcannot open lock file\n");
196		goto out;
197	}
198	if (!getline(fp) || flock(fileno(fp), LOCK_SH|LOCK_NB) == 0) {
199		(void) fclose(fp);	/* unlocks as well */
200		printf("\tno daemon to abort\n");
201		goto out;
202	}
203	(void) fclose(fp);
204	if (kill(pid = atoi(line), SIGTERM) < 0) {
205		if (errno == ESRCH)
206			printf("\tno daemon to abort\n");
207		else
208			printf("\tWarning: daemon (pid %d) not killed\n", pid);
209	} else
210		printf("\tdaemon (pid %d) killed\n", pid);
211out:
212	seteuid(uid);
213}
214
215/*
216 * Write a message into the status file.
217 */
218static void
219upstat(struct printer *pp, const char *msg)
220{
221	register int fd;
222	char statfile[MAXPATHLEN];
223
224	status_file_name(pp, statfile, sizeof statfile);
225	umask(0);
226	fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
227	if (fd < 0) {
228		printf("\tcannot create status file: %s\n", strerror(errno));
229		return;
230	}
231	(void) ftruncate(fd, 0);
232	if (msg == (char *)NULL)
233		(void) write(fd, "\n", 1);
234	else
235		(void) write(fd, msg, strlen(msg));
236	(void) close(fd);
237}
238
239static int
240doselect(struct dirent *d)
241{
242	int c = d->d_name[0];
243
244	if ((c == 't' || c == 'c' || c == 'd') && d->d_name[1] == 'f')
245		return(1);
246	return(0);
247}
248
249/*
250 * Comparison routine for scandir. Sort by job number and machine, then
251 * by `cf', `tf', or `df', then by the sequence letter A-Z, a-z.
252 */
253static int
254sortq(const void *a, const void *b)
255{
256	const struct dirent **d1, **d2;
257	int c1, c2;
258
259	d1 = (const struct dirent **)a;
260	d2 = (const struct dirent **)b;
261	if ((c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3)))
262		return(c1);
263	c1 = (*d1)->d_name[0];
264	c2 = (*d2)->d_name[0];
265	if (c1 == c2)
266		return((*d1)->d_name[2] - (*d2)->d_name[2]);
267	if (c1 == 'c')
268		return(-1);
269	if (c1 == 'd' || c2 == 'c')
270		return(1);
271	return(-1);
272}
273
274/*
275 * Remove all spool files and temporaries from the spooling area.
276 * Or, perhaps:
277 * Remove incomplete jobs from spooling area.
278 */
279void
280clean(struct printer *pp)
281{
282	register int i, n;
283	register char *cp, *cp1, *lp;
284	struct dirent **queue;
285	int nitems;
286
287	printf("%s:\n", pp->printer);
288
289	lp = line;
290	cp = pp->spool_dir;
291	while (lp < &line[sizeof(line) - 1]) {
292		if ((*lp++ = *cp++) == 0)
293			break;
294	}
295	lp[-1] = '/';
296
297	seteuid(euid);
298	nitems = scandir(pp->spool_dir, &queue, doselect, sortq);
299	seteuid(uid);
300	if (nitems < 0) {
301		printf("\tcannot examine spool directory\n");
302		return;
303	}
304	if (nitems == 0)
305		return;
306	i = 0;
307	do {
308		cp = queue[i]->d_name;
309		if (*cp == 'c') {
310			n = 0;
311			while (i + 1 < nitems) {
312				cp1 = queue[i + 1]->d_name;
313				if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3))
314					break;
315				i++;
316				n++;
317			}
318			if (n == 0) {
319				strncpy(lp, cp, sizeof(line) - strlen(line) - 1);
320				line[sizeof(line) - 1] = '\0';
321				unlinkf(line);
322			}
323		} else {
324			/*
325			 * Must be a df with no cf (otherwise, it would have
326			 * been skipped above) or a tf file (which can always
327			 * be removed).
328			 */
329			strncpy(lp, cp, sizeof(line) - strlen(line) - 1);
330			line[sizeof(line) - 1] = '\0';
331			unlinkf(line);
332		}
333     	} while (++i < nitems);
334}
335
336static void
337unlinkf(char *name)
338{
339	seteuid(euid);
340	if (unlink(name) < 0)
341		printf("\tcannot remove %s\n", name);
342	else
343		printf("\tremoved %s\n", name);
344	seteuid(uid);
345}
346
347/*
348 * Enable queuing to the printer (allow lpr's).
349 */
350void
351enable(struct printer *pp)
352{
353	struct stat stbuf;
354	char lf[MAXPATHLEN];
355
356	lock_file_name(pp, lf, sizeof lf);
357	printf("%s:\n", pp->printer);
358
359	/*
360	 * Turn off the group execute bit of the lock file to enable queuing.
361	 */
362	seteuid(euid);
363	if (stat(lf, &stbuf) >= 0) {
364		if (chmod(lf, stbuf.st_mode & ~LFM_QUEUE_DIS) < 0)
365			printf("\tcannot enable queuing\n");
366		else
367			printf("\tqueuing enabled\n");
368	}
369	seteuid(uid);
370}
371
372/*
373 * Disable queuing.
374 */
375void
376disable(struct printer *pp)
377{
378	register int fd;
379	struct stat stbuf;
380	char lf[MAXPATHLEN];
381
382	lock_file_name(pp, lf, sizeof lf);
383	printf("%s:\n", pp->printer);
384	/*
385	 * Turn on the group execute bit of the lock file to disable queuing.
386	 */
387	seteuid(euid);
388	if (stat(lf, &stbuf) >= 0) {
389		if (chmod(lf, stbuf.st_mode | LFM_QUEUE_DIS) < 0)
390			printf("\tcannot disable queuing: %s\n",
391			       strerror(errno));
392		else
393			printf("\tqueuing disabled\n");
394	} else if (errno == ENOENT) {
395		if ((fd = open(lf, O_WRONLY|O_CREAT,
396			       LOCK_FILE_MODE | LFM_QUEUE_DIS)) < 0)
397			printf("\tcannot create lock file: %s\n",
398			       strerror(errno));
399		else {
400			(void) close(fd);
401			printf("\tqueuing disabled\n");
402		}
403	} else
404		printf("\tcannot stat lock file\n");
405	seteuid(uid);
406}
407
408/*
409 * Disable queuing and printing and put a message into the status file
410 * (reason for being down).
411 */
412void
413down(int argc, char *argv[])
414{
415        int cmdstatus, more;
416	struct printer myprinter, *pp = &myprinter;
417
418	if (argc == 1) {
419		printf("Usage: down {all | printer} [message ...]\n");
420		return;
421	}
422	if (!strcmp(argv[1], "all")) {
423		more = firstprinter(pp, &cmdstatus);
424		if (cmdstatus)
425			goto looperr;
426		while (more) {
427			putmsg(pp, argc - 2, argv + 2);
428			do {
429				more = nextprinter(pp, &cmdstatus);
430looperr:
431				switch (cmdstatus) {
432				case PCAPERR_TCOPEN:
433					printf("warning: %s: unresolved "
434					       "tc= reference(s) ",
435					       pp->printer);
436				case PCAPERR_SUCCESS:
437					break;
438				default:
439					fatal(pp, pcaperr(cmdstatus));
440				}
441			} while (more && cmdstatus);
442		}
443		return;
444	}
445	init_printer(pp);
446	cmdstatus = getprintcap(argv[1], pp);
447	switch (cmdstatus) {
448	default:
449		fatal(pp, pcaperr(cmdstatus));
450	case PCAPERR_NOTFOUND:
451		printf("unknown printer %s\n", argv[1]);
452		return;
453	case PCAPERR_TCOPEN:
454		printf("warning: %s: unresolved tc= reference(s)", argv[1]);
455		break;
456	case PCAPERR_SUCCESS:
457		break;
458	}
459	putmsg(pp, argc - 2, argv + 2);
460}
461
462static void
463putmsg(struct printer *pp, int argc, char **argv)
464{
465	register int fd;
466	register char *cp1, *cp2;
467	char buf[1024];
468	char file[MAXPATHLEN];
469	struct stat stbuf;
470
471	printf("%s:\n", pp->printer);
472	/*
473	 * Turn on the group execute bit of the lock file to disable queuing;
474	 * turn on the owner execute bit of the lock file to disable printing.
475	 */
476	lock_file_name(pp, file, sizeof file);
477	seteuid(euid);
478	if (stat(file, &stbuf) >= 0) {
479		if (chmod(file, stbuf.st_mode|LFM_PRINT_DIS|LFM_QUEUE_DIS) < 0)
480			printf("\tcannot disable queuing: %s\n",
481			       strerror(errno));
482		else
483			printf("\tprinter and queuing disabled\n");
484	} else if (errno == ENOENT) {
485		if ((fd = open(file, O_WRONLY|O_CREAT,
486			       LOCK_FILE_MODE|LFM_PRINT_DIS|LFM_QUEUE_DIS)) < 0)
487			printf("\tcannot create lock file: %s\n",
488			       strerror(errno));
489		else {
490			(void) close(fd);
491			printf("\tprinter and queuing disabled\n");
492		}
493		seteuid(uid);
494		return;
495	} else
496		printf("\tcannot stat lock file\n");
497	/*
498	 * Write the message into the status file.
499	 */
500	status_file_name(pp, file, sizeof file);
501	fd = open(file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
502	if (fd < 0) {
503		printf("\tcannot create status file: %s\n", strerror(errno));
504		seteuid(uid);
505		return;
506	}
507	seteuid(uid);
508	(void) ftruncate(fd, 0);
509	if (argc <= 0) {
510		(void) write(fd, "\n", 1);
511		(void) close(fd);
512		return;
513	}
514	cp1 = buf;
515	while (--argc >= 0) {
516		cp2 = *argv++;
517		while ((cp1 - buf) < sizeof(buf) && (*cp1++ = *cp2++))
518			;
519		cp1[-1] = ' ';
520	}
521	cp1[-1] = '\n';
522	*cp1 = '\0';
523	(void) write(fd, buf, strlen(buf));
524	(void) close(fd);
525}
526
527/*
528 * Exit lpc
529 */
530void
531quit(int argc __unused, char *argv[] __unused)
532{
533	exit(0);
534}
535
536/*
537 * Kill and restart the daemon.
538 */
539void
540restart(struct printer *pp)
541{
542	abortpr(pp, 0);
543	startpr(pp, 0);
544}
545
546/*
547 * Enable printing on the specified printer and startup the daemon.
548 */
549void
550startcmd(struct printer *pp)
551{
552	startpr(pp, 1);
553}
554
555static void
556startpr(struct printer *pp, int chgenable)
557{
558	struct stat stbuf;
559	char lf[MAXPATHLEN];
560
561	lock_file_name(pp, lf, sizeof lf);
562	printf("%s:\n", pp->printer);
563
564	/*
565	 * For chgenable==1 ('start'), turn off the LFM_PRINT_DIS bit of the
566	 * lock file to re-enable printing.  For chgenable==2 ('up'), also
567	 * turn off the LFM_QUEUE_DIS bit to re-enable queueing.
568	 */
569	seteuid(euid);
570	if (chgenable && stat(lf, &stbuf) >= 0) {
571		mode_t bits = (chgenable == 2 ? 0 : LFM_QUEUE_DIS);
572		if (chmod(lf, stbuf.st_mode & (LOCK_FILE_MODE | bits)) < 0)
573			printf("\tcannot enable printing\n");
574		else
575			printf("\tprinting enabled\n");
576	}
577	if (!startdaemon(pp))
578		printf("\tcouldn't start daemon\n");
579	else
580		printf("\tdaemon started\n");
581	seteuid(uid);
582}
583
584/*
585 * Print the status of the printer queue.
586 */
587void
588status(struct printer *pp)
589{
590	struct stat stbuf;
591	register int fd, i;
592	register struct dirent *dp;
593	DIR *dirp;
594	char file[MAXPATHLEN];
595
596	printf("%s:\n", pp->printer);
597	lock_file_name(pp, file, sizeof file);
598	if (stat(file, &stbuf) >= 0) {
599		printf("\tqueuing is %s\n",
600		       ((stbuf.st_mode & LFM_QUEUE_DIS) ? "disabled"
601			: "enabled"));
602		printf("\tprinting is %s\n",
603		       ((stbuf.st_mode & LFM_PRINT_DIS) ? "disabled"
604			: "enabled"));
605	} else {
606		printf("\tqueuing is enabled\n");
607		printf("\tprinting is enabled\n");
608	}
609	if ((dirp = opendir(pp->spool_dir)) == NULL) {
610		printf("\tcannot examine spool directory\n");
611		return;
612	}
613	i = 0;
614	while ((dp = readdir(dirp)) != NULL) {
615		if (*dp->d_name == 'c' && dp->d_name[1] == 'f')
616			i++;
617	}
618	closedir(dirp);
619	if (i == 0)
620		printf("\tno entries in spool area\n");
621	else if (i == 1)
622		printf("\t1 entry in spool area\n");
623	else
624		printf("\t%d entries in spool area\n", i);
625	fd = open(file, O_RDONLY);
626	if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) {
627		(void) close(fd);	/* unlocks as well */
628		printf("\tprinter idle\n");
629		return;
630	}
631	(void) close(fd);
632	/* print out the contents of the status file, if it exists */
633	status_file_name(pp, file, sizeof file);
634	fd = open(file, O_RDONLY|O_SHLOCK);
635	if (fd >= 0) {
636		(void) fstat(fd, &stbuf);
637		if (stbuf.st_size > 0) {
638			putchar('\t');
639			while ((i = read(fd, line, sizeof(line))) > 0)
640				(void) fwrite(line, 1, i, stdout);
641		}
642		(void) close(fd);	/* unlocks as well */
643	}
644}
645
646/*
647 * Stop the specified daemon after completing the current job and disable
648 * printing.
649 */
650void
651stop(struct printer *pp)
652{
653	register int fd;
654	struct stat stbuf;
655	char lf[MAXPATHLEN];
656
657	lock_file_name(pp, lf, sizeof lf);
658	printf("%s:\n", pp->printer);
659
660	/*
661	 * Turn on the owner execute bit of the lock file to disable printing.
662	 */
663	seteuid(euid);
664	if (stat(lf, &stbuf) >= 0) {
665		if (chmod(lf, stbuf.st_mode | LFM_PRINT_DIS) < 0)
666			printf("\tcannot disable printing: %s\n",
667			       strerror(errno));
668		else {
669			upstat(pp, "printing disabled\n");
670			printf("\tprinting disabled\n");
671		}
672	} else if (errno == ENOENT) {
673		if ((fd = open(lf, O_WRONLY|O_CREAT,
674			       LOCK_FILE_MODE | LFM_PRINT_DIS)) < 0)
675			printf("\tcannot create lock file: %s\n",
676			       strerror(errno));
677		else {
678			(void) close(fd);
679			upstat(pp, "printing disabled\n");
680			printf("\tprinting disabled\n");
681		}
682	} else
683		printf("\tcannot stat lock file\n");
684	seteuid(uid);
685}
686
687struct	jobqueue **queue;
688int	nitems;
689time_t	mtime;
690
691/*
692 * Put the specified jobs at the top of printer queue.
693 */
694void
695topq(int argc, char *argv[])
696{
697	register int i;
698	struct stat stbuf;
699	int cmdstatus, changed;
700	struct printer myprinter, *pp = &myprinter;
701
702	if (argc < 3) {
703		printf("Usage: topq printer [jobnum ...] [user ...]\n");
704		return;
705	}
706
707	--argc;
708	++argv;
709	init_printer(pp);
710	cmdstatus = getprintcap(*argv, pp);
711	switch(cmdstatus) {
712	default:
713		fatal(pp, pcaperr(cmdstatus));
714	case PCAPERR_NOTFOUND:
715		printf("unknown printer %s\n", *argv);
716		return;
717	case PCAPERR_TCOPEN:
718		printf("warning: %s: unresolved tc= reference(s)", *argv);
719		break;
720	case PCAPERR_SUCCESS:
721		break;
722	}
723	printf("%s:\n", pp->printer);
724
725	seteuid(euid);
726	if (chdir(pp->spool_dir) < 0) {
727		printf("\tcannot chdir to %s\n", pp->spool_dir);
728		goto out;
729	}
730	seteuid(uid);
731	nitems = getq(pp, &queue);
732	if (nitems == 0)
733		return;
734	changed = 0;
735	mtime = queue[0]->job_time;
736	for (i = argc; --i; ) {
737		if (doarg(argv[i]) == 0) {
738			printf("\tjob %s is not in the queue\n", argv[i]);
739			continue;
740		} else
741			changed++;
742	}
743	for (i = 0; i < nitems; i++)
744		free(queue[i]);
745	free(queue);
746	if (!changed) {
747		printf("\tqueue order unchanged\n");
748		return;
749	}
750	/*
751	 * Turn on the public execute bit of the lock file to
752	 * get lpd to rebuild the queue after the current job.
753	 */
754	seteuid(euid);
755	if (changed && stat(pp->lock_file, &stbuf) >= 0)
756		(void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE);
757
758out:
759	seteuid(uid);
760}
761
762/*
763 * Reposition the job by changing the modification time of
764 * the control file.
765 */
766static int
767touch(struct jobqueue *jq)
768{
769	struct timeval tvp[2];
770	int ret;
771
772	tvp[0].tv_sec = tvp[1].tv_sec = --mtime;
773	tvp[0].tv_usec = tvp[1].tv_usec = 0;
774	seteuid(euid);
775	ret = utimes(jq->job_cfname, tvp);
776	seteuid(uid);
777	return (ret);
778}
779
780/*
781 * Checks if specified job name is in the printer's queue.
782 * Returns:  negative (-1) if argument name is not in the queue.
783 */
784static int
785doarg(char *job)
786{
787	register struct jobqueue **qq;
788	register int jobnum, n;
789	register char *cp, *machine;
790	int cnt = 0;
791	FILE *fp;
792
793	/*
794	 * Look for a job item consisting of system name, colon, number
795	 * (example: ucbarpa:114)
796	 */
797	if ((cp = strchr(job, ':')) != NULL) {
798		machine = job;
799		*cp++ = '\0';
800		job = cp;
801	} else
802		machine = NULL;
803
804	/*
805	 * Check for job specified by number (example: 112 or 235ucbarpa).
806	 */
807	if (isdigit(*job)) {
808		jobnum = 0;
809		do
810			jobnum = jobnum * 10 + (*job++ - '0');
811		while (isdigit(*job));
812		for (qq = queue + nitems; --qq >= queue; ) {
813			n = 0;
814			for (cp = (*qq)->job_cfname+3; isdigit(*cp); )
815				n = n * 10 + (*cp++ - '0');
816			if (jobnum != n)
817				continue;
818			if (*job && strcmp(job, cp) != 0)
819				continue;
820			if (machine != NULL && strcmp(machine, cp) != 0)
821				continue;
822			if (touch(*qq) == 0) {
823				printf("\tmoved %s\n", (*qq)->job_cfname);
824				cnt++;
825			}
826		}
827		return(cnt);
828	}
829	/*
830	 * Process item consisting of owner's name (example: henry).
831	 */
832	for (qq = queue + nitems; --qq >= queue; ) {
833		seteuid(euid);
834		fp = fopen((*qq)->job_cfname, "r");
835		seteuid(uid);
836		if (fp == NULL)
837			continue;
838		while (getline(fp) > 0)
839			if (line[0] == 'P')
840				break;
841		(void) fclose(fp);
842		if (line[0] != 'P' || strcmp(job, line+1) != 0)
843			continue;
844		if (touch(*qq) == 0) {
845			printf("\tmoved %s\n", (*qq)->job_cfname);
846			cnt++;
847		}
848	}
849	return(cnt);
850}
851
852/*
853 * Enable everything and start printer (undo `down').
854 */
855void
856up(struct printer *pp)
857{
858	startpr(pp, 2);
859}
860