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 35 #ifndef lint 36 static const char rcsid[] = 37 "$FreeBSD: src/usr.sbin/lpr/lpc/movejobs.c,v 1.1.2.1 2002/07/26 03:12:07 gad Exp $"; 38 #endif /* not lint */ 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 <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 #include "lp.h" 57 #include "lpc.h" 58 #include "matchjobs.h" 59 #include "extern.h" 60 61 /* Values for origcmd in tqbq_common() */ 62 #define IS_TOPQ 1 63 #define IS_BOTQ 2 64 65 static int process_jobs(int _argc, char *_argv[], process_jqe 66 _process_rtn, void *myinfo); 67 static process_jqe touch_jqe; 68 static void tqbq_common(int _argc, char *_argv[], int _origcmd); 69 70 /* 71 * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF. 72 * Define a wrapper which can take 'char', either signed or unsigned. 73 */ 74 #define isdigitch(Anychar) isdigit(((int) Anychar) & 255) 75 76 struct touchjqe_info { /* for topq/bottomq */ 77 time_t newtime; 78 }; 79 80 static int nitems; 81 static struct jobqueue **queue; 82 83 /* 84 * Process all the jobs, as specified by the user. 85 */ 86 static int 87 process_jobs(int argc, char *argv[], process_jqe process_rtn, void *myinfo) 88 { 89 struct jobspec_hdr jobs_wanted; 90 int i, matchcnt, pjres; 91 92 STAILQ_INIT(&jobs_wanted); 93 for (i = 0; i < argc; i++) { 94 pjres = parse_jobspec(argv[i], &jobs_wanted); 95 if (pjres == 0) { 96 printf("\tinvalid job specifier: %s\n", argv[i]); 97 continue; 98 } 99 } 100 matchcnt = scanq_jobspec(nitems, queue, SCQ_JSORDER, &jobs_wanted, 101 process_rtn, myinfo); 102 103 free_jobspec(&jobs_wanted); 104 return (matchcnt); 105 } 106 107 /* 108 * Reposition the job by changing the modification time of the 109 * control file. 110 */ 111 static int 112 touch_jqe(void *myinfo, struct jobqueue *jq, struct jobspec *jspec) 113 { 114 struct timeval tvp[2]; 115 struct touchjqe_info *touch_info; 116 int ret; 117 118 /* 119 * If the entire queue has been scanned for the current jobspec, 120 * then let the user know if there were no jobs matched by that 121 * specification. 122 */ 123 if (jq == NULL) { 124 if (jspec->matchcnt == 0) { 125 format_jobspec(jspec, FMTJS_VERBOSE); 126 if (jspec->pluralfmt) 127 printf("\tjobs %s are not in the queue\n", 128 jspec->fmtoutput); 129 else 130 printf("\tjob %s is not in the queue\n", 131 jspec->fmtoutput); 132 } 133 return (1); 134 } 135 136 /* 137 * Do a little juggling with "matched" vs "processed", so a single 138 * job can be matched by multiple specifications, and yet it will 139 * be moved only once. This is so, eg, 'topq lp 7 7' will not 140 * complain "job 7 is not in queue" for the second specification. 141 */ 142 jq->job_matched = 0; 143 if (jq->job_processed) { 144 printf("\tmoved %s earlier\n", jq->job_cfname); 145 return (1); 146 } 147 jq->job_processed = 1; 148 149 touch_info = myinfo; 150 tvp[0].tv_sec = tvp[1].tv_sec = ++touch_info->newtime; 151 tvp[0].tv_usec = tvp[1].tv_usec = 0; 152 seteuid(euid); 153 ret = utimes(jq->job_cfname, tvp); 154 seteuid(uid); 155 156 if (ret == 0) { 157 if (jspec->matcheduser) 158 printf("\tmoved %s (user %s)\n", jq->job_cfname, 159 jspec->matcheduser); 160 else 161 printf("\tmoved %s\n", jq->job_cfname); 162 } 163 return (ret); 164 } 165 166 /* 167 * Put the specified jobs at the bottom of printer queue. 168 */ 169 void 170 bottomq_cmd(int argc, char *argv[]) 171 { 172 173 if (argc < 3) { 174 printf("usage: bottomq printer [jobspec ...]\n"); 175 return; 176 } 177 --argc; /* First argv was the command name */ 178 ++argv; 179 180 tqbq_common(argc, argv, IS_BOTQ); 181 } 182 183 /* 184 * Put the specified jobs at the top of printer queue. 185 */ 186 void 187 topq_cmd(int argc, char *argv[]) 188 { 189 190 if (argc < 3) { 191 printf("usage: topq printer [jobspec ...]\n"); 192 return; 193 } 194 --argc; /* First argv was the command name */ 195 ++argv; 196 197 tqbq_common(argc, argv, IS_TOPQ); 198 } 199 200 /* 201 * Processing in common between topq and bottomq commands. 202 */ 203 void 204 tqbq_common(int argc, char *argv[], int origcmd) 205 { 206 struct printer myprinter, *pp; 207 struct touchjqe_info touch_info; 208 int i, movecnt, setres; 209 210 pp = setup_myprinter(*argv, &myprinter, SUMP_CHDIR_SD); 211 if (pp == NULL) 212 return; 213 --argc; /* Second argv was the printer name */ 214 ++argv; 215 216 nitems = getq(pp, &queue); 217 if (nitems == 0) { 218 printf("\tthere are no jobs in the queue\n"); 219 free_printer(pp); 220 return; 221 } 222 223 /* 224 * The only real difference between topq and bottomq is the 225 * initial value used for newtime. 226 */ 227 switch (origcmd) { 228 case IS_BOTQ: 229 /* 230 * When moving jobs to the bottom of the queue, pick a 231 * starting value which is one second after the last job 232 * in the queue. 233 */ 234 touch_info.newtime = queue[nitems - 1]->job_time + 1; 235 break; 236 case IS_TOPQ: 237 /* 238 * When moving jobs to the top of the queue, the greatest 239 * number of jobs which could be moved is all the jobs 240 * that are in the queue. Pick a starting value which 241 * leaves plenty of room for all existing jobs. 242 */ 243 touch_info.newtime = queue[0]->job_time - nitems - 5; 244 break; 245 default: 246 printf("\ninternal error in topq/bottomq processing.\n"); 247 return; 248 } 249 250 movecnt = process_jobs(argc, argv, touch_jqe, &touch_info); 251 252 /* 253 * If any jobs were moved, then chmod the lock file to notify any 254 * active process for this queue that the queue has changed, so 255 * it will rescan the queue to find out the new job order. 256 */ 257 if (movecnt == 0) 258 printf("\tqueue order unchanged\n"); 259 else { 260 setres = set_qstate(SQS_QCHANGED, pp->lock_file); 261 if (setres < 0) 262 printf("\t* queue order changed for %s, but the\n" 263 "\t* attempt to set_qstate() failed [%d]!\n", 264 pp->printer, setres); 265 } 266 267 for (i = 0; i < nitems; i++) 268 free(queue[i]); 269 free(queue); 270 free_printer(pp); 271 } 272 273