matchjobs.c revision 220586
1100203Sgad/* 2100203Sgad * ------+---------+---------+---------+---------+---------+---------+---------* 3220586Sgad * Copyright (c) 2002,2011 - 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 220586 2011-04-13 00:36:19Z 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; 404139464Sgad const char *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 421139464Sgad jnum = calc_jobnum(jq->job_cfname, &cf_hoststr); 422100203Sgad cfinf = NULL; 423100203Sgad match = 0; /* assume the job will not match */ 424100203Sgad jspec->matcheduser = NULL; 425100203Sgad 426100203Sgad /* 427100203Sgad * Check the job-number range. 428100203Sgad */ 429100203Sgad if (jspec->startnum >= 0) { 430100203Sgad if (jnum < jspec->startnum) 431100203Sgad goto nomatch; 432100203Sgad if (jnum > jspec->endrange) 433100203Sgad goto nomatch; 434100203Sgad } 435100203Sgad 436100203Sgad /* 437100203Sgad * Check the hostname. Strictly speaking this should be done by 438100203Sgad * reading the control file, but it is less expensive to check 439100203Sgad * the hostname-part of the control file name. Also, this value 440100203Sgad * can be easily seen in 'lpq -l', while there is no easy way for 441100203Sgad * a user/operator to see the hostname in the control file. 442100203Sgad */ 443100203Sgad if (jspec->wantedhost != NULL) { 444100203Sgad if (fnmatch(jspec->wantedhost, cf_hoststr, 0) != 0) 445100203Sgad goto nomatch; 446100203Sgad } 447100203Sgad 448100203Sgad /* 449100203Sgad * Check for a match on the user name. This has to be done 450100203Sgad * by reading the control file. 451100203Sgad */ 452100203Sgad if (jspec->wanteduser != NULL) { 453100203Sgad cfinf = ctl_readcf("fakeq", jq->job_cfname); 454100203Sgad if (cfinf == NULL) 455100203Sgad goto nomatch; 456220586Sgad if (fnmatch(jspec->wanteduser, cfinf->cji_acctuser, 0) != 0) 457100203Sgad goto nomatch; 458100203Sgad } 459100203Sgad 460100203Sgad /* This job matches all of the specified criteria. */ 461100203Sgad match = 1; 462100203Sgad jq->job_matched = 1; /* avoid matching the job twice */ 463100203Sgad jspec->matchcnt++; 464100203Sgad if (jspec->wanteduser != NULL) { 465100203Sgad /* 466100203Sgad * If the user specified a userid (which may have been a 467100203Sgad * pattern), then the caller's "doentry()" routine might 468100203Sgad * want to know the userid of this job that matched. 469100203Sgad */ 470220586Sgad jspec->matcheduser = strdup(cfinf->cji_acctuser); 471100203Sgad } 472100203Sgad#if DEBUG_SCANJS 473100203Sgad printf("\t [ job matched! ]\n"); 474100203Sgad#endif 475100203Sgad 476100203Sgadnomatch: 477100203Sgad if (cfinf != NULL) 478100203Sgad ctl_freeinf(cfinf); 479100203Sgad return (match); 480100203Sgad} 481100203Sgad 482100203Sgad/* 483100203Sgad * Scan a queue for all jobs which match a jobspec. The queue is scanned 484100203Sgad * from top to bottom. 485100203Sgad * 486100203Sgad * The caller can provide a routine which will be executed for each job 487100203Sgad * that does match. Note that the processing routine might do anything 488100203Sgad * to the matched job -- including the removal of it. 489100203Sgad * 490100203Sgad * This returns the number of jobs which were matched. 491100203Sgad */ 492100203Sgadint 493100203Sgadscanq_jobspec(int qcount, struct jobqueue **squeue, int sopts, struct 494100203Sgad jobspec_hdr *js_hdr, process_jqe doentry, void *doentryinfo) 495100203Sgad{ 496100203Sgad struct jobqueue **qent; 497100203Sgad struct jobspec *jspec; 498100203Sgad int cnt, matched, total; 499100203Sgad 500100203Sgad if (qcount < 1) 501100203Sgad return (0); 502100203Sgad if (js_hdr == NULL) 503100203Sgad return (-1); 504100203Sgad 505100203Sgad /* The caller must specify one of the scanning orders */ 506100203Sgad if ((sopts & (SCQ_JSORDER|SCQ_QORDER)) == 0) 507100203Sgad return (-1); 508100203Sgad 509100203Sgad total = 0; 510100203Sgad if (sopts & SCQ_JSORDER) { 511100203Sgad /* 512100203Sgad * For each job specification, scan through the queue 513100203Sgad * looking for every job that matches. 514100203Sgad */ 515100203Sgad STAILQ_FOREACH(jspec, js_hdr, nextjs) { 516100203Sgad for (qent = squeue, cnt = 0; cnt < qcount; 517100203Sgad qent++, cnt++) { 518100203Sgad matched = match_jobspec(*qent, jspec); 519100203Sgad if (!matched) 520100203Sgad continue; 521100203Sgad total++; 522100203Sgad if (doentry != NULL) 523100203Sgad doentry(doentryinfo, *qent, jspec); 524100203Sgad if (jspec->matcheduser != NULL) { 525100203Sgad free(jspec->matcheduser); 526100203Sgad jspec->matcheduser = NULL; 527100203Sgad } 528100203Sgad } 529100203Sgad /* 530100203Sgad * The entire queue has been scanned for this 531100203Sgad * jobspec. Call the user's routine again with 532100203Sgad * a NULL queue-entry, so it can print out any 533100203Sgad * kind of per-jobspec summary. 534100203Sgad */ 535100203Sgad if (doentry != NULL) 536100203Sgad doentry(doentryinfo, NULL, jspec); 537100203Sgad } 538100203Sgad } else { 539100203Sgad /* 540100203Sgad * For each job in the queue, check all of the job 541100203Sgad * specifications to see if any one of them matches 542100203Sgad * that job. 543100203Sgad */ 544100203Sgad for (qent = squeue, cnt = 0; cnt < qcount; 545100203Sgad qent++, cnt++) { 546100203Sgad STAILQ_FOREACH(jspec, js_hdr, nextjs) { 547100203Sgad matched = match_jobspec(*qent, jspec); 548100203Sgad if (!matched) 549100203Sgad continue; 550100203Sgad total++; 551100203Sgad if (doentry != NULL) 552100203Sgad doentry(doentryinfo, *qent, jspec); 553100203Sgad if (jspec->matcheduser != NULL) { 554100203Sgad free(jspec->matcheduser); 555100203Sgad jspec->matcheduser = NULL; 556100203Sgad } 557100203Sgad /* 558100203Sgad * Once there is a match, then there is no 559100203Sgad * point in checking this same job against 560100203Sgad * all the other jobspec's. 561100203Sgad */ 562100203Sgad break; 563100203Sgad } 564100203Sgad } 565100203Sgad } 566100203Sgad 567100203Sgad return (total); 568100203Sgad} 569