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