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