matchjobs.c revision 117541
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 35117541Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 36117541Sgad__FBSDID("$FreeBSD: head/usr.sbin/lpr/common_source/matchjobs.c 117541 2003-07-14 05:15:21Z gad $"); 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 47100203Sgad#include <dirent.h> /* for MAXNAMLEN, for job_cfname in lp.h! */ 48100203Sgad#include <ctype.h> 49100203Sgad#include <errno.h> 50100203Sgad#include <fnmatch.h> 51100203Sgad#include <stdio.h> 52100203Sgad#include <stdlib.h> 53100203Sgad#include <string.h> 54100203Sgad#include <unistd.h> 55100203Sgad#include "ctlinfo.h" 56100203Sgad#include "lp.h" 57100203Sgad#include "matchjobs.h" 58100203Sgad 59100203Sgad#define DEBUG_PARSEJS 0 /* set to 1 when testing */ 60100203Sgad#define DEBUG_SCANJS 0 /* set to 1 when testing */ 61100203Sgad 62100203Sgadstatic int match_jobspec(struct jobqueue *_jq, struct jobspec *_jspec); 63100203Sgad 64100203Sgad/* 65100203Sgad * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF. 66100203Sgad * Define a wrapper which can take 'char', either signed or unsigned. 67100203Sgad */ 68100203Sgad#define isdigitch(Anychar) isdigit(((int) Anychar) & 255) 69100203Sgad 70100203Sgad/* 71100203Sgad * Format a single jobspec into a string fit for printing. 72100203Sgad */ 73100203Sgadvoid 74100203Sgadformat_jobspec(struct jobspec *jspec, int fmt_wanted) 75100203Sgad{ 76100203Sgad char rangestr[40], buildstr[200]; 77100203Sgad const char fromuser[] = "from user "; 78100203Sgad const char fromhost[] = "from host "; 79100203Sgad size_t strsize; 80100203Sgad 81100203Sgad /* 82100203Sgad * If the struct already has a fmtstring, then release it 83100203Sgad * before building a new one. 84100203Sgad */ 85100203Sgad if (jspec->fmtoutput != NULL) { 86100203Sgad free(jspec->fmtoutput); 87100203Sgad jspec->fmtoutput = NULL; 88100203Sgad } 89100203Sgad 90100203Sgad jspec->pluralfmt = 1; /* assume a "plural result" */ 91100203Sgad rangestr[0] = '\0'; 92100203Sgad if (jspec->startnum >= 0) { 93100203Sgad if (jspec->startnum != jspec->endrange) 94100203Sgad snprintf(rangestr, sizeof(rangestr), "%ld-%ld", 95100203Sgad jspec->startnum, jspec->endrange); 96100203Sgad else { 97100203Sgad jspec->pluralfmt = 0; 98100203Sgad snprintf(rangestr, sizeof(rangestr), "%ld", 99100203Sgad jspec->startnum); 100100203Sgad } 101100203Sgad } 102100203Sgad 103100203Sgad strsize = sizeof(buildstr); 104100203Sgad buildstr[0] = '\0'; 105100203Sgad switch (fmt_wanted) { 106100203Sgad case FMTJS_TERSE: 107100203Sgad /* Build everything but the hostname in a temp string. */ 108100203Sgad if (jspec->wanteduser != NULL) 109100203Sgad strlcat(buildstr, jspec->wanteduser, strsize); 110100203Sgad if (rangestr[0] != '\0') { 111100203Sgad if (buildstr[0] != '\0') 112100203Sgad strlcat(buildstr, ":", strsize); 113100203Sgad strlcat(buildstr, rangestr, strsize); 114100203Sgad } 115100203Sgad if (jspec->wantedhost != NULL) 116100203Sgad strlcat(buildstr, "@", strsize); 117100203Sgad 118100203Sgad /* Get space for the final result, including hostname */ 119100203Sgad strsize = strlen(buildstr) + 1; 120100203Sgad if (jspec->wantedhost != NULL) 121100203Sgad strsize += strlen(jspec->wantedhost); 122100203Sgad jspec->fmtoutput = malloc(strsize); 123100203Sgad 124100203Sgad /* Put together the final result */ 125100203Sgad strlcpy(jspec->fmtoutput, buildstr, strsize); 126100203Sgad if (jspec->wantedhost != NULL) 127100203Sgad strlcat(jspec->fmtoutput, jspec->wantedhost, strsize); 128100203Sgad break; 129100203Sgad 130100203Sgad case FMTJS_VERBOSE: 131100203Sgad default: 132100203Sgad /* Build everything but the hostname in a temp string. */ 133100203Sgad strlcat(buildstr, rangestr, strsize); 134100203Sgad if (jspec->wanteduser != NULL) { 135100203Sgad if (rangestr[0] != '\0') 136100203Sgad strlcat(buildstr, " ", strsize); 137100203Sgad strlcat(buildstr, fromuser, strsize); 138100203Sgad strlcat(buildstr, jspec->wanteduser, strsize); 139100203Sgad } 140100203Sgad if (jspec->wantedhost != NULL) { 141100203Sgad if (jspec->wanteduser == NULL) { 142100203Sgad if (rangestr[0] != '\0') 143100203Sgad strlcat(buildstr, " ", strsize); 144100203Sgad strlcat(buildstr, fromhost, strsize); 145100203Sgad } else 146100203Sgad strlcat(buildstr, "@", strsize); 147100203Sgad } 148100203Sgad 149100203Sgad /* Get space for the final result, including hostname */ 150100203Sgad strsize = strlen(buildstr) + 1; 151100203Sgad if (jspec->wantedhost != NULL) 152100203Sgad strsize += strlen(jspec->wantedhost); 153100203Sgad jspec->fmtoutput = malloc(strsize); 154100203Sgad 155100203Sgad /* Put together the final result */ 156100203Sgad strlcpy(jspec->fmtoutput, buildstr, strsize); 157100203Sgad if (jspec->wantedhost != NULL) 158100203Sgad strlcat(jspec->fmtoutput, jspec->wantedhost, strsize); 159100203Sgad break; 160100203Sgad } 161100203Sgad} 162100203Sgad 163100203Sgad/* 164100203Sgad * Free all the jobspec-related information. 165100203Sgad */ 166100203Sgadvoid 167100203Sgadfree_jobspec(struct jobspec_hdr *js_hdr) 168100203Sgad{ 169100203Sgad struct jobspec *jsinf; 170100203Sgad 171100203Sgad while (!STAILQ_EMPTY(js_hdr)) { 172100203Sgad jsinf = STAILQ_FIRST(js_hdr); 173100203Sgad STAILQ_REMOVE_HEAD(js_hdr, nextjs); 174100203Sgad if (jsinf->fmtoutput) 175100203Sgad free(jsinf->fmtoutput); 176100203Sgad if (jsinf->matcheduser) 177100203Sgad free(jsinf->matcheduser); 178100203Sgad free(jsinf); 179100203Sgad } 180100203Sgad} 181100203Sgad 182100203Sgad/* 183100203Sgad * This routine takes a string as typed in from the user, and parses it 184100203Sgad * into a job-specification. A job specification would match one or more 185100203Sgad * jobs in the queue of some single printer (the specification itself does 186100203Sgad * not indicate which queue should be searched). 187100203Sgad * 188100203Sgad * This recognizes a job-number range by itself (all digits, or a range 189100203Sgad * indicated by "digits-digits"), or a userid by itself. If a `:' is 190100203Sgad * found, it is treated as a separator between a job-number range and 191100203Sgad * a userid, where the job number range is the side which has a digit as 192100203Sgad * the first character. If an `@' is found, everything to the right of 193100203Sgad * it is treated as the hostname the job originated from. 194100203Sgad * 195100203Sgad * So, the user can specify: 196100203Sgad * jobrange userid userid:jobrange jobrange:userid 197100203Sgad * jobrange@hostname jobrange:userid@hostname 198100203Sgad * userid@hostname userid:jobrange@hostname 199100203Sgad * 200100203Sgad * XXX - it would be nice to add "not options" too, such as ^user, 201100203Sgad * ^jobrange, and @^hostname. 202100203Sgad * 203100203Sgad * This routine may modify the original input string if that input is 204100203Sgad * valid. If the input was *not* valid, then this routine should return 205100203Sgad * with the input string the same as when the routine was called. 206100203Sgad */ 207100203Sgadint 208100203Sgadparse_jobspec(char *jobstr, struct jobspec_hdr *js_hdr) 209100203Sgad{ 210100203Sgad struct jobspec *jsinfo; 211100203Sgad char *atsign, *colon, *lhside, *numstr, *period, *rhside; 212100203Sgad int jobnum; 213100203Sgad 214100203Sgad#if DEBUG_PARSEJS 215100203Sgad printf("\t [ pjs-input = %s ]\n", jobstr); 216100203Sgad#endif 217100203Sgad 218100203Sgad if ((jobstr == NULL) || (*jobstr == '\0')) 219100203Sgad return (0); 220100203Sgad 221100203Sgad jsinfo = malloc(sizeof(struct jobspec)); 222100203Sgad memset(jsinfo, 0, sizeof(struct jobspec)); 223100203Sgad jsinfo->startnum = jsinfo->endrange = -1; 224100203Sgad 225100203Sgad /* Find the separator characters, and nullify them. */ 226100203Sgad numstr = NULL; 227100203Sgad atsign = strchr(jobstr, '@'); 228100203Sgad colon = strchr(jobstr, ':'); 229100203Sgad if (atsign != NULL) 230100203Sgad *atsign = '\0'; 231100203Sgad if (colon != NULL) 232100203Sgad *colon = '\0'; 233100203Sgad 234100203Sgad /* The at-sign always indicates a hostname. */ 235100203Sgad if (atsign != NULL) { 236100203Sgad rhside = atsign + 1; 237100203Sgad if (*rhside != '\0') 238100203Sgad jsinfo->wantedhost = rhside; 239100203Sgad } 240100203Sgad 241100203Sgad /* Finish splitting the input into three parts. */ 242100203Sgad rhside = NULL; 243100203Sgad if (colon != NULL) { 244100203Sgad rhside = colon + 1; 245100203Sgad if (*rhside == '\0') 246100203Sgad rhside = NULL; 247100203Sgad } 248100203Sgad lhside = NULL; 249100203Sgad if (*jobstr != '\0') 250100203Sgad lhside = jobstr; 251100203Sgad 252100203Sgad /* 253100203Sgad * If there is a `:' here, then it's either jobrange:userid, 254100203Sgad * userid:jobrange, or (if @hostname was not given) perhaps it 255100203Sgad * might be hostname:jobnum. The side which has a digit as the 256100203Sgad * first character is assumed to be the jobrange. It is an 257100203Sgad * input error if both sides start with a digit, or if neither 258100203Sgad * side starts with a digit. 259100203Sgad */ 260100203Sgad if ((lhside != NULL) && (rhside != NULL)) { 261100203Sgad if (isdigitch(*lhside)) { 262100203Sgad if (isdigitch(*rhside)) 263100203Sgad goto bad_input; 264100203Sgad numstr = lhside; 265100203Sgad jsinfo->wanteduser = rhside; 266100203Sgad } else if (isdigitch(*rhside)) { 267100203Sgad numstr = rhside; 268100203Sgad /* 269100203Sgad * The original implementation of 'lpc topq' accepted 270100203Sgad * hostname:jobnum. If the input did not include a 271100203Sgad * @hostname, then assume the userid is a hostname if 272100203Sgad * it includes a '.'. 273100203Sgad */ 274100203Sgad period = strchr(lhside, '.'); 275100203Sgad if ((atsign == NULL) && (period != NULL)) 276100203Sgad jsinfo->wantedhost = lhside; 277100203Sgad else 278100203Sgad jsinfo->wanteduser = lhside; 279100203Sgad } else { 280100203Sgad /* Neither side is a job number = user error */ 281100203Sgad goto bad_input; 282100203Sgad } 283100203Sgad } else if (lhside != NULL) { 284100203Sgad if (isdigitch(*lhside)) 285100203Sgad numstr = lhside; 286100203Sgad else 287100203Sgad jsinfo->wanteduser = lhside; 288100203Sgad } else if (rhside != NULL) { 289100203Sgad if (isdigitch(*rhside)) 290100203Sgad numstr = rhside; 291100203Sgad else 292100203Sgad jsinfo->wanteduser = rhside; 293100203Sgad } 294100203Sgad 295100203Sgad /* 296100203Sgad * Break down the numstr. It should be all digits, or a range 297100203Sgad * specified as "\d+-\d+". 298100203Sgad */ 299100203Sgad if (numstr != NULL) { 300100203Sgad errno = 0; 301100203Sgad jobnum = strtol(numstr, &numstr, 10); 302100203Sgad if (errno != 0) /* error in conversion */ 303100203Sgad goto bad_input; 304100203Sgad if (jobnum < 0) /* a bogus value for this purpose */ 305100203Sgad goto bad_input; 306100203Sgad if (jobnum > 99999) /* too large for job number */ 307100203Sgad goto bad_input; 308100203Sgad jsinfo->startnum = jsinfo->endrange = jobnum; 309100203Sgad 310100203Sgad /* Check for a range of numbers */ 311100203Sgad if ((*numstr == '-') && (isdigitch(*(numstr + 1)))) { 312100203Sgad numstr++; 313100203Sgad errno = 0; 314100203Sgad jobnum = strtol(numstr, &numstr, 10); 315100203Sgad if (errno != 0) /* error in conversion */ 316100203Sgad goto bad_input; 317100203Sgad if (jobnum < jsinfo->startnum) 318100203Sgad goto bad_input; 319100203Sgad if (jobnum > 99999) /* too large for job number */ 320100203Sgad goto bad_input; 321100203Sgad jsinfo->endrange = jobnum; 322100203Sgad } 323100203Sgad 324100203Sgad /* 325100203Sgad * If there is anything left in the numstr, and if the 326100203Sgad * original string did not include a userid or a hostname, 327100203Sgad * then this might be the ancient form of '\d+hostname' 328101677Sschweikh * (with no separator between jobnum and hostname). Accept 329100203Sgad * that for backwards compatibility, but otherwise any 330100203Sgad * remaining characters mean a user-error. Note that the 331100203Sgad * ancient form accepted only a single number, but this 332100203Sgad * will also accept a range of numbers. 333100203Sgad */ 334100203Sgad if (*numstr != '\0') { 335100203Sgad if (atsign != NULL) 336100203Sgad goto bad_input; 337100203Sgad if (jsinfo->wantedhost != NULL) 338100203Sgad goto bad_input; 339100203Sgad if (jsinfo->wanteduser != NULL) 340100203Sgad goto bad_input; 341100203Sgad /* Treat as the rest of the string as a hostname */ 342100203Sgad jsinfo->wantedhost = numstr; 343100203Sgad } 344100203Sgad } 345100203Sgad 346100203Sgad if ((jsinfo->startnum < 0) && (jsinfo->wanteduser == NULL) && 347100203Sgad (jsinfo->wantedhost == NULL)) 348100203Sgad goto bad_input; 349100203Sgad 350100203Sgad /* 351100203Sgad * The input was valid, in the sense that it could be parsed 352100203Sgad * into the individual parts. Add this jobspec to the list 353100203Sgad * of jobspecs. 354100203Sgad */ 355100203Sgad STAILQ_INSERT_TAIL(js_hdr, jsinfo, nextjs); 356100203Sgad 357100203Sgad#if DEBUG_PARSEJS 358100203Sgad printf("\t [ will check for"); 359100203Sgad if (jsinfo->startnum >= 0) { 360100203Sgad if (jsinfo->startnum == jsinfo->endrange) 361100203Sgad printf(" jobnum = %ld", jsinfo->startnum); 362100203Sgad else 363100203Sgad printf(" jobrange = %ld to %ld", jsinfo->startnum, 364100203Sgad jsinfo->endrange); 365100203Sgad } else { 366100203Sgad printf(" jobs"); 367100203Sgad } 368100203Sgad if ((jsinfo->wanteduser != NULL) || (jsinfo->wantedhost != NULL)) { 369100203Sgad printf(" from"); 370100203Sgad if (jsinfo->wanteduser != NULL) 371100203Sgad printf(" user = %s", jsinfo->wanteduser); 372100203Sgad if (jsinfo->wantedhost != NULL) 373100203Sgad printf(" host = %s", jsinfo->wantedhost); 374100203Sgad } 375100203Sgad printf("]\n"); 376100203Sgad#endif 377100203Sgad 378100203Sgad return (1); 379100203Sgad 380100203Sgadbad_input: 381100203Sgad /* 382100203Sgad * Restore any `@' and `:', in case the calling routine wants to 383100203Sgad * write an error message which includes the input string. 384100203Sgad */ 385100203Sgad if (atsign != NULL) 386100203Sgad *atsign = '@'; 387100203Sgad if (colon != NULL) 388100203Sgad *colon = ':'; 389100203Sgad if (jsinfo != NULL) 390100203Sgad free(jsinfo); 391100203Sgad return (0); 392100203Sgad} 393100203Sgad 394100203Sgad/* 395100203Sgad * Check to see if a given job (specified by a jobqueue entry) matches 396100203Sgad * all of the specifications in a given jobspec. 397100203Sgad * 398100203Sgad * Returns 0 if no match, 1 if the job does match. 399100203Sgad */ 400100203Sgadstatic int 401100203Sgadmatch_jobspec(struct jobqueue *jq, struct jobspec *jspec) 402100203Sgad{ 403100203Sgad struct cjobinfo *cfinf; 404100203Sgad char *cp, *cf_numstr, *cf_hoststr; 405100203Sgad int jnum, match; 406100203Sgad 407100203Sgad#if DEBUG_SCANJS 408100203Sgad printf("\t [ match-js checking %s ]\n", jq->job_cfname); 409100203Sgad#endif 410100203Sgad 411100203Sgad if (jspec == NULL || jq == NULL) 412100203Sgad return (0); 413100203Sgad 414100203Sgad /* 415100203Sgad * Keep track of which jobs have already been matched by this 416100203Sgad * routine, and thus (probably) already processed. 417100203Sgad */ 418100203Sgad if (jq->job_matched) 419100203Sgad return (0); 420100203Sgad 421100203Sgad /* 422100203Sgad * The standard `cf' file has the job number start in position 4, 423100203Sgad * but some implementations have that as an extra file-sequence 424100203Sgad * letter, and start the job number in position 5. The job 425100203Sgad * number is usually three bytes, but may be as many as five. 426100203Sgad * 427100203Sgad * XXX - All this nonsense should really be handled in a single 428100203Sgad * place, like getq()... 429100203Sgad */ 430100203Sgad cf_numstr = jq->job_cfname + 3; 431100203Sgad if (!isdigitch(*cf_numstr)) 432100203Sgad cf_numstr++; 433100203Sgad jnum = 0; 434100203Sgad for (cp = cf_numstr; (cp < cf_numstr + 5) && isdigitch(*cp); cp++) 435100203Sgad jnum = jnum * 10 + (*cp - '0'); 436100203Sgad cf_hoststr = cp; 437100203Sgad cfinf = NULL; 438100203Sgad match = 0; /* assume the job will not match */ 439100203Sgad jspec->matcheduser = NULL; 440100203Sgad 441100203Sgad /* 442100203Sgad * Check the job-number range. 443100203Sgad */ 444100203Sgad if (jspec->startnum >= 0) { 445100203Sgad if (jnum < jspec->startnum) 446100203Sgad goto nomatch; 447100203Sgad if (jnum > jspec->endrange) 448100203Sgad goto nomatch; 449100203Sgad } 450100203Sgad 451100203Sgad /* 452100203Sgad * Check the hostname. Strictly speaking this should be done by 453100203Sgad * reading the control file, but it is less expensive to check 454100203Sgad * the hostname-part of the control file name. Also, this value 455100203Sgad * can be easily seen in 'lpq -l', while there is no easy way for 456100203Sgad * a user/operator to see the hostname in the control file. 457100203Sgad */ 458100203Sgad if (jspec->wantedhost != NULL) { 459100203Sgad if (fnmatch(jspec->wantedhost, cf_hoststr, 0) != 0) 460100203Sgad goto nomatch; 461100203Sgad } 462100203Sgad 463100203Sgad /* 464100203Sgad * Check for a match on the user name. This has to be done 465100203Sgad * by reading the control file. 466100203Sgad */ 467100203Sgad if (jspec->wanteduser != NULL) { 468100203Sgad cfinf = ctl_readcf("fakeq", jq->job_cfname); 469100203Sgad if (cfinf == NULL) 470100203Sgad goto nomatch; 471100203Sgad if (fnmatch(jspec->wanteduser, cfinf->cji_username, 0) != 0) 472100203Sgad goto nomatch; 473100203Sgad } 474100203Sgad 475100203Sgad /* This job matches all of the specified criteria. */ 476100203Sgad match = 1; 477100203Sgad jq->job_matched = 1; /* avoid matching the job twice */ 478100203Sgad jspec->matchcnt++; 479100203Sgad if (jspec->wanteduser != NULL) { 480100203Sgad /* 481100203Sgad * If the user specified a userid (which may have been a 482100203Sgad * pattern), then the caller's "doentry()" routine might 483100203Sgad * want to know the userid of this job that matched. 484100203Sgad */ 485100203Sgad jspec->matcheduser = strdup(cfinf->cji_username); 486100203Sgad } 487100203Sgad#if DEBUG_SCANJS 488100203Sgad printf("\t [ job matched! ]\n"); 489100203Sgad#endif 490100203Sgad 491100203Sgadnomatch: 492100203Sgad if (cfinf != NULL) 493100203Sgad ctl_freeinf(cfinf); 494100203Sgad return (match); 495100203Sgad} 496100203Sgad 497100203Sgad/* 498100203Sgad * Scan a queue for all jobs which match a jobspec. The queue is scanned 499100203Sgad * from top to bottom. 500100203Sgad * 501100203Sgad * The caller can provide a routine which will be executed for each job 502100203Sgad * that does match. Note that the processing routine might do anything 503100203Sgad * to the matched job -- including the removal of it. 504100203Sgad * 505100203Sgad * This returns the number of jobs which were matched. 506100203Sgad */ 507100203Sgadint 508100203Sgadscanq_jobspec(int qcount, struct jobqueue **squeue, int sopts, struct 509100203Sgad jobspec_hdr *js_hdr, process_jqe doentry, void *doentryinfo) 510100203Sgad{ 511100203Sgad struct jobqueue **qent; 512100203Sgad struct jobspec *jspec; 513100203Sgad int cnt, matched, total; 514100203Sgad 515100203Sgad if (qcount < 1) 516100203Sgad return (0); 517100203Sgad if (js_hdr == NULL) 518100203Sgad return (-1); 519100203Sgad 520100203Sgad /* The caller must specify one of the scanning orders */ 521100203Sgad if ((sopts & (SCQ_JSORDER|SCQ_QORDER)) == 0) 522100203Sgad return (-1); 523100203Sgad 524100203Sgad total = 0; 525100203Sgad if (sopts & SCQ_JSORDER) { 526100203Sgad /* 527100203Sgad * For each job specification, scan through the queue 528100203Sgad * looking for every job that matches. 529100203Sgad */ 530100203Sgad STAILQ_FOREACH(jspec, js_hdr, nextjs) { 531100203Sgad for (qent = squeue, cnt = 0; cnt < qcount; 532100203Sgad qent++, cnt++) { 533100203Sgad matched = match_jobspec(*qent, jspec); 534100203Sgad if (!matched) 535100203Sgad continue; 536100203Sgad total++; 537100203Sgad if (doentry != NULL) 538100203Sgad doentry(doentryinfo, *qent, jspec); 539100203Sgad if (jspec->matcheduser != NULL) { 540100203Sgad free(jspec->matcheduser); 541100203Sgad jspec->matcheduser = NULL; 542100203Sgad } 543100203Sgad } 544100203Sgad /* 545100203Sgad * The entire queue has been scanned for this 546100203Sgad * jobspec. Call the user's routine again with 547100203Sgad * a NULL queue-entry, so it can print out any 548100203Sgad * kind of per-jobspec summary. 549100203Sgad */ 550100203Sgad if (doentry != NULL) 551100203Sgad doentry(doentryinfo, NULL, jspec); 552100203Sgad } 553100203Sgad } else { 554100203Sgad /* 555100203Sgad * For each job in the queue, check all of the job 556100203Sgad * specifications to see if any one of them matches 557100203Sgad * that job. 558100203Sgad */ 559100203Sgad for (qent = squeue, cnt = 0; cnt < qcount; 560100203Sgad qent++, cnt++) { 561100203Sgad STAILQ_FOREACH(jspec, js_hdr, nextjs) { 562100203Sgad matched = match_jobspec(*qent, jspec); 563100203Sgad if (!matched) 564100203Sgad continue; 565100203Sgad total++; 566100203Sgad if (doentry != NULL) 567100203Sgad doentry(doentryinfo, *qent, jspec); 568100203Sgad if (jspec->matcheduser != NULL) { 569100203Sgad free(jspec->matcheduser); 570100203Sgad jspec->matcheduser = NULL; 571100203Sgad } 572100203Sgad /* 573100203Sgad * Once there is a match, then there is no 574100203Sgad * point in checking this same job against 575100203Sgad * all the other jobspec's. 576100203Sgad */ 577100203Sgad break; 578100203Sgad } 579100203Sgad } 580100203Sgad } 581100203Sgad 582100203Sgad return (total); 583100203Sgad} 584