1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 274