1dd8faa9fSGarance A Drosehn /* 2dd8faa9fSGarance A Drosehn * ------+---------+---------+---------+---------+---------+---------+---------* 3*ad1f7851SGarance A Drosehn * Copyright (c) 2002,2011 - Garance Alistair Drosehn <gad@FreeBSD.org>. 4dd8faa9fSGarance A Drosehn * All rights reserved. 5dd8faa9fSGarance A Drosehn * 6dd8faa9fSGarance A Drosehn * Redistribution and use in source and binary forms, with or without 7dd8faa9fSGarance A Drosehn * modification, are permitted provided that the following conditions 8dd8faa9fSGarance A Drosehn * are met: 9dd8faa9fSGarance A Drosehn * 1. Redistributions of source code must retain the above copyright 10dd8faa9fSGarance A Drosehn * notice, this list of conditions and the following disclaimer. 11dd8faa9fSGarance A Drosehn * 2. Redistributions in binary form must reproduce the above copyright 12dd8faa9fSGarance A Drosehn * notice, this list of conditions and the following disclaimer in the 13dd8faa9fSGarance A Drosehn * documentation and/or other materials provided with the distribution. 14dd8faa9fSGarance A Drosehn * 15dd8faa9fSGarance A Drosehn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16dd8faa9fSGarance A Drosehn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17dd8faa9fSGarance A Drosehn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18dd8faa9fSGarance A Drosehn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19dd8faa9fSGarance A Drosehn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20dd8faa9fSGarance A Drosehn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21dd8faa9fSGarance A Drosehn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22dd8faa9fSGarance A Drosehn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23dd8faa9fSGarance A Drosehn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24dd8faa9fSGarance A Drosehn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25dd8faa9fSGarance A Drosehn * SUCH DAMAGE. 26dd8faa9fSGarance A Drosehn * 27dd8faa9fSGarance A Drosehn * The views and conclusions contained in the software and documentation 28dd8faa9fSGarance A Drosehn * are those of the authors and should not be interpreted as representing 29dd8faa9fSGarance A Drosehn * official policies, either expressed or implied, of the FreeBSD Project 30dd8faa9fSGarance A Drosehn * or FreeBSD, Inc. 31dd8faa9fSGarance A Drosehn * 32dd8faa9fSGarance A Drosehn * ------+---------+---------+---------+---------+---------+---------+---------* 33dd8faa9fSGarance A Drosehn */ 34dd8faa9fSGarance A Drosehn 351f589b47SGarance A Drosehn #include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 361f589b47SGarance A Drosehn __FBSDID("$FreeBSD$"); 37dd8faa9fSGarance A Drosehn 38dd8faa9fSGarance A Drosehn /* 39dd8faa9fSGarance A Drosehn * movejobs.c - The lpc commands which move jobs around. 40dd8faa9fSGarance A Drosehn */ 41dd8faa9fSGarance A Drosehn 42dd8faa9fSGarance A Drosehn #include <sys/file.h> 43dd8faa9fSGarance A Drosehn #include <sys/param.h> 44dd8faa9fSGarance A Drosehn #include <sys/queue.h> 45dd8faa9fSGarance A Drosehn #include <sys/time.h> 46dd8faa9fSGarance A Drosehn 47dd8faa9fSGarance A Drosehn #include <dirent.h> /* for MAXNAMLEN, for job_cfname in lp.h! */ 48dd8faa9fSGarance A Drosehn #include <ctype.h> 49dd8faa9fSGarance A Drosehn #include <errno.h> 50dd8faa9fSGarance A Drosehn #include <fnmatch.h> 51dd8faa9fSGarance A Drosehn #include <stdio.h> 52dd8faa9fSGarance A Drosehn #include <stdlib.h> 53dd8faa9fSGarance A Drosehn #include <string.h> 54dd8faa9fSGarance A Drosehn #include <unistd.h> 55dd8faa9fSGarance A Drosehn #include "ctlinfo.h" 56dd8faa9fSGarance A Drosehn #include "lp.h" 57dd8faa9fSGarance A Drosehn #include "matchjobs.h" 58dd8faa9fSGarance A Drosehn 59dd8faa9fSGarance A Drosehn #define DEBUG_PARSEJS 0 /* set to 1 when testing */ 60dd8faa9fSGarance A Drosehn #define DEBUG_SCANJS 0 /* set to 1 when testing */ 61dd8faa9fSGarance A Drosehn 62dd8faa9fSGarance A Drosehn static int match_jobspec(struct jobqueue *_jq, struct jobspec *_jspec); 63dd8faa9fSGarance A Drosehn 64dd8faa9fSGarance A Drosehn /* 65dd8faa9fSGarance A Drosehn * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF. 66dd8faa9fSGarance A Drosehn * Define a wrapper which can take 'char', either signed or unsigned. 67dd8faa9fSGarance A Drosehn */ 68dd8faa9fSGarance A Drosehn #define isdigitch(Anychar) isdigit(((int) Anychar) & 255) 69dd8faa9fSGarance A Drosehn 70dd8faa9fSGarance A Drosehn /* 71dd8faa9fSGarance A Drosehn * Format a single jobspec into a string fit for printing. 72dd8faa9fSGarance A Drosehn */ 73dd8faa9fSGarance A Drosehn void 74dd8faa9fSGarance A Drosehn format_jobspec(struct jobspec *jspec, int fmt_wanted) 75dd8faa9fSGarance A Drosehn { 76dd8faa9fSGarance A Drosehn char rangestr[40], buildstr[200]; 77dd8faa9fSGarance A Drosehn const char fromuser[] = "from user "; 78dd8faa9fSGarance A Drosehn const char fromhost[] = "from host "; 79dd8faa9fSGarance A Drosehn size_t strsize; 80dd8faa9fSGarance A Drosehn 81dd8faa9fSGarance A Drosehn /* 82dd8faa9fSGarance A Drosehn * If the struct already has a fmtstring, then release it 83dd8faa9fSGarance A Drosehn * before building a new one. 84dd8faa9fSGarance A Drosehn */ 85dd8faa9fSGarance A Drosehn if (jspec->fmtoutput != NULL) { 86dd8faa9fSGarance A Drosehn free(jspec->fmtoutput); 87dd8faa9fSGarance A Drosehn jspec->fmtoutput = NULL; 88dd8faa9fSGarance A Drosehn } 89dd8faa9fSGarance A Drosehn 90dd8faa9fSGarance A Drosehn jspec->pluralfmt = 1; /* assume a "plural result" */ 91dd8faa9fSGarance A Drosehn rangestr[0] = '\0'; 92dd8faa9fSGarance A Drosehn if (jspec->startnum >= 0) { 93dd8faa9fSGarance A Drosehn if (jspec->startnum != jspec->endrange) 94dd8faa9fSGarance A Drosehn snprintf(rangestr, sizeof(rangestr), "%ld-%ld", 95dd8faa9fSGarance A Drosehn jspec->startnum, jspec->endrange); 96dd8faa9fSGarance A Drosehn else { 97dd8faa9fSGarance A Drosehn jspec->pluralfmt = 0; 98dd8faa9fSGarance A Drosehn snprintf(rangestr, sizeof(rangestr), "%ld", 99dd8faa9fSGarance A Drosehn jspec->startnum); 100dd8faa9fSGarance A Drosehn } 101dd8faa9fSGarance A Drosehn } 102dd8faa9fSGarance A Drosehn 103dd8faa9fSGarance A Drosehn strsize = sizeof(buildstr); 104dd8faa9fSGarance A Drosehn buildstr[0] = '\0'; 105dd8faa9fSGarance A Drosehn switch (fmt_wanted) { 106dd8faa9fSGarance A Drosehn case FMTJS_TERSE: 107dd8faa9fSGarance A Drosehn /* Build everything but the hostname in a temp string. */ 108dd8faa9fSGarance A Drosehn if (jspec->wanteduser != NULL) 109dd8faa9fSGarance A Drosehn strlcat(buildstr, jspec->wanteduser, strsize); 110dd8faa9fSGarance A Drosehn if (rangestr[0] != '\0') { 111dd8faa9fSGarance A Drosehn if (buildstr[0] != '\0') 112dd8faa9fSGarance A Drosehn strlcat(buildstr, ":", strsize); 113dd8faa9fSGarance A Drosehn strlcat(buildstr, rangestr, strsize); 114dd8faa9fSGarance A Drosehn } 115dd8faa9fSGarance A Drosehn if (jspec->wantedhost != NULL) 116dd8faa9fSGarance A Drosehn strlcat(buildstr, "@", strsize); 117dd8faa9fSGarance A Drosehn 118dd8faa9fSGarance A Drosehn /* Get space for the final result, including hostname */ 119dd8faa9fSGarance A Drosehn strsize = strlen(buildstr) + 1; 120dd8faa9fSGarance A Drosehn if (jspec->wantedhost != NULL) 121dd8faa9fSGarance A Drosehn strsize += strlen(jspec->wantedhost); 122dd8faa9fSGarance A Drosehn jspec->fmtoutput = malloc(strsize); 123dd8faa9fSGarance A Drosehn 124dd8faa9fSGarance A Drosehn /* Put together the final result */ 125dd8faa9fSGarance A Drosehn strlcpy(jspec->fmtoutput, buildstr, strsize); 126dd8faa9fSGarance A Drosehn if (jspec->wantedhost != NULL) 127dd8faa9fSGarance A Drosehn strlcat(jspec->fmtoutput, jspec->wantedhost, strsize); 128dd8faa9fSGarance A Drosehn break; 129dd8faa9fSGarance A Drosehn 130dd8faa9fSGarance A Drosehn case FMTJS_VERBOSE: 131dd8faa9fSGarance A Drosehn default: 132dd8faa9fSGarance A Drosehn /* Build everything but the hostname in a temp string. */ 133dd8faa9fSGarance A Drosehn strlcat(buildstr, rangestr, strsize); 134dd8faa9fSGarance A Drosehn if (jspec->wanteduser != NULL) { 135dd8faa9fSGarance A Drosehn if (rangestr[0] != '\0') 136dd8faa9fSGarance A Drosehn strlcat(buildstr, " ", strsize); 137dd8faa9fSGarance A Drosehn strlcat(buildstr, fromuser, strsize); 138dd8faa9fSGarance A Drosehn strlcat(buildstr, jspec->wanteduser, strsize); 139dd8faa9fSGarance A Drosehn } 140dd8faa9fSGarance A Drosehn if (jspec->wantedhost != NULL) { 141dd8faa9fSGarance A Drosehn if (jspec->wanteduser == NULL) { 142dd8faa9fSGarance A Drosehn if (rangestr[0] != '\0') 143dd8faa9fSGarance A Drosehn strlcat(buildstr, " ", strsize); 144dd8faa9fSGarance A Drosehn strlcat(buildstr, fromhost, strsize); 145dd8faa9fSGarance A Drosehn } else 146dd8faa9fSGarance A Drosehn strlcat(buildstr, "@", strsize); 147dd8faa9fSGarance A Drosehn } 148dd8faa9fSGarance A Drosehn 149dd8faa9fSGarance A Drosehn /* Get space for the final result, including hostname */ 150dd8faa9fSGarance A Drosehn strsize = strlen(buildstr) + 1; 151dd8faa9fSGarance A Drosehn if (jspec->wantedhost != NULL) 152dd8faa9fSGarance A Drosehn strsize += strlen(jspec->wantedhost); 153dd8faa9fSGarance A Drosehn jspec->fmtoutput = malloc(strsize); 154dd8faa9fSGarance A Drosehn 155dd8faa9fSGarance A Drosehn /* Put together the final result */ 156dd8faa9fSGarance A Drosehn strlcpy(jspec->fmtoutput, buildstr, strsize); 157dd8faa9fSGarance A Drosehn if (jspec->wantedhost != NULL) 158dd8faa9fSGarance A Drosehn strlcat(jspec->fmtoutput, jspec->wantedhost, strsize); 159dd8faa9fSGarance A Drosehn break; 160dd8faa9fSGarance A Drosehn } 161dd8faa9fSGarance A Drosehn } 162dd8faa9fSGarance A Drosehn 163dd8faa9fSGarance A Drosehn /* 164dd8faa9fSGarance A Drosehn * Free all the jobspec-related information. 165dd8faa9fSGarance A Drosehn */ 166dd8faa9fSGarance A Drosehn void 167dd8faa9fSGarance A Drosehn free_jobspec(struct jobspec_hdr *js_hdr) 168dd8faa9fSGarance A Drosehn { 169dd8faa9fSGarance A Drosehn struct jobspec *jsinf; 170dd8faa9fSGarance A Drosehn 171dd8faa9fSGarance A Drosehn while (!STAILQ_EMPTY(js_hdr)) { 172dd8faa9fSGarance A Drosehn jsinf = STAILQ_FIRST(js_hdr); 173dd8faa9fSGarance A Drosehn STAILQ_REMOVE_HEAD(js_hdr, nextjs); 174dd8faa9fSGarance A Drosehn if (jsinf->fmtoutput) 175dd8faa9fSGarance A Drosehn free(jsinf->fmtoutput); 176dd8faa9fSGarance A Drosehn if (jsinf->matcheduser) 177dd8faa9fSGarance A Drosehn free(jsinf->matcheduser); 178dd8faa9fSGarance A Drosehn free(jsinf); 179dd8faa9fSGarance A Drosehn } 180dd8faa9fSGarance A Drosehn } 181dd8faa9fSGarance A Drosehn 182dd8faa9fSGarance A Drosehn /* 183dd8faa9fSGarance A Drosehn * This routine takes a string as typed in from the user, and parses it 184dd8faa9fSGarance A Drosehn * into a job-specification. A job specification would match one or more 185dd8faa9fSGarance A Drosehn * jobs in the queue of some single printer (the specification itself does 186dd8faa9fSGarance A Drosehn * not indicate which queue should be searched). 187dd8faa9fSGarance A Drosehn * 188dd8faa9fSGarance A Drosehn * This recognizes a job-number range by itself (all digits, or a range 189dd8faa9fSGarance A Drosehn * indicated by "digits-digits"), or a userid by itself. If a `:' is 190dd8faa9fSGarance A Drosehn * found, it is treated as a separator between a job-number range and 191dd8faa9fSGarance A Drosehn * a userid, where the job number range is the side which has a digit as 192dd8faa9fSGarance A Drosehn * the first character. If an `@' is found, everything to the right of 193dd8faa9fSGarance A Drosehn * it is treated as the hostname the job originated from. 194dd8faa9fSGarance A Drosehn * 195dd8faa9fSGarance A Drosehn * So, the user can specify: 196dd8faa9fSGarance A Drosehn * jobrange userid userid:jobrange jobrange:userid 197dd8faa9fSGarance A Drosehn * jobrange@hostname jobrange:userid@hostname 198dd8faa9fSGarance A Drosehn * userid@hostname userid:jobrange@hostname 199dd8faa9fSGarance A Drosehn * 200dd8faa9fSGarance A Drosehn * XXX - it would be nice to add "not options" too, such as ^user, 201dd8faa9fSGarance A Drosehn * ^jobrange, and @^hostname. 202dd8faa9fSGarance A Drosehn * 203dd8faa9fSGarance A Drosehn * This routine may modify the original input string if that input is 204dd8faa9fSGarance A Drosehn * valid. If the input was *not* valid, then this routine should return 205dd8faa9fSGarance A Drosehn * with the input string the same as when the routine was called. 206dd8faa9fSGarance A Drosehn */ 207dd8faa9fSGarance A Drosehn int 208dd8faa9fSGarance A Drosehn parse_jobspec(char *jobstr, struct jobspec_hdr *js_hdr) 209dd8faa9fSGarance A Drosehn { 210dd8faa9fSGarance A Drosehn struct jobspec *jsinfo; 211dd8faa9fSGarance A Drosehn char *atsign, *colon, *lhside, *numstr, *period, *rhside; 212dd8faa9fSGarance A Drosehn int jobnum; 213dd8faa9fSGarance A Drosehn 214dd8faa9fSGarance A Drosehn #if DEBUG_PARSEJS 215dd8faa9fSGarance A Drosehn printf("\t [ pjs-input = %s ]\n", jobstr); 216dd8faa9fSGarance A Drosehn #endif 217dd8faa9fSGarance A Drosehn 218dd8faa9fSGarance A Drosehn if ((jobstr == NULL) || (*jobstr == '\0')) 219dd8faa9fSGarance A Drosehn return (0); 220dd8faa9fSGarance A Drosehn 221dd8faa9fSGarance A Drosehn jsinfo = malloc(sizeof(struct jobspec)); 222dd8faa9fSGarance A Drosehn memset(jsinfo, 0, sizeof(struct jobspec)); 223dd8faa9fSGarance A Drosehn jsinfo->startnum = jsinfo->endrange = -1; 224dd8faa9fSGarance A Drosehn 225dd8faa9fSGarance A Drosehn /* Find the separator characters, and nullify them. */ 226dd8faa9fSGarance A Drosehn numstr = NULL; 227dd8faa9fSGarance A Drosehn atsign = strchr(jobstr, '@'); 228dd8faa9fSGarance A Drosehn colon = strchr(jobstr, ':'); 229dd8faa9fSGarance A Drosehn if (atsign != NULL) 230dd8faa9fSGarance A Drosehn *atsign = '\0'; 231dd8faa9fSGarance A Drosehn if (colon != NULL) 232dd8faa9fSGarance A Drosehn *colon = '\0'; 233dd8faa9fSGarance A Drosehn 234dd8faa9fSGarance A Drosehn /* The at-sign always indicates a hostname. */ 235dd8faa9fSGarance A Drosehn if (atsign != NULL) { 236dd8faa9fSGarance A Drosehn rhside = atsign + 1; 237dd8faa9fSGarance A Drosehn if (*rhside != '\0') 238dd8faa9fSGarance A Drosehn jsinfo->wantedhost = rhside; 239dd8faa9fSGarance A Drosehn } 240dd8faa9fSGarance A Drosehn 241dd8faa9fSGarance A Drosehn /* Finish splitting the input into three parts. */ 242dd8faa9fSGarance A Drosehn rhside = NULL; 243dd8faa9fSGarance A Drosehn if (colon != NULL) { 244dd8faa9fSGarance A Drosehn rhside = colon + 1; 245dd8faa9fSGarance A Drosehn if (*rhside == '\0') 246dd8faa9fSGarance A Drosehn rhside = NULL; 247dd8faa9fSGarance A Drosehn } 248dd8faa9fSGarance A Drosehn lhside = NULL; 249dd8faa9fSGarance A Drosehn if (*jobstr != '\0') 250dd8faa9fSGarance A Drosehn lhside = jobstr; 251dd8faa9fSGarance A Drosehn 252dd8faa9fSGarance A Drosehn /* 253dd8faa9fSGarance A Drosehn * If there is a `:' here, then it's either jobrange:userid, 254dd8faa9fSGarance A Drosehn * userid:jobrange, or (if @hostname was not given) perhaps it 255dd8faa9fSGarance A Drosehn * might be hostname:jobnum. The side which has a digit as the 256dd8faa9fSGarance A Drosehn * first character is assumed to be the jobrange. It is an 257dd8faa9fSGarance A Drosehn * input error if both sides start with a digit, or if neither 258dd8faa9fSGarance A Drosehn * side starts with a digit. 259dd8faa9fSGarance A Drosehn */ 260dd8faa9fSGarance A Drosehn if ((lhside != NULL) && (rhside != NULL)) { 261dd8faa9fSGarance A Drosehn if (isdigitch(*lhside)) { 262dd8faa9fSGarance A Drosehn if (isdigitch(*rhside)) 263dd8faa9fSGarance A Drosehn goto bad_input; 264dd8faa9fSGarance A Drosehn numstr = lhside; 265dd8faa9fSGarance A Drosehn jsinfo->wanteduser = rhside; 266dd8faa9fSGarance A Drosehn } else if (isdigitch(*rhside)) { 267dd8faa9fSGarance A Drosehn numstr = rhside; 268dd8faa9fSGarance A Drosehn /* 269dd8faa9fSGarance A Drosehn * The original implementation of 'lpc topq' accepted 270dd8faa9fSGarance A Drosehn * hostname:jobnum. If the input did not include a 271dd8faa9fSGarance A Drosehn * @hostname, then assume the userid is a hostname if 272dd8faa9fSGarance A Drosehn * it includes a '.'. 273dd8faa9fSGarance A Drosehn */ 274dd8faa9fSGarance A Drosehn period = strchr(lhside, '.'); 275dd8faa9fSGarance A Drosehn if ((atsign == NULL) && (period != NULL)) 276dd8faa9fSGarance A Drosehn jsinfo->wantedhost = lhside; 277dd8faa9fSGarance A Drosehn else 278dd8faa9fSGarance A Drosehn jsinfo->wanteduser = lhside; 279dd8faa9fSGarance A Drosehn } else { 280dd8faa9fSGarance A Drosehn /* Neither side is a job number = user error */ 281dd8faa9fSGarance A Drosehn goto bad_input; 282dd8faa9fSGarance A Drosehn } 283dd8faa9fSGarance A Drosehn } else if (lhside != NULL) { 284dd8faa9fSGarance A Drosehn if (isdigitch(*lhside)) 285dd8faa9fSGarance A Drosehn numstr = lhside; 286dd8faa9fSGarance A Drosehn else 287dd8faa9fSGarance A Drosehn jsinfo->wanteduser = lhside; 288dd8faa9fSGarance A Drosehn } else if (rhside != NULL) { 289dd8faa9fSGarance A Drosehn if (isdigitch(*rhside)) 290dd8faa9fSGarance A Drosehn numstr = rhside; 291dd8faa9fSGarance A Drosehn else 292dd8faa9fSGarance A Drosehn jsinfo->wanteduser = rhside; 293dd8faa9fSGarance A Drosehn } 294dd8faa9fSGarance A Drosehn 295dd8faa9fSGarance A Drosehn /* 296dd8faa9fSGarance A Drosehn * Break down the numstr. It should be all digits, or a range 297dd8faa9fSGarance A Drosehn * specified as "\d+-\d+". 298dd8faa9fSGarance A Drosehn */ 299dd8faa9fSGarance A Drosehn if (numstr != NULL) { 300dd8faa9fSGarance A Drosehn errno = 0; 301dd8faa9fSGarance A Drosehn jobnum = strtol(numstr, &numstr, 10); 302dd8faa9fSGarance A Drosehn if (errno != 0) /* error in conversion */ 303dd8faa9fSGarance A Drosehn goto bad_input; 304dd8faa9fSGarance A Drosehn if (jobnum < 0) /* a bogus value for this purpose */ 305dd8faa9fSGarance A Drosehn goto bad_input; 306dd8faa9fSGarance A Drosehn if (jobnum > 99999) /* too large for job number */ 307dd8faa9fSGarance A Drosehn goto bad_input; 308dd8faa9fSGarance A Drosehn jsinfo->startnum = jsinfo->endrange = jobnum; 309dd8faa9fSGarance A Drosehn 310dd8faa9fSGarance A Drosehn /* Check for a range of numbers */ 311dd8faa9fSGarance A Drosehn if ((*numstr == '-') && (isdigitch(*(numstr + 1)))) { 312dd8faa9fSGarance A Drosehn numstr++; 313dd8faa9fSGarance A Drosehn errno = 0; 314dd8faa9fSGarance A Drosehn jobnum = strtol(numstr, &numstr, 10); 315dd8faa9fSGarance A Drosehn if (errno != 0) /* error in conversion */ 316dd8faa9fSGarance A Drosehn goto bad_input; 317dd8faa9fSGarance A Drosehn if (jobnum < jsinfo->startnum) 318dd8faa9fSGarance A Drosehn goto bad_input; 319dd8faa9fSGarance A Drosehn if (jobnum > 99999) /* too large for job number */ 320dd8faa9fSGarance A Drosehn goto bad_input; 321dd8faa9fSGarance A Drosehn jsinfo->endrange = jobnum; 322dd8faa9fSGarance A Drosehn } 323dd8faa9fSGarance A Drosehn 324dd8faa9fSGarance A Drosehn /* 325dd8faa9fSGarance A Drosehn * If there is anything left in the numstr, and if the 326dd8faa9fSGarance A Drosehn * original string did not include a userid or a hostname, 327dd8faa9fSGarance A Drosehn * then this might be the ancient form of '\d+hostname' 3282b239dd1SJens Schweikhardt * (with no separator between jobnum and hostname). Accept 329dd8faa9fSGarance A Drosehn * that for backwards compatibility, but otherwise any 330dd8faa9fSGarance A Drosehn * remaining characters mean a user-error. Note that the 331dd8faa9fSGarance A Drosehn * ancient form accepted only a single number, but this 332dd8faa9fSGarance A Drosehn * will also accept a range of numbers. 333dd8faa9fSGarance A Drosehn */ 334dd8faa9fSGarance A Drosehn if (*numstr != '\0') { 335dd8faa9fSGarance A Drosehn if (atsign != NULL) 336dd8faa9fSGarance A Drosehn goto bad_input; 337dd8faa9fSGarance A Drosehn if (jsinfo->wantedhost != NULL) 338dd8faa9fSGarance A Drosehn goto bad_input; 339dd8faa9fSGarance A Drosehn if (jsinfo->wanteduser != NULL) 340dd8faa9fSGarance A Drosehn goto bad_input; 341dd8faa9fSGarance A Drosehn /* Treat as the rest of the string as a hostname */ 342dd8faa9fSGarance A Drosehn jsinfo->wantedhost = numstr; 343dd8faa9fSGarance A Drosehn } 344dd8faa9fSGarance A Drosehn } 345dd8faa9fSGarance A Drosehn 346dd8faa9fSGarance A Drosehn if ((jsinfo->startnum < 0) && (jsinfo->wanteduser == NULL) && 347dd8faa9fSGarance A Drosehn (jsinfo->wantedhost == NULL)) 348dd8faa9fSGarance A Drosehn goto bad_input; 349dd8faa9fSGarance A Drosehn 350dd8faa9fSGarance A Drosehn /* 351dd8faa9fSGarance A Drosehn * The input was valid, in the sense that it could be parsed 352dd8faa9fSGarance A Drosehn * into the individual parts. Add this jobspec to the list 353dd8faa9fSGarance A Drosehn * of jobspecs. 354dd8faa9fSGarance A Drosehn */ 355dd8faa9fSGarance A Drosehn STAILQ_INSERT_TAIL(js_hdr, jsinfo, nextjs); 356dd8faa9fSGarance A Drosehn 357dd8faa9fSGarance A Drosehn #if DEBUG_PARSEJS 358dd8faa9fSGarance A Drosehn printf("\t [ will check for"); 359dd8faa9fSGarance A Drosehn if (jsinfo->startnum >= 0) { 360dd8faa9fSGarance A Drosehn if (jsinfo->startnum == jsinfo->endrange) 361dd8faa9fSGarance A Drosehn printf(" jobnum = %ld", jsinfo->startnum); 362dd8faa9fSGarance A Drosehn else 363dd8faa9fSGarance A Drosehn printf(" jobrange = %ld to %ld", jsinfo->startnum, 364dd8faa9fSGarance A Drosehn jsinfo->endrange); 365dd8faa9fSGarance A Drosehn } else { 366dd8faa9fSGarance A Drosehn printf(" jobs"); 367dd8faa9fSGarance A Drosehn } 368dd8faa9fSGarance A Drosehn if ((jsinfo->wanteduser != NULL) || (jsinfo->wantedhost != NULL)) { 369dd8faa9fSGarance A Drosehn printf(" from"); 370dd8faa9fSGarance A Drosehn if (jsinfo->wanteduser != NULL) 371dd8faa9fSGarance A Drosehn printf(" user = %s", jsinfo->wanteduser); 372dd8faa9fSGarance A Drosehn if (jsinfo->wantedhost != NULL) 373dd8faa9fSGarance A Drosehn printf(" host = %s", jsinfo->wantedhost); 374dd8faa9fSGarance A Drosehn } 375dd8faa9fSGarance A Drosehn printf("]\n"); 376dd8faa9fSGarance A Drosehn #endif 377dd8faa9fSGarance A Drosehn 378dd8faa9fSGarance A Drosehn return (1); 379dd8faa9fSGarance A Drosehn 380dd8faa9fSGarance A Drosehn bad_input: 381dd8faa9fSGarance A Drosehn /* 382dd8faa9fSGarance A Drosehn * Restore any `@' and `:', in case the calling routine wants to 383dd8faa9fSGarance A Drosehn * write an error message which includes the input string. 384dd8faa9fSGarance A Drosehn */ 385dd8faa9fSGarance A Drosehn if (atsign != NULL) 386dd8faa9fSGarance A Drosehn *atsign = '@'; 387dd8faa9fSGarance A Drosehn if (colon != NULL) 388dd8faa9fSGarance A Drosehn *colon = ':'; 389dd8faa9fSGarance A Drosehn if (jsinfo != NULL) 390dd8faa9fSGarance A Drosehn free(jsinfo); 391dd8faa9fSGarance A Drosehn return (0); 392dd8faa9fSGarance A Drosehn } 393dd8faa9fSGarance A Drosehn 394dd8faa9fSGarance A Drosehn /* 395dd8faa9fSGarance A Drosehn * Check to see if a given job (specified by a jobqueue entry) matches 396dd8faa9fSGarance A Drosehn * all of the specifications in a given jobspec. 397dd8faa9fSGarance A Drosehn * 398dd8faa9fSGarance A Drosehn * Returns 0 if no match, 1 if the job does match. 399dd8faa9fSGarance A Drosehn */ 400dd8faa9fSGarance A Drosehn static int 401dd8faa9fSGarance A Drosehn match_jobspec(struct jobqueue *jq, struct jobspec *jspec) 402dd8faa9fSGarance A Drosehn { 403dd8faa9fSGarance A Drosehn struct cjobinfo *cfinf; 404c547dbe8SGarance A Drosehn const char *cf_hoststr; 405dd8faa9fSGarance A Drosehn int jnum, match; 406dd8faa9fSGarance A Drosehn 407dd8faa9fSGarance A Drosehn #if DEBUG_SCANJS 408dd8faa9fSGarance A Drosehn printf("\t [ match-js checking %s ]\n", jq->job_cfname); 409dd8faa9fSGarance A Drosehn #endif 410dd8faa9fSGarance A Drosehn 411dd8faa9fSGarance A Drosehn if (jspec == NULL || jq == NULL) 412dd8faa9fSGarance A Drosehn return (0); 413dd8faa9fSGarance A Drosehn 414dd8faa9fSGarance A Drosehn /* 415dd8faa9fSGarance A Drosehn * Keep track of which jobs have already been matched by this 416dd8faa9fSGarance A Drosehn * routine, and thus (probably) already processed. 417dd8faa9fSGarance A Drosehn */ 418dd8faa9fSGarance A Drosehn if (jq->job_matched) 419dd8faa9fSGarance A Drosehn return (0); 420dd8faa9fSGarance A Drosehn 421c547dbe8SGarance A Drosehn jnum = calc_jobnum(jq->job_cfname, &cf_hoststr); 422dd8faa9fSGarance A Drosehn cfinf = NULL; 423dd8faa9fSGarance A Drosehn match = 0; /* assume the job will not match */ 424dd8faa9fSGarance A Drosehn jspec->matcheduser = NULL; 425dd8faa9fSGarance A Drosehn 426dd8faa9fSGarance A Drosehn /* 427dd8faa9fSGarance A Drosehn * Check the job-number range. 428dd8faa9fSGarance A Drosehn */ 429dd8faa9fSGarance A Drosehn if (jspec->startnum >= 0) { 430dd8faa9fSGarance A Drosehn if (jnum < jspec->startnum) 431dd8faa9fSGarance A Drosehn goto nomatch; 432dd8faa9fSGarance A Drosehn if (jnum > jspec->endrange) 433dd8faa9fSGarance A Drosehn goto nomatch; 434dd8faa9fSGarance A Drosehn } 435dd8faa9fSGarance A Drosehn 436dd8faa9fSGarance A Drosehn /* 437dd8faa9fSGarance A Drosehn * Check the hostname. Strictly speaking this should be done by 438dd8faa9fSGarance A Drosehn * reading the control file, but it is less expensive to check 439dd8faa9fSGarance A Drosehn * the hostname-part of the control file name. Also, this value 440dd8faa9fSGarance A Drosehn * can be easily seen in 'lpq -l', while there is no easy way for 441dd8faa9fSGarance A Drosehn * a user/operator to see the hostname in the control file. 442dd8faa9fSGarance A Drosehn */ 443dd8faa9fSGarance A Drosehn if (jspec->wantedhost != NULL) { 444dd8faa9fSGarance A Drosehn if (fnmatch(jspec->wantedhost, cf_hoststr, 0) != 0) 445dd8faa9fSGarance A Drosehn goto nomatch; 446dd8faa9fSGarance A Drosehn } 447dd8faa9fSGarance A Drosehn 448dd8faa9fSGarance A Drosehn /* 449dd8faa9fSGarance A Drosehn * Check for a match on the user name. This has to be done 450dd8faa9fSGarance A Drosehn * by reading the control file. 451dd8faa9fSGarance A Drosehn */ 452dd8faa9fSGarance A Drosehn if (jspec->wanteduser != NULL) { 453dd8faa9fSGarance A Drosehn cfinf = ctl_readcf("fakeq", jq->job_cfname); 454dd8faa9fSGarance A Drosehn if (cfinf == NULL) 455dd8faa9fSGarance A Drosehn goto nomatch; 456*ad1f7851SGarance A Drosehn if (fnmatch(jspec->wanteduser, cfinf->cji_acctuser, 0) != 0) 457dd8faa9fSGarance A Drosehn goto nomatch; 458dd8faa9fSGarance A Drosehn } 459dd8faa9fSGarance A Drosehn 460dd8faa9fSGarance A Drosehn /* This job matches all of the specified criteria. */ 461dd8faa9fSGarance A Drosehn match = 1; 462dd8faa9fSGarance A Drosehn jq->job_matched = 1; /* avoid matching the job twice */ 463dd8faa9fSGarance A Drosehn jspec->matchcnt++; 464dd8faa9fSGarance A Drosehn if (jspec->wanteduser != NULL) { 465dd8faa9fSGarance A Drosehn /* 466dd8faa9fSGarance A Drosehn * If the user specified a userid (which may have been a 467dd8faa9fSGarance A Drosehn * pattern), then the caller's "doentry()" routine might 468dd8faa9fSGarance A Drosehn * want to know the userid of this job that matched. 469dd8faa9fSGarance A Drosehn */ 470*ad1f7851SGarance A Drosehn jspec->matcheduser = strdup(cfinf->cji_acctuser); 471dd8faa9fSGarance A Drosehn } 472dd8faa9fSGarance A Drosehn #if DEBUG_SCANJS 473dd8faa9fSGarance A Drosehn printf("\t [ job matched! ]\n"); 474dd8faa9fSGarance A Drosehn #endif 475dd8faa9fSGarance A Drosehn 476dd8faa9fSGarance A Drosehn nomatch: 477dd8faa9fSGarance A Drosehn if (cfinf != NULL) 478dd8faa9fSGarance A Drosehn ctl_freeinf(cfinf); 479dd8faa9fSGarance A Drosehn return (match); 480dd8faa9fSGarance A Drosehn } 481dd8faa9fSGarance A Drosehn 482dd8faa9fSGarance A Drosehn /* 483dd8faa9fSGarance A Drosehn * Scan a queue for all jobs which match a jobspec. The queue is scanned 484dd8faa9fSGarance A Drosehn * from top to bottom. 485dd8faa9fSGarance A Drosehn * 486dd8faa9fSGarance A Drosehn * The caller can provide a routine which will be executed for each job 487dd8faa9fSGarance A Drosehn * that does match. Note that the processing routine might do anything 488dd8faa9fSGarance A Drosehn * to the matched job -- including the removal of it. 489dd8faa9fSGarance A Drosehn * 490dd8faa9fSGarance A Drosehn * This returns the number of jobs which were matched. 491dd8faa9fSGarance A Drosehn */ 492dd8faa9fSGarance A Drosehn int 493dd8faa9fSGarance A Drosehn scanq_jobspec(int qcount, struct jobqueue **squeue, int sopts, struct 494dd8faa9fSGarance A Drosehn jobspec_hdr *js_hdr, process_jqe doentry, void *doentryinfo) 495dd8faa9fSGarance A Drosehn { 496dd8faa9fSGarance A Drosehn struct jobqueue **qent; 497dd8faa9fSGarance A Drosehn struct jobspec *jspec; 498dd8faa9fSGarance A Drosehn int cnt, matched, total; 499dd8faa9fSGarance A Drosehn 500dd8faa9fSGarance A Drosehn if (qcount < 1) 501dd8faa9fSGarance A Drosehn return (0); 502dd8faa9fSGarance A Drosehn if (js_hdr == NULL) 503dd8faa9fSGarance A Drosehn return (-1); 504dd8faa9fSGarance A Drosehn 505dd8faa9fSGarance A Drosehn /* The caller must specify one of the scanning orders */ 506dd8faa9fSGarance A Drosehn if ((sopts & (SCQ_JSORDER|SCQ_QORDER)) == 0) 507dd8faa9fSGarance A Drosehn return (-1); 508dd8faa9fSGarance A Drosehn 509dd8faa9fSGarance A Drosehn total = 0; 510dd8faa9fSGarance A Drosehn if (sopts & SCQ_JSORDER) { 511dd8faa9fSGarance A Drosehn /* 512dd8faa9fSGarance A Drosehn * For each job specification, scan through the queue 513dd8faa9fSGarance A Drosehn * looking for every job that matches. 514dd8faa9fSGarance A Drosehn */ 515dd8faa9fSGarance A Drosehn STAILQ_FOREACH(jspec, js_hdr, nextjs) { 516dd8faa9fSGarance A Drosehn for (qent = squeue, cnt = 0; cnt < qcount; 517dd8faa9fSGarance A Drosehn qent++, cnt++) { 518dd8faa9fSGarance A Drosehn matched = match_jobspec(*qent, jspec); 519dd8faa9fSGarance A Drosehn if (!matched) 520dd8faa9fSGarance A Drosehn continue; 521dd8faa9fSGarance A Drosehn total++; 522dd8faa9fSGarance A Drosehn if (doentry != NULL) 523dd8faa9fSGarance A Drosehn doentry(doentryinfo, *qent, jspec); 524dd8faa9fSGarance A Drosehn if (jspec->matcheduser != NULL) { 525dd8faa9fSGarance A Drosehn free(jspec->matcheduser); 526dd8faa9fSGarance A Drosehn jspec->matcheduser = NULL; 527dd8faa9fSGarance A Drosehn } 528dd8faa9fSGarance A Drosehn } 529dd8faa9fSGarance A Drosehn /* 530dd8faa9fSGarance A Drosehn * The entire queue has been scanned for this 531dd8faa9fSGarance A Drosehn * jobspec. Call the user's routine again with 532dd8faa9fSGarance A Drosehn * a NULL queue-entry, so it can print out any 533dd8faa9fSGarance A Drosehn * kind of per-jobspec summary. 534dd8faa9fSGarance A Drosehn */ 535dd8faa9fSGarance A Drosehn if (doentry != NULL) 536dd8faa9fSGarance A Drosehn doentry(doentryinfo, NULL, jspec); 537dd8faa9fSGarance A Drosehn } 538dd8faa9fSGarance A Drosehn } else { 539dd8faa9fSGarance A Drosehn /* 540dd8faa9fSGarance A Drosehn * For each job in the queue, check all of the job 541dd8faa9fSGarance A Drosehn * specifications to see if any one of them matches 542dd8faa9fSGarance A Drosehn * that job. 543dd8faa9fSGarance A Drosehn */ 544dd8faa9fSGarance A Drosehn for (qent = squeue, cnt = 0; cnt < qcount; 545dd8faa9fSGarance A Drosehn qent++, cnt++) { 546dd8faa9fSGarance A Drosehn STAILQ_FOREACH(jspec, js_hdr, nextjs) { 547dd8faa9fSGarance A Drosehn matched = match_jobspec(*qent, jspec); 548dd8faa9fSGarance A Drosehn if (!matched) 549dd8faa9fSGarance A Drosehn continue; 550dd8faa9fSGarance A Drosehn total++; 551dd8faa9fSGarance A Drosehn if (doentry != NULL) 552dd8faa9fSGarance A Drosehn doentry(doentryinfo, *qent, jspec); 553dd8faa9fSGarance A Drosehn if (jspec->matcheduser != NULL) { 554dd8faa9fSGarance A Drosehn free(jspec->matcheduser); 555dd8faa9fSGarance A Drosehn jspec->matcheduser = NULL; 556dd8faa9fSGarance A Drosehn } 557dd8faa9fSGarance A Drosehn /* 558dd8faa9fSGarance A Drosehn * Once there is a match, then there is no 559dd8faa9fSGarance A Drosehn * point in checking this same job against 560dd8faa9fSGarance A Drosehn * all the other jobspec's. 561dd8faa9fSGarance A Drosehn */ 562dd8faa9fSGarance A Drosehn break; 563dd8faa9fSGarance A Drosehn } 564dd8faa9fSGarance A Drosehn } 565dd8faa9fSGarance A Drosehn } 566dd8faa9fSGarance A Drosehn 567dd8faa9fSGarance A Drosehn return (total); 568dd8faa9fSGarance A Drosehn } 569