]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/lpr/lpc/movejobs.c
MFC r326276:
[FreeBSD/FreeBSD.git] / usr.sbin / lpr / lpc / movejobs.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * ------+---------+---------+---------+---------+---------+---------+---------*
5  * Copyright (c) 2002   - Garance Alistair Drosehn <gad@FreeBSD.org>.
6  * 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  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * The views and conclusions contained in the software and documentation
30  * are those of the authors and should not be interpreted as representing
31  * official policies, either expressed or implied, of the FreeBSD Project
32  * or FreeBSD, Inc.
33  *
34  * ------+---------+---------+---------+---------+---------+---------+---------*
35  */
36
37 #include "lp.cdefs.h"           /* A cross-platform version of <sys/cdefs.h> */
38 __FBSDID("$FreeBSD$");
39
40 /*
41  * movejobs.c - The lpc commands which move jobs around.
42  */
43
44 #include <sys/file.h>
45 #include <sys/param.h>
46 #include <sys/queue.h>
47 #include <sys/time.h>
48 #include <sys/stat.h>
49
50 #include <ctype.h>
51 #include <dirent.h>     /* just for MAXNAMLEN, for job_cfname in lp.h! */
52 #include <err.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include "lp.h"
58 #include "lpc.h"
59 #include "matchjobs.h"
60 #include "extern.h"
61
62 /* Values for origcmd in tqbq_common() */
63 #define IS_TOPQ         1
64 #define IS_BOTQ         2
65
66 static int       process_jobs(int _argc, char *_argv[], process_jqe
67                     _process_rtn, void *myinfo);
68 static process_jqe touch_jqe;
69 static void      tqbq_common(int _argc, char *_argv[], int _origcmd);
70
71 /*
72  * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF.
73  * Define a wrapper which can take 'char', either signed or unsigned.
74  */
75 #define isdigitch(Anychar)    isdigit(((int) Anychar) & 255)
76
77 struct touchjqe_info {                  /* for topq/bottomq */
78         time_t   newtime;
79 };
80
81 static int       nitems;
82 static struct jobqueue **queue;
83
84 /*
85  * Process all the jobs, as specified by the user.
86  */
87 static int
88 process_jobs(int argc, char *argv[], process_jqe process_rtn, void *myinfo)
89 {
90         struct jobspec_hdr jobs_wanted;
91         int i, matchcnt, pjres;
92
93         STAILQ_INIT(&jobs_wanted);
94         for (i = 0; i < argc; i++) {
95                 pjres = parse_jobspec(argv[i], &jobs_wanted);
96                 if (pjres == 0) {
97                         printf("\tinvalid job specifier: %s\n", argv[i]);
98                         continue;
99                 }
100         }
101         matchcnt = scanq_jobspec(nitems, queue, SCQ_JSORDER, &jobs_wanted,
102             process_rtn, myinfo);
103
104         free_jobspec(&jobs_wanted);
105         return (matchcnt);
106 }
107
108 /*
109  * Reposition the job by changing the modification time of the
110  * control file.
111  */
112 static int
113 touch_jqe(void *myinfo, struct jobqueue *jq, struct jobspec *jspec)
114 {
115         struct timeval tvp[2];
116         struct touchjqe_info *touch_info;
117         int ret;
118
119         /*
120          * If the entire queue has been scanned for the current jobspec,
121          * then let the user know if there were no jobs matched by that
122          * specification.
123          */
124         if (jq == NULL) {
125                 if (jspec->matchcnt == 0) {
126                         format_jobspec(jspec, FMTJS_VERBOSE);
127                         if (jspec->pluralfmt)
128                                 printf("\tjobs %s are not in the queue\n",
129                                     jspec->fmtoutput);
130                         else
131                                 printf("\tjob %s is not in the queue\n",
132                                     jspec->fmtoutput);
133                 }
134                 return (1);
135         }
136
137         /*
138          * Do a little juggling with "matched" vs "processed", so a single
139          * job can be matched by multiple specifications, and yet it will
140          * be moved only once.  This is so, eg, 'topq lp 7 7' will not
141          * complain "job 7 is not in queue" for the second specification.
142          */
143         jq->job_matched = 0;
144         if (jq->job_processed) {
145                 printf("\tmoved %s earlier\n", jq->job_cfname);
146                 return (1);
147         }
148         jq->job_processed = 1;
149
150         touch_info = myinfo;
151         tvp[0].tv_sec = tvp[1].tv_sec = ++touch_info->newtime;
152         tvp[0].tv_usec = tvp[1].tv_usec = 0;
153         PRIV_START
154         ret = utimes(jq->job_cfname, tvp);
155         PRIV_END
156
157         if (ret == 0) {
158                 if (jspec->matcheduser)
159                         printf("\tmoved %s  (user %s)\n", jq->job_cfname,
160                             jspec->matcheduser);
161                 else
162                         printf("\tmoved %s\n", jq->job_cfname);
163         }
164         return (ret);
165 }
166
167 /*
168  * Put the specified jobs at the bottom of printer queue.
169  */
170 void
171 bottomq_cmd(int argc, char *argv[])
172 {
173
174         if (argc < 3) {
175                 printf("usage: bottomq printer [jobspec ...]\n");
176                 return;
177         }
178         --argc;                 /* First argv was the command name */
179         ++argv;
180
181         tqbq_common(argc, argv, IS_BOTQ);
182 }
183
184 /*
185  * Put the specified jobs at the top of printer queue.
186  */
187 void
188 topq_cmd(int argc, char *argv[])
189 {
190
191         if (argc < 3) {
192                 printf("usage: topq printer [jobspec ...]\n");
193                 return;
194         }
195         --argc;                 /* First argv was the command name */
196         ++argv;
197
198         tqbq_common(argc, argv, IS_TOPQ);
199
200
201 /*
202  * Processing in common between topq and bottomq commands.
203  */
204 void
205 tqbq_common(int argc, char *argv[], int origcmd)
206 {
207         struct printer myprinter, *pp;
208         struct touchjqe_info touch_info;
209         int i, movecnt, setres;
210
211         pp = setup_myprinter(*argv, &myprinter, SUMP_CHDIR_SD);
212         if (pp == NULL)
213                 return;
214         --argc;                 /* Second argv was the printer name */
215         ++argv;
216
217         nitems = getq(pp, &queue);
218         if (nitems == 0) {
219                 printf("\tthere are no jobs in the queue\n");
220                 free_printer(pp);
221                 return;
222         }
223
224         /*
225          * The only real difference between topq and bottomq is the
226          * initial value used for newtime.
227          */
228         switch (origcmd) {
229         case IS_BOTQ:
230                 /*
231                  * When moving jobs to the bottom of the queue, pick a
232                  * starting value which is one second after the last job
233                  * in the queue.
234                 */
235                 touch_info.newtime = queue[nitems - 1]->job_time + 1;
236                 break;
237         case IS_TOPQ:
238                 /*
239                  * When moving jobs to the top of the queue, the greatest
240                  * number of jobs which could be moved is all the jobs
241                  * that are in the queue.  Pick a starting value which
242                  * leaves plenty of room for all existing jobs.
243                  */
244                 touch_info.newtime = queue[0]->job_time - nitems - 5;
245                 break;
246         default:
247                 printf("\ninternal error in topq/bottomq processing.\n");
248                 return;
249         }
250
251         movecnt = process_jobs(argc, argv, touch_jqe, &touch_info);
252
253         /*
254          * If any jobs were moved, then chmod the lock file to notify any
255          * active process for this queue that the queue has changed, so
256          * it will rescan the queue to find out the new job order. 
257          */
258         if (movecnt == 0)
259                 printf("\tqueue order unchanged\n");
260         else {
261                 setres = set_qstate(SQS_QCHANGED, pp->lock_file);
262                 if (setres < 0)
263                         printf("\t* queue order changed for %s, but the\n"
264                             "\t* attempt to set_qstate() failed [%d]!\n",
265                             pp->printer, setres);
266         }
267
268         for (i = 0; i < nitems; i++)
269                 free(queue[i]);
270         free(queue);
271         free_printer(pp);
272
273