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