X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fcups.git;a=blobdiff_plain;f=scheduler%2Fjob.c;h=ab1ffab2bee1aca194e6fb809282fcd392d89f17;hp=cdeaec6d689672a31965aba78154c551d07c2449;hb=bc44d92092094935265183305a38196ce2822756;hpb=4400e98de24bd267328aa20d57951fb6678297fe diff --git a/scheduler/job.c b/scheduler/job.c index cdeaec6d6..ab1ffab2b 100644 --- a/scheduler/job.c +++ b/scheduler/job.c @@ -1,59 +1,63 @@ /* - * "$Id: job.c 5051 2006-02-02 16:13:16Z mike $" + * "$Id: job.c 6671 2007-07-13 23:35:24Z mike $" * * Job management routines for the Common UNIX Printing System (CUPS). * - * Copyright 1997-2006 by Easy Software Products, all rights reserved. + * Copyright 2007 by Apple Inc. + * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the - * property of Easy Software Products and are protected by Federal - * copyright law. Distribution and use rights are outlined in the file - * "LICENSE.txt" which should have been included with this file. If this - * file is missing or damaged please contact Easy Software Products - * at: - * - * Attn: CUPS Licensing Information - * Easy Software Products - * 44141 Airport View Drive, Suite 204 - * Hollywood, Maryland 20636 USA - * - * Voice: (301) 373-9600 - * EMail: cups-info@cups.org - * WWW: http://www.cups.org + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". * * Contents: * * cupsdAddJob() - Add a new job to the job queue... * cupsdCancelJob() - Cancel the specified print job. - * cupsdCancelJobs() - Cancel all jobs for the given destination/user... - * cupsdCheckJobs() - Check the pending jobs and start any if the - * destination is available. + * cupsdCancelJobs() - Cancel all jobs for the given + * destination/user... + * cupsdCheckJobs() - Check the pending jobs and start any if + * the destination is available. * cupsdCleanJobs() - Clean out old jobs. + * cupsdFinishJob() - Finish a job. * cupsdFreeAllJobs() - Free all jobs from memory. * cupsdFindJob() - Find the specified job. * cupsdGetPrinterJobCount() - Get the number of pending, processing, - * or held jobs in a printer or class. * cupsdGetUserJobCount() - Get the number of pending, processing, - * or held jobs for a user. * cupsdHoldJob() - Hold the specified job. * cupsdLoadAllJobs() - Load all jobs from disk. + * cupsdLoadJob() - Load a single job... * cupsdMoveJob() - Move the specified job to a different * destination. * cupsdReleaseJob() - Release the specified job. * cupsdRestartJob() - Restart the specified job. + * cupsdSaveAllJobs() - Save a summary of all jobs to disk. * cupsdSaveJob() - Save a job to disk. * cupsdSetJobHoldUntil() - Set the hold time for a job... - * cupsdSetJobPriority() - Set the priority of a job, moving it up/down - * in the list as needed. - * cupsdStartJob() - Start a print job. + * cupsdSetJobPriority() - Set the priority of a job, moving it + * up/down in the list as needed. * cupsdStopAllJobs() - Stop all print jobs. * cupsdStopJob() - Stop a print job. - * cupsdUpdateJob() - Read a status update from a job's filters. - * compare_active_jobs() - Compare the job IDs and priorities of two jobs. + * cupsdUnloadCompletedJobs() - Flush completed job history from memory. + * compare_active_jobs() - Compare the job IDs and priorities of two + * jobs. * compare_jobs() - Compare the job IDs of two jobs. - * ipp_length() - Compute the size of the buffer needed to hold - * the textual IPP attributes. - * set_hold_until() - Set the hold time and update job-hold-until attribute. + * free_job() - Free all memory used by a job. + * ipp_length() - Compute the size of the buffer needed to + * hold the textual IPP attributes. + * load_job_cache() - Load jobs from the job.cache file. + * load_next_job_id() - Load the NextJobId value from the + * job.cache file. + * load_request_root() - Load jobs from the RequestRoot directory. + * set_time() - Set one of the "time-at-xyz" attributes... + * set_hold_until() - Set the hold time and update job-hold-until + * attribute... + * start_job() - Start a print job. + * unload_job() - Unload a job from memory. + * update_job() - Read a status update from a jobs filters. + * update_job_attrs() - Update the job-printer-* attributes. */ /* @@ -85,9 +89,17 @@ static mime_filter_t gziptoany_filter = static int compare_active_jobs(void *first, void *second, void *data); static int compare_jobs(void *first, void *second, void *data); +static void free_job(cupsd_job_t *job); static int ipp_length(ipp_t *ipp); +static void load_job_cache(const char *filename); +static void load_next_job_id(const char *filename); +static void load_request_root(void); static void set_time(cupsd_job_t *job, const char *name); static void set_hold_until(cupsd_job_t *job, time_t holdtime); +static void start_job(cupsd_job_t *job, cupsd_printer_t *printer); +static void unload_job(cupsd_job_t *job); +static void update_job(cupsd_job_t *job); +static void update_job_attrs(cupsd_job_t *job); /* @@ -103,12 +115,16 @@ cupsdAddJob(int priority, /* I - Job priority */ job = calloc(sizeof(cupsd_job_t), 1); - job->id = NextJobId ++; - job->priority = priority; - job->back_pipes[0] = -1; - job->back_pipes[1] = -1; - job->print_pipes[0] = -1; - job->print_pipes[1] = -1; + job->id = NextJobId ++; + job->priority = priority; + job->back_pipes[0] = -1; + job->back_pipes[1] = -1; + job->print_pipes[0] = -1; + job->print_pipes[1] = -1; + job->side_pipes[0] = -1; + job->side_pipes[1] = -1; + job->status_pipes[0] = -1; + job->status_pipes[1] = -1; cupsdSetString(&job->dest, dest); @@ -128,44 +144,88 @@ cupsdAddJob(int priority, /* I - Job priority */ */ void -cupsdCancelJob(cupsd_job_t *job, /* I - Job to cancel */ - int purge) /* I - Purge jobs? */ +cupsdCancelJob(cupsd_job_t *job, /* I - Job to cancel */ + int purge, /* I - Purge jobs? */ + ipp_jstate_t newstate) /* I - New job state */ { int i; /* Looping var */ char filename[1024]; /* Job filename */ + cupsd_printer_t *printer; /* Printer used by job */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCancelJob: id = %d", job->id); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCancelJob: id = %d", job->id); /* - * Remove the job from the active list... + * Stop any processes that are working on the current job... */ - cupsArrayRemove(ActiveJobs, job); + printer = job->printer; + + if (job->state_value == IPP_JOB_PROCESSING) + cupsdStopJob(job, 0); + + cupsdLoadJob(job); + + if (job->attrs) + job->state->values[0].integer = newstate; + + job->state_value = newstate; + + set_time(job, "time-at-completed"); /* - * Stop any processes that are working on the current job... + * Send any pending notifications and then expire them... */ - if (job->state->values[0].integer == IPP_JOB_PROCESSING) - cupsdStopJob(job, 0); + switch (newstate) + { + default : + break; - cupsArrayRemove(ActiveJobs, job); + case IPP_JOB_CANCELED : + cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, printer, job, + purge ? "Job purged." : "Job canceled."); + break; - job->state->values[0].integer = IPP_JOB_CANCELLED; + case IPP_JOB_ABORTED : + cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, printer, job, + "Job aborted; please consult the error_log file " + "for details."); + break; - set_time(job, "time-at-completed"); + case IPP_JOB_COMPLETED : + /* + * Clear the printer's printer-state-message and move on... + */ + + printer->state_message[0] = '\0'; + + cupsdSetPrinterState(printer, IPP_PRINTER_IDLE, 0); + + cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, printer, job, + "Job completed."); + break; + } cupsdExpireSubscriptions(NULL, job); + /* + * Remove the job from the active list... + */ + + cupsArrayRemove(ActiveJobs, job); + /* * Remove any authentication data... */ - snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, - job->id); + snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id); unlink(filename); + cupsdClearString(&job->auth_username); + cupsdClearString(&job->auth_domain); + cupsdClearString(&job->auth_password); + /* * Remove the print file for good if we aren't preserving jobs or * files... @@ -173,8 +233,8 @@ cupsdCancelJob(cupsd_job_t *job, /* I - Job to cancel */ job->current_file = 0; - if (!JobHistory || !JobFiles || purge || - (job->dtype & CUPS_PRINTER_REMOTE)) + if (!JobHistory || !JobFiles || purge || (job->dtype & CUPS_PRINTER_REMOTE)) + { for (i = 1; i <= job->num_files; i ++) { snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, @@ -182,6 +242,17 @@ cupsdCancelJob(cupsd_job_t *job, /* I - Job to cancel */ unlink(filename); } + if (job->num_files > 0) + { + free(job->filetypes); + free(job->compressions); + + job->num_files = 0; + job->filetypes = NULL; + job->compressions = NULL; + } + } + if (JobHistory && !purge && !(job->dtype & CUPS_PRINTER_REMOTE)) { /* @@ -210,19 +281,7 @@ cupsdCancelJob(cupsd_job_t *job, /* I - Job to cancel */ * Free all memory used... */ - if (job->attrs != NULL) - ippDelete(job->attrs); - - if (job->num_files > 0) - { - free(job->compressions); - free(job->filetypes); - } - - cupsdClearString(&job->username); - cupsdClearString(&job->dest); - - free(job); + free_job(job); } } @@ -249,10 +308,7 @@ cupsdCancelJobs(const char *dest, /* I - Destination to cancel */ * Cancel all jobs matching this destination/user... */ - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - purge ? "Job purged." : "Job canceled."); - - cupsdCancelJob(job, purge); + cupsdCancelJob(job, purge, IPP_JOB_CANCELED); } cupsdCheckJobs(); @@ -274,6 +330,10 @@ cupsdCheckJobs(void) DEBUG_puts("cupsdCheckJobs()"); + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d", + cupsArrayCount(ActiveJobs), Sleeping, NeedReload); + for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); job; job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) @@ -282,17 +342,26 @@ cupsdCheckJobs(void) * Start held jobs if they are ready... */ - if (job->state->values[0].integer == IPP_JOB_HELD && + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdCheckJobs: Job %d: state_value=%d, loaded=%s", + job->id, job->state_value, job->attrs ? "yes" : "no"); + + if (job->state_value == IPP_JOB_HELD && job->hold_until && job->hold_until < time(NULL)) + { + if (job->pending_timeout) + cupsdTimeoutJob(job); /* Add trailing banner as needed */ + job->state->values[0].integer = IPP_JOB_PENDING; + job->state_value = IPP_JOB_PENDING; + } /* * Start pending jobs if the destination is available... */ - if (job->state->values[0].integer == IPP_JOB_PENDING && !NeedReload && - !Sleeping) + if (job->state_value == IPP_JOB_PENDING && !NeedReload && !Sleeping) { printer = cupsdFindDest(job->dest); pclass = NULL; @@ -306,13 +375,12 @@ cupsdCheckJobs(void) pclass = printer; - if (!(pclass->type & CUPS_PRINTER_REMOTE)) - { - if (pclass->state != IPP_PRINTER_STOPPED) - printer = cupsdFindAvailablePrinter(job->dest); - else - printer = NULL; - } + if (pclass->state == IPP_PRINTER_STOPPED) + printer = NULL; + else if (pclass->type & CUPS_PRINTER_REMOTE) + break; + else + printer = cupsdFindAvailablePrinter(printer->name); } if (!printer && !pclass) @@ -323,13 +391,14 @@ cupsdCheckJobs(void) */ cupsdLogMessage(CUPSD_LOG_WARN, - "Printer/class %s has gone away; cancelling job %d!", - job->dest, job->id); + "[Job %d] Printer/class %s has gone away; canceling job!", + job->id, job->dest); cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the destination printer/class has gone away."); + "Job canceled because the destination printer/class has " + "gone away."); - cupsdCancelJob(job, 1); + cupsdCancelJob(job, 1, IPP_JOB_ABORTED); } else if (printer) { @@ -356,10 +425,25 @@ cupsdCheckJobs(void) "job-actual-printer-uri", NULL, printer->uri); } - if (printer->state == IPP_PRINTER_IDLE || /* Printer is idle */ - ((printer->type & CUPS_PRINTER_REMOTE) && /* Printer is remote */ - !printer->job)) /* and not printing a job */ - cupsdStartJob(job, printer); + if ((!(printer->type & CUPS_PRINTER_DISCOVERED) && /* Printer is local */ + printer->state == IPP_PRINTER_IDLE) || /* and idle */ + ((printer->type & CUPS_PRINTER_DISCOVERED) && /* Printer is remote */ + !printer->job)) /* and not printing */ + { + /* + * Clear any message and reasons for the queue... + */ + + printer->state_message[0] = '\0'; + + cupsdSetPrinterReasons(printer, "none"); + + /* + * Start the job... + */ + + start_job(job, printer); + } } } } @@ -376,14 +460,14 @@ cupsdCleanJobs(void) cupsd_job_t *job; /* Current job */ - if (!MaxJobs) + if (MaxJobs <= 0) return; for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); job && cupsArrayCount(Jobs) >= MaxJobs; job = (cupsd_job_t *)cupsArrayNext(Jobs)) - if (job->state->values[0].integer >= IPP_JOB_CANCELLED) - cupsdCancelJob(job, 1); + if (job->state_value >= IPP_JOB_CANCELED) + cupsdCancelJob(job, 1, IPP_JOB_CANCELED); } @@ -394,33 +478,31 @@ cupsdCleanJobs(void) void cupsdFinishJob(cupsd_job_t *job) /* I - Job */ { - int job_history; /* Did cupsdCancelJob() keep the job? */ cupsd_printer_t *printer; /* Current printer */ + ipp_attribute_t *attr; /* job-hold-until attribute */ - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdFinishJob: job %d, file %d is complete.", + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] File %d is complete.", job->id, job->current_file - 1); - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFinishJob: job->status is %d", - job->status); + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] cupsdFinishJob: job->status is %d", + job->id, job->status); - if (job->status_buffer && job->current_file >= job->num_files) + if (job->status_buffer && + (job->status < 0 || job->current_file >= job->num_files)) { /* * Close the pipe and clear the input bit. */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdFinishJob: Removing fd %d from InputSet...", - job->status_buffer->fd); - - FD_CLR(job->status_buffer->fd, InputSet); + cupsdRemoveSelect(job->status_buffer->fd); cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdFinishJob: Closing status input pipe %d...", - job->status_buffer->fd); + "[Job %d] cupsdFinishJob: Closing status pipes [ %d %d ]...", + job->id, job->status_pipes[0], job->status_pipes[1]); + cupsdClosePipe(job->status_pipes); cupsdStatBufDelete(job->status_buffer); job->status_buffer = NULL; @@ -428,13 +510,44 @@ cupsdFinishJob(cupsd_job_t *job) /* I - Job */ printer = job->printer; + update_job_attrs(job); + if (job->status < 0) { /* * Backend had errors; stop it... */ - switch (-job->status) + int exit_code; /* Exit code from backend */ + + + /* + * Convert the status to an exit code. Due to the way the W* macros are + * implemented on MacOS X (bug?), we have to store the exit status in a + * variable first and then convert... + */ + + exit_code = -job->status; + if (WIFEXITED(exit_code)) + exit_code = WEXITSTATUS(exit_code); + else + exit_code = job->status; + + cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Backend returned status %d (%s)", + job->id, exit_code, + exit_code == CUPS_BACKEND_FAILED ? "failed" : + exit_code == CUPS_BACKEND_AUTH_REQUIRED ? + "authentication required" : + exit_code == CUPS_BACKEND_HOLD ? "hold job" : + exit_code == CUPS_BACKEND_STOP ? "stop printer" : + exit_code == CUPS_BACKEND_CANCEL ? "cancel job" : + exit_code < 0 ? "crashed" : "unknown"); + + /* + * Do what needs to be done... + */ + + switch (exit_code) { default : case CUPS_BACKEND_FAILED : @@ -444,7 +557,18 @@ cupsdFinishJob(cupsd_job_t *job) /* I - Job */ */ cupsdStopJob(job, 0); - job->state->values[0].integer = IPP_JOB_PENDING; + + if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)) + { + /* + * Mark the job as pending again - we'll retry on another + * printer... + */ + + job->state->values[0].integer = IPP_JOB_PENDING; + job->state_value = IPP_JOB_PENDING; + } + cupsdSaveJob(job); /* @@ -471,14 +595,11 @@ cupsdFinishJob(cupsd_job_t *job) /* I - Job */ */ cupsdLogMessage(CUPSD_LOG_ERROR, - "Canceling job %d since it could not be sent after %d tries.", + "[Job %d] Canceling job since it could not be " + "sent after %d tries.", job->id, JobRetryLimit); - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled since it could not be sent after %d tries.", - JobRetryLimit); - - cupsdCancelJob(job, 0); + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); } else { @@ -487,10 +608,23 @@ cupsdFinishJob(cupsd_job_t *job) /* I - Job */ */ set_hold_until(job, time(NULL) + JobRetryInterval); + + cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, + "Job held due to fax errors; please consult " + "the error_log file for details."); + cupsdSetPrinterState(printer, IPP_PRINTER_IDLE, 0); } } else if (!strcmp(printer->error_policy, "abort-job")) - cupsdCancelJob(job, 0); + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); + else + { + cupsdSetPrinterState(printer, IPP_PRINTER_STOPPED, 1); + + cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, printer, job, + "Job stopped due to backend errors; please consult " + "the error_log file for details."); + } break; case CUPS_BACKEND_CANCEL : @@ -498,7 +632,7 @@ cupsdFinishJob(cupsd_job_t *job) /* I - Job */ * Cancel the job... */ - cupsdCancelJob(job, 0); + cupsdCancelJob(job, 0, IPP_JOB_CANCELED); break; case CUPS_BACKEND_HOLD : @@ -507,8 +641,17 @@ cupsdFinishJob(cupsd_job_t *job) /* I - Job */ */ cupsdStopJob(job, 0); + cupsdSetJobHoldUntil(job, "indefinite"); + + job->state->values[0].integer = IPP_JOB_HELD; + job->state_value = IPP_JOB_HELD; + cupsdSaveJob(job); + + cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, printer, job, + "Job held due to backend errors; please consult " + "the error_log file for details."); break; case CUPS_BACKEND_STOP : @@ -517,13 +660,37 @@ cupsdFinishJob(cupsd_job_t *job) /* I - Job */ */ cupsdStopJob(job, 0); + + job->state->values[0].integer = IPP_JOB_PENDING; + job->state_value = IPP_JOB_PENDING; + cupsdSaveJob(job); cupsdSetPrinterState(printer, IPP_PRINTER_STOPPED, 1); + + cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, printer, job, + "Job stopped due to backend errors; please consult " + "the error_log file for details."); break; case CUPS_BACKEND_AUTH_REQUIRED : cupsdStopJob(job, 0); - cupsdSetJobHoldUntil(job, "authenticated"); + + cupsdSetJobHoldUntil(job, "auth-info-required"); + + if ((attr = ippFindAttribute(job->attrs, "job-hold-until", + IPP_TAG_KEYWORD)) == NULL) + attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME); + + if (attr) + { + attr->value_tag = IPP_TAG_KEYWORD; + cupsdSetString(&(attr->values[0].string.text), + "auth-info-required"); + } + + job->state->values[0].integer = IPP_JOB_HELD; + job->state_value = IPP_JOB_HELD; + cupsdSaveJob(job); cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, printer, job, @@ -545,7 +712,7 @@ cupsdFinishJob(cupsd_job_t *job) /* I - Job */ cupsdStopJob(job, 1); cupsdSaveJob(job); - cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, job->printer, job, + cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, printer, job, "Job stopped due to filter errors; please consult the " "error_log file for details."); cupsdCheckJobs(); @@ -563,7 +730,7 @@ cupsdFinishJob(cupsd_job_t *job) /* I - Job */ */ FilterLevel -= job->cost; - cupsdStartJob(job, job->printer); + start_job(job, printer); } else { @@ -571,25 +738,7 @@ cupsdFinishJob(cupsd_job_t *job) /* I - Job */ * Close out this job... */ - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job completed successfully."); - - job_history = JobHistory && !(job->dtype & CUPS_PRINTER_REMOTE); - - cupsdCancelJob(job, 0); - - if (job_history) - { - job->state->values[0].integer = IPP_JOB_COMPLETED; - cupsdSaveJob(job); - } - - /* - * Clear the printer's state_message and move on... - */ - - printer->state_message[0] = '\0'; - + cupsdCancelJob(job, 0, IPP_JOB_COMPLETED); cupsdCheckJobs(); } } @@ -606,9 +755,13 @@ cupsdFreeAllJobs(void) cupsd_job_t *job; /* Current job */ + if (!Jobs) + return; + cupsdHoldSignals(); - cupsdStopAllJobs(); + cupsdStopAllJobs(1); + cupsdSaveAllJobs(); for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); job; @@ -617,15 +770,7 @@ cupsdFreeAllJobs(void) cupsArrayRemove(Jobs, job); cupsArrayRemove(ActiveJobs, job); - ippDelete(job->attrs); - - if (job->num_files > 0) - { - free(job->compressions); - free(job->filetypes); - } - - free(job); + free_job(job); } cupsdReleaseSignals(); @@ -664,7 +809,7 @@ cupsdGetPrinterJobCount( for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0; job; job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) - if (!strcasecmp(job->dest, dest)) + if (job->dest && !strcasecmp(job->dest, dest)) count ++; return (count); @@ -701,14 +846,17 @@ cupsdGetUserJobCount( void cupsdHoldJob(cupsd_job_t *job) /* I - Job data */ { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdHoldJob: id = %d", job->id); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdHoldJob: id = %d", job->id); - if (job->state->values[0].integer == IPP_JOB_PROCESSING) + if (job->state_value == IPP_JOB_PROCESSING) cupsdStopJob(job, 0); + else + cupsdLoadJob(job); DEBUG_puts("cupsdHoldJob: setting state to held..."); job->state->values[0].integer = IPP_JOB_HELD; + job->state_value = IPP_JOB_HELD; cupsdSaveJob(job); @@ -723,30 +871,14 @@ cupsdHoldJob(cupsd_job_t *job) /* I - Job data */ void cupsdLoadAllJobs(void) { - cups_dir_t *dir; /* Directory */ - cups_dentry_t *dent; /* Directory entry */ - char filename[1024]; /* Full filename of job file */ - cups_file_t *fp; /* Job file */ - cupsd_job_t *job; /* New job */ - int jobid, /* Current job ID */ - fileid; /* Current file ID */ - ipp_attribute_t *attr; /* Job attribute */ - char method[HTTP_MAX_URI], - /* Method portion of URI */ - username[HTTP_MAX_URI], - /* Username portion of URI */ - host[HTTP_MAX_URI], - /* Host portion of URI */ - resource[HTTP_MAX_URI]; - /* Resource portion of URI */ - int port; /* Port portion of URI */ - const char *dest; /* Destination */ - mime_type_t **filetypes; /* New filetypes array */ - int *compressions; /* New compressions array */ + char filename[1024]; /* Full filename of job.cache file */ + struct stat fileinfo, /* Information on job.cache file */ + dirinfo; /* Information on RequestRoot dir */ + /* - * First create the job lists... + * Create the job arrays as needed... */ if (!Jobs) @@ -756,234 +888,253 @@ cupsdLoadAllJobs(void) ActiveJobs = cupsArrayNew(compare_active_jobs, NULL); /* - * Then open the requests directory... + * See whether the job.cache file is older than the RequestRoot directory... */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdLoadAllJobs: Scanning %s...", - RequestRoot); + snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir); - if ((dir = cupsDirOpen(RequestRoot)) == NULL) + if (stat(filename, &fileinfo)) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Unable to open spool directory %s: %s", - RequestRoot, strerror(errno)); - return; + fileinfo.st_mtime = 0; + + if (errno != ENOENT) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to get file information for \"%s\" - %s", + filename, strerror(errno)); + } + + if (stat(RequestRoot, &dirinfo)) + { + dirinfo.st_mtime = 0; + + if (errno != ENOENT) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to get directory information for \"%s\" - %s", + RequestRoot, strerror(errno)); } /* - * Read all the c##### files... + * Load the most recent source for job data... */ - while ((dent = cupsDirRead(dir)) != NULL) - if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c') - { - /* - * Allocate memory for the job... - */ - - if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Ran out of memory for jobs!"); - cupsDirClose(dir); - return; - } + if (dirinfo.st_mtime > fileinfo.st_mtime) + { + load_request_root(); - if ((job->attrs = ippNew()) == NULL) - { - free(job); - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Ran out of memory for job attributes!"); - cupsDirClose(dir); - return; - } + load_next_job_id(filename); + } + else + load_job_cache(filename); - /* - * Assign the job ID... - */ + /* + * Clean out old jobs as needed... + */ - job->id = atoi(dent->filename + 1); - job->back_pipes[0] = -1; - job->back_pipes[1] = -1; - job->print_pipes[0] = -1; - job->print_pipes[1] = -1; + if (MaxJobs > 0 && cupsArrayCount(Jobs) >= MaxJobs) + cupsdCleanJobs(); +} - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdLoadAllJobs: Loading attributes for job %d...", - job->id); - if (job->id >= NextJobId) - NextJobId = job->id + 1; +/* + * 'cupsdLoadJob()' - Load a single job... + */ - /* - * Load the job control file... - */ +void +cupsdLoadJob(cupsd_job_t *job) /* I - Job */ +{ + char jobfile[1024]; /* Job filename */ + cups_file_t *fp; /* Job file */ + int fileid; /* Current file ID */ + ipp_attribute_t *attr; /* Job attribute */ + const char *dest; /* Destination name */ + cupsd_printer_t *destptr; /* Pointer to destination */ + mime_type_t **filetypes; /* New filetypes array */ + int *compressions; /* New compressions array */ - snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->filename); - if ((fp = cupsFileOpen(filename, "r")) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Unable to open job control file " - "\"%s\" - %s!", - filename, strerror(errno)); - ippDelete(job->attrs); - free(job); - unlink(filename); - continue; - } - else - { - if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, - job->attrs) != IPP_DATA) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Unable to read job control file " - "\"%s\"!", - filename); - cupsFileClose(fp); - ippDelete(job->attrs); - free(job); - unlink(filename); - continue; - } - cupsFileClose(fp); - } + if (job->attrs) + { + if (job->state_value > IPP_JOB_STOPPED) + job->access_time = time(NULL); - if ((job->state = ippFindAttribute(job->attrs, "job-state", IPP_TAG_ENUM)) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Missing or bad job-state attribute " - "in control file \"%s\"!", - filename); - ippDelete(job->attrs); - free(job); - unlink(filename); - continue; - } + return; + } - if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", IPP_TAG_URI)) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: No job-printer-uri attribute in " - "control file \"%s\"!", - filename); - ippDelete(job->attrs); - free(job); - unlink(filename); - continue; - } + if ((job->attrs = ippNew()) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Ran out of memory for job attributes!", job->id); + return; + } - httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); + /* + * Load job attributes... + */ - if ((dest = cupsdValidateDest(host, resource, &(job->dtype), - NULL)) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Unable to queue job for destination " - "\"%s\"!", - attr->values[0].string.text); - ippDelete(job->attrs); - free(job); - unlink(filename); - continue; - } + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading attributes...", job->id); - cupsdSetString(&job->dest, dest); + snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, job->id); + if ((fp = cupsFileOpen(jobfile, "r")) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to open job control file \"%s\" - %s!", + job->id, jobfile, strerror(errno)); + ippDelete(job->attrs); + job->attrs = NULL; + return; + } - job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed", - IPP_TAG_INTEGER); - job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME); + if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, job->attrs) != IPP_DATA) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to read job control file \"%s\"!", + job->id, jobfile); + cupsFileClose(fp); + ippDelete(job->attrs); + job->attrs = NULL; + unlink(jobfile); + return; + } - if ((attr = ippFindAttribute(job->attrs, "job-priority", - IPP_TAG_INTEGER)) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Missing or bad job-priority " - "attribute in control file \"%s\"!", - filename); - ippDelete(job->attrs); - free(job); - unlink(filename); - continue; - } - job->priority = attr->values[0].integer; + cupsFileClose(fp); - if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name", - IPP_TAG_NAME)) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Missing or bad " - "job-originating-user-name attribute in control file " - "\"%s\"!", - filename); - ippDelete(job->attrs); - free(job); - unlink(filename); - continue; - } - cupsdSetString(&job->username, attr->values[0].string.text); + /* + * Copy attribute data to the job object... + */ - /* - * Insert the job into the array, sorting by job priority and ID... - */ + if ((job->state = ippFindAttribute(job->attrs, "job-state", + IPP_TAG_ENUM)) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Missing or bad job-state attribute in " + "control file!", + job->id); + ippDelete(job->attrs); + job->attrs = NULL; + unlink(jobfile); + return; + } - cupsArrayAdd(Jobs, job); - if (job->state->values[0].integer < IPP_JOB_STOPPED) - cupsArrayAdd(ActiveJobs,job); + job->state_value = (ipp_jstate_t)job->state->values[0].integer; - /* - * Set the job hold-until time and state... - */ + if (!job->dest) + { + if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", + IPP_TAG_URI)) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] No job-printer-uri attribute in control file!", + job->id); + ippDelete(job->attrs); + job->attrs = NULL; + unlink(jobfile); + return; + } - if (job->state->values[0].integer == IPP_JOB_HELD) - { - if ((attr = ippFindAttribute(job->attrs, "job-hold-until", - IPP_TAG_KEYWORD)) == NULL) - attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME); + if ((dest = cupsdValidateDest(attr->values[0].string.text, &(job->dtype), + &destptr)) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to queue job for destination \"%s\"!", + job->id, attr->values[0].string.text); + ippDelete(job->attrs); + job->attrs = NULL; + unlink(jobfile); + return; + } - if (attr == NULL) - job->state->values[0].integer = IPP_JOB_PENDING; - else - cupsdSetJobHoldUntil(job, attr->values[0].string.text); - } - else if (job->state->values[0].integer == IPP_JOB_PROCESSING) - job->state->values[0].integer = IPP_JOB_PENDING; + cupsdSetString(&job->dest, dest); + } + else if ((destptr = cupsdFindDest(job->dest)) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to queue job for destination \"%s\"!", + job->id, job->dest); + ippDelete(job->attrs); + job->attrs = NULL; + unlink(jobfile); + return; + } + + job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed", + IPP_TAG_INTEGER); + job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME); + + if (!job->priority) + { + if ((attr = ippFindAttribute(job->attrs, "job-priority", + IPP_TAG_INTEGER)) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Missing or bad job-priority attribute in " + "control file!", job->id); + ippDelete(job->attrs); + job->attrs = NULL; + unlink(jobfile); + return; + } + + job->priority = attr->values[0].integer; + } + + if (!job->username) + { + if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name", + IPP_TAG_NAME)) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Missing or bad job-originating-user-name " + "attribute in control file!", job->id); + ippDelete(job->attrs); + job->attrs = NULL; + unlink(jobfile); + return; } + cupsdSetString(&job->username, attr->values[0].string.text); + } + /* - * Read all the d##### files... + * Set the job hold-until time and state... */ - cupsDirRewind(dir); + if (job->state_value == IPP_JOB_HELD) + { + if ((attr = ippFindAttribute(job->attrs, "job-hold-until", + IPP_TAG_KEYWORD)) == NULL) + attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME); - while ((dent = cupsDirRead(dir)) != NULL) - if (strlen(dent->filename) > 7 && dent->filename[0] == 'd' && - strchr(dent->filename, '-')) + if (attr) + cupsdSetJobHoldUntil(job, attr->values[0].string.text); + else { - /* - * Find the job... - */ + job->state->values[0].integer = IPP_JOB_PENDING; + job->state_value = IPP_JOB_PENDING; + } + } + else if (job->state_value == IPP_JOB_PROCESSING) + { + job->state->values[0].integer = IPP_JOB_PENDING; + job->state_value = IPP_JOB_PENDING; + } - jobid = atoi(dent->filename + 1); - fileid = atoi(strchr(dent->filename, '-') + 1); + if (!job->num_files) + { + /* + * Find all the d##### files... + */ - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdLoadAllJobs: Auto-typing document file %s...", - dent->filename); + for (fileid = 1; fileid < 10000; fileid ++) + { + snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot, + job->id, fileid); - snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->filename); + if (access(jobfile, 0)) + break; - if ((job = cupsdFindJob(jobid)) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Orphaned print file \"%s\"!", - filename); - unlink(filename); - continue; - } + cupsdLogMessage(CUPSD_LOG_DEBUG, + "[Job %d] Auto-typing document file \"%s\"...", + job->id, jobfile); if (fileid > job->num_files) { @@ -997,15 +1148,16 @@ cupsdLoadAllJobs(void) compressions = (int *)realloc(job->compressions, sizeof(int) * fileid); filetypes = (mime_type_t **)realloc(job->filetypes, - sizeof(mime_type_t *) * fileid); + sizeof(mime_type_t *) * + fileid); } - if (compressions == NULL || filetypes == NULL) + if (!compressions || !filetypes) { cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdLoadAllJobs: Ran out of memory for job file " - "types!"); - continue; + "[Job %d] Ran out of memory for job file types!", + job->id); + return; } job->compressions = compressions; @@ -1013,21 +1165,55 @@ cupsdLoadAllJobs(void) job->num_files = fileid; } - job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, filename, NULL, + job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, jobfile, NULL, job->compressions + fileid - 1); - if (job->filetypes[fileid - 1] == NULL) + if (!job->filetypes[fileid - 1]) job->filetypes[fileid - 1] = mimeType(MimeDatabase, "application", "vnd.cups-raw"); } - - cupsDirClose(dir); + } /* - * Clean out old jobs as needed... + * Load authentication information as needed... */ - cupsdCleanJobs(); + if (job->state_value < IPP_JOB_STOPPED) + { + snprintf(jobfile, sizeof(jobfile), "%s/a%05d", RequestRoot, job->id); + + cupsdClearString(&job->auth_username); + cupsdClearString(&job->auth_domain); + cupsdClearString(&job->auth_password); + + if ((fp = cupsFileOpen(jobfile, "r")) != NULL) + { + int i, /* Looping var */ + bytes; /* Size of auth data */ + char line[255], /* Line from file */ + data[255]; /* Decoded data */ + + + for (i = 0; + i < destptr->num_auth_info_required && + cupsFileGets(fp, line, sizeof(line)); + i ++) + { + bytes = sizeof(data); + httpDecode64_2(data, &bytes, line); + + if (!strcmp(destptr->auth_info_required[i], "username")) + cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s", data); + else if (!strcmp(destptr->auth_info_required[i], "domain")) + cupsdSetStringf(&job->auth_domain, "AUTH_DOMAIN=%s", data); + else if (!strcmp(destptr->auth_info_required[i], "password")) + cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s", data); + } + + cupsFileClose(fp); + } + } + job->access_time = time(NULL); } @@ -1036,38 +1222,54 @@ cupsdLoadAllJobs(void) */ void -cupsdMoveJob(cupsd_job_t *job, /* I - Job */ - const char *dest) /* I - Destination */ +cupsdMoveJob(cupsd_job_t *job, /* I - Job */ + cupsd_printer_t *p) /* I - Destination printer or class */ { ipp_attribute_t *attr; /* job-printer-uri attribute */ - cupsd_printer_t *p; /* Destination printer or class */ + const char *olddest; /* Old destination */ + cupsd_printer_t *oldp; /* Old pointer */ /* - * Find the printer... + * Don't move completed jobs... */ - if ((p = cupsdFindDest(dest)) == NULL) + if (job->state_value > IPP_JOB_STOPPED) return; /* - * Don't move completed jobs... + * Get the old destination... */ - if (job->state->values[0].integer >= IPP_JOB_PROCESSING) - return; + olddest = job->dest; + + if (job->printer) + oldp = job->printer; + else + oldp = cupsdFindDest(olddest); /* * Change the destination information... */ - cupsdSetString(&job->dest, dest); + cupsdLoadJob(job); + + cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, oldp, job, + "Job #%d moved from %s to %s.", job->id, olddest, + p->name); + + cupsdSetString(&job->dest, p->name); job->dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT); - if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", IPP_TAG_URI)) != NULL) + if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", + IPP_TAG_URI)) != NULL) cupsdSetString(&(attr->values[0].string.text), p->uri); + cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job, + "Job #%d moved from %s to %s.", job->id, olddest, + p->name); + cupsdSaveJob(job); } @@ -1079,13 +1281,14 @@ cupsdMoveJob(cupsd_job_t *job, /* I - Job */ void cupsdReleaseJob(cupsd_job_t *job) /* I - Job */ { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReleaseJob: id = %d", job->id); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReleaseJob: id = %d", job->id); - if (job->state->values[0].integer == IPP_JOB_HELD) + if (job->state_value == IPP_JOB_HELD) { DEBUG_puts("cupsdReleaseJob: setting state to pending..."); job->state->values[0].integer = IPP_JOB_PENDING; + job->state_value = IPP_JOB_PENDING; cupsdSaveJob(job); cupsdCheckJobs(); } @@ -1099,18 +1302,101 @@ cupsdReleaseJob(cupsd_job_t *job) /* I - Job */ void cupsdRestartJob(cupsd_job_t *job) /* I - Job */ { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdRestartJob: id = %d", job->id); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRestartJob: id = %d", job->id); - if (job->state->values[0].integer == IPP_JOB_STOPPED || JobFiles) + if (job->state_value == IPP_JOB_STOPPED || job->num_files) { + ipp_jstate_t old_state; /* Old job state */ + + + cupsdLoadJob(job); + + old_state = job->state_value; + job->tries = 0; job->state->values[0].integer = IPP_JOB_PENDING; + job->state_value = IPP_JOB_PENDING; + cupsdSaveJob(job); + + if (old_state > IPP_JOB_STOPPED) + cupsArrayAdd(ActiveJobs, job); + cupsdCheckJobs(); } } +/* + * 'cupsdSaveAllJobs()' - Save a summary of all jobs to disk. + */ + +void +cupsdSaveAllJobs(void) +{ + int i; /* Looping var */ + cups_file_t *fp; /* Job cache file */ + char temp[1024]; /* Temporary string */ + cupsd_job_t *job; /* Current job */ + time_t curtime; /* Current time */ + struct tm *curdate; /* Current date */ + + + snprintf(temp, sizeof(temp), "%s/job.cache", CacheDir); + if ((fp = cupsFileOpen(temp, "w")) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to create job cache file \"%s\" - %s", + temp, strerror(errno)); + return; + } + + cupsdLogMessage(CUPSD_LOG_INFO, "Saving job cache file \"%s\"...", temp); + + /* + * Restrict access to the file... + */ + + fchown(cupsFileNumber(fp), getuid(), Group); + fchmod(cupsFileNumber(fp), ConfigFilePerm); + + /* + * Write a small header to the file... + */ + + curtime = time(NULL); + curdate = localtime(&curtime); + strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate); + + cupsFilePuts(fp, "# Job cache file for " CUPS_SVERSION "\n"); + cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp); + cupsFilePrintf(fp, "NextJobId %d\n", NextJobId); + + /* + * Write each job known to the system... + */ + + for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); + job; + job = (cupsd_job_t *)cupsArrayNext(Jobs)) + { + cupsFilePrintf(fp, "\n", job->id); + cupsFilePrintf(fp, "State %d\n", job->state_value); + cupsFilePrintf(fp, "Priority %d\n", job->priority); + cupsFilePrintf(fp, "Username %s\n", job->username); + cupsFilePrintf(fp, "Destination %s\n", job->dest); + cupsFilePrintf(fp, "DestType %d\n", job->dtype); + 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, + job->filetypes[i]->type, job->compressions[i]); + cupsFilePuts(fp, "\n"); + } + + cupsFileClose(fp); +} + + /* * 'cupsdSaveJob()' - Save a job to disk. */ @@ -1122,21 +1408,28 @@ cupsdSaveJob(cupsd_job_t *job) /* I - Job */ cups_file_t *fp; /* Job file */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSaveJob(job=%p(%d)): job->attrs=%p", + job, job->id, job->attrs); + snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id); if ((fp = cupsFileOpen(filename, "w")) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdSaveJob: Unable to create job control file " - "\"%s\" - %s.", - filename, strerror(errno)); + "[Job %d] Unable to create job control file \"%s\" - %s.", + job->id, filename, strerror(errno)); return; } fchmod(cupsFileNumber(fp), 0600); fchown(cupsFileNumber(fp), RunUser, Group); - ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, job->attrs); + job->attrs->state = IPP_IDLE; + + if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, + job->attrs) != IPP_DATA) + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to write job control file!", job->id); cupsFileClose(fp); } @@ -1157,12 +1450,12 @@ cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */ int second; /* Hold second */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSetJobHoldUntil(%d, \"%s\")", + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil(%d, \"%s\")", job->id, when); second = 0; - if (!strcmp(when, "indefinite") || !strcmp(when, "authenticated")) + if (!strcmp(when, "indefinite") || !strcmp(when, "auth-info-required")) { /* * Hold indefinitely... @@ -1186,7 +1479,7 @@ cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */ ((29 - curdate->tm_hour) * 60 + 59 - curdate->tm_min) * 60 + 60 - curdate->tm_sec; } - else if (!strcmp(when, "evening") || strcmp(when, "night")) + else if (!strcmp(when, "evening") || !strcmp(when, "night")) { /* * Hold to 6pm unless local time is > 6pm or < 6am. @@ -1201,7 +1494,7 @@ cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */ job->hold_until = curtime + ((17 - curdate->tm_hour) * 60 + 59 - curdate->tm_min) * 60 + 60 - curdate->tm_sec; - } + } else if (!strcmp(when, "second-shift")) { /* @@ -1217,7 +1510,7 @@ cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */ job->hold_until = curtime + ((15 - curdate->tm_hour) * 60 + 59 - curdate->tm_min) * 60 + 60 - curdate->tm_sec; - } + } else if (!strcmp(when, "third-shift")) { /* @@ -1233,7 +1526,7 @@ cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */ job->hold_until = curtime + ((23 - curdate->tm_hour) * 60 + 59 - curdate->tm_min) * 60 + 60 - curdate->tm_sec; - } + } else if (!strcmp(when, "weekend")) { /* @@ -1269,10 +1562,10 @@ cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */ */ if (job->hold_until < curtime) - job->hold_until += 24 * 60 * 60 * 60; + job->hold_until += 24 * 60 * 60; } - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSetJobHoldUntil: hold_until = %d", + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil: hold_until = %d", (int)job->hold_until); } @@ -1294,7 +1587,7 @@ cupsdSetJobPriority( * Don't change completed jobs... */ - if (job->state->values[0].integer >= IPP_JOB_PROCESSING) + if (job->state_value >= IPP_JOB_PROCESSING) return; /* @@ -1305,7 +1598,8 @@ cupsdSetJobPriority( job->priority = priority; - if ((attr = ippFindAttribute(job->attrs, "job-priority", IPP_TAG_INTEGER)) != NULL) + if ((attr = ippFindAttribute(job->attrs, "job-priority", + IPP_TAG_INTEGER)) != NULL) attr->values[0].integer = priority; else ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority", @@ -1318,124 +1612,878 @@ cupsdSetJobPriority( /* - * 'cupsdStartJob()' - Start a print job. + * 'cupsdStopAllJobs()' - Stop all print jobs. */ void -cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ - cupsd_printer_t *printer) /* I - Printer to print job */ +cupsdStopAllJobs(int force) /* I - 1 = Force all filters to stop */ { - int i; /* Looping var */ - int slot; /* Pipe slot */ - cups_array_t *filters; /* Filters for job */ - mime_filter_t *filter, /* Current filter */ - port_monitor; /* Port monitor filter */ - char method[255], /* Method for output */ - *optptr, /* Pointer to options */ - *valptr; /* Pointer in value string */ - ipp_attribute_t *attr; /* Current attribute */ - struct stat backinfo; /* Backend file information */ - int backroot; /* Run backend as root? */ - int pid; /* Process ID of new filter process */ - int banner_page; /* 1 if banner page, 0 otherwise */ - int statusfds[2], /* Pipes used between the filters and scheduler */ - filterfds[2][2];/* Pipes used between the filters */ - int envc; /* Number of environment variables */ - char *argv[8], /* Filter command-line arguments */ - sani_uri[1024], /* Sanitized DEVICE_URI env var */ - filename[1024], /* Job filename */ - command[1024], /* Full path to filter/backend command */ - jobid[255], /* Job ID string */ - title[IPP_MAX_NAME], - /* Job title string */ - copies[255], /* # copies string */ - *envp[MAX_ENV], /* Environment variables */ - charset[255], /* CHARSET environment variable */ - class_name[255],/* CLASS environment variable */ - classification[1024], - /* CLASSIFICATION environment variable */ - content_type[1024], - /* CONTENT_TYPE environment variable */ - device_uri[1024], - /* DEVICE_URI environment variable */ - lang[255], /* LANG environment variable */ - ppd[1024], /* PPD environment variable */ - printer_name[255], - /* PRINTER environment variable */ - rip_max_cache[255]; - /* RIP_MAX_CACHE environment variable */ - static char *options = NULL;/* Full list of options */ - static int optlength = 0; /* Length of option buffer */ + cupsd_job_t *job; /* Current job */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob() id = %d, file = %d/%d", - job->id, job->current_file, job->num_files); + DEBUG_puts("cupsdStopAllJobs()"); - if (job->num_files == 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Job ID %d has no files! Cancelling it!", - job->id); + for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); + job; + job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) + if (job->state_value == IPP_JOB_PROCESSING) + { + cupsdStopJob(job, force); + job->state->values[0].integer = IPP_JOB_PENDING; + job->state_value = IPP_JOB_PENDING; + } +} - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because it has no files."); - cupsdCancelJob(job, 0); - return; - } +/* + * 'cupsdStopJob()' - Stop a print job. + */ - /* - * Figure out what filters are required to convert from - * the source to the destination type... - */ +void +cupsdStopJob(cupsd_job_t *job, /* I - Job */ + int force) /* I - 1 = Force all filters to stop */ +{ + int i; /* Looping var */ - filters = NULL; - job->cost = 0; - if (printer->raw) - { - /* - * Remote jobs and raw queues go directly to the printer without - * filtering... - */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] cupsdStopJob: force = %d", job->id, force); - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdStartJob: Sending job to queue tagged as raw..."); + if (job->state_value != IPP_JOB_PROCESSING) + return; - filters = NULL; - } - else - { - /* - * Local jobs get filtered... - */ + FilterLevel -= job->cost; - filters = mimeFilter(MimeDatabase, job->filetypes[job->current_file], - printer->filetype, &(job->cost), MAX_FILTERS - 1); + if (job->printer->state == IPP_PRINTER_PROCESSING) + cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0); - if (!filters) + job->state->values[0].integer = IPP_JOB_STOPPED; + job->state_value = IPP_JOB_STOPPED; + job->printer->job = NULL; + job->printer = NULL; + + job->current_file --; + + for (i = 0; job->filters[i]; i ++) + if (job->filters[i] > 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to convert file %d to printable format for job %d!", - job->current_file, job->id); - cupsdLogMessage(CUPSD_LOG_INFO, - "Hint: Do you have ESP Ghostscript installed?"); + cupsdEndProcess(job->filters[i], force); + job->filters[i] = 0; + } - if (LogLevel < CUPSD_LOG_DEBUG) - cupsdLogMessage(CUPSD_LOG_INFO, - "Hint: Try setting the LogLevel to \"debug\"."); + if (job->backend > 0) + { + cupsdEndProcess(job->backend, force); + job->backend = 0; + } - job->current_file ++; + cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] Closing print pipes [ %d %d ]...", + job->id, job->print_pipes[0], job->print_pipes[1]); - if (job->current_file == job->num_files) - { - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because it has no files that can be printed."); + cupsdClosePipe(job->print_pipes); - cupsdCancelJob(job, 0); - } + cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] Closing back pipes [ %d %d ]...", + job->id, job->back_pipes[0], job->back_pipes[1]); - return; - } + cupsdClosePipe(job->back_pipes); + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] Closing side pipes [ %d %d ]...", + job->id, job->side_pipes[0], job->side_pipes[1]); + + cupsdClosePipe(job->side_pipes); + + if (job->status_buffer) + { + /* + * Close the pipe and clear the input bit. + */ + + cupsdRemoveSelect(job->status_buffer->fd); + + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] Closing status pipes [ %d %d ]...", job->id, + job->status_pipes[0], job->status_pipes[1]); + + cupsdClosePipe(job->status_pipes); + cupsdStatBufDelete(job->status_buffer); + + job->status_buffer = NULL; + } +} + + +/* + * 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory. + */ + +void +cupsdUnloadCompletedJobs(void) +{ + cupsd_job_t *job; /* Current job */ + time_t expire; /* Expiration time */ + + + expire = time(NULL) - 60; + + for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); + job; + job = (cupsd_job_t *)cupsArrayNext(Jobs)) + if (job->attrs && job->state_value >= IPP_JOB_STOPPED && + job->access_time < expire) + unload_job(job); +} + + +/* + * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs. + */ + +static int /* O - Difference */ +compare_active_jobs(void *first, /* I - First job */ + void *second, /* I - Second job */ + void *data) /* I - App data (not used) */ +{ + int diff; /* Difference */ + + + if ((diff = ((cupsd_job_t *)second)->priority - + ((cupsd_job_t *)first)->priority) != 0) + return (diff); + else + return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id); +} + + +/* + * 'compare_jobs()' - Compare the job IDs of two jobs. + */ + +static int /* O - Difference */ +compare_jobs(void *first, /* I - First job */ + void *second, /* I - Second job */ + void *data) /* I - App data (not used) */ +{ + return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id); +} + + +/* + * 'free_job()' - Free all memory used by a job. + */ + +static void +free_job(cupsd_job_t *job) /* I - Job */ +{ + cupsdClearString(&job->username); + cupsdClearString(&job->dest); + cupsdClearString(&job->auth_username); + cupsdClearString(&job->auth_domain); + cupsdClearString(&job->auth_password); +#ifdef HAVE_GSSAPI + cupsdClearString(&job->ccname); +#endif /* HAVE_GSSAPI */ + + if (job->num_files > 0) + { + free(job->compressions); + free(job->filetypes); + } + + ippDelete(job->attrs); + + free(job); +} + + +/* + * 'ipp_length()' - Compute the size of the buffer needed to hold + * the textual IPP attributes. + */ + +static int /* O - Size of attribute buffer */ +ipp_length(ipp_t *ipp) /* I - IPP request */ +{ + int bytes; /* Number of bytes */ + int i; /* Looping var */ + ipp_attribute_t *attr; /* Current attribute */ + + + /* + * Loop through all attributes... + */ + + bytes = 0; + + for (attr = ipp->attrs; attr != NULL; attr = attr->next) + { + /* + * Skip attributes that won't be sent to filters... + */ + + if (attr->value_tag == IPP_TAG_MIMETYPE || + attr->value_tag == IPP_TAG_NAMELANG || + attr->value_tag == IPP_TAG_TEXTLANG || + attr->value_tag == IPP_TAG_URI || + attr->value_tag == IPP_TAG_URISCHEME) + continue; + + if (strncmp(attr->name, "time-", 5) == 0) + continue; + + /* + * Add space for a leading space and commas between each value. + * For the first attribute, the leading space isn't used, so the + * extra byte can be used as the nul terminator... + */ + + bytes ++; /* " " separator */ + bytes += attr->num_values; /* "," separators */ + + /* + * Boolean attributes appear as "foo,nofoo,foo,nofoo", while + * other attributes appear as "foo=value1,value2,...,valueN". + */ + + if (attr->value_tag != IPP_TAG_BOOLEAN) + bytes += strlen(attr->name); + else + bytes += attr->num_values * strlen(attr->name); + + /* + * Now add the size required for each value in the attribute... + */ + + switch (attr->value_tag) + { + case IPP_TAG_INTEGER : + case IPP_TAG_ENUM : + /* + * Minimum value of a signed integer is -2147483647, or 11 digits. + */ + + bytes += attr->num_values * 11; + break; + + case IPP_TAG_BOOLEAN : + /* + * Add two bytes for each false ("no") value... + */ + + for (i = 0; i < attr->num_values; i ++) + if (!attr->values[i].boolean) + bytes += 2; + break; + + case IPP_TAG_RANGE : + /* + * A range is two signed integers separated by a hyphen, or + * 23 characters max. + */ + + bytes += attr->num_values * 23; + break; + + case IPP_TAG_RESOLUTION : + /* + * A resolution is two signed integers separated by an "x" and + * suffixed by the units, or 26 characters max. + */ + + bytes += attr->num_values * 26; + break; + + case IPP_TAG_STRING : + case IPP_TAG_TEXT : + case IPP_TAG_NAME : + case IPP_TAG_KEYWORD : + case IPP_TAG_CHARSET : + case IPP_TAG_LANGUAGE : + case IPP_TAG_URI : + /* + * Strings can contain characters that need quoting. We need + * at least 2 * len + 2 characters to cover the quotes and + * any backslashes in the string. + */ + + for (i = 0; i < attr->num_values; i ++) + bytes += 2 * strlen(attr->values[i].string.text) + 2; + break; + + default : + break; /* anti-compiler-warning-code */ + } + } + + return (bytes); +} + + +/* + * 'load_job_cache()' - Load jobs from the job.cache file. + */ + +static void +load_job_cache(const char *filename) /* I - job.cache filename */ +{ + cups_file_t *fp; /* job.cache file */ + char line[1024], /* Line buffer */ + *value; /* Value on line */ + int linenum; /* Line number in file */ + cupsd_job_t *job; /* Current job */ + int jobid; /* Job ID */ + char jobfile[1024]; /* Job filename */ + + + /* + * Open the job.cache file... + */ + + if ((fp = cupsFileOpen(filename, "r")) == NULL) + { + if (errno != ENOENT) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to open job cache file \"%s\": %s", + filename, strerror(errno)); + + load_request_root(); + + return; + } + + /* + * Read entries from the job cache file and create jobs as needed. + */ + + cupsdLogMessage(CUPSD_LOG_INFO, "Loading job cache file \"%s\"...", + filename); + + linenum = 0; + job = NULL; + + while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) + { + if (!strcasecmp(line, "NextJobId")) + { + if (value) + NextJobId = atoi(value); + } + else if (!strcasecmp(line, " directive on line %d!", + linenum); + continue; + } + + if (!value) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Missing job ID on line %d!", linenum); + continue; + } + + jobid = atoi(value); + + if (jobid < 1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Bad job ID %d on line %d!", jobid, + linenum); + continue; + } + + snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, jobid); + if (access(jobfile, 0)) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Files have gone away!", + jobid); + continue; + } + + job = calloc(1, sizeof(cupsd_job_t)); + if (!job) + { + cupsdLogMessage(CUPSD_LOG_EMERG, + "[Job %d] Unable to allocate memory for job!", jobid); + break; + } + + job->id = jobid; + job->back_pipes[0] = -1; + job->back_pipes[1] = -1; + job->print_pipes[0] = -1; + job->print_pipes[1] = -1; + job->side_pipes[0] = -1; + job->side_pipes[1] = -1; + job->status_pipes[0] = -1; + job->status_pipes[1] = -1; + + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading from cache...", job->id); + } + else if (!job) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Missing directive on line %d!", linenum); + continue; + } + else if (!strcasecmp(line, "")) + { + cupsArrayAdd(Jobs, job); + + if (job->state_value <= IPP_JOB_STOPPED) + { + cupsArrayAdd(ActiveJobs, job); + cupsdLoadJob(job); + } + + job = NULL; + } + else if (!value) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d!", linenum); + continue; + } + else if (!strcasecmp(line, "State")) + { + job->state_value = (ipp_jstate_t)atoi(value); + + if (job->state_value < IPP_JOB_PENDING) + job->state_value = IPP_JOB_PENDING; + else if (job->state_value > IPP_JOB_COMPLETED) + job->state_value = IPP_JOB_COMPLETED; + } + else if (!strcasecmp(line, "Priority")) + { + job->priority = atoi(value); + } + else if (!strcasecmp(line, "Username")) + { + cupsdSetString(&job->username, value); + } + else if (!strcasecmp(line, "Destination")) + { + cupsdSetString(&job->dest, value); + } + else if (!strcasecmp(line, "DestType")) + { + job->dtype = (cups_ptype_t)atoi(value); + } + else if (!strcasecmp(line, "NumFiles")) + { + job->num_files = atoi(value); + + if (job->num_files < 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Bad NumFiles value %d on line %d!", + job->num_files, linenum); + job->num_files = 0; + continue; + } + + if (job->num_files > 0) + { + snprintf(jobfile, sizeof(jobfile), "%s/d%05d-001", RequestRoot, + job->id); + if (access(jobfile, 0)) + { + cupsdLogMessage(CUPSD_LOG_INFO, + "[Job %d] Data files have gone away!", job->id); + job->num_files = 0; + continue; + } + + job->filetypes = calloc(job->num_files, sizeof(mime_type_t *)); + job->compressions = calloc(job->num_files, sizeof(int)); + + if (!job->filetypes || !job->compressions) + { + cupsdLogMessage(CUPSD_LOG_EMERG, + "[Job %d] Unable to allocate memory for %d files!", + job->id, job->num_files); + break; + } + } + } + else if (!strcasecmp(line, "File")) + { + int number, /* File number */ + compression; /* Compression value */ + char super[MIME_MAX_SUPER], /* MIME super type */ + type[MIME_MAX_TYPE]; /* MIME type */ + + + if (sscanf(value, "%d%*[ \t]%15[^/]/%255s%d", &number, super, type, + &compression) != 4) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File on line %d!", linenum); + continue; + } + + if (number < 1 || number > job->num_files) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File number %d on line %d!", + number, linenum); + continue; + } + + number --; + + job->compressions[number] = compression; + job->filetypes[number] = mimeType(MimeDatabase, super, type); + + if (!job->filetypes[number]) + { + /* + * If the original MIME type is unknown, auto-type it! + */ + + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unknown MIME type %s/%s for file %d!", + job->id, super, type, number + 1); + + snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot, + job->id, number + 1); + job->filetypes[number] = mimeFileType(MimeDatabase, jobfile, NULL, + job->compressions + number); + + /* + * If that didn't work, assume it is raw... + */ + + if (!job->filetypes[number]) + job->filetypes[number] = mimeType(MimeDatabase, "application", + "vnd.cups-raw"); + } + } + else + cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown %s directive on line %d!", + line, linenum); + } + + cupsFileClose(fp); +} + + +/* + * 'load_next_job_id()' - Load the NextJobId value from the job.cache file. + */ + +static void +load_next_job_id(const char *filename) /* I - job.cache filename */ +{ + cups_file_t *fp; /* job.cache file */ + char line[1024], /* Line buffer */ + *value; /* Value on line */ + int linenum; /* Line number in file */ + int next_job_id; /* NextJobId value from line */ + + + /* + * Read the NextJobId directive from the job.cache file and use + * the value (if any). + */ + + if ((fp = cupsFileOpen(filename, "r")) == NULL) + { + if (errno != ENOENT) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to open job cache file \"%s\": %s", + filename, strerror(errno)); + + return; + } + + cupsdLogMessage(CUPSD_LOG_INFO, + "Loading NextJobId from job cache file \"%s\"...", filename); + + linenum = 0; + + while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) + { + if (!strcasecmp(line, "NextJobId")) + { + if (value) + { + next_job_id = atoi(value); + + if (next_job_id > NextJobId) + NextJobId = next_job_id; + } + break; + } + } + + cupsFileClose(fp); +} + + +/* + * 'load_request_root()' - Load jobs from the RequestRoot directory. + */ + +static void +load_request_root(void) +{ + cups_dir_t *dir; /* Directory */ + cups_dentry_t *dent; /* Directory entry */ + cupsd_job_t *job; /* New job */ + + + /* + * Open the requests directory... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG, "Scanning %s for jobs...", RequestRoot); + + if ((dir = cupsDirOpen(RequestRoot)) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to open spool directory \"%s\": %s", + RequestRoot, strerror(errno)); + return; + } + + /* + * Read all the c##### files... + */ + + while ((dent = cupsDirRead(dir)) != NULL) + if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c') + { + /* + * Allocate memory for the job... + */ + + if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Ran out of memory for jobs!"); + cupsDirClose(dir); + return; + } + + /* + * Assign the job ID... + */ + + job->id = atoi(dent->filename + 1); + job->back_pipes[0] = -1; + job->back_pipes[1] = -1; + job->print_pipes[0] = -1; + job->print_pipes[1] = -1; + job->side_pipes[0] = -1; + job->side_pipes[1] = -1; + job->status_pipes[0] = -1; + job->status_pipes[1] = -1; + + if (job->id >= NextJobId) + NextJobId = job->id + 1; + + /* + * Load the job... + */ + + cupsdLoadJob(job); + + /* + * Insert the job into the array, sorting by job priority and ID... + */ + + cupsArrayAdd(Jobs, job); + + if (job->state_value <= IPP_JOB_STOPPED) + cupsArrayAdd(ActiveJobs, job); + else + unload_job(job); + } + + cupsDirClose(dir); +} + + +/* + * 'set_time()' - Set one of the "time-at-xyz" attributes... + */ + +static void +set_time(cupsd_job_t *job, /* I - Job to update */ + const char *name) /* I - Name of attribute */ +{ + ipp_attribute_t *attr; /* Time attribute */ + + + if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL) + { + attr->value_tag = IPP_TAG_INTEGER; + attr->values[0].integer = time(NULL); + } +} + + +/* + * 'set_hold_until()' - Set the hold time and update job-hold-until attribute... + */ + +static void +set_hold_until(cupsd_job_t *job, /* I - Job to update */ + time_t holdtime) /* I - Hold until time */ +{ + ipp_attribute_t *attr; /* job-hold-until attribute */ + struct tm *holddate; /* Hold date */ + char holdstr[64]; /* Hold time */ + + + /* + * Set the hold_until value and hold the job... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG, "set_hold_until: hold_until = %d", + (int)holdtime); + + job->state->values[0].integer = IPP_JOB_HELD; + job->state_value = IPP_JOB_HELD; + job->hold_until = holdtime; + + /* + * Update the job-hold-until attribute with a string representing GMT + * time (HH:MM:SS)... + */ + + holddate = gmtime(&holdtime); + snprintf(holdstr, sizeof(holdstr), "%d:%d:%d", holddate->tm_hour, + holddate->tm_min, holddate->tm_sec); + + if ((attr = ippFindAttribute(job->attrs, "job-hold-until", + IPP_TAG_KEYWORD)) == NULL) + attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME); + + /* + * Either add the attribute or update the value of the existing one + */ + + if (attr == NULL) + attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD, + "job-hold-until", NULL, holdstr); + else + cupsdSetString(&attr->values[0].string.text, holdstr); + + cupsdSaveJob(job); +} + + +/* + * 'start_job()' - Start a print job. + */ + +static void +start_job(cupsd_job_t *job, /* I - Job ID */ + cupsd_printer_t *printer) /* I - Printer to print job */ +{ + int i; /* Looping var */ + int slot; /* Pipe slot */ + cups_array_t *filters, /* Filters for job */ + *prefilters; /* Filters with prefilters */ + mime_filter_t *filter, /* Current filter */ + *prefilter, /* Prefilter */ + port_monitor; /* Port monitor filter */ + char method[255], /* Method for output */ + *optptr, /* Pointer to options */ + *valptr; /* Pointer in value string */ + ipp_attribute_t *attr; /* Current attribute */ + struct stat backinfo; /* Backend file information */ + int backroot; /* Run backend as root? */ + int pid; /* Process ID of new filter process */ + int banner_page; /* 1 if banner page, 0 otherwise */ + int filterfds[2][2];/* Pipes used between filters */ + int envc; /* Number of environment variables */ + char **argv, /* Filter command-line arguments */ + sani_uri[1024], /* Sanitized DEVICE_URI env var */ + filename[1024], /* Job filename */ + command[1024], /* Full path to command */ + jobid[255], /* Job ID string */ + title[IPP_MAX_NAME], + /* Job title string */ + copies[255], /* # copies string */ + *envp[MAX_ENV + 15], + /* Environment variables */ + charset[255], /* CHARSET env variable */ + class_name[255],/* CLASS env variable */ + classification[1024], + /* CLASSIFICATION env variable */ + content_type[1024], + /* CONTENT_TYPE env variable */ + device_uri[1024], + /* DEVICE_URI env variable */ + final_content_type[1024], + /* FINAL_CONTENT_TYPE env variable */ + lang[255], /* LANG env variable */ + ppd[1024], /* PPD env variable */ + printer_name[255], + /* PRINTER env variable */ + rip_max_cache[255]; + /* RIP_MAX_CACHE env variable */ + static char *options = NULL;/* Full list of options */ + static int optlength = 0; /* Length of option buffer */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] start_job: file = %d/%d", + job->id, job->current_file, job->num_files); + + if (job->num_files == 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] No files, canceling job!", + job->id); + + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); + return; + } + + /* + * Figure out what filters are required to convert from + * the source to the destination type... + */ + + filters = NULL; + job->cost = 0; + + if (printer->raw) + { + /* + * Remote jobs and raw queues go directly to the printer without + * filtering... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG, + "[Job %d] Sending job to queue tagged as raw...", job->id); + + filters = NULL; + } + else + { + /* + * Local jobs get filtered... + */ + + filters = mimeFilter(MimeDatabase, job->filetypes[job->current_file], + printer->filetype, &(job->cost)); + + if (!filters) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to convert file %d to printable format!", + job->current_file, job->id); + cupsdLogMessage(CUPSD_LOG_INFO, + "Hint: Do you have ESP Ghostscript installed?"); + + if (LogLevel < CUPSD_LOG_DEBUG) + cupsdLogMessage(CUPSD_LOG_INFO, + "Hint: Try setting the LogLevel to \"debug\"."); + + job->current_file ++; + + if (job->current_file == job->num_files) + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); + + return; + } /* * Remove NULL ("-") filters... @@ -1452,8 +2500,43 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ cupsArrayDelete(filters); filters = NULL; } + + /* + * If this printer has any pre-filters, insert the required pre-filter + * in the filters array... + */ + + if (printer->prefiltertype && filters) + { + prefilters = cupsArrayNew(NULL, NULL); + + for (filter = (mime_filter_t *)cupsArrayFirst(filters); + filter; + filter = (mime_filter_t *)cupsArrayNext(filters)) + { + if ((prefilter = mimeFilterLookup(MimeDatabase, filter->src, + printer->prefiltertype))) + { + cupsArrayAdd(prefilters, prefilter); + job->cost += prefilter->cost; + } + + cupsArrayAdd(prefilters, filter); + } + + cupsArrayDelete(filters); + filters = prefilters; + } } + /* + * Set a minimum cost of 100 for all jobs so that FilterLimit + * works with raw queues and other low-cost paths. + */ + + if (job->cost < 100) + job->cost = 100; + /* * See if the filter cost is too high... */ @@ -1468,10 +2551,10 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ cupsArrayDelete(filters); cupsdLogMessage(CUPSD_LOG_INFO, - "Holding job %d because filter limit has been reached.", + "[Job %d] Holding because filter limit has been reached.", job->id); - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdStartJob: id=%d, file=%d, cost=%d, level=%d, limit=%d", + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] start_job: file=%d, cost=%d, level=%d, limit=%d", job->id, job->current_file, job->cost, FilterLevel, FilterLimit); return; @@ -1480,33 +2563,32 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ FilterLevel += job->cost; /* - * Add decompression filters, if any... + * Add decompression/raw filter as needed... */ - if (job->compressions[job->current_file]) + if ((!printer->raw && job->compressions[job->current_file]) || + (!filters && !printer->remote && + (job->num_files > 1 || !strncmp(printer->device_uri, "file:", 5)))) { /* * Add gziptoany filter to the front of the list... */ + if (!filters) + filters = cupsArrayNew(NULL, NULL); + if (!cupsArrayInsert(filters, &gziptoany_filter)) { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add decompression filter - %s", - strerror(errno)); + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to add decompression filter - %s", + job->id, strerror(errno)); - if (filters != NULL) - free(filters); + cupsArrayDelete(filters); job->current_file ++; if (job->current_file == job->num_files) - { - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the print file could not be " - "decompressed."); - - cupsdCancelJob(job, 0); - } + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); return; } @@ -1522,24 +2604,21 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ * Add port monitor to the end of the list... */ + if (!filters) + filters = cupsArrayNew(NULL, NULL); + if (!cupsArrayAdd(filters, &port_monitor)) { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add port monitor - %s", - strerror(errno)); + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to add port monitor - %s", + job->id, strerror(errno)); - if (filters != NULL) - free(filters); + cupsArrayDelete(filters); job->current_file ++; if (job->current_file == job->num_files) - { - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the port monitor could not be " - "added."); - - cupsdCancelJob(job, 0); - } + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); return; } @@ -1553,15 +2632,45 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ */ job->state->values[0].integer = IPP_JOB_PROCESSING; + job->state_value = IPP_JOB_PROCESSING; + job->status = 0; job->printer = printer; printer->job = job; + cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0); if (job->current_file == 0) { + /* + * Set the processing time... + */ + set_time(job, "time-at-processing"); + + /* + * Create the backchannel pipes and make them non-blocking... + */ + cupsdOpenPipe(job->back_pipes); + + fcntl(job->back_pipes[0], F_SETFL, + fcntl(job->back_pipes[0], F_GETFL) | O_NONBLOCK); + + fcntl(job->back_pipes[1], F_SETFL, + fcntl(job->back_pipes[1], F_GETFL) | O_NONBLOCK); + + /* + * Create the side-channel pipes and make them non-blocking... + */ + + socketpair(AF_LOCAL, SOCK_STREAM, 0, job->side_pipes); + + fcntl(job->side_pipes[0], F_SETFL, + fcntl(job->side_pipes[0], F_GETFL) | O_NONBLOCK); + + fcntl(job->side_pipes[1], F_SETFL, + fcntl(job->side_pipes[1], F_GETFL) | O_NONBLOCK); } /* @@ -1570,17 +2679,19 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ if (job->job_sheets == NULL) { - cupsdLogMessage(CUPSD_LOG_DEBUG, "No job-sheets attribute."); + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] No job-sheets attribute.", + job->id); if ((job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL) cupsdLogMessage(CUPSD_LOG_DEBUG, - "... but someone added one without setting job_sheets!"); + "[Job %d] ... but someone added one without setting " + "job_sheets!", job->id); } else if (job->job_sheets->num_values == 1) - cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s", - job->job_sheets->values[0].string.text); + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] job-sheets=%s", job->id, + job->job_sheets->values[0].string.text); else - cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s,%s", + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] job-sheets=%s,%s", job->id, job->job_sheets->values[0].string.text, job->job_sheets->values[1].string.text); @@ -1598,7 +2709,8 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ else banner_page = 0; - cupsdLogMessage(CUPSD_LOG_DEBUG, "banner_page = %d", banner_page); + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] banner_page = %d", job->id, + banner_page); /* * Building the options string is harder than it needs to be, but @@ -1620,18 +2732,14 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ if (optptr == NULL) { cupsdLogMessage(CUPSD_LOG_CRIT, - "cupsdStartJob: Unable to allocate %d bytes for option buffer for job %d!", - i, job->id); + "[Job %d] Unable to allocate %d bytes for option buffer!", + job->id, i); - if (filters != NULL) - free(filters); + cupsArrayDelete(filters); FilterLevel -= job->cost; - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the server ran out of memory."); - - cupsdCancelJob(job, 0); + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); return; } @@ -1652,7 +2760,7 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ for (attr = job->attrs->attrs; attr != NULL; attr = attr->next) { - if (strcmp(attr->name, "copies") == 0 && + if (!strcmp(attr->name, "copies") && attr->value_tag == IPP_TAG_INTEGER) { /* @@ -1662,7 +2770,7 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ if (!banner_page) sprintf(copies, "%d", attr->values[0].integer); } - else if (strcmp(attr->name, "job-name") == 0 && + else if (!strcmp(attr->name, "job-name") && (attr->value_tag == IPP_TAG_NAME || attr->value_tag == IPP_TAG_NAMELANG)) strlcpy(title, attr->values[0].string.text, sizeof(title)); @@ -1675,29 +2783,33 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ if (attr->value_tag == IPP_TAG_MIMETYPE || attr->value_tag == IPP_TAG_NAMELANG || attr->value_tag == IPP_TAG_TEXTLANG || - attr->value_tag == IPP_TAG_URI || + (attr->value_tag == IPP_TAG_URI && strcmp(attr->name, "job-uuid")) || attr->value_tag == IPP_TAG_URISCHEME || attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */ continue; - if (strncmp(attr->name, "time-", 5) == 0) + if (!strncmp(attr->name, "time-", 5)) continue; - if (strncmp(attr->name, "job-", 4) == 0 && + if (!strncmp(attr->name, "job-", 4) && strcmp(attr->name, "job-uuid") && !(printer->type & CUPS_PRINTER_REMOTE)) continue; - if (strncmp(attr->name, "job-", 4) == 0 && - strcmp(attr->name, "job-billing") != 0 && - strcmp(attr->name, "job-sheets") != 0 && - strcmp(attr->name, "job-hold-until") != 0 && - strcmp(attr->name, "job-priority") != 0) + if (!strncmp(attr->name, "job-", 4) && + strcmp(attr->name, "job-uuid") && + strcmp(attr->name, "job-billing") && + strcmp(attr->name, "job-sheets") && + strcmp(attr->name, "job-hold-until") && + strcmp(attr->name, "job-priority")) continue; - if ((strcmp(attr->name, "page-label") == 0 || - strcmp(attr->name, "page-border") == 0 || - strncmp(attr->name, "number-up", 9) == 0 || - strcmp(attr->name, "page-set") == 0) && + if ((!strcmp(attr->name, "page-label") || + !strcmp(attr->name, "page-border") || + !strncmp(attr->name, "number-up", 9) || + !strcmp(attr->name, "page-ranges") || + !strcmp(attr->name, "page-set") || + !strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") || + !strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed")) && banner_page) continue; @@ -1762,6 +2874,7 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ case IPP_TAG_KEYWORD : case IPP_TAG_CHARSET : case IPP_TAG_LANGUAGE : + case IPP_TAG_URI : for (valptr = attr->values[i].string.text; *valptr;) { if (strchr(" \t\n\\\'\"", *valptr)) @@ -1795,11 +2908,16 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ * * This allows legacy printer drivers that use the old System V * printing interface to be used by CUPS. + * + * For remote jobs, we send all of the files in the argument list. */ + if (printer->remote && job->num_files > 1) + argv = calloc(7 + job->num_files, sizeof(char *)); + else + argv = calloc(8, sizeof(char *)); + sprintf(jobid, "%d", job->id); - snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, - job->id, job->current_file + 1); argv[0] = printer->name; argv[1] = jobid; @@ -1807,12 +2925,26 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ argv[3] = title; argv[4] = copies; argv[5] = options; - argv[6] = filename; - argv[7] = NULL; - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdStartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"", - argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); + if (printer->remote && job->num_files > 1) + { + for (i = 0; i < job->num_files; i ++) + { + snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, + job->id, i + 1); + argv[6 + i] = strdup(filename); + } + } + else + { + snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, + job->id, job->current_file + 1); + argv[6] = filename; + } + + for (i = 0; argv[i]; i ++) + cupsdLogMessage(CUPSD_LOG_DEBUG, + "[Job %d] argv[%d]=\"%s\"", job->id, i, argv[i]); /* * Create environment variable strings for the filters... @@ -1870,7 +3002,8 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s", job->filetypes[job->current_file]->super, job->filetypes[job->current_file]->type); - snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s", printer->device_uri); + snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s", + printer->device_uri); cupsdSanitizeURI(printer->device_uri, sani_uri, sizeof(sani_uri)); snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot, printer->name); snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", printer->name); @@ -1886,6 +3019,16 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ envp[envc ++] = device_uri; envp[envc ++] = printer_name; + if (!printer->remote && !printer->raw && + (filter = (mime_filter_t *)cupsArrayLast(filters)) != NULL && + filter->dst) + { + snprintf(final_content_type, sizeof(final_content_type), + "FINAL_CONTENT_TYPE=%s/%s", + filter->dst->super, filter->dst->type); + envp[envc ++] = final_content_type; + } + if (Classification && !banner_page) { if ((attr = ippFindAttribute(job->attrs, "job-sheets", @@ -1909,78 +3052,75 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ envp[envc ++] = class_name; } + if (job->auth_username) + envp[envc ++] = job->auth_username; + if (job->auth_domain) + envp[envc ++] = job->auth_domain; + if (job->auth_password) + envp[envc ++] = job->auth_password; + +#ifdef HAVE_GSSAPI + if (job->ccname) + envp[envc ++] = job->ccname; +#endif /* HAVE_GSSAPI */ + envp[envc] = NULL; for (i = 0; i < envc; i ++) - if (strncmp(envp[i], "DEVICE_URI=", 11)) - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: envp[%d]=\"%s\"", - i, envp[i]); + if (!strncmp(envp[i], "AUTH_", 5)) + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] envp[%d]=\"AUTH_%c****\"", + job->id, i, envp[i][5]); + else if (strncmp(envp[i], "DEVICE_URI=", 11)) + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] envp[%d]=\"%s\"", + job->id, i, envp[i]); else - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdStartJob: envp[%d]=\"DEVICE_URI=%s\"", i, sani_uri); + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] envp[%d]=\"DEVICE_URI=%s\"", + job->id, i, sani_uri); - job->current_file ++; + if (printer->remote) + job->current_file = job->num_files; + else + job->current_file ++; /* * Now create processes for all of the filters... */ - if (cupsdOpenPipe(statusfds)) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create job status pipes - %s.", - strerror(errno)); - snprintf(printer->state_message, sizeof(printer->state_message), - "Unable to create status pipes - %s.", strerror(errno)); - - cupsdAddPrinterHistory(printer); - - if (filters != NULL) - free(filters); - - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the server could not create the job status pipes."); - - cupsdCancelJob(job, 0); - return; - } - - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: statusfds = [ %d %d ]", - statusfds[0], statusfds[1]); - -#ifdef FD_CLOEXEC - fcntl(statusfds[0], F_SETFD, FD_CLOEXEC); - fcntl(statusfds[1], F_SETFD, FD_CLOEXEC); -#endif /* FD_CLOEXEC */ - - job->status_buffer = cupsdStatBufNew(statusfds[0], "[Job %d]", - job->id); - job->status = 0; - memset(job->filters, 0, sizeof(job->filters)); - - filterfds[1][0] = open("/dev/null", O_RDONLY); + filterfds[0][0] = -1; + filterfds[0][1] = -1; + filterfds[1][0] = -1; filterfds[1][1] = -1; - if (filterfds[1][0] < 0) + if (!job->status_buffer) { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"/dev/null\" - %s.", - strerror(errno)); - snprintf(printer->state_message, sizeof(printer->state_message), - "Unable to open \"/dev/null\" - %s.", strerror(errno)); + if (cupsdOpenPipe(job->status_pipes)) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to create job status pipes - %s.", + job->id, strerror(errno)); + snprintf(printer->state_message, sizeof(printer->state_message), + "Unable to create status pipes - %s.", strerror(errno)); - cupsdAddPrinterHistory(printer); + cupsdAddPrinterHistory(printer); - if (filters != NULL) - free(filters); + cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, + "Job canceled because the server could not create the job " + "status pipes."); - cupsdClosePipe(statusfds); - cupsdCancelJob(job, 0); - return; - } + goto abort_job; + } + + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] start_job: status_pipes = [ %d %d ]", + job->id, job->status_pipes[0], job->status_pipes[1]); - fcntl(filterfds[1][0], F_SETFD, fcntl(filterfds[1][0], F_GETFD) | FD_CLOEXEC); + job->status_buffer = cupsdStatBufNew(job->status_pipes[0], "[Job %d]", + job->id); + job->status_level = CUPSD_LOG_INFO; + } - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filterfds[%d] = [ %d %d ]", - 1, filterfds[1][0], filterfds[1][1]); + job->status = 0; + memset(job->filters, 0, sizeof(job->filters)); for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters); filter; @@ -1997,22 +3137,17 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ if (cupsdOpenPipe(filterfds[slot])) { cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create job filter pipes - %s.", - strerror(errno)); + "[Job %d] Unable to create job filter pipes - %s.", + job->id, strerror(errno)); snprintf(printer->state_message, sizeof(printer->state_message), "Unable to create filter pipes - %s.", strerror(errno)); cupsdAddPrinterHistory(printer); - cupsArrayDelete(filters); - - cupsdClosePipe(statusfds); - cupsdClosePipe(filterfds[!slot]); - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the server could not create the filter pipes."); + "Job canceled because the server could not create the " + "filter pipes."); - cupsdCancelJob(job, 0); - return; + goto abort_job; } } else @@ -2024,111 +3159,108 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ if (cupsdOpenPipe(job->print_pipes)) { cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create job backend pipes - %s.", - strerror(errno)); + "[Job %d] Unable to create job backend pipes - %s.", + job->id, strerror(errno)); snprintf(printer->state_message, sizeof(printer->state_message), "Unable to create backend pipes - %s.", strerror(errno)); cupsdAddPrinterHistory(printer); - cupsArrayDelete(filters); - - cupsdClosePipe(statusfds); - cupsdClosePipe(filterfds[!slot]); - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the server could not create the backend pipes."); + "Job canceled because the server could not create " + "the backend pipes."); - cupsdCancelJob(job, 0); - return; + goto abort_job; } } else { job->print_pipes[0] = -1; - if (!strncmp(printer->device_uri, "file:/dev/", 10) && - strcmp(printer->device_uri, "file:/dev/null")) - job->print_pipes[1] = open(printer->device_uri + 5, - O_WRONLY | O_EXCL); - else if (!strncmp(printer->device_uri, "file:///dev/", 12) && - strcmp(printer->device_uri, "file:///dev/null")) - job->print_pipes[1] = open(printer->device_uri + 7, - O_WRONLY | O_EXCL); + if (!strcmp(printer->device_uri, "file:/dev/null") || + !strcmp(printer->device_uri, "file:///dev/null")) + job->print_pipes[1] = -1; else - job->print_pipes[1] = open(printer->device_uri + 5, - O_WRONLY | O_CREAT | O_TRUNC, 0600); - - if (job->print_pipes[1] < 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to open output file \"%s\" - %s.", - printer->device_uri, strerror(errno)); - snprintf(printer->state_message, sizeof(printer->state_message), - "Unable to open output file \"%s\" - %s.", - printer->device_uri, strerror(errno)); - - cupsdAddPrinterHistory(printer); - - cupsArrayDelete(filters); + if (!strncmp(printer->device_uri, "file:/dev/", 10)) + job->print_pipes[1] = open(printer->device_uri + 5, + O_WRONLY | O_EXCL); + else if (!strncmp(printer->device_uri, "file:///dev/", 12)) + job->print_pipes[1] = open(printer->device_uri + 7, + O_WRONLY | O_EXCL); + else if (!strncmp(printer->device_uri, "file:///", 8)) + job->print_pipes[1] = open(printer->device_uri + 7, + O_WRONLY | O_CREAT | O_TRUNC, 0600); + else + job->print_pipes[1] = open(printer->device_uri + 5, + O_WRONLY | O_CREAT | O_TRUNC, 0600); - cupsdClosePipe(statusfds); - cupsdClosePipe(filterfds[!slot]); + if (job->print_pipes[1] < 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to open output file \"%s\" - %s.", + job->id, printer->device_uri, strerror(errno)); + snprintf(printer->state_message, sizeof(printer->state_message), + "Unable to open output file \"%s\" - %s.", + printer->device_uri, strerror(errno)); - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the server could not open the output file."); + cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, + "Job canceled because the server could not open the " + "output file."); - cupsdCancelJob(job, 0); - return; - } + goto abort_job; + } - fcntl(job->print_pipes[1], F_SETFD, - fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC); + fcntl(job->print_pipes[1], F_SETFD, + fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC); + } } - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartJob: print_pipes = [ %d %d ]", - job->print_pipes[0], job->print_pipes[1]); + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] start_job: print_pipes = [ %d %d ]", + job->id, job->print_pipes[0], job->print_pipes[1]); } filterfds[slot][0] = job->print_pipes[0]; filterfds[slot][1] = job->print_pipes[1]; } - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filter = \"%s\"", command); - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filterfds[%d] = [ %d %d ]", - slot, filterfds[slot][0], filterfds[slot][1]); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] start_job: filter=\"%s\"", + job->id, command); + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] start_job: filterfds[%d]=[ %d %d ]", + job->id, slot, filterfds[slot][0], filterfds[slot][1]); pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0], - filterfds[slot][1], statusfds[1], - job->back_pipes[0], 0, job->filters + i); + filterfds[slot][1], job->status_pipes[1], + job->back_pipes[0], job->side_pipes[0], 0, + job->filters + i); cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...", - !slot, filterfds[!slot][0], filterfds[!slot][1]); + "[Job %d] start_job: Closing filter pipes for slot %d " + "[ %d %d ]...", + job->id, !slot, filterfds[!slot][0], filterfds[!slot][1]); cupsdClosePipe(filterfds[!slot]); if (pid == 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.", - filter->filter, strerror(errno)); + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to start filter \"%s\" - %s.", + job->id, filter->filter, strerror(errno)); snprintf(printer->state_message, sizeof(printer->state_message), "Unable to start filter \"%s\" - %s.", filter->filter, strerror(errno)); cupsdAddPrinterHistory(printer); - cupsArrayDelete(filters); - - cupsdAddPrinterHistory(printer); - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the server could not execute a filter."); + "Job canceled because the server could not execute a " + "filter."); - cupsdCancelJob(job, 0); - return; + goto abort_job; } - cupsdLogMessage(CUPSD_LOG_INFO, "Started filter %s (PID %d) for job %d.", - command, pid, job->id); + cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Started filter %s (PID %d)", + job->id, command, pid); argv[6] = NULL; slot = !slot; @@ -2153,7 +3285,7 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ if (RunUser) backroot = 0; - else if (lstat(command, &backinfo)) + else if (stat(command, &backinfo)) backroot = 0; else backroot = !(backinfo.st_mode & (S_IRWXG | S_IRWXO)); @@ -2161,86 +3293,67 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ argv[0] = sani_uri; filterfds[slot][0] = -1; - filterfds[slot][1] = open("/dev/null", O_WRONLY); - - if (filterfds[slot][1] < 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"/dev/null\" - %s.", - strerror(errno)); - snprintf(printer->state_message, sizeof(printer->state_message), - "Unable to open \"/dev/null\" - %s.", strerror(errno)); - - cupsdAddPrinterHistory(printer); - - cupsdClosePipe(statusfds); - - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the server could not open a file."); - - cupsdCancelJob(job, 0); - return; - } + filterfds[slot][1] = -1; - fcntl(filterfds[slot][1], F_SETFD, - fcntl(filterfds[slot][1], F_GETFD) | FD_CLOEXEC); - - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: backend = \"%s\"", - command); - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdStartJob: filterfds[%d] = [ %d %d ]", + cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] start_job: backend=\"%s\"", + job->id, command); + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] start_job: filterfds[%d] = [ %d %d ]", job->id, slot, filterfds[slot][0], filterfds[slot][1]); pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0], - filterfds[slot][1], statusfds[1], - job->back_pipes[1], backroot, - &(job->backend)); + filterfds[slot][1], job->status_pipes[1], + job->back_pipes[1], job->side_pipes[1], + backroot, &(job->backend)); if (pid == 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start backend \"%s\" - %s.", - method, strerror(errno)); + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to start backend \"%s\" - %s.", + job->id, method, strerror(errno)); snprintf(printer->state_message, sizeof(printer->state_message), - "Unable to start backend \"%s\" - %s.", method, strerror(errno)); - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartJob: Closing print pipes [ %d %d ]...", - job->print_pipes[0], job->print_pipes[1]); - - cupsdClosePipe(job->print_pipes); - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartJob: Closing back pipes [ %d %d ]...", - job->back_pipes[0], job->back_pipes[1]); - - cupsdClosePipe(job->back_pipes); + "Unable to start backend \"%s\" - %s.", method, + strerror(errno)); cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job canceled because the server could not execute the backend."); + "Job canceled because the server could not execute " + "the backend."); - cupsdCancelJob(job, 0); - return; + goto abort_job; } else { - cupsdLogMessage(CUPSD_LOG_INFO, - "Started backend %s (PID %d) for job %d.", - command, pid, job->id); + cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Started backend %s (PID %d)", + job->id, command, pid); } } if (job->current_file == job->num_files) { cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartJob: Closing print pipes [ %d %d ]...", - job->print_pipes[0], job->print_pipes[1]); + "[Job %d] start_job: Closing print pipes [ %d %d ]...", + job->id, job->print_pipes[0], job->print_pipes[1]); cupsdClosePipe(job->print_pipes); cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartJob: Closing back pipes [ %d %d ]...", - job->back_pipes[0], job->back_pipes[1]); + "[Job %d] start_job: Closing back pipes [ %d %d ]...", + job->id, job->back_pipes[0], job->back_pipes[1]); cupsdClosePipe(job->back_pipes); + + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] start_job: Closing side pipes [ %d %d ]...", + job->id, job->side_pipes[0], job->side_pipes[1]); + + cupsdClosePipe(job->side_pipes); + + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] start_job: Closing status output pipe %d...", + job->id, job->status_pipes[1]); + + close(job->status_pipes[1]); + job->status_pipes[1] = -1; } } else @@ -2251,155 +3364,115 @@ cupsdStartJob(cupsd_job_t *job, /* I - Job ID */ if (job->current_file == job->num_files) { cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartJob: Closing print pipes [ %d %d ]...", - job->print_pipes[0], job->print_pipes[1]); + "[Job %d] start_job: Closing print pipes [ %d %d ]...", + job->id, job->print_pipes[0], job->print_pipes[1]); cupsdClosePipe(job->print_pipes); + + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] start_job: Closing status output pipe %d...", + job->id, job->status_pipes[1]); + + close(job->status_pipes[1]); + job->status_pipes[1] = -1; } } cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...", - slot, filterfds[slot][0], filterfds[slot][1]); - + "[Job %d] start_job: Closing filter pipes for slot %d " + "[ %d %d ]...", + job->id, slot, filterfds[slot][0], filterfds[slot][1]); cupsdClosePipe(filterfds[slot]); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartJob: Closing status output pipe %d...", - statusfds[1]); - - close(statusfds[1]); + if (printer->remote && job->num_files > 1) + { + for (i = 0; i < job->num_files; i ++) + free(argv[i + 6]); + } - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartJob: Adding fd %d to InputSet...", - job->status_buffer->fd); + free(argv); - FD_SET(job->status_buffer->fd, InputSet); + cupsdAddSelect(job->status_buffer->fd, (cupsd_selfunc_t)update_job, NULL, + job); cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.", job->id); -} - - -/* - * 'cupsdStopAllJobs()' - Stop all print jobs. - */ - -void -cupsdStopAllJobs(void) -{ - cupsd_job_t *job; /* Current job */ - - - DEBUG_puts("cupsdStopAllJobs()"); - - for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); - job; - job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) - if (job->state->values[0].integer == IPP_JOB_PROCESSING) - { - cupsdStopJob(job, 1); - job->state->values[0].integer = IPP_JOB_PENDING; - } -} - - -/* - * 'cupsdStopJob()' - Stop a print job. - */ - -void -cupsdStopJob(cupsd_job_t *job, /* I - Job */ - int force) /* I - 1 = Force all filters to stop */ -{ - int i; /* Looping var */ - - - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopJob: id = %d, force = %d", - job->id, force); - - if (job->state->values[0].integer != IPP_JOB_PROCESSING) - return; - FilterLevel -= job->cost; - - if (job->status < 0 && - !(job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)) && - !(job->printer->type & CUPS_PRINTER_FAX) && - !strcmp(job->printer->error_policy, "stop-printer")) - cupsdSetPrinterState(job->printer, IPP_PRINTER_STOPPED, 1); - else if (job->printer->state != IPP_PRINTER_STOPPED) - cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0); - - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopJob: printer state is %d", - job->printer->state); - - job->state->values[0].integer = IPP_JOB_STOPPED; - job->printer->job = NULL; - job->printer = NULL; + return; - job->current_file --; - for (i = 0; job->filters[i]; i ++) - if (job->filters[i] > 0) - { - cupsdEndProcess(job->filters[i], force); - job->filters[i] = 0; - } + /* + * If we get here, we need to abort the current job and close out all + * files and pipes... + */ - if (job->backend > 0) + abort_job: + + for (slot = 0; slot < 2; slot ++) { - cupsdEndProcess(job->backend, force); - job->backend = 0; + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "[Job %d] start_job: Closing filter pipes for slot %d " + "[ %d %d ]...", + job->id, slot, filterfds[slot][0], filterfds[slot][1]); + cupsdClosePipe(filterfds[slot]); } cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStopJob: Closing print pipes [ %d %d ]...", - job->print_pipes[0], job->print_pipes[1]); - - cupsdClosePipe(job->print_pipes); + "[Job %d] start_job: Closing status pipes [ %d %d ]...", + job->id, job->status_pipes[0], job->status_pipes[1]); + cupsdClosePipe(job->status_pipes); + cupsdStatBufDelete(job->status_buffer); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStopJob: Closing back pipes [ %d %d ]...", - job->back_pipes[0], job->back_pipes[1]); + job->status_buffer = NULL; - cupsdClosePipe(job->back_pipes); + cupsArrayDelete(filters); - if (job->status_buffer) + if (printer->remote && job->num_files > 1) { - /* - * Close the pipe and clear the input bit. - */ + for (i = 0; i < job->num_files; i ++) + free(argv[i + 6]); + } - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStopJob: Removing fd %d from InputSet...", - job->status_buffer->fd); + free(argv); - FD_CLR(job->status_buffer->fd, InputSet); + cupsdStopJob(job, 0); +} - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStopJob: Closing status input pipe %d...", - job->status_buffer->fd); - cupsdStatBufDelete(job->status_buffer); +/* + * 'unload_job()' - Unload a job from memory. + */ - job->status_buffer = NULL; - } +static void +unload_job(cupsd_job_t *job) /* I - Job */ +{ + if (!job->attrs) + return; + + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Unloading...", job->id); + + ippDelete(job->attrs); + + job->attrs = NULL; + job->state = NULL; + job->sheets = NULL; + job->job_sheets = NULL; } /* - * 'cupsdUpdateJob()' - Read a status update from a job's filters. + * 'update_job()' - Read a status update from a job's filters. */ void -cupsdUpdateJob(cupsd_job_t *job) /* I - Job to check */ +update_job(cupsd_job_t *job) /* I - Job to check */ { int i; /* Looping var */ int copies; /* Number of copies printed */ char message[1024], /* Message text */ *ptr; /* Pointer update... */ - int loglevel; /* Log level for message */ + int loglevel, /* Log level for message */ + event = 0; /* Events? */ while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel, @@ -2429,11 +3502,34 @@ cupsdUpdateJob(cupsd_job_t *job) /* I - Job to check */ } else if (!sscanf(message, "%*d%d", &copies)) copies = 1; - + job->sheets->values[0].integer += copies; if (job->printer->page_limit) - cupsdUpdateQuota(job->printer, job->username, copies, 0); + { + cupsd_quota_t *q = cupsdUpdateQuota(job->printer, job->username, + copies, 0); + +#ifdef __APPLE__ + if (AppleQuotas && q->page_count == -3) + { + /* + * Quota limit exceeded, cancel job in progress immediately... + */ + + cupsdLogMessage(CUPSD_LOG_INFO, + "[Job %d] Canceled because pages exceed user %s " + "quota limit on printer %s (%s).", + job->id, job->username, job->printer->name, + job->printer->info); + + cupsdCancelJob(job, 1, IPP_JOB_CANCELED); + return; + } +#else + (void)q; +#endif /* __APPLE__ */ + } } cupsdLogPage(job, message); @@ -2443,8 +3539,16 @@ cupsdUpdateJob(cupsd_job_t *job) /* I - Job to check */ } else if (loglevel == CUPSD_LOG_STATE) { - cupsdSetPrinterReasons(job->printer, message); - cupsdAddPrinterHistory(job->printer); + if (!strcmp(message, "paused")) + cupsdStopPrinter(job->printer, 1); + else + { + cupsdSetPrinterReasons(job->printer, message); + cupsdAddPrinterHistory(job->printer); + event |= CUPSD_EVENT_PRINTER_STATE_CHANGED; + } + + update_job_attrs(job); } else if (loglevel == CUPSD_LOG_ATTR) { @@ -2452,7 +3556,31 @@ cupsdUpdateJob(cupsd_job_t *job) /* I - Job to check */ * Set attribute(s)... */ - /**** TODO ****/ + int num_attrs; /* Number of attributes */ + cups_option_t *attrs; /* Attributes */ + const char *attr; /* Attribute */ + + + num_attrs = cupsParseOptions(message, 0, &attrs); + + if ((attr = cupsGetOption("auth-info-required", num_attrs, + attrs)) != NULL) + cupsdSetAuthInfoRequired(job->printer, attr, NULL); + + if ((attr = cupsGetOption("printer-alert", num_attrs, attrs)) != NULL) + { + cupsdSetString(&job->printer->alert, attr); + event |= CUPSD_EVENT_PRINTER_STATE_CHANGED; + } + + if ((attr = cupsGetOption("printer-alert-description", num_attrs, + attrs)) != NULL) + { + cupsdSetString(&job->printer->alert_description, attr); + event |= CUPSD_EVENT_PRINTER_STATE_CHANGED; + } + + cupsFreeOptions(num_attrs, attrs); } #ifdef __APPLE__ else if (!strncmp(message, "recoverable:", 12)) @@ -2466,6 +3594,7 @@ cupsdUpdateJob(cupsd_job_t *job) /* I - Job to check */ cupsdSetString(&job->printer->recoverable, ptr); cupsdAddPrinterHistory(job->printer); + event |= CUPSD_EVENT_PRINTER_STATE_CHANGED; } else if (!strncmp(message, "recovered:", 10)) { @@ -2478,24 +3607,37 @@ cupsdUpdateJob(cupsd_job_t *job) /* I - Job to check */ cupsdSetString(&job->printer->recoverable, ptr); cupsdAddPrinterHistory(job->printer); + event |= CUPSD_EVENT_PRINTER_STATE_CHANGED; } #endif /* __APPLE__ */ - else + else if (loglevel <= job->status_level) { /* * Some message to show in the printer-state-message attribute... */ + job->status_level = loglevel; + strlcpy(job->printer->state_message, message, sizeof(job->printer->state_message)); cupsdAddPrinterHistory(job->printer); + event |= CUPSD_EVENT_PRINTER_STATE_CHANGED; + + update_job_attrs(job); } if (!strchr(job->status_buffer->buffer, '\n')) break; } - if (ptr == NULL) + if ((event & CUPSD_EVENT_PRINTER_STATE_CHANGED)) + cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE_CHANGED, job->printer, NULL, + (job->printer->type & CUPS_PRINTER_CLASS) ? + "Class \"%s\" state changed." : + "Printer \"%s\" state changed.", + job->printer->name); + + if (ptr == NULL && !job->status_buffer->bufused) { /* * See if all of the filters and the backend have returned their @@ -2520,226 +3662,84 @@ cupsdUpdateJob(cupsd_job_t *job) /* I - Job to check */ /* - * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs. - */ - -static int /* O - Difference */ -compare_active_jobs(void *first, /* I - First job */ - void *second, /* I - Second job */ - void *data) /* I - App data (not used) */ -{ - int diff; /* Difference */ - - - if ((diff = ((cupsd_job_t *)first)->priority - ((cupsd_job_t *)second)->priority) != 0) - return (diff); - else - return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id); -} - - -/* - * 'compare_jobs()' - Compare the job IDs of two jobs. - */ - -static int /* O - Difference */ -compare_jobs(void *first, /* I - First job */ - void *second, /* I - Second job */ - void *data) /* I - App data (not used) */ -{ - return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id); -} - - -/* - * 'ipp_length()' - Compute the size of the buffer needed to hold - * the textual IPP attributes. + * 'update_job_attrs()' - Update the job-printer-* attributes. */ -int /* O - Size of buffer to hold IPP attributes */ -ipp_length(ipp_t *ipp) /* I - IPP request */ +void +update_job_attrs(cupsd_job_t *job) /* I - Job to update */ { - int bytes; /* Number of bytes */ int i; /* Looping var */ - ipp_attribute_t *attr; /* Current attribute */ + int num_reasons; /* Actual number of reasons */ + const char * const *reasons; /* Reasons */ + static const char *none = "none", /* "none" */ + *paused = "paused"; + /* "paused" */ /* - * Loop through all attributes... + * Get/create the job-printer-state-* attributes... */ - bytes = 0; - - for (attr = ipp->attrs; attr != NULL; attr = attr->next) - { - /* - * Skip attributes that won't be sent to filters... - */ - - if (attr->value_tag == IPP_TAG_MIMETYPE || - attr->value_tag == IPP_TAG_NAMELANG || - attr->value_tag == IPP_TAG_TEXTLANG || - attr->value_tag == IPP_TAG_URI || - attr->value_tag == IPP_TAG_URISCHEME) - continue; - - if (strncmp(attr->name, "time-", 5) == 0) - continue; - - /* - * Add space for a leading space and commas between each value. - * For the first attribute, the leading space isn't used, so the - * extra byte can be used as the nul terminator... - */ - - bytes ++; /* " " separator */ - bytes += attr->num_values; /* "," separators */ - - /* - * Boolean attributes appear as "foo,nofoo,foo,nofoo", while - * other attributes appear as "foo=value1,value2,...,valueN". - */ - - if (attr->value_tag != IPP_TAG_BOOLEAN) - bytes += strlen(attr->name); - else - bytes += attr->num_values * strlen(attr->name); - - /* - * Now add the size required for each value in the attribute... - */ - - switch (attr->value_tag) - { - case IPP_TAG_INTEGER : - case IPP_TAG_ENUM : - /* - * Minimum value of a signed integer is -2147483647, or 11 digits. - */ - - bytes += attr->num_values * 11; - break; - - case IPP_TAG_BOOLEAN : - /* - * Add two bytes for each false ("no") value... - */ - - for (i = 0; i < attr->num_values; i ++) - if (!attr->values[i].boolean) - bytes += 2; - break; - - case IPP_TAG_RANGE : - /* - * A range is two signed integers separated by a hyphen, or - * 23 characters max. - */ - - bytes += attr->num_values * 23; - break; - - case IPP_TAG_RESOLUTION : - /* - * A resolution is two signed integers separated by an "x" and - * suffixed by the units, or 26 characters max. - */ - - bytes += attr->num_values * 26; - break; - - case IPP_TAG_STRING : - case IPP_TAG_TEXT : - case IPP_TAG_NAME : - case IPP_TAG_KEYWORD : - case IPP_TAG_CHARSET : - case IPP_TAG_LANGUAGE : - /* - * Strings can contain characters that need quoting. We need - * at least 2 * len + 2 characters to cover the quotes and - * any backslashes in the string. - */ - - for (i = 0; i < attr->num_values; i ++) - bytes += 2 * strlen(attr->values[i].string.text) + 2; - break; - - default : - break; /* anti-compiler-warning-code */ - } - } - - return (bytes); -} - - -/* - * 'set_time()' - Set one of the "time-at-xyz" attributes... - */ - -static void -set_time(cupsd_job_t *job, /* I - Job to update */ - const char *name) /* I - Name of attribute */ -{ - ipp_attribute_t *attr; /* Time attribute */ - - - if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL) + if (!job->printer_message) { - attr->value_tag = IPP_TAG_INTEGER; - attr->values[0].integer = time(NULL); + if ((job->printer_message = ippFindAttribute(job->attrs, + "job-printer-state-message", + IPP_TAG_TEXT)) == NULL) + job->printer_message = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_TEXT, + "job-printer-state-message", + NULL, ""); } -} - - -/* - * 'set_hold_until()' - Set the hold time and update job-hold-until attribute... - */ - -static void -set_hold_until(cupsd_job_t *job, /* I - Job to update */ - time_t holdtime) /* I - Hold until time */ -{ - ipp_attribute_t *attr; /* job-hold-until attribute */ - struct tm *holddate; /* Hold date */ - char holdstr[64]; /* Hold time */ + if (!job->printer_reasons) + job->printer_reasons = ippFindAttribute(job->attrs, + "job-printer-state-reasons", + IPP_TAG_KEYWORD); /* - * Set the hold_until value and hold the job... + * If the job isn't printing, return now... */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "set_hold_until: hold_until = %d", (int)holdtime); - - job->state->values[0].integer = IPP_JOB_HELD; - job->hold_until = holdtime; + if (!job->printer) + return; /* - * Update the job-hold-until attribute with a string representing GMT - * time (HH:MM:SS)... + * Otherwise copy the printer-state-message value... */ - holddate = gmtime(&holdtime); - snprintf(holdstr, sizeof(holdstr), "%d:%d:%d", holddate->tm_hour, - holddate->tm_min, holddate->tm_sec); - - if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL) - attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME); + if (job->printer->state_message[0]) + cupsdSetString(&(job->printer_message->values[0].string.text), + job->printer->state_message); /* - * Either add the attribute or update the value of the existing one + * ... and the printer-state-reasons value... */ - if (attr == NULL) - attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD, - "job-hold-until", NULL, holdstr); + if (job->printer->num_reasons == 0) + { + num_reasons = 1; + reasons = job->printer->state == IPP_PRINTER_STOPPED ? &paused : &none; + } else - cupsdSetString(&attr->values[0].string.text, holdstr); + { + num_reasons = job->printer->num_reasons; + reasons = (const char * const *)job->printer->reasons; + } - cupsdSaveJob(job); + if (!job->printer_reasons || job->printer_reasons->num_values != num_reasons) + { + ippDeleteAttribute(job->attrs, job->printer_reasons); + + job->printer_reasons = ippAddStrings(job->attrs, + IPP_TAG_JOB, IPP_TAG_KEYWORD, + "job-printer-state-reasons", + num_reasons, NULL, NULL); + } + + for (i = 0; i < num_reasons; i ++) + cupsdSetString(&(job->printer_reasons->values[i].string.text), reasons[i]); } /* - * End of "$Id: job.c 5051 2006-02-02 16:13:16Z mike $". + * End of "$Id: job.c 6671 2007-07-13 23:35:24Z mike $". */