From f16ea703e17a409484dae572e88caa844502186f Mon Sep 17 00:00:00 2001 From: msweet Date: Thu, 31 Jul 2014 00:02:30 +0000 Subject: [PATCH] Performance fixes for Get-Jobs (STR #2913) Cache a few additional job attributes so that we normally do not need to load the job attributes from the 'c' files. If we do need to load them, limit the returned jobs to 500 at a time. Implement first-index operation attribute. git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@12067 a1ca3aef-8c08-0410-bb20-df032aa958be --- CHANGES.txt | 51 +++++++------- scheduler/ipp.c | 173 ++++++++++++++++++++++++++++++++++++++---------- scheduler/job.c | 34 ++++++++-- scheduler/job.h | 3 + 4 files changed, 200 insertions(+), 61 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 529cc4e455..d3a258b325 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,16 +3,17 @@ CHANGES.txt - 2.0b1 - 2014-07-30 CHANGES IN CUPS V2.0b1 - - Dropped OpenSSL support in favor of GNU TLS. - - Dropped "dark wake" support on OS X, which was preventing portables - from going to sleep when there was a stuck job. We now use a variation - of the CUPS 1.4 sleep support to do a cleaner sleep - () - - Dropped support for AIX, HP-UX, and OSF/1 (aka Digital UNIX) - - Dropped lppasswd and support for Digest authentication in in the - scheduler (STR #4321) - - CUPS TLS support now supports certificate validation and policy - enforcement (STR #1616) + - Added a "--list-filters" option to the cupsfilter command (STR #4325) + - Added systemd support (STR #3917) + - Added support for re-sending a job as a raster file if a higher-level + format such as PDF fails () + - Added support for regular expression matching in the MIME type rules + () + - Added support for TLS certificate validation and policy enforcement + (STR #1616) + - Added support for simultaneous XML and test output from ipptool. + - Added support for PAUSE directive in ipptool test files. + - Added support for auto-typing of TIFF files by ipptool (STR #4418) - The scheduler now returns completed jobs in the correct newest-to- oldest order (STR #4396) - The configure script now supports target-specific tools for pkg-config @@ -25,16 +26,27 @@ CHANGES IN CUPS V2.0b1 default () - Adopted Linux man page conventions and updated all man pages (STR #4372, STR #4329) + - The scheduler now supports the "first-index" operation attribute for + the Get-Jobs operation (STR #2913) - Changed the default AccessLogLevel and PageLogFormat to disable the access_log and page_log files by default () - - Added a "--list-filters" option to the cupsfilter command (STR #4325) - - Added systemd support (STR #3917) - - Added support for re-sending a job as a raster file if a higher-level - format such as PDF fails () - - Added support for regular expression matching in the MIME type rules - () - cupsRasterInterpretPPD now supports the Orientation header in order to support long-edge feed raster printers () + - The scheduler now allows run-as-root backends to have group read and + execute permissions (STR #2935) + - The ippFindAttribute and ippFindNextAttribute functions now support + hierarchical searches (STR #4395) + - Dropped OpenSSL support in favor of GNU TLS. + - Dropped "dark wake" support on OS X, which was preventing portables + from going to sleep when there was a stuck job. We now use a variation + of the CUPS 1.4 sleep support to do a cleaner sleep + () + - Dropped support for AIX, HP-UX, and OSF/1 (aka Digital UNIX) + - Dropped lppasswd and support for Digest authentication in in the + scheduler (STR #4321) + - The scheduler now caches more job history data and limits the number + of completed jobs returned by Get-Jobs as needed in order to prevent a + denial-of-service on busy servers (STR #2913) - The filter/backend sandbox on OS X now defaults to a more strict whitelist () - Increased the default idle exit timeout to 60 seconds on OS X @@ -43,12 +55,5 @@ CHANGES IN CUPS V2.0b1 () - The scheduler now uses to close the default printer definition in printers.conf (STR #4153) - - The scheduler now allows run-as-root backends to have group read and - execute permissions (STR #2935) - - The ippFindAttribute and ippFindNextAttribute functions now support - hierarchical searches (STR #4395) - - Added support for simultaneous XML and test output from ipptool. - - Added support for PAUSE directive in ipptool test files. - - Added support for auto-typing of TIFF files by ipptool (STR #4418) - Canceling all jobs in the web interface now just cancels the jobs (STR #1914) diff --git a/scheduler/ipp.c b/scheduler/ipp.c index 563d296ab3..1f6fe5a39d 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -1511,8 +1511,7 @@ add_job(cupsd_client_t *con, /* I - Client connection */ } if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) == NULL) - ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, - "Untitled"); + ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled"); else if ((attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_NAMELANG) || attr->num_values != 1) @@ -1592,6 +1591,9 @@ add_job(cupsd_client_t *con, /* I - Client connection */ ippDeleteAttribute(job->attrs, auth_info); } + if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL) + cupsdSetString(&(job->name), attr->values[0].string.text); + if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name", IPP_TAG_ZERO)) != NULL) { @@ -1686,8 +1688,7 @@ add_job(cupsd_client_t *con, /* I - Client connection */ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, printer->uri); - if ((attr = ippFindAttribute(job->attrs, "job-k-octets", - IPP_TAG_INTEGER)) != NULL) + if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL) attr->values[0].integer = 0; else ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-k-octets", 0); @@ -4328,8 +4329,9 @@ copy_banner(cupsd_client_t *con, /* I - Client connection */ kbytes = (cupsFileTell(out) + 1023) / 1024; - if ((attr = ippFindAttribute(job->attrs, "job-k-octets", - IPP_TAG_INTEGER)) != NULL) + job->koctets += kbytes; + + if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL) attr->values[0].integer += kbytes; cupsFileClose(out); @@ -4751,7 +4753,55 @@ copy_job_attrs(cupsd_client_t *con, /* I - Client connection */ "job-uri", NULL, job_uri); } - copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0, exclude); + if (job->attrs) + { + copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0, exclude); + } + else + { + /* + * Generate attributes from the job structure... + */ + + if (!ra || cupsArrayFind(ra, "job-id")) + ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id); + + if (!ra || cupsArrayFind(ra, "job-k-octets")) + ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-k-octets", job->koctets); + + if (job->name && (!ra || cupsArrayFind(ra, "job-name"))) + ippAddString(con->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_NAME), "job-name", NULL, job->name); + + if (job->username && (!ra || cupsArrayFind(ra, "job-originating-user-name"))) + ippAddString(con->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_NAME), "job-originating-user-name", NULL, job->username); + + if (!ra || cupsArrayFind(ra, "job-state")) + ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state_value); + + if (!ra || cupsArrayFind(ra, "job-state-reasons")) + { + switch (job->state_value) + { + default : /* Should never get here for processing, pending, held, or stopped jobs since they don't get unloaded... */ + break; + case IPP_JSTATE_ABORTED : + ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-aborted-by-system"); + break; + case IPP_JSTATE_CANCELED : + ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-canceled-by-user"); + break; + case IPP_JSTATE_COMPLETED : + ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-completed-successfully"); + break; + } + } + + if (job->completed_time && (!ra || cupsArrayFind(ra, "time-at-completed"))) + ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-completed", (int)job->completed_time); + + if (job->completed_time && (!ra || cupsArrayFind(ra, "time-at-creation"))) + ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)job->creation_time); + } } @@ -6101,9 +6151,13 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ int port; /* Port portion of URI */ int job_comparison; /* Job comparison */ ipp_jstate_t job_state; /* job-state value */ - int first_job_id; /* First job ID */ - int limit; /* Maximum number of jobs to return */ + int first_job_id = 1, /* First job ID */ + first_index = 1, /* First index */ + current_index = 0; /* Current index */ + int limit = 0; /* Maximum number of jobs to return */ int count; /* Number of jobs that match */ + int need_load_job = 0; /* Do we need to load the job? */ + const char *job_attr; /* Job attribute requested */ ipp_attribute_t *job_ids; /* job-ids attribute */ cupsd_job_t *job; /* Current job pointer */ cupsd_printer_t *printer; /* Printer */ @@ -6269,8 +6323,7 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ * See if they want to limit the number of jobs reported... */ - if ((attr = ippFindAttribute(con->request, "limit", - IPP_TAG_INTEGER)) != NULL) + if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL) { if (job_ids) { @@ -6282,11 +6335,20 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ limit = attr->values[0].integer; } - else - limit = 0; - if ((attr = ippFindAttribute(con->request, "first-job-id", - IPP_TAG_INTEGER)) != NULL) + if ((attr = ippFindAttribute(con->request, "first-index", IPP_TAG_INTEGER)) != NULL) + { + if (job_ids) + { + send_ipp_status(con, IPP_CONFLICT, + _("The %s attribute cannot be provided with job-ids."), + "first-index"); + return; + } + + first_index = attr->values[0].integer; + } + else if ((attr = ippFindAttribute(con->request, "first-job-id", IPP_TAG_INTEGER)) != NULL) { if (job_ids) { @@ -6298,15 +6360,12 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ first_job_id = attr->values[0].integer; } - else - first_job_id = 1; /* * See if we only want to see jobs for a specific user... */ - if ((attr = ippFindAttribute(con->request, "my-jobs", - IPP_TAG_BOOLEAN)) != NULL && job_ids) + if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL && job_ids) { send_ipp_status(con, IPP_CONFLICT, _("The %s attribute cannot be provided with job-ids."), @@ -6319,6 +6378,42 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ username[0] = '\0'; ra = create_requested_array(con->request); + for (job_attr = (char *)cupsArrayFirst(ra); job_attr; job_attr = (char *)cupsArrayNext(ra)) + if (strcmp(job_attr, "job-id") && + strcmp(job_attr, "job-k-octets") && + strcmp(job_attr, "job-media-progress") && + strcmp(job_attr, "job-more-info") && + strcmp(job_attr, "job-name") && + strcmp(job_attr, "job-originating-user-name") && + strcmp(job_attr, "job-preserved") && + strcmp(job_attr, "job-printer-up-time") && + strcmp(job_attr, "job-printer-uri") && + strcmp(job_attr, "job-state") && + strcmp(job_attr, "job-state-reasons") && + strcmp(job_attr, "job-uri") && + strcmp(job_attr, "time-at-completed") && + strcmp(job_attr, "time-at-creation") && + strcmp(job_attr, "number-of-documents")) + { + need_load_job = 1; + break; + } + + if (need_load_job && (limit == 0 || limit > 500) && (list == Jobs || delete_list)) + { + /* + * Limit expensive Get-Jobs for job history to 500 jobs... + */ + + ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "limit", 500); + + if (limit) + ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER, "limit", limit); + + limit = 500; + + cupsdLogClient(con, CUPSD_LOG_INFO, "Limiting Get-Jobs response to %d jobs.", limit); + } /* * OK, build a list of jobs for this printer... @@ -6345,13 +6440,15 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ { job = cupsdFindJob(job_ids->values[i].integer); - cupsdLoadJob(job); - - if (!job->attrs) + if (need_load_job && !job->attrs) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", - job->id); - continue; + cupsdLoadJob(job); + + if (!job->attrs) + { + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", job->id); + continue; + } } if (i > 0) @@ -6401,13 +6498,19 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ if (job->id < first_job_id) continue; - cupsdLoadJob(job); + current_index ++; + if (current_index < first_index) + continue; - if (!job->attrs) + if (need_load_job && !job->attrs) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", - job->id); - continue; + cupsdLoadJob(job); + + if (!job->attrs) + { + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", job->id); + continue; + } } if (username[0] && _cups_strcasecmp(username, job->username)) @@ -8141,8 +8244,9 @@ print_job(cupsd_client_t *con, /* I - Client connection */ cupsdUpdateQuota(printer, job->username, 0, kbytes); - if ((attr = ippFindAttribute(job->attrs, "job-k-octets", - IPP_TAG_INTEGER)) != NULL) + job->koctets += kbytes; + + if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL) attr->values[0].integer += kbytes; /* @@ -9375,8 +9479,9 @@ send_document(cupsd_client_t *con, /* I - Client connection */ cupsdUpdateQuota(printer, job->username, 0, kbytes); - if ((attr = ippFindAttribute(job->attrs, "job-k-octets", - IPP_TAG_INTEGER)) != NULL) + job->koctets += kbytes; + + if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL) attr->values[0].integer += kbytes; snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id, diff --git a/scheduler/job.c b/scheduler/job.c index 0d9aafbc13..9d500d1fbf 100644 --- a/scheduler/job.c +++ b/scheduler/job.c @@ -1675,9 +1675,10 @@ cupsdLoadJob(cupsd_job_t *job) /* I - Job */ job->file_time = 0; job->history_time = 0; - if (job->state_value >= IPP_JOB_CANCELED && - (attr = ippFindAttribute(job->attrs, "time-at-completed", - IPP_TAG_INTEGER)) != NULL) + if ((attr = ippFindAttribute(job->attrs, "time-at-creation", IPP_TAG_INTEGER)) != NULL) + job->creation_time = attr->values[0].integer; + + if (job->state_value >= IPP_JOB_CANCELED && (attr = ippFindAttribute(job->attrs, "time-at-completed", IPP_TAG_INTEGER)) != NULL) { job->completed_time = attr->values[0].integer; @@ -1826,6 +1827,12 @@ cupsdLoadJob(cupsd_job_t *job) /* I - Job */ cupsdSetString(&job->username, attr->values[0].string.text); } + if (!job->name) + { + if ((attr = ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME)) != NULL) + cupsdSetString(&job->name, attr->values[0].string.text); + } + /* * Set the job hold-until time and state... */ @@ -1850,6 +1857,9 @@ cupsdLoadJob(cupsd_job_t *job) /* I - Job */ job->state_value = IPP_JOB_PENDING; } + if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL) + job->koctets = attr->values[0].integer; + if (!job->num_files) { /* @@ -2155,14 +2165,18 @@ cupsdSaveAllJobs(void) { cupsFilePrintf(fp, "\n", job->id); cupsFilePrintf(fp, "State %d\n", job->state_value); + cupsFilePrintf(fp, "Created %ld\n", (long)job->creation_time); if (job->completed_time) cupsFilePrintf(fp, "Completed %ld\n", (long)job->completed_time); cupsFilePrintf(fp, "Priority %d\n", job->priority); if (job->hold_until) cupsFilePrintf(fp, "HoldUntil %ld\n", (long)job->hold_until); cupsFilePrintf(fp, "Username %s\n", job->username); + if (job->name) + cupsFilePutConf(fp, "Name", job->name); cupsFilePrintf(fp, "Destination %s\n", job->dest); cupsFilePrintf(fp, "DestType %d\n", job->dtype); + cupsFilePrintf(fp, "KOctets %d\n", job->koctets); cupsFilePrintf(fp, "NumFiles %d\n", job->num_files); for (i = 0; i < job->num_files; i ++) cupsFilePrintf(fp, "File %d %s/%s %d\n", i + 1, job->filetypes[i]->super, @@ -4114,7 +4128,7 @@ load_job_cache(const char *filename) /* I - job.cache filename */ cupsArrayAdd(ActiveJobs, job); else if (job->state_value > IPP_JOB_STOPPED) { - if (!job->completed_time) + if (!job->completed_time || !job->creation_time || !job->name || !job->koctets) { cupsdLoadJob(job); unload_job(job); @@ -4137,6 +4151,14 @@ load_job_cache(const char *filename) /* I - job.cache filename */ else if (job->state_value > IPP_JOB_COMPLETED) job->state_value = IPP_JOB_COMPLETED; } + else if (!_cups_strcasecmp(line, "Name")) + { + cupsdSetString(&(job->name), value); + } + else if (!_cups_strcasecmp(line, "Created")) + { + job->creation_time = strtol(value, NULL, 10); + } else if (!_cups_strcasecmp(line, "Completed")) { job->completed_time = strtol(value, NULL, 10); @@ -4161,6 +4183,10 @@ load_job_cache(const char *filename) /* I - job.cache filename */ { job->dtype = (cups_ptype_t)atoi(value); } + else if (!_cups_strcasecmp(line, "KOctets")) + { + job->koctets = atoi(value); + } else if (!_cups_strcasecmp(line, "NumFiles")) { job->num_files = atoi(value); diff --git a/scheduler/job.h b/scheduler/job.h index ef66078e1f..37b20dadd5 100644 --- a/scheduler/job.h +++ b/scheduler/job.h @@ -39,6 +39,8 @@ struct cupsd_job_s /**** Job request ****/ * waiting on files */ char *username; /* Printing user */ char *dest; /* Destination printer or class */ + char *name; /* Job name/title */ + int koctets; /* job-k-octets */ cups_ptype_t dtype; /* Destination type */ cupsd_printer_t *printer; /* Printer this job is assigned to */ int num_files; /* Number of files in job */ @@ -47,6 +49,7 @@ struct cupsd_job_s /**** Job request ****/ ipp_attribute_t *sheets; /* job-media-sheets-completed */ time_t access_time, /* Last access time */ cancel_time, /* When to cancel/send SIGTERM */ + creation_time, /* When job was created */ completed_time, /* When job was completed (0 if not) */ file_time, /* Job file retain time */ history_time, /* Job history retain time */ -- 2.47.2