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