1100203Sgad/* 2100203Sgad * ------+---------+---------+---------+---------+---------+---------+---------* 3100203Sgad * Copyright (c) 2002 - Garance Alistair Drosehn <gad@FreeBSD.org>. 4100203Sgad * All rights reserved. 5100203Sgad * 6100203Sgad * Redistribution and use in source and binary forms, with or without 7100203Sgad * modification, are permitted provided that the following conditions 8100203Sgad * are met: 9100203Sgad * 1. Redistributions of source code must retain the above copyright 10100203Sgad * notice, this list of conditions and the following disclaimer. 11100203Sgad * 2. Redistributions in binary form must reproduce the above copyright 12100203Sgad * notice, this list of conditions and the following disclaimer in the 13100203Sgad * documentation and/or other materials provided with the distribution. 14100203Sgad * 15100203Sgad * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16100203Sgad * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17100203Sgad * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18100203Sgad * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19100203Sgad * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20100203Sgad * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21100203Sgad * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22100203Sgad * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23100203Sgad * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24100203Sgad * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25100203Sgad * SUCH DAMAGE. 26100203Sgad * 27100203Sgad * The views and conclusions contained in the software and documentation 28100203Sgad * are those of the authors and should not be interpreted as representing 29100203Sgad * official policies, either expressed or implied, of the FreeBSD Project 30100203Sgad * or FreeBSD, Inc. 31100203Sgad * 32100203Sgad * ------+---------+---------+---------+---------+---------+---------+---------* 33100203Sgad */ 34100203Sgad 35117599Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 36117599Sgad__FBSDID("$FreeBSD$"); 37100203Sgad 38100203Sgad/* 39100203Sgad * movejobs.c - The lpc commands which move jobs around. 40100203Sgad */ 41100203Sgad 42100203Sgad#include <sys/file.h> 43100203Sgad#include <sys/param.h> 44100203Sgad#include <sys/queue.h> 45100203Sgad#include <sys/time.h> 46100203Sgad#include <sys/stat.h> 47100203Sgad 48100203Sgad#include <ctype.h> 49100203Sgad#include <dirent.h> /* just for MAXNAMLEN, for job_cfname in lp.h! */ 50241852Seadler#include <err.h> 51100203Sgad#include <stdio.h> 52100203Sgad#include <stdlib.h> 53100203Sgad#include <string.h> 54100203Sgad#include <unistd.h> 55100203Sgad#include "lp.h" 56100203Sgad#include "lpc.h" 57100203Sgad#include "matchjobs.h" 58100203Sgad#include "extern.h" 59100203Sgad 60100203Sgad/* Values for origcmd in tqbq_common() */ 61100203Sgad#define IS_TOPQ 1 62100203Sgad#define IS_BOTQ 2 63100203Sgad 64100203Sgadstatic int process_jobs(int _argc, char *_argv[], process_jqe 65100203Sgad _process_rtn, void *myinfo); 66100203Sgadstatic process_jqe touch_jqe; 67100203Sgadstatic void tqbq_common(int _argc, char *_argv[], int _origcmd); 68100203Sgad 69100203Sgad/* 70100203Sgad * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF. 71100203Sgad * Define a wrapper which can take 'char', either signed or unsigned. 72100203Sgad */ 73100203Sgad#define isdigitch(Anychar) isdigit(((int) Anychar) & 255) 74100203Sgad 75100203Sgadstruct touchjqe_info { /* for topq/bottomq */ 76100203Sgad time_t newtime; 77100203Sgad}; 78100203Sgad 79100203Sgadstatic int nitems; 80100203Sgadstatic struct jobqueue **queue; 81100203Sgad 82100203Sgad/* 83100203Sgad * Process all the jobs, as specified by the user. 84100203Sgad */ 85100203Sgadstatic int 86100203Sgadprocess_jobs(int argc, char *argv[], process_jqe process_rtn, void *myinfo) 87100203Sgad{ 88100203Sgad struct jobspec_hdr jobs_wanted; 89100203Sgad int i, matchcnt, pjres; 90100203Sgad 91100203Sgad STAILQ_INIT(&jobs_wanted); 92100203Sgad for (i = 0; i < argc; i++) { 93100203Sgad pjres = parse_jobspec(argv[i], &jobs_wanted); 94100203Sgad if (pjres == 0) { 95100203Sgad printf("\tinvalid job specifier: %s\n", argv[i]); 96100203Sgad continue; 97100203Sgad } 98100203Sgad } 99100203Sgad matchcnt = scanq_jobspec(nitems, queue, SCQ_JSORDER, &jobs_wanted, 100100203Sgad process_rtn, myinfo); 101100203Sgad 102100203Sgad free_jobspec(&jobs_wanted); 103100203Sgad return (matchcnt); 104100203Sgad} 105100203Sgad 106100203Sgad/* 107100203Sgad * Reposition the job by changing the modification time of the 108100203Sgad * control file. 109100203Sgad */ 110100203Sgadstatic int 111100203Sgadtouch_jqe(void *myinfo, struct jobqueue *jq, struct jobspec *jspec) 112100203Sgad{ 113100203Sgad struct timeval tvp[2]; 114100203Sgad struct touchjqe_info *touch_info; 115100203Sgad int ret; 116100203Sgad 117100203Sgad /* 118100203Sgad * If the entire queue has been scanned for the current jobspec, 119100203Sgad * then let the user know if there were no jobs matched by that 120100203Sgad * specification. 121100203Sgad */ 122100203Sgad if (jq == NULL) { 123100203Sgad if (jspec->matchcnt == 0) { 124100203Sgad format_jobspec(jspec, FMTJS_VERBOSE); 125100203Sgad if (jspec->pluralfmt) 126100203Sgad printf("\tjobs %s are not in the queue\n", 127100203Sgad jspec->fmtoutput); 128100203Sgad else 129100203Sgad printf("\tjob %s is not in the queue\n", 130100203Sgad jspec->fmtoutput); 131100203Sgad } 132100203Sgad return (1); 133100203Sgad } 134100203Sgad 135100203Sgad /* 136100203Sgad * Do a little juggling with "matched" vs "processed", so a single 137100203Sgad * job can be matched by multiple specifications, and yet it will 138100203Sgad * be moved only once. This is so, eg, 'topq lp 7 7' will not 139100203Sgad * complain "job 7 is not in queue" for the second specification. 140100203Sgad */ 141100203Sgad jq->job_matched = 0; 142100203Sgad if (jq->job_processed) { 143100203Sgad printf("\tmoved %s earlier\n", jq->job_cfname); 144100203Sgad return (1); 145100203Sgad } 146100203Sgad jq->job_processed = 1; 147100203Sgad 148100203Sgad touch_info = myinfo; 149100203Sgad tvp[0].tv_sec = tvp[1].tv_sec = ++touch_info->newtime; 150100203Sgad tvp[0].tv_usec = tvp[1].tv_usec = 0; 151241852Seadler PRIV_START 152100203Sgad ret = utimes(jq->job_cfname, tvp); 153241852Seadler PRIV_END 154100203Sgad 155100203Sgad if (ret == 0) { 156100203Sgad if (jspec->matcheduser) 157100203Sgad printf("\tmoved %s (user %s)\n", jq->job_cfname, 158100203Sgad jspec->matcheduser); 159100203Sgad else 160100203Sgad printf("\tmoved %s\n", jq->job_cfname); 161100203Sgad } 162100203Sgad return (ret); 163100203Sgad} 164100203Sgad 165100203Sgad/* 166100203Sgad * Put the specified jobs at the bottom of printer queue. 167100203Sgad */ 168100203Sgadvoid 169100203Sgadbottomq_cmd(int argc, char *argv[]) 170100203Sgad{ 171100203Sgad 172100203Sgad if (argc < 3) { 173100203Sgad printf("usage: bottomq printer [jobspec ...]\n"); 174100203Sgad return; 175100203Sgad } 176100203Sgad --argc; /* First argv was the command name */ 177100203Sgad ++argv; 178100203Sgad 179100203Sgad tqbq_common(argc, argv, IS_BOTQ); 180100203Sgad} 181100203Sgad 182100203Sgad/* 183100203Sgad * Put the specified jobs at the top of printer queue. 184100203Sgad */ 185100203Sgadvoid 186100203Sgadtopq_cmd(int argc, char *argv[]) 187100203Sgad{ 188100203Sgad 189100203Sgad if (argc < 3) { 190100203Sgad printf("usage: topq printer [jobspec ...]\n"); 191100203Sgad return; 192100203Sgad } 193100203Sgad --argc; /* First argv was the command name */ 194100203Sgad ++argv; 195100203Sgad 196100203Sgad tqbq_common(argc, argv, IS_TOPQ); 197100203Sgad} 198100203Sgad 199100203Sgad/* 200100203Sgad * Processing in common between topq and bottomq commands. 201100203Sgad */ 202100203Sgadvoid 203100203Sgadtqbq_common(int argc, char *argv[], int origcmd) 204100203Sgad{ 205100203Sgad struct printer myprinter, *pp; 206100203Sgad struct touchjqe_info touch_info; 207100203Sgad int i, movecnt, setres; 208100203Sgad 209100203Sgad pp = setup_myprinter(*argv, &myprinter, SUMP_CHDIR_SD); 210100203Sgad if (pp == NULL) 211100203Sgad return; 212100203Sgad --argc; /* Second argv was the printer name */ 213100203Sgad ++argv; 214100203Sgad 215100203Sgad nitems = getq(pp, &queue); 216100203Sgad if (nitems == 0) { 217100203Sgad printf("\tthere are no jobs in the queue\n"); 218100203Sgad free_printer(pp); 219100203Sgad return; 220100203Sgad } 221100203Sgad 222100203Sgad /* 223100203Sgad * The only real difference between topq and bottomq is the 224100203Sgad * initial value used for newtime. 225100203Sgad */ 226100203Sgad switch (origcmd) { 227100203Sgad case IS_BOTQ: 228100203Sgad /* 229100203Sgad * When moving jobs to the bottom of the queue, pick a 230100203Sgad * starting value which is one second after the last job 231100203Sgad * in the queue. 232100203Sgad */ 233100203Sgad touch_info.newtime = queue[nitems - 1]->job_time + 1; 234100203Sgad break; 235100203Sgad case IS_TOPQ: 236100203Sgad /* 237100203Sgad * When moving jobs to the top of the queue, the greatest 238100203Sgad * number of jobs which could be moved is all the jobs 239100203Sgad * that are in the queue. Pick a starting value which 240100203Sgad * leaves plenty of room for all existing jobs. 241100203Sgad */ 242100203Sgad touch_info.newtime = queue[0]->job_time - nitems - 5; 243100203Sgad break; 244100203Sgad default: 245100203Sgad printf("\ninternal error in topq/bottomq processing.\n"); 246100203Sgad return; 247100203Sgad } 248100203Sgad 249100203Sgad movecnt = process_jobs(argc, argv, touch_jqe, &touch_info); 250100203Sgad 251100203Sgad /* 252100203Sgad * If any jobs were moved, then chmod the lock file to notify any 253100203Sgad * active process for this queue that the queue has changed, so 254100203Sgad * it will rescan the queue to find out the new job order. 255100203Sgad */ 256100203Sgad if (movecnt == 0) 257100203Sgad printf("\tqueue order unchanged\n"); 258100203Sgad else { 259100203Sgad setres = set_qstate(SQS_QCHANGED, pp->lock_file); 260100203Sgad if (setres < 0) 261100203Sgad printf("\t* queue order changed for %s, but the\n" 262100203Sgad "\t* attempt to set_qstate() failed [%d]!\n", 263100203Sgad pp->printer, setres); 264100203Sgad } 265100203Sgad 266100203Sgad for (i = 0; i < nitems; i++) 267100203Sgad free(queue[i]); 268100203Sgad free(queue); 269100203Sgad free_printer(pp); 270100203Sgad} 271100203Sgad 272