1100203Sgad/*
2100203Sgad * ------+---------+---------+---------+---------+---------+---------+---------*
3100203Sgad * Copyright (c) 2002   - Garance Alistair Drosehn <gad@FreeBSD.org>.
4100203Sgad * All rights reserved.
5100203Sgad *
6100203Sgad * Redistribution and use in source and binary forms, with or without
7100203Sgad * modification, are permitted provided that the following conditions
8100203Sgad * are met:
9100203Sgad *   1. Redistributions of source code must retain the above copyright
10100203Sgad *      notice, this list of conditions and the following disclaimer.
11100203Sgad *   2. Redistributions in binary form must reproduce the above copyright
12100203Sgad *      notice, this list of conditions and the following disclaimer in the
13100203Sgad *      documentation and/or other materials provided with the distribution.
14100203Sgad *
15100203Sgad * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16100203Sgad * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17100203Sgad * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18100203Sgad * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19100203Sgad * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20100203Sgad * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21100203Sgad * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22100203Sgad * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23100203Sgad * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24100203Sgad * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25100203Sgad * SUCH DAMAGE.
26100203Sgad *
27100203Sgad * The views and conclusions contained in the software and documentation
28100203Sgad * are those of the authors and should not be interpreted as representing
29100203Sgad * official policies, either expressed or implied, of the FreeBSD Project
30100203Sgad * or FreeBSD, Inc.
31100203Sgad *
32100203Sgad * ------+---------+---------+---------+---------+---------+---------+---------*
33100203Sgad */
34100203Sgad
35117599Sgad#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
36117599Sgad__FBSDID("$FreeBSD$");
37100203Sgad
38100203Sgad/*
39100203Sgad * movejobs.c - The lpc commands which move jobs around.
40100203Sgad */
41100203Sgad
42100203Sgad#include <sys/file.h>
43100203Sgad#include <sys/param.h>
44100203Sgad#include <sys/queue.h>
45100203Sgad#include <sys/time.h>
46100203Sgad#include <sys/stat.h>
47100203Sgad
48100203Sgad#include <ctype.h>
49100203Sgad#include <dirent.h>	/* just for MAXNAMLEN, for job_cfname in lp.h! */
50241852Seadler#include <err.h>
51100203Sgad#include <stdio.h>
52100203Sgad#include <stdlib.h>
53100203Sgad#include <string.h>
54100203Sgad#include <unistd.h>
55100203Sgad#include "lp.h"
56100203Sgad#include "lpc.h"
57100203Sgad#include "matchjobs.h"
58100203Sgad#include "extern.h"
59100203Sgad
60100203Sgad/* Values for origcmd in tqbq_common() */
61100203Sgad#define IS_TOPQ		1
62100203Sgad#define IS_BOTQ 	2
63100203Sgad
64100203Sgadstatic int	 process_jobs(int _argc, char *_argv[], process_jqe
65100203Sgad		    _process_rtn, void *myinfo);
66100203Sgadstatic process_jqe touch_jqe;
67100203Sgadstatic void 	 tqbq_common(int _argc, char *_argv[], int _origcmd);
68100203Sgad
69100203Sgad/*
70100203Sgad * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF.
71100203Sgad * Define a wrapper which can take 'char', either signed or unsigned.
72100203Sgad */
73100203Sgad#define isdigitch(Anychar)    isdigit(((int) Anychar) & 255)
74100203Sgad
75100203Sgadstruct touchjqe_info {			/* for topq/bottomq */
76100203Sgad	time_t	 newtime;
77100203Sgad};
78100203Sgad
79100203Sgadstatic int	 nitems;
80100203Sgadstatic struct jobqueue **queue;
81100203Sgad
82100203Sgad/*
83100203Sgad * Process all the jobs, as specified by the user.
84100203Sgad */
85100203Sgadstatic int
86100203Sgadprocess_jobs(int argc, char *argv[], process_jqe process_rtn, void *myinfo)
87100203Sgad{
88100203Sgad	struct jobspec_hdr jobs_wanted;
89100203Sgad	int i, matchcnt, pjres;
90100203Sgad
91100203Sgad	STAILQ_INIT(&jobs_wanted);
92100203Sgad	for (i = 0; i < argc; i++) {
93100203Sgad		pjres = parse_jobspec(argv[i], &jobs_wanted);
94100203Sgad		if (pjres == 0) {
95100203Sgad			printf("\tinvalid job specifier: %s\n", argv[i]);
96100203Sgad			continue;
97100203Sgad		}
98100203Sgad	}
99100203Sgad	matchcnt = scanq_jobspec(nitems, queue, SCQ_JSORDER, &jobs_wanted,
100100203Sgad	    process_rtn, myinfo);
101100203Sgad
102100203Sgad	free_jobspec(&jobs_wanted);
103100203Sgad	return (matchcnt);
104100203Sgad}
105100203Sgad
106100203Sgad/*
107100203Sgad * Reposition the job by changing the modification time of the
108100203Sgad * control file.
109100203Sgad */
110100203Sgadstatic int
111100203Sgadtouch_jqe(void *myinfo, struct jobqueue *jq, struct jobspec *jspec)
112100203Sgad{
113100203Sgad	struct timeval tvp[2];
114100203Sgad	struct touchjqe_info *touch_info;
115100203Sgad	int ret;
116100203Sgad
117100203Sgad	/*
118100203Sgad	 * If the entire queue has been scanned for the current jobspec,
119100203Sgad	 * then let the user know if there were no jobs matched by that
120100203Sgad	 * specification.
121100203Sgad	 */
122100203Sgad	if (jq == NULL) {
123100203Sgad		if (jspec->matchcnt == 0) {
124100203Sgad			format_jobspec(jspec, FMTJS_VERBOSE);
125100203Sgad			if (jspec->pluralfmt)
126100203Sgad				printf("\tjobs %s are not in the queue\n",
127100203Sgad				    jspec->fmtoutput);
128100203Sgad			else
129100203Sgad				printf("\tjob %s is not in the queue\n",
130100203Sgad				    jspec->fmtoutput);
131100203Sgad		}
132100203Sgad		return (1);
133100203Sgad	}
134100203Sgad
135100203Sgad	/*
136100203Sgad	 * Do a little juggling with "matched" vs "processed", so a single
137100203Sgad	 * job can be matched by multiple specifications, and yet it will
138100203Sgad	 * be moved only once.  This is so, eg, 'topq lp 7 7' will not
139100203Sgad	 * complain "job 7 is not in queue" for the second specification.
140100203Sgad	 */
141100203Sgad	jq->job_matched = 0;
142100203Sgad	if (jq->job_processed) {
143100203Sgad		printf("\tmoved %s earlier\n", jq->job_cfname);
144100203Sgad		return (1);
145100203Sgad	}
146100203Sgad	jq->job_processed = 1;
147100203Sgad
148100203Sgad	touch_info = myinfo;
149100203Sgad	tvp[0].tv_sec = tvp[1].tv_sec = ++touch_info->newtime;
150100203Sgad	tvp[0].tv_usec = tvp[1].tv_usec = 0;
151241852Seadler	PRIV_START
152100203Sgad	ret = utimes(jq->job_cfname, tvp);
153241852Seadler	PRIV_END
154100203Sgad
155100203Sgad	if (ret == 0) {
156100203Sgad		if (jspec->matcheduser)
157100203Sgad			printf("\tmoved %s  (user %s)\n", jq->job_cfname,
158100203Sgad			    jspec->matcheduser);
159100203Sgad		else
160100203Sgad			printf("\tmoved %s\n", jq->job_cfname);
161100203Sgad	}
162100203Sgad	return (ret);
163100203Sgad}
164100203Sgad
165100203Sgad/*
166100203Sgad * Put the specified jobs at the bottom of printer queue.
167100203Sgad */
168100203Sgadvoid
169100203Sgadbottomq_cmd(int argc, char *argv[])
170100203Sgad{
171100203Sgad
172100203Sgad	if (argc < 3) {
173100203Sgad		printf("usage: bottomq printer [jobspec ...]\n");
174100203Sgad		return;
175100203Sgad	}
176100203Sgad	--argc;			/* First argv was the command name */
177100203Sgad	++argv;
178100203Sgad
179100203Sgad	tqbq_common(argc, argv, IS_BOTQ);
180100203Sgad}
181100203Sgad
182100203Sgad/*
183100203Sgad * Put the specified jobs at the top of printer queue.
184100203Sgad */
185100203Sgadvoid
186100203Sgadtopq_cmd(int argc, char *argv[])
187100203Sgad{
188100203Sgad
189100203Sgad	if (argc < 3) {
190100203Sgad		printf("usage: topq printer [jobspec ...]\n");
191100203Sgad		return;
192100203Sgad	}
193100203Sgad	--argc;			/* First argv was the command name */
194100203Sgad	++argv;
195100203Sgad
196100203Sgad	tqbq_common(argc, argv, IS_TOPQ);
197100203Sgad}
198100203Sgad
199100203Sgad/*
200100203Sgad * Processing in common between topq and bottomq commands.
201100203Sgad */
202100203Sgadvoid
203100203Sgadtqbq_common(int argc, char *argv[], int origcmd)
204100203Sgad{
205100203Sgad	struct printer myprinter, *pp;
206100203Sgad	struct touchjqe_info touch_info;
207100203Sgad	int i, movecnt, setres;
208100203Sgad
209100203Sgad	pp = setup_myprinter(*argv, &myprinter, SUMP_CHDIR_SD);
210100203Sgad	if (pp == NULL)
211100203Sgad		return;
212100203Sgad	--argc;			/* Second argv was the printer name */
213100203Sgad	++argv;
214100203Sgad
215100203Sgad	nitems = getq(pp, &queue);
216100203Sgad	if (nitems == 0) {
217100203Sgad		printf("\tthere are no jobs in the queue\n");
218100203Sgad		free_printer(pp);
219100203Sgad		return;
220100203Sgad	}
221100203Sgad
222100203Sgad	/*
223100203Sgad	 * The only real difference between topq and bottomq is the
224100203Sgad	 * initial value used for newtime.
225100203Sgad	 */
226100203Sgad	switch (origcmd) {
227100203Sgad	case IS_BOTQ:
228100203Sgad		/*
229100203Sgad		 * When moving jobs to the bottom of the queue, pick a
230100203Sgad		 * starting value which is one second after the last job
231100203Sgad		 * in the queue.
232100203Sgad		*/
233100203Sgad		touch_info.newtime = queue[nitems - 1]->job_time + 1;
234100203Sgad		break;
235100203Sgad	case IS_TOPQ:
236100203Sgad		/*
237100203Sgad		 * When moving jobs to the top of the queue, the greatest
238100203Sgad		 * number of jobs which could be moved is all the jobs
239100203Sgad		 * that are in the queue.  Pick a starting value which
240100203Sgad		 * leaves plenty of room for all existing jobs.
241100203Sgad		 */
242100203Sgad		touch_info.newtime = queue[0]->job_time - nitems - 5;
243100203Sgad		break;
244100203Sgad	default:
245100203Sgad		printf("\ninternal error in topq/bottomq processing.\n");
246100203Sgad		return;
247100203Sgad	}
248100203Sgad
249100203Sgad	movecnt = process_jobs(argc, argv, touch_jqe, &touch_info);
250100203Sgad
251100203Sgad	/*
252100203Sgad	 * If any jobs were moved, then chmod the lock file to notify any
253100203Sgad	 * active process for this queue that the queue has changed, so
254100203Sgad	 * it will rescan the queue to find out the new job order.
255100203Sgad	 */
256100203Sgad	if (movecnt == 0)
257100203Sgad		printf("\tqueue order unchanged\n");
258100203Sgad	else {
259100203Sgad		setres = set_qstate(SQS_QCHANGED, pp->lock_file);
260100203Sgad		if (setres < 0)
261100203Sgad			printf("\t* queue order changed for %s, but the\n"
262100203Sgad			    "\t* attempt to set_qstate() failed [%d]!\n",
263100203Sgad			    pp->printer, setres);
264100203Sgad	}
265100203Sgad
266100203Sgad	for (i = 0; i < nitems; i++)
267100203Sgad		free(queue[i]);
268100203Sgad	free(queue);
269100203Sgad	free_printer(pp);
270100203Sgad}
271100203Sgad
272