/*
- * "$Id: job.c 4992 2006-01-26 17:50:58Z mike $"
+ * "$Id: job.c 5452 2006-04-22 22:17:32Z mike $"
*
* Job management routines for the Common UNIX Printing System (CUPS).
*
- * Copyright 1997-2005 by Easy Software Products, all rights reserved.
+ * Copyright 1997-2006 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
*
* 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.
+ * cupsdUpdateJob() - Read a status update from a jobs filters.
+ * 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.
*/
/*
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);
/*
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->status_pipes[0] = -1;
+ job->status_pipes[1] = -1;
cupsdSetString(&job->dest, dest);
char filename[1024]; /* Job filename */
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCancelJob: id = %d", job->id);
-
- /*
- * Remove the job from the active list...
- */
-
- cupsArrayRemove(ActiveJobs, job);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCancelJob: id = %d", job->id);
/*
* Stop any processes that are working on the current job...
*/
- if (job->state->values[0].integer == IPP_JOB_PROCESSING)
+ if (job->state_value == IPP_JOB_PROCESSING)
cupsdStopJob(job, 0);
- cupsArrayRemove(ActiveJobs, job);
+ cupsdLoadJob(job);
+
+ if (job->attrs)
+ job->state->values[0].integer = IPP_JOB_CANCELLED;
- job->state->values[0].integer = IPP_JOB_CANCELLED;
+ job->state_value = IPP_JOB_CANCELLED;
set_time(job, "time-at-completed");
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);
/*
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,
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))
{
/*
* 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);
}
}
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))
* 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))
+ {
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)
+ if (job->state_value == IPP_JOB_PENDING && !NeedReload && !Sleeping)
{
printer = cupsdFindDest(job->dest);
pclass = NULL;
job->dest, job->id);
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);
}
"job-actual-printer-uri", NULL, printer->uri);
}
- if (printer->state == IPP_PRINTER_IDLE || /* Printer is idle */
+ if ((!(printer->type & CUPS_PRINTER_REMOTE) && /* Printer is local */
+ printer->state == IPP_PRINTER_IDLE) || /* and idle */
((printer->type & CUPS_PRINTER_REMOTE) && /* Printer is remote */
- !printer->job)) /* and not printing a job */
- cupsdStartJob(job, printer);
+ !printer->job)) /* and not printing */
+ start_job(job, printer);
}
}
}
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)
+ if (job->state_value >= IPP_JOB_CANCELLED)
cupsdCancelJob(job, 1);
}
cupsd_printer_t *printer; /* Current printer */
- 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",
FD_CLR(job->status_buffer->fd, InputSet);
cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdFinishJob: Closing status input pipe %d...",
- job->status_buffer->fd);
+ "cupsdFinishJob: Closing status pipes [ %d %d ]...",
+ job->status_pipes[0], job->status_pipes[1]);
+ cupsdClosePipe(job->status_pipes);
cupsdStatBufDelete(job->status_buffer);
job->status_buffer = NULL;
}
+ printer = job->printer;
+
if (job->status < 0)
{
/*
* Backend had errors; stop it...
*/
- printer = job->printer;
-
switch (-job->status)
{
default :
cupsdStopJob(job, 0);
job->state->values[0].integer = IPP_JOB_PENDING;
+ job->state_value = IPP_JOB_PENDING;
cupsdSaveJob(job);
/*
*/
cupsdLogMessage(CUPSD_LOG_ERROR,
- "Canceling job %d since it could not be sent after %d tries.",
+ "Canceling job %d 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.",
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, printer, job,
+ "Job canceled since it could not be sent after %d "
+ "tries.",
JobRetryLimit);
cupsdCancelJob(job, 0);
else if (job->status > 0)
{
/*
- * Filter had errors; cancel it...
+ * Filter had errors; stop job...
*/
- if (job->current_file < job->num_files)
- cupsdStartJob(job, job->printer);
- else
- {
- cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
- "Job aborted due to filter errors; please consult the "
- "error_log file for details.");
-
- job_history = JobHistory && !(job->dtype & CUPS_PRINTER_REMOTE);
-
- cupsdCancelJob(job, 0);
-
- if (job_history)
- {
- job->state->values[0].integer = IPP_JOB_ABORTED;
- cupsdSaveJob(job);
- }
-
- cupsdCheckJobs();
- }
+ cupsdStopJob(job, 1);
+ cupsdSaveJob(job);
+ cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, printer, job,
+ "Job stopped due to filter errors; please consult the "
+ "error_log file for details.");
+ cupsdCheckJobs();
}
else
{
if (job->current_file < job->num_files)
{
+ /*
+ * Start the next file in the job...
+ */
+
FilterLevel -= job->cost;
- cupsdStartJob(job, job->printer);
+ start_job(job, printer);
}
else
{
- cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
+ /*
+ * Close out this job...
+ */
+
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, printer, job,
"Job completed successfully.");
job_history = JobHistory && !(job->dtype & CUPS_PRINTER_REMOTE);
if (job_history)
{
job->state->values[0].integer = IPP_JOB_COMPLETED;
+ job->state_value = IPP_JOB_COMPLETED;
cupsdSaveJob(job);
}
+ /*
+ * Clear the printer's state_message and state_reasons and move on...
+ */
+
+ printer->state_message[0] = '\0';
+
+ cupsdSetPrinterReasons(printer, "");
+
cupsdCheckJobs();
}
}
cupsd_job_t *job; /* Current job */
+ if (!Jobs)
+ return;
+
cupsdHoldSignals();
cupsdStopAllJobs();
+ cupsdSaveAllJobs();
for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
job;
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();
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);
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);
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)
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 */
+ char scheme[32], /* Scheme portion of URI */
+ username[64], /* Username portion of URI */
+ host[HTTP_MAX_HOST],
+ /* 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 */
- 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, "Ran out of memory for job attributes!");
+ return;
+ }
- httpSeparateURI(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, "Loading attributes for job %d...",
+ 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,
+ "Unable to open job control file \"%s\" - %s!",
+ 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, "Unable to read job control file \"%s\"!",
+ 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,
+ "Missing or bad job-state attribute in control "
+ "file \"%s\"!",
+ jobfile);
+ 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,
+ "No job-printer-uri attribute in control file \"%s\"!",
+ jobfile);
+ 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);
+ httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, scheme,
+ sizeof(scheme), username, sizeof(username), host,
+ sizeof(host), &port, resource, sizeof(resource));
- 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;
+ if ((dest = cupsdValidateDest(host, resource, &(job->dtype),
+ NULL)) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to queue job for destination \"%s\"!",
+ attr->values[0].string.text);
+ ippDelete(job->attrs);
+ job->attrs = NULL;
+ unlink(jobfile);
+ return;
}
- /*
- * Read all the d##### files...
- */
+ cupsdSetString(&job->dest, dest);
+ }
- cupsDirRewind(dir);
+ job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed",
+ IPP_TAG_INTEGER);
+ job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
- while ((dent = cupsDirRead(dir)) != NULL)
- if (strlen(dent->filename) > 7 && dent->filename[0] == 'd' &&
- strchr(dent->filename, '-'))
+ if (!job->priority)
+ {
+ if ((attr = ippFindAttribute(job->attrs, "job-priority",
+ IPP_TAG_INTEGER)) == NULL)
{
- /*
- * Find the job...
- */
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Missing or bad job-priority attribute in control "
+ "file \"%s\"!", jobfile);
+ ippDelete(job->attrs);
+ job->attrs = NULL;
+ unlink(jobfile);
+ return;
+ }
- jobid = atoi(dent->filename + 1);
- fileid = atoi(strchr(dent->filename, '-') + 1);
+ job->priority = attr->values[0].integer;
+ }
- cupsdLogMessage(CUPSD_LOG_DEBUG,
- "cupsdLoadAllJobs: Auto-typing document file %s...",
- dent->filename);
+ if (!job->username)
+ {
+ if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name",
+ IPP_TAG_NAME)) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Missing or bad job-originating-user-name attribute "
+ "in control file \"%s\"!", jobfile);
+ ippDelete(job->attrs);
+ job->attrs = NULL;
+ unlink(jobfile);
+ return;
+ }
- snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->filename);
+ cupsdSetString(&job->username, attr->values[0].string.text);
+ }
- if ((job = cupsdFindJob(jobid)) == NULL)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "cupsdLoadAllJobs: Orphaned print file \"%s\"!",
- filename);
- unlink(filename);
- continue;
- }
+ /*
+ * Set the job hold-until time and state...
+ */
+
+ 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);
+
+ if (attr)
+ cupsdSetJobHoldUntil(job, attr->values[0].string.text);
+ else
+ {
+ 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;
+ }
+
+ if (!job->num_files)
+ {
+ /*
+ * Find all the d##### files...
+ */
+
+ for (fileid = 1; fileid < 10000; fileid ++)
+ {
+ snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
+ job->id, fileid);
+
+ if (access(jobfile, 0))
+ break;
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Auto-typing document file \"%s\"...",
+ jobfile);
if (fileid > job->num_files)
{
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;
+ "Ran out of memory for job file types!");
+ return;
}
job->compressions = compressions;
job->num_files = fileid;
}
- job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, filename,
+ 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...
- */
-
- cupsdCleanJobs();
+ job->access_time = time(NULL);
}
*/
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);
}
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();
}
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)
{
+ cupsdLoadJob(job);
+
job->tries = 0;
job->state->values[0].integer = IPP_JOB_PENDING;
+ job->state_value = IPP_JOB_PENDING;
cupsdSaveJob(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, "<Job %d>\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, "</Job>\n");
+ }
+
+ cupsFileClose(fp);
+}
+
+
/*
* 'cupsdSaveJob()' - Save a job to disk.
*/
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.",
+ "Unable to create job control file \"%s\" - %s.",
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, "Unable to write job control file \"%s\"!",
+ filename);
cupsFileClose(fp);
}
int second; /* Hold second */
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSetJobHoldUntil(%d, \"%s\")",
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil(%d, \"%s\")",
job->id, when);
second = 0;
((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.
job->hold_until += 24 * 60 * 60 * 60;
}
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSetJobHoldUntil: hold_until = %d",
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil: hold_until = %d",
(int)job->hold_until);
}
* Don't change completed jobs...
*/
- if (job->state->values[0].integer >= IPP_JOB_PROCESSING)
+ if (job->state_value >= IPP_JOB_PROCESSING)
return;
/*
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",
/*
- * '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(void)
{
- 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 */
- 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[100], /* 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 */
-
-
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob() id = %d, file = %d/%d",
- job->id, job->current_file, job->num_files);
-
- if (job->num_files == 0)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR, "Job ID %d has no files! Cancelling it!",
- job->id);
-
- cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
- "Job canceled because it has no files.");
-
- cupsdCancelJob(job, 0);
- return;
- }
+ cupsd_job_t *job; /* Current job */
- /*
- * Figure out what filters are required to convert from
- * the source to the destination type...
- */
- filters = NULL;
- job->cost = 0;
+ DEBUG_puts("cupsdStopAllJobs()");
- if (printer->raw)
- {
- /*
- * Remote jobs and raw queues go directly to the printer without
- * filtering...
- */
+ for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
+ job;
+ job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
+ if (job->state_value == IPP_JOB_PROCESSING)
+ {
+ cupsdStopJob(job, 1);
+ job->state->values[0].integer = IPP_JOB_PENDING;
+ job->state_value = IPP_JOB_PENDING;
+ }
+}
- cupsdLogMessage(CUPSD_LOG_DEBUG,
- "cupsdStartJob: Sending job to queue tagged as raw...");
- filters = NULL;
- }
- else
- {
- /*
- * Local jobs get filtered...
- */
+/*
+ * 'cupsdStopJob()' - Stop a print job.
+ */
- filters = mimeFilter(MimeDatabase, job->filetypes[job->current_file],
- printer->filetype, &(job->cost), MAX_FILTERS - 1);
+void
+cupsdStopJob(cupsd_job_t *job, /* I - Job */
+ int force) /* I - 1 = Force all filters to stop */
+{
+ int i; /* Looping var */
- if (!filters)
- {
- 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?");
- if (LogLevel < CUPSD_LOG_DEBUG)
- cupsdLogMessage(CUPSD_LOG_INFO,
- "Hint: Try setting the LogLevel to \"debug\".");
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStopJob: id = %d, force = %d",
+ job->id, force);
- job->current_file ++;
+ if (job->state_value != IPP_JOB_PROCESSING)
+ return;
- 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.");
+ FilterLevel -= job->cost;
- cupsdCancelJob(job, 0);
- }
+ 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);
- return;
- }
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStopJob: printer state is %d",
+ job->printer->state);
- /*
- * Remove NULL ("-") filters...
- */
+ job->state->values[0].integer = IPP_JOB_STOPPED;
+ job->state_value = IPP_JOB_STOPPED;
+ job->printer->job = NULL;
+ job->printer = NULL;
- for (filter = (mime_filter_t *)cupsArrayFirst(filters);
- filter;
- filter = (mime_filter_t *)cupsArrayNext(filters))
- if (!strcmp(filter->filter, "-"))
- cupsArrayRemove(filters, filter);
+ job->current_file --;
- if (cupsArrayCount(filters) == 0)
+ for (i = 0; job->filters[i]; i ++)
+ if (job->filters[i] > 0)
{
- cupsArrayDelete(filters);
- filters = NULL;
+ cupsdEndProcess(job->filters[i], force);
+ job->filters[i] = 0;
}
- }
- /*
- * See if the filter cost is too high...
- */
-
- if ((FilterLevel + job->cost) > FilterLimit && FilterLevel > 0 &&
- FilterLimit > 0)
+ if (job->backend > 0)
{
- /*
- * Don't print this job quite yet...
- */
+ cupsdEndProcess(job->backend, force);
+ job->backend = 0;
+ }
- cupsArrayDelete(filters);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdStopJob: Closing print pipes [ %d %d ]...",
+ job->print_pipes[0], job->print_pipes[1]);
- cupsdLogMessage(CUPSD_LOG_INFO,
- "Holding job %d because filter limit has been reached.",
- job->id);
- cupsdLogMessage(CUPSD_LOG_DEBUG,
- "cupsdStartJob: id=%d, file=%d, cost=%d, level=%d, limit=%d",
- job->id, job->current_file, job->cost, FilterLevel,
- FilterLimit);
- return;
- }
+ cupsdClosePipe(job->print_pipes);
- FilterLevel += job->cost;
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdStopJob: Closing back pipes [ %d %d ]...",
+ job->back_pipes[0], job->back_pipes[1]);
- /*
- * Add decompression filters, if any...
- */
+ cupsdClosePipe(job->back_pipes);
- if (job->compressions[job->current_file])
+ if (job->status_buffer)
{
/*
- * Add gziptoany filter to the front of the list...
+ * Close the pipe and clear the input bit.
*/
- if (!cupsArrayInsert(filters, &gziptoany_filter))
- {
- cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add decompression filter - %s",
- strerror(errno));
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdStopJob: Removing fd %d from InputSet...",
+ job->status_buffer->fd);
- if (filters != NULL)
- free(filters);
+ FD_CLR(job->status_buffer->fd, InputSet);
- job->current_file ++;
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdStopJob: Closing status pipes [ %d %d ]...",
+ job->status_pipes[0], job->status_pipes[1]);
- 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);
- }
+ cupsdClosePipe(job->status_pipes);
+ cupsdStatBufDelete(job->status_buffer);
- return;
- }
+ job->status_buffer = NULL;
}
+}
- /*
- * Add port monitor, if any...
- */
-
- if (printer->port_monitor)
- {
- /*
- * Add port monitor to the end of the list...
- */
-
- if (!cupsArrayAdd(filters, &port_monitor))
- {
- cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add port monitor - %s",
- strerror(errno));
-
- if (filters != NULL)
- free(filters);
- job->current_file ++;
+/*
+ * 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory.
+ */
- 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.");
+void
+cupsdUnloadCompletedJobs(void)
+{
+ cupsd_job_t *job; /* Current job */
+ time_t expire; /* Expiration time */
- cupsdCancelJob(job, 0);
- }
- return;
- }
+ expire = time(NULL) - 60;
- snprintf(port_monitor.filter, sizeof(port_monitor.filter),
- "%s/monitor/%s", ServerBin, printer->port_monitor);
- }
+ 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);
+}
- /*
- * Update the printer and job state to "processing"...
- */
- job->state->values[0].integer = IPP_JOB_PROCESSING;
- job->status = 0;
- job->printer = printer;
- printer->job = job;
- cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
+/*
+ * 'cupsdUpdateJob()' - Read a status update from a job's filters.
+ */
- if (job->current_file == 0)
- {
- set_time(job, "time-at-processing");
- cupsdOpenPipe(job->back_pipes);
- }
+void
+cupsdUpdateJob(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 */
- /*
- * Determine if we are printing a banner page or not...
- */
- if (job->job_sheets == NULL)
+ while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
+ message, sizeof(message))) != NULL)
{
- cupsdLogMessage(CUPSD_LOG_DEBUG, "No job-sheets attribute.");
- 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!");
- }
- else if (job->job_sheets->num_values == 1)
- cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s",
- job->job_sheets->values[0].string.text);
- else
- cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s,%s",
- job->job_sheets->values[0].string.text,
- job->job_sheets->values[1].string.text);
+ /*
+ * Process page and printer state messages as needed...
+ */
- if (printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
- banner_page = 0;
- else if (job->job_sheets == NULL)
- banner_page = 0;
- else if (strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
- job->current_file == 0)
- banner_page = 1;
- else if (job->job_sheets->num_values > 1 &&
- strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
- job->current_file == (job->num_files - 1))
- banner_page = 1;
- else
- banner_page = 0;
+ if (loglevel == CUPSD_LOG_PAGE)
+ {
+ /*
+ * Page message; send the message to the page_log file and update the
+ * job sheet count...
+ */
- cupsdLogMessage(CUPSD_LOG_DEBUG, "banner_page = %d", banner_page);
+ if (job->sheets != NULL)
+ {
+ if (!strncasecmp(message, "total ", 6))
+ {
+ /*
+ * Got a total count of pages from a backend or filter...
+ */
- /*
- * Building the options string is harder than it needs to be, but
- * for the moment we need to pass strings for command-line args and
- * not IPP attribute pointers... :)
- *
- * First allocate/reallocate the option buffer as needed...
- */
+ copies = atoi(message + 6);
+ copies -= job->sheets->values[0].integer; /* Just track the delta */
+ }
+ else if (!sscanf(message, "%*d%d", &copies))
+ copies = 1;
+
+ job->sheets->values[0].integer += copies;
- i = ipp_length(job->attrs);
+ if (job->printer->page_limit)
+ cupsdUpdateQuota(job->printer, job->username, copies, 0);
+ }
- if (i > optlength)
- {
- if (optlength == 0)
- optptr = malloc(i);
- else
- optptr = realloc(options, i);
+ cupsdLogPage(job, message);
- if (optptr == NULL)
+ cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
+ "Printed %d page(s).", job->sheets->values[0].integer);
+ }
+ else if (loglevel == CUPSD_LOG_STATE)
{
- cupsdLogMessage(CUPSD_LOG_CRIT,
- "cupsdStartJob: Unable to allocate %d bytes for option buffer for job %d!",
- i, job->id);
-
- if (filters != NULL)
- free(filters);
-
- FilterLevel -= job->cost;
-
- cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
- "Job canceled because the server ran out of memory.");
-
- cupsdCancelJob(job, 0);
- return;
+ cupsdSetPrinterReasons(job->printer, message);
+ cupsdAddPrinterHistory(job->printer);
}
+ else if (loglevel == CUPSD_LOG_ATTR)
+ {
+ /*
+ * Set attribute(s)...
+ */
- options = optptr;
- optlength = i;
- }
+ /**** TODO ****/
+ }
+#ifdef __APPLE__
+ else if (!strncmp(message, "recoverable:", 12))
+ {
+ cupsdSetPrinterReasons(job->printer,
+ "+com.apple.print.recoverable-warning");
- /*
- * Now loop through the attributes and convert them to the textual
- * representation used by the filters...
- */
+ ptr = message + 12;
+ while (isspace(*ptr & 255))
+ ptr ++;
- optptr = options;
- *optptr = '\0';
+ cupsdSetString(&job->printer->recoverable, ptr);
+ cupsdAddPrinterHistory(job->printer);
+ }
+ else if (!strncmp(message, "recovered:", 10))
+ {
+ cupsdSetPrinterReasons(job->printer,
+ "-com.apple.print.recoverable-warning");
- snprintf(title, sizeof(title), "%s-%d", printer->name, job->id);
- strcpy(copies, "1");
+ ptr = message + 10;
+ while (isspace(*ptr & 255))
+ ptr ++;
- for (attr = job->attrs->attrs; attr != NULL; attr = attr->next)
- {
- if (strcmp(attr->name, "copies") == 0 &&
- attr->value_tag == IPP_TAG_INTEGER)
+ cupsdSetString(&job->printer->recoverable, ptr);
+ cupsdAddPrinterHistory(job->printer);
+ }
+#endif /* __APPLE__ */
+ else if (loglevel <= CUPSD_LOG_INFO)
{
/*
- * Don't use the # copies attribute if we are printing the job sheets...
+ * Some message to show in the printer-state-message attribute...
*/
- if (!banner_page)
- sprintf(copies, "%d", attr->values[0].integer);
+ strlcpy(job->printer->state_message, message,
+ sizeof(job->printer->state_message));
+ cupsdAddPrinterHistory(job->printer);
}
- else if (strcmp(attr->name, "job-name") == 0 &&
- (attr->value_tag == IPP_TAG_NAME ||
- attr->value_tag == IPP_TAG_NAMELANG))
- strlcpy(title, attr->values[0].string.text, sizeof(title));
- else if (attr->group_tag == IPP_TAG_JOB)
- {
- /*
- * Filter out other unwanted attributes...
- */
- 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 ||
- attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */
- continue;
+ if (!strchr(job->status_buffer->buffer, '\n'))
+ break;
+ }
- if (strncmp(attr->name, "time-", 5) == 0)
- continue;
+ if (ptr == NULL)
+ {
+ /*
+ * See if all of the filters and the backend have returned their
+ * exit statuses.
+ */
- if (strncmp(attr->name, "job-", 4) == 0 &&
- !(printer->type & CUPS_PRINTER_REMOTE))
- continue;
+ for (i = 0; job->filters[i] < 0; i ++);
- 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)
- continue;
+ if (job->filters[i])
+ return;
- 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) &&
- banner_page)
- continue;
+ if (job->current_file >= job->num_files && job->backend > 0)
+ return;
- /*
- * Otherwise add them to the list...
- */
+ /*
+ * Handle the end of job stuff...
+ */
- if (optptr > options)
- strlcat(optptr, " ", optlength - (optptr - options));
+ cupsdFinishJob(job);
+ }
+}
- if (attr->value_tag != IPP_TAG_BOOLEAN)
- {
- strlcat(optptr, attr->name, optlength - (optptr - options));
- strlcat(optptr, "=", optlength - (optptr - options));
- }
- for (i = 0; i < attr->num_values; i ++)
- {
- if (i)
- strlcat(optptr, ",", optlength - (optptr - options));
+/*
+ * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
+ */
- optptr += strlen(optptr);
+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 */
- switch (attr->value_tag)
- {
- case IPP_TAG_INTEGER :
- case IPP_TAG_ENUM :
- snprintf(optptr, optlength - (optptr - options),
- "%d", attr->values[i].integer);
- break;
- case IPP_TAG_BOOLEAN :
- if (!attr->values[i].boolean)
- strlcat(optptr, "no", optlength - (optptr - options));
+ 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);
+}
- case IPP_TAG_NOVALUE :
- strlcat(optptr, attr->name,
- optlength - (optptr - options));
- break;
- case IPP_TAG_RANGE :
- if (attr->values[i].range.lower == attr->values[i].range.upper)
- snprintf(optptr, optlength - (optptr - options) - 1,
- "%d", attr->values[i].range.lower);
- else
- snprintf(optptr, optlength - (optptr - options) - 1,
- "%d-%d", attr->values[i].range.lower,
- attr->values[i].range.upper);
- break;
+/*
+ * 'compare_jobs()' - Compare the job IDs of two jobs.
+ */
- case IPP_TAG_RESOLUTION :
- snprintf(optptr, optlength - (optptr - options) - 1,
- "%dx%d%s", attr->values[i].resolution.xres,
- attr->values[i].resolution.yres,
- attr->values[i].resolution.units == IPP_RES_PER_INCH ?
- "dpi" : "dpc");
- break;
+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);
+}
- case IPP_TAG_STRING :
- case IPP_TAG_TEXT :
- case IPP_TAG_NAME :
- case IPP_TAG_KEYWORD :
- case IPP_TAG_CHARSET :
- case IPP_TAG_LANGUAGE :
- for (valptr = attr->values[i].string.text; *valptr;)
- {
- if (strchr(" \t\n\\\'\"", *valptr))
- *optptr++ = '\\';
- *optptr++ = *valptr++;
- }
- *optptr = '\0';
- break;
+/*
+ * '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);
+
+ 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, "<Job"))
+ {
+ if (job)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Missing </Job> 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,
+ "Unable to allocate memory for job %d!", 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->status_pipes[0] = -1;
+ job->status_pipes[1] = -1;
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading job %d from cache...", job->id);
+ }
+ else if (!job)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Missing <Job #> directive on line %d!", linenum);
+ continue;
+ }
+ else if (!strcasecmp(line, "</Job>"))
+ {
+ 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 = 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,
+ "Data files for job %d 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,
+ "Unable to allocate memory for %d files!",
+ 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,
+ "Unknown MIME type %s/%s for file %d of job %d!",
+ super, type, number + 1, job->id);
+
+ 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->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 */
- default :
- break; /* anti-compiler-warning-code */
- }
- }
- optptr += strlen(optptr);
- }
+ 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 */
+
/*
- * Build the command-line arguments for the filters. Each filter
- * has 6 or 7 arguments:
- *
- * argv[0] = printer
- * argv[1] = job ID
- * argv[2] = username
- * argv[3] = title
- * argv[4] = # copies
- * argv[5] = options
- * argv[6] = filename (optional; normally stdin)
- *
- * This allows legacy printer drivers that use the old System V
- * printing interface to be used by CUPS.
+ * Set the hold_until value and hold the job...
*/
- sprintf(jobid, "%d", job->id);
- snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
- job->id, job->current_file + 1);
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "set_hold_until: hold_until = %d",
+ (int)holdtime);
- argv[0] = printer->name;
- argv[1] = jobid;
- argv[2] = job->username;
- argv[3] = title;
- argv[4] = copies;
- argv[5] = options;
- argv[6] = filename;
- argv[7] = NULL;
+ 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);
- 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 ((attr = ippFindAttribute(job->attrs, "job-hold-until",
+ IPP_TAG_KEYWORD)) == NULL)
+ attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
/*
- * Create environment variable strings for the filters...
+ * Either add the attribute or update the value of the existing one
*/
- attr = ippFindAttribute(job->attrs, "attributes-natural-language",
- IPP_TAG_LANGUAGE);
+ 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);
- switch (strlen(attr->values[0].string.text))
- {
- default :
- /*
- * This is an unknown or badly formatted language code; use
- * the POSIX locale...
- */
+ cupsdSaveJob(job);
+}
- strcpy(lang, "LANG=C");
- break;
- case 2 :
- /*
- * Just the language code (ll)...
- */
+/*
+ * 'start_job()' - Start a print job.
+ */
- snprintf(lang, sizeof(lang), "LANG=%s",
- attr->values[0].string.text);
- break;
+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 */
+ 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 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 + 11],
+ /* 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 */
+ int remote_job; /* Remote print job? */
+ static char *options = NULL;/* Full list of options */
+ static int optlength = 0; /* Length of option buffer */
- case 5 :
- /*
- * Language and country code (ll-cc)...
- */
- snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c",
- attr->values[0].string.text[0],
- attr->values[0].string.text[1],
- toupper(attr->values[0].string.text[3] & 255),
- toupper(attr->values[0].string.text[4] & 255));
- break;
- }
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job: id = %d, file = %d/%d",
+ job->id, job->current_file, job->num_files);
- attr = ippFindAttribute(job->attrs, "document-format",
- IPP_TAG_MIMETYPE);
- if (attr != NULL &&
- (optptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
- snprintf(charset, sizeof(charset), "CHARSET=%s", optptr + 8);
- else
+ if (job->num_files == 0)
{
- attr = ippFindAttribute(job->attrs, "attributes-charset",
- IPP_TAG_CHARSET);
- snprintf(charset, sizeof(charset), "CHARSET=%s",
- attr->values[0].string.text);
- }
-
- 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);
- 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);
- snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Job ID %d has no files! Cancelling it!",
+ job->id);
- envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
+ "Job canceled because it has no files.");
- envp[envc ++] = charset;
- envp[envc ++] = lang;
- envp[envc ++] = ppd;
- envp[envc ++] = rip_max_cache;
- envp[envc ++] = content_type;
- envp[envc ++] = device_uri;
- envp[envc ++] = printer_name;
+ cupsdCancelJob(job, 0);
+ return;
+ }
- if (Classification && !banner_page)
+ if (printer->raw && !strncmp(printer->device_uri, "file:", 5))
{
- if ((attr = ippFindAttribute(job->attrs, "job-sheets",
- IPP_TAG_NAME)) == NULL)
- snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
- Classification);
- else if (attr->num_values > 1 &&
- strcmp(attr->values[1].string.text, "none") != 0)
- snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
- attr->values[1].string.text);
- else
- snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
- attr->values[0].string.text);
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Job ID %d cannot be printed to raw queue pointing to "
+ "a file!",
+ job->id);
- envp[envc ++] = classification;
+ strlcpy(printer->state_message, "Raw printers cannot use file: devices!",
+ sizeof(printer->state_message));
+ cupsdStopPrinter(printer, 1);
+ cupsdAddPrinterHistory(printer);
+ return;
}
- if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
+ /*
+ * Figure out what filters are required to convert from
+ * the source to the destination type...
+ */
+
+ filters = NULL;
+ job->cost = 0;
+
+ if (printer->raw)
{
- snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest);
- envp[envc ++] = class_name;
+ /*
+ * 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...
+ */
- envp[envc] = NULL;
+ filters = mimeFilter(MimeDatabase, job->filetypes[job->current_file],
+ printer->filetype, &(job->cost));
- for (i = 0; i < envc; i ++)
- if (strncmp(envp[i], "DEVICE_URI=", 11))
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: envp[%d]=\"%s\"",
- i, envp[i]);
- else
- cupsdLogMessage(CUPSD_LOG_DEBUG,
- "cupsdStartJob: envp[%d]=\"DEVICE_URI=%s\"", i, sani_uri);
+ if (!filters)
+ {
+ 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?");
+
+ if (LogLevel < CUPSD_LOG_DEBUG)
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "Hint: Try setting the LogLevel to \"debug\".");
- job->current_file ++;
+ job->current_file ++;
- /*
- * Now create processes for all of the filters...
- */
+ 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.");
- 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));
+ cupsdCancelJob(job, 0);
+ }
- cupsdAddPrinterHistory(printer);
+ return;
+ }
- if (filters != NULL)
- free(filters);
+ /*
+ * Remove NULL ("-") filters...
+ */
- cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
- "Job canceled because the server could not create the job status pipes.");
+ for (filter = (mime_filter_t *)cupsArrayFirst(filters);
+ filter;
+ filter = (mime_filter_t *)cupsArrayNext(filters))
+ if (!strcmp(filter->filter, "-"))
+ cupsArrayRemove(filters, filter);
- cupsdCancelJob(job, 0);
- return;
+ if (cupsArrayCount(filters) == 0)
+ {
+ cupsArrayDelete(filters);
+ filters = NULL;
+ }
}
- 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 */
+ /*
+ * Set a minimum cost of 100 for all jobs so that FilterLimit
+ * works with raw queues and other low-cost paths.
+ */
- job->status_buffer = cupsdStatBufNew(statusfds[0], "[Job %d]",
- job->id);
- job->status = 0;
- memset(job->filters, 0, sizeof(job->filters));
+ if (job->cost < 100)
+ job->cost = 100;
- filterfds[1][0] = open("/dev/null", O_RDONLY);
- filterfds[1][1] = -1;
+ /*
+ * See if the filter cost is too high...
+ */
- if (filterfds[1][0] < 0)
+ if ((FilterLevel + job->cost) > FilterLimit && FilterLevel > 0 &&
+ FilterLimit > 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);
+ /*
+ * Don't print this job quite yet...
+ */
- if (filters != NULL)
- free(filters);
+ cupsArrayDelete(filters);
- cupsdClosePipe(statusfds);
- cupsdCancelJob(job, 0);
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "Holding job %d because filter limit has been reached.",
+ job->id);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: id=%d, file=%d, cost=%d, level=%d, "
+ "limit=%d",
+ job->id, job->current_file, job->cost, FilterLevel,
+ FilterLimit);
return;
}
- fcntl(filterfds[1][0], F_SETFD, fcntl(filterfds[1][0], F_GETFD) | FD_CLOEXEC);
+ FilterLevel += job->cost;
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
- 1, filterfds[1][0], filterfds[1][1]);
+ /*
+ * Determine if we are printing to a remote printer...
+ */
- for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
- filter;
- i ++, filter = (mime_filter_t *)cupsArrayNext(filters))
+ remote_job = printer->raw && job->num_files > 1 &&
+ !strncmp(printer->device_uri, "ipp://", 6);
+
+ /*
+ * Add decompression filters, if any...
+ */
+
+ if (!remote_job && job->compressions[job->current_file])
{
- if (filter->filter[0] != '/')
- snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
- filter->filter);
- else
- strlcpy(command, filter->filter, sizeof(command));
+ /*
+ * Add gziptoany filter to the front of the list...
+ */
- if (i < (cupsArrayCount(filters) - 1))
+ if (!cupsArrayInsert(filters, &gziptoany_filter))
{
- if (cupsdOpenPipe(filterfds[slot]))
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to create job filter pipes - %s.",
- strerror(errno));
- snprintf(printer->state_message, sizeof(printer->state_message),
- "Unable to create filter pipes - %s.", strerror(errno));
- cupsdAddPrinterHistory(printer);
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to add decompression filter - %s",
+ strerror(errno));
- cupsArrayDelete(filters);
+ cupsArrayDelete(filters);
- cupsdClosePipe(statusfds);
- cupsdClosePipe(filterfds[!slot]);
+ job->current_file ++;
+ if (job->current_file == job->num_files)
+ {
cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
- "Job canceled because the server could not create the filter pipes.");
+ "Job canceled because the print file could not be "
+ "decompressed.");
- cupsdCancelJob(job, 0);
- return;
+ cupsdCancelJob(job, 0);
}
+
+ return;
}
- else
- {
- if (job->current_file == 1)
- {
- if (strncmp(printer->device_uri, "file:", 5) != 0)
- {
- if (cupsdOpenPipe(job->print_pipes))
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to create job backend pipes - %s.",
- strerror(errno));
- snprintf(printer->state_message, sizeof(printer->state_message),
- "Unable to create backend pipes - %s.", strerror(errno));
- cupsdAddPrinterHistory(printer);
+ }
- cupsArrayDelete(filters);
+ /*
+ * Add port monitor, if any...
+ */
- cupsdClosePipe(statusfds);
- cupsdClosePipe(filterfds[!slot]);
+ if (printer->port_monitor)
+ {
+ /*
+ * Add port monitor to the end of the list...
+ */
- cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
- "Job canceled because the server could not create the backend pipes.");
+ if (!cupsArrayAdd(filters, &port_monitor))
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add port monitor - %s",
+ strerror(errno));
- cupsdCancelJob(job, 0);
- return;
- }
- }
- 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);
- else
- job->print_pipes[1] = open(printer->device_uri + 5,
- O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ cupsArrayDelete(filters);
- 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));
+ job->current_file ++;
- cupsdAddPrinterHistory(printer);
+ 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.");
- cupsArrayDelete(filters);
+ cupsdCancelJob(job, 0);
+ }
- cupsdClosePipe(statusfds);
- cupsdClosePipe(filterfds[!slot]);
+ return;
+ }
- cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
- "Job canceled because the server could not open the output file.");
+ snprintf(port_monitor.filter, sizeof(port_monitor.filter),
+ "%s/monitor/%s", ServerBin, printer->port_monitor);
+ }
- cupsdCancelJob(job, 0);
- return;
- }
+ /*
+ * Update the printer and job state to "processing"...
+ */
- fcntl(job->print_pipes[1], F_SETFD,
- fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
- }
+ 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);
- cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartJob: print_pipes = [ %d %d ]",
- job->print_pipes[0], job->print_pipes[1]);
- }
+ if (job->current_file == 0)
+ {
+ set_time(job, "time-at-processing");
+ cupsdOpenPipe(job->back_pipes);
+ }
- filterfds[slot][0] = job->print_pipes[0];
- filterfds[slot][1] = job->print_pipes[1];
- }
+ /*
+ * Determine if we are printing a banner page or not...
+ */
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filter = \"%s\"", command);
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
- slot, filterfds[slot][0], filterfds[slot][1]);
+ if (job->job_sheets == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "No job-sheets attribute.");
+ 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!");
+ }
+ else if (job->job_sheets->num_values == 1)
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s",
+ job->job_sheets->values[0].string.text);
+ else
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s,%s",
+ job->job_sheets->values[0].string.text,
+ job->job_sheets->values[1].string.text);
- pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
- filterfds[slot][1], statusfds[1],
- job->back_pipes[0], 0, job->filters + i);
+ if (printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
+ banner_page = 0;
+ else if (job->job_sheets == NULL)
+ banner_page = 0;
+ else if (strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
+ job->current_file == 0)
+ banner_page = 1;
+ else if (job->job_sheets->num_values > 1 &&
+ strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
+ job->current_file == (job->num_files - 1))
+ banner_page = 1;
+ else
+ banner_page = 0;
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
- !slot, filterfds[!slot][0], filterfds[!slot][1]);
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "banner_page = %d", banner_page);
- cupsdClosePipe(filterfds[!slot]);
+ /*
+ * Building the options string is harder than it needs to be, but
+ * for the moment we need to pass strings for command-line args and
+ * not IPP attribute pointers... :)
+ *
+ * First allocate/reallocate the option buffer as needed...
+ */
- if (pid == 0)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
- filter->filter, strerror(errno));
- snprintf(printer->state_message, sizeof(printer->state_message),
- "Unable to start filter \"%s\" - %s.",
- filter->filter, strerror(errno));
+ i = ipp_length(job->attrs);
- cupsdAddPrinterHistory(printer);
+ if (i > optlength)
+ {
+ if (optlength == 0)
+ optptr = malloc(i);
+ else
+ optptr = realloc(options, i);
+
+ if (optptr == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_CRIT,
+ "Unable to allocate %d bytes for option buffer for "
+ "job %d!", i, job->id);
cupsArrayDelete(filters);
- cupsdAddPrinterHistory(printer);
+ FilterLevel -= job->cost;
cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
- "Job canceled because the server could not execute a filter.");
+ "Job canceled because the server ran out of memory.");
cupsdCancelJob(job, 0);
return;
}
- cupsdLogMessage(CUPSD_LOG_INFO, "Started filter %s (PID %d) for job %d.",
- command, pid, job->id);
-
- argv[6] = NULL;
- slot = !slot;
+ options = optptr;
+ optlength = i;
}
- cupsArrayDelete(filters);
-
/*
- * Finally, pipe the final output into a backend process if needed...
+ * Now loop through the attributes and convert them to the textual
+ * representation used by the filters...
*/
- if (strncmp(printer->device_uri, "file:", 5) != 0)
+ optptr = options;
+ *optptr = '\0';
+
+ snprintf(title, sizeof(title), "%s-%d", printer->name, job->id);
+ strcpy(copies, "1");
+
+ for (attr = job->attrs->attrs; attr != NULL; attr = attr->next)
{
- if (job->current_file == 1)
+ if (!strcmp(attr->name, "copies") &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ {
+ /*
+ * Don't use the # copies attribute if we are printing the job sheets...
+ */
+
+ if (!banner_page)
+ sprintf(copies, "%d", attr->values[0].integer);
+ }
+ 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));
+ else if (attr->group_tag == IPP_TAG_JOB)
{
- sscanf(printer->device_uri, "%254[^:]", method);
- snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, method);
+ /*
+ * Filter out other unwanted attributes...
+ */
- argv[0] = sani_uri;
+ 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 && strcmp(attr->name, "job-uuid")) ||
+ attr->value_tag == IPP_TAG_URISCHEME ||
+ attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */
+ continue;
- filterfds[slot][0] = -1;
- filterfds[slot][1] = open("/dev/null", O_WRONLY);
+ if (!strncmp(attr->name, "time-", 5))
+ continue;
- 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));
+ if (!strncmp(attr->name, "job-", 4) && strcmp(attr->name, "job-uuid") &&
+ !(printer->type & CUPS_PRINTER_REMOTE))
+ continue;
- cupsdAddPrinterHistory(printer);
+ 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;
- cupsdClosePipe(statusfds);
+ if ((!strcmp(attr->name, "page-label") ||
+ !strcmp(attr->name, "page-border") ||
+ !strncmp(attr->name, "number-up", 9) ||
+ !strcmp(attr->name, "page-set") ||
+ !strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
+ !strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed")) &&
+ banner_page)
+ continue;
- cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
- "Job canceled because the server could not open a file.");
+ /*
+ * Otherwise add them to the list...
+ */
- cupsdCancelJob(job, 0);
- return;
+ if (optptr > options)
+ strlcat(optptr, " ", optlength - (optptr - options));
+
+ if (attr->value_tag != IPP_TAG_BOOLEAN)
+ {
+ strlcat(optptr, attr->name, optlength - (optptr - options));
+ strlcat(optptr, "=", optlength - (optptr - options));
}
- fcntl(filterfds[slot][1], F_SETFD,
- fcntl(filterfds[slot][1], F_GETFD) | FD_CLOEXEC);
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ strlcat(optptr, ",", optlength - (optptr - options));
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: backend = \"%s\"",
- command);
- cupsdLogMessage(CUPSD_LOG_DEBUG,
- "cupsdStartJob: filterfds[%d] = [ %d %d ]",
- slot, filterfds[slot][0], filterfds[slot][1]);
+ optptr += strlen(optptr);
- pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
- filterfds[slot][1], statusfds[1],
- job->back_pipes[1], 1,
- &(job->backend));
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ snprintf(optptr, optlength - (optptr - options),
+ "%d", attr->values[i].integer);
+ break;
- if (pid == 0)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start backend \"%s\" - %s.",
- method, strerror(errno));
- snprintf(printer->state_message, sizeof(printer->state_message),
- "Unable to start backend \"%s\" - %s.", method, strerror(errno));
+ case IPP_TAG_BOOLEAN :
+ if (!attr->values[i].boolean)
+ strlcat(optptr, "no", optlength - (optptr - options));
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartJob: Closing print pipes [ %d %d ]...",
- job->print_pipes[0], job->print_pipes[1]);
+ case IPP_TAG_NOVALUE :
+ strlcat(optptr, attr->name,
+ optlength - (optptr - options));
+ break;
- cupsdClosePipe(job->print_pipes);
+ case IPP_TAG_RANGE :
+ if (attr->values[i].range.lower == attr->values[i].range.upper)
+ snprintf(optptr, optlength - (optptr - options) - 1,
+ "%d", attr->values[i].range.lower);
+ else
+ snprintf(optptr, optlength - (optptr - options) - 1,
+ "%d-%d", attr->values[i].range.lower,
+ attr->values[i].range.upper);
+ break;
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartJob: Closing back pipes [ %d %d ]...",
- job->back_pipes[0], job->back_pipes[1]);
+ case IPP_TAG_RESOLUTION :
+ snprintf(optptr, optlength - (optptr - options) - 1,
+ "%dx%d%s", attr->values[i].resolution.xres,
+ attr->values[i].resolution.yres,
+ attr->values[i].resolution.units == IPP_RES_PER_INCH ?
+ "dpi" : "dpc");
+ break;
- cupsdClosePipe(job->back_pipes);
+ 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 :
+ for (valptr = attr->values[i].string.text; *valptr;)
+ {
+ if (strchr(" \t\n\\\'\"", *valptr))
+ *optptr++ = '\\';
+ *optptr++ = *valptr++;
+ }
- cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
- "Job canceled because the server could not execute the backend.");
+ *optptr = '\0';
+ break;
- cupsdCancelJob(job, 0);
- return;
- }
- else
- {
- cupsdLogMessage(CUPSD_LOG_INFO,
- "Started backend %s (PID %d) for job %d.",
- command, pid, job->id);
+ default :
+ break; /* anti-compiler-warning-code */
+ }
}
+
+ optptr += strlen(optptr);
}
+ }
- 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]);
+ /*
+ * Build the command-line arguments for the filters. Each filter
+ * has 6 or 7 arguments:
+ *
+ * argv[0] = printer
+ * argv[1] = job ID
+ * argv[2] = username
+ * argv[3] = title
+ * argv[4] = # copies
+ * argv[5] = options
+ * argv[6] = filename (optional; normally stdin)
+ *
+ * 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.
+ */
- cupsdClosePipe(job->print_pipes);
+ if (remote_job)
+ argv = calloc(7 + job->num_files, sizeof(char *));
+ else
+ argv = calloc(8, sizeof(char *));
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartJob: Closing back pipes [ %d %d ]...",
- job->back_pipes[0], job->back_pipes[1]);
+ sprintf(jobid, "%d", job->id);
- cupsdClosePipe(job->back_pipes);
+ argv[0] = printer->name;
+ argv[1] = jobid;
+ argv[2] = job->username;
+ argv[3] = title;
+ argv[4] = copies;
+ argv[5] = options;
+
+ if (remote_job)
+ {
+ 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
{
- filterfds[slot][0] = -1;
- filterfds[slot][1] = -1;
-
- 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]);
-
- cupsdClosePipe(job->print_pipes);
- }
+ snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
+ job->id, job->current_file + 1);
+ argv[6] = filename;
}
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
- slot, filterfds[slot][0], filterfds[slot][1]);
-
- cupsdClosePipe(filterfds[slot]);
+ for (i = 0; argv[i]; i ++)
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "[Job %d] argv[%d]=\"%s\"", job->id, i, argv[i]);
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartJob: Closing status output pipe %d...",
- statusfds[1]);
+ /*
+ * Create environment variable strings for the filters...
+ */
- close(statusfds[1]);
+ attr = ippFindAttribute(job->attrs, "attributes-natural-language",
+ IPP_TAG_LANGUAGE);
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStartJob: Adding fd %d to InputSet...",
- job->status_buffer->fd);
+ switch (strlen(attr->values[0].string.text))
+ {
+ default :
+ /*
+ * This is an unknown or badly formatted language code; use
+ * the POSIX locale...
+ */
- FD_SET(job->status_buffer->fd, InputSet);
-}
+ strcpy(lang, "LANG=C");
+ break;
+ case 2 :
+ /*
+ * Just the language code (ll)...
+ */
-/*
- * 'cupsdStopAllJobs()' - Stop all print jobs.
- */
+ snprintf(lang, sizeof(lang), "LANG=%s",
+ attr->values[0].string.text);
+ break;
-void
-cupsdStopAllJobs(void)
-{
- cupsd_job_t *job; /* Current job */
+ case 5 :
+ /*
+ * Language and country code (ll-cc)...
+ */
+ snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c",
+ attr->values[0].string.text[0],
+ attr->values[0].string.text[1],
+ toupper(attr->values[0].string.text[3] & 255),
+ toupper(attr->values[0].string.text[4] & 255));
+ break;
+ }
- DEBUG_puts("cupsdStopAllJobs()");
+ attr = ippFindAttribute(job->attrs, "document-format",
+ IPP_TAG_MIMETYPE);
+ if (attr != NULL &&
+ (optptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
+ snprintf(charset, sizeof(charset), "CHARSET=%s", optptr + 8);
+ else
+ {
+ attr = ippFindAttribute(job->attrs, "attributes-charset",
+ IPP_TAG_CHARSET);
+ snprintf(charset, sizeof(charset), "CHARSET=%s",
+ attr->values[0].string.text);
+ }
- 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;
- }
-}
+ 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);
+ 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);
+ snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
+ envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
-/*
- * 'cupsdStopJob()' - Stop a print job.
- */
+ envp[envc ++] = charset;
+ envp[envc ++] = lang;
+ envp[envc ++] = ppd;
+ envp[envc ++] = rip_max_cache;
+ envp[envc ++] = content_type;
+ envp[envc ++] = device_uri;
+ envp[envc ++] = printer_name;
-void
-cupsdStopJob(cupsd_job_t *job, /* I - Job */
- int force) /* I - 1 = Force all filters to stop */
-{
- int i; /* Looping var */
+ if ((filter = (mime_filter_t *)cupsArrayLast(filters)) != NULL)
+ {
+ 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",
+ IPP_TAG_NAME)) == NULL)
+ snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
+ Classification);
+ else if (attr->num_values > 1 &&
+ strcmp(attr->values[1].string.text, "none") != 0)
+ snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
+ attr->values[1].string.text);
+ else
+ snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
+ attr->values[0].string.text);
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopJob: id = %d, force = %d",
- job->id, force);
+ envp[envc ++] = classification;
+ }
- if (job->state->values[0].integer != IPP_JOB_PROCESSING)
- return;
+ if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
+ {
+ snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest);
+ envp[envc ++] = class_name;
+ }
- FilterLevel -= job->cost;
+ envp[envc] = NULL;
- 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);
+ for (i = 0; i < envc; i ++)
+ 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, "[Job %d] envp[%d]=\"DEVICE_URI=%s\"",
+ job->id, i, sani_uri);
- cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopJob: printer state is %d",
- job->printer->state);
+ if (remote_job)
+ job->current_file = job->num_files;
+ else
+ job->current_file ++;
- job->state->values[0].integer = IPP_JOB_STOPPED;
- job->printer->job = NULL;
- job->printer = NULL;
+ /*
+ * Now create processes for all of the filters...
+ */
- job->current_file --;
+ filterfds[0][0] = -1;
+ filterfds[0][1] = -1;
+ filterfds[1][0] = -1;
+ filterfds[1][1] = -1;
- for (i = 0; job->filters[i]; i ++)
- if (job->filters[i] > 0)
+ if (!job->status_buffer)
+ {
+ if (cupsdOpenPipe(job->status_pipes))
{
- cupsdEndProcess(job->filters[i], force);
- job->filters[i] = 0;
+ 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);
+
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
+ "Job canceled because the server could not create the job "
+ "status pipes.");
+
+ goto abort_job;
}
-
- if (job->backend > 0)
- {
- cupsdEndProcess(job->backend, force);
- job->backend = 0;
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job: status_pipes = [ %d %d ]",
+ job->status_pipes[0], job->status_pipes[1]);
+
+ job->status_buffer = cupsdStatBufNew(job->status_pipes[0], "[Job %d]",
+ job->id);
}
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopJob: Closing print pipes [ %d %d ]...",
- job->print_pipes[0], job->print_pipes[1]);
-
- cupsdClosePipe(job->print_pipes);
-
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopJob: Closing back pipes [ %d %d ]...",
- job->back_pipes[0], job->back_pipes[1]);
+ job->status = 0;
+ memset(job->filters, 0, sizeof(job->filters));
- cupsdClosePipe(job->back_pipes);
+ filterfds[1][0] = open("/dev/null", O_RDONLY);
- if (job->status_buffer)
+ if (filterfds[1][0] < 0)
{
- /*
- * Close the pipe and clear the input bit.
- */
-
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopJob: Removing fd %d from InputSet...",
- job->status_buffer->fd);
-
- FD_CLR(job->status_buffer->fd, InputSet);
+ 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));
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopJob: Closing status input pipe %d...",
- job->status_buffer->fd);
+ cupsdAddPrinterHistory(printer);
- cupsdStatBufDelete(job->status_buffer);
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
+ "Job canceled because the server could not open /dev/null.");
- job->status_buffer = NULL;
+ goto abort_job;
}
-}
-
-
-/*
- * 'cupsdUpdateJob()' - Read a status update from a job's filters.
- */
-void
-cupsdUpdateJob(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 */
+ fcntl(filterfds[1][0], F_SETFD, fcntl(filterfds[1][0], F_GETFD) | FD_CLOEXEC);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job: filterfds[%d] = [ %d %d ]",
+ 1, filterfds[1][0], filterfds[1][1]);
- while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
- message, sizeof(message))) != NULL)
+ for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
+ filter;
+ i ++, filter = (mime_filter_t *)cupsArrayNext(filters))
{
- /*
- * Process page and printer state messages as needed...
- */
+ if (filter->filter[0] != '/')
+ snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
+ filter->filter);
+ else
+ strlcpy(command, filter->filter, sizeof(command));
- if (loglevel == CUPSD_LOG_PAGE)
+ if (i < (cupsArrayCount(filters) - 1))
{
- /*
- * Page message; send the message to the page_log file and update the
- * job sheet count...
- */
-
- if (job->sheets != NULL)
+ if (cupsdOpenPipe(filterfds[slot]))
{
- if (!strncasecmp(message, "total ", 6))
- {
- /*
- * Got a total count of pages from a backend or filter...
- */
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to create job filter pipes - %s.",
+ strerror(errno));
+ snprintf(printer->state_message, sizeof(printer->state_message),
+ "Unable to create filter pipes - %s.", strerror(errno));
+ cupsdAddPrinterHistory(printer);
- copies = atoi(message + 6);
- copies -= job->sheets->values[0].integer; /* Just track the delta */
- }
- else if (!sscanf(message, "%*d%d", &copies))
- copies = 1;
-
- job->sheets->values[0].integer += copies;
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
+ "Job canceled because the server could not create the "
+ "filter pipes.");
- if (job->printer->page_limit)
- cupsdUpdateQuota(job->printer, job->username, copies, 0);
+ goto abort_job;
}
-
- cupsdLogPage(job, message);
-
- cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
- "Printed %d page(s).", job->sheets->values[0].integer);
}
- else if (loglevel == CUPSD_LOG_STATE)
- cupsdSetPrinterReasons(job->printer, message);
- else if (loglevel == CUPSD_LOG_ATTR)
+ else
{
- /*
- * Set attribute(s)...
- */
-
- /**** TODO ****/
- }
+ if (job->current_file == 1)
+ {
+ if (strncmp(printer->device_uri, "file:", 5) != 0)
+ {
+ if (cupsdOpenPipe(job->print_pipes))
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to create job backend pipes - %s.",
+ strerror(errno));
+ snprintf(printer->state_message, sizeof(printer->state_message),
+ "Unable to create backend pipes - %s.", strerror(errno));
+ cupsdAddPrinterHistory(printer);
- if (!strchr(job->status_buffer->buffer, '\n'))
- break;
- }
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
+ "Job canceled because the server could not create "
+ "the backend pipes.");
- if (ptr == NULL)
- {
- /*
- * See if all of the filters and the backend have returned their
- * exit statuses.
- */
+ 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);
+ else
+ job->print_pipes[1] = open(printer->device_uri + 5,
+ O_WRONLY | O_CREAT | O_TRUNC, 0600);
- for (i = 0; job->filters[i] < 0; i ++);
+ 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));
- if (job->filters[i])
- return;
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
+ "Job canceled because the server could not open the "
+ "output file.");
- if (job->current_file >= job->num_files && job->backend > 0)
- return;
+ goto abort_job;
+ }
- /*
- * Handle the end of job stuff...
- */
+ fcntl(job->print_pipes[1], F_SETFD,
+ fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
+ }
- cupsdFinishJob(job);
- }
-}
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: print_pipes = [ %d %d ]",
+ job->print_pipes[0], job->print_pipes[1]);
+ }
+ filterfds[slot][0] = job->print_pipes[0];
+ filterfds[slot][1] = job->print_pipes[1];
+ }
-/*
- * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
- */
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job: filter=\"%s\"",
+ command);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: filterfds[%d]=[ %d %d ]",
+ slot, filterfds[slot][0], filterfds[slot][1]);
-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 */
+ pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
+ filterfds[slot][1], job->status_pipes[1],
+ job->back_pipes[0], 0, job->filters + i);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: Closing filter pipes for slot %d "
+ "[ %d %d ]...",
+ !slot, filterfds[!slot][0], filterfds[!slot][1]);
- 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);
-}
+ cupsdClosePipe(filterfds[!slot]);
+ if (pid == 0)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
+ filter->filter, strerror(errno));
+ snprintf(printer->state_message, sizeof(printer->state_message),
+ "Unable to start filter \"%s\" - %s.",
+ filter->filter, strerror(errno));
-/*
- * 'compare_jobs()' - Compare the job IDs of two jobs.
- */
+ cupsdAddPrinterHistory(printer);
-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);
-}
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
+ "Job canceled because the server could not execute a "
+ "filter.");
+ goto abort_job;
+ }
-/*
- * 'ipp_length()' - Compute the size of the buffer needed to hold
- * the textual IPP attributes.
- */
+ cupsdLogMessage(CUPSD_LOG_INFO, "Started filter %s (PID %d) for job %d.",
+ command, pid, job->id);
-int /* O - Size of buffer to hold IPP attributes */
-ipp_length(ipp_t *ipp) /* I - IPP request */
-{
- int bytes; /* Number of bytes */
- int i; /* Looping var */
- ipp_attribute_t *attr; /* Current attribute */
+ argv[6] = NULL;
+ slot = !slot;
+ }
+ cupsArrayDelete(filters);
/*
- * Loop through all attributes...
+ * Finally, pipe the final output into a backend process if needed...
*/
- bytes = 0;
-
- for (attr = ipp->attrs; attr != NULL; attr = attr->next)
+ if (strncmp(printer->device_uri, "file:", 5) != 0)
{
- /*
- * Skip attributes that won't be sent to filters...
- */
+ if (job->current_file == 1)
+ {
+ sscanf(printer->device_uri, "%254[^:]", method);
+ snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, method);
- 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;
+ /*
+ * See if the backend needs to run as root...
+ */
- if (strncmp(attr->name, "time-", 5) == 0)
- continue;
+ if (RunUser)
+ backroot = 0;
+ else if (stat(command, &backinfo))
+ backroot = 0;
+ else
+ backroot = !(backinfo.st_mode & (S_IRWXG | S_IRWXO));
- /*
- * 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...
- */
+ argv[0] = sani_uri;
- bytes ++; /* " " separator */
- bytes += attr->num_values; /* "," separators */
+ filterfds[slot][0] = -1;
+ filterfds[slot][1] = open("/dev/null", O_WRONLY);
- /*
- * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
- * other attributes appear as "foo=value1,value2,...,valueN".
- */
+ 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));
- if (attr->value_tag != IPP_TAG_BOOLEAN)
- bytes += strlen(attr->name);
- else
- bytes += attr->num_values * strlen(attr->name);
+ cupsdAddPrinterHistory(printer);
- /*
- * Now add the size required for each value in the attribute...
- */
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
+ "Job canceled because the server could not open a file.");
- switch (attr->value_tag)
- {
- case IPP_TAG_INTEGER :
- case IPP_TAG_ENUM :
- /*
- * Minimum value of a signed integer is -2147483647, or 11 digits.
- */
+ goto abort_job;
+ }
- bytes += attr->num_values * 11;
- break;
+ fcntl(filterfds[slot][1], F_SETFD,
+ fcntl(filterfds[slot][1], F_GETFD) | FD_CLOEXEC);
- case IPP_TAG_BOOLEAN :
- /*
- * Add two bytes for each false ("no") value...
- */
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job: backend=\"%s\"",
+ command);
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: filterfds[%d] = [ %d %d ]",
+ slot, filterfds[slot][0], filterfds[slot][1]);
- for (i = 0; i < attr->num_values; i ++)
- if (!attr->values[i].boolean)
- bytes += 2;
- break;
+ pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
+ filterfds[slot][1], job->status_pipes[1],
+ job->back_pipes[1], backroot,
+ &(job->backend));
- case IPP_TAG_RANGE :
- /*
- * A range is two signed integers separated by a hyphen, or
- * 23 characters max.
- */
+ if (pid == 0)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start backend \"%s\" - %s.",
+ method, strerror(errno));
+ snprintf(printer->state_message, sizeof(printer->state_message),
+ "Unable to start backend \"%s\" - %s.", method,
+ strerror(errno));
- bytes += attr->num_values * 23;
- break;
+ cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
+ "Job canceled because the server could not execute "
+ "the backend.");
- case IPP_TAG_RESOLUTION :
- /*
- * A resolution is two signed integers separated by an "x" and
- * suffixed by the units, or 26 characters max.
- */
+ goto abort_job;
+ }
+ else
+ {
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "Started backend %s (PID %d) for job %d.",
+ command, pid, job->id);
+ }
+ }
- bytes += attr->num_values * 26;
- break;
+ if (job->current_file == job->num_files)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: Closing print pipes [ %d %d ]...",
+ job->print_pipes[0], job->print_pipes[1]);
- 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.
- */
+ cupsdClosePipe(job->print_pipes);
- for (i = 0; i < attr->num_values; i ++)
- bytes += 2 * strlen(attr->values[i].string.text) + 2;
- break;
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: Closing back pipes [ %d %d ]...",
+ job->back_pipes[0], job->back_pipes[1]);
- default :
- break; /* anti-compiler-warning-code */
+ cupsdClosePipe(job->back_pipes);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: Closing status output pipe %d...",
+ job->status_pipes[1]);
+
+ close(job->status_pipes[1]);
+ job->status_pipes[1] = -1;
}
}
+ else
+ {
+ filterfds[slot][0] = -1;
+ filterfds[slot][1] = -1;
- return (bytes);
-}
+ if (job->current_file == job->num_files)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: Closing print pipes [ %d %d ]...",
+ job->print_pipes[0], job->print_pipes[1]);
+ cupsdClosePipe(job->print_pipes);
-/*
- * 'set_time()' - Set one of the "time-at-xyz" attributes...
- */
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: Closing status output pipe %d...",
+ job->status_pipes[1]);
-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 */
+ close(job->status_pipes[1]);
+ job->status_pipes[1] = -1;
+ }
+ }
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: Closing filter pipes for slot %d "
+ "[ %d %d ]...",
+ slot, filterfds[slot][0], filterfds[slot][1]);
+ cupsdClosePipe(filterfds[slot]);
- if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL)
+ if (remote_job)
{
- attr->value_tag = IPP_TAG_INTEGER;
- attr->values[0].integer = time(NULL);
+ for (i = 0; i < job->num_files; i ++)
+ free(argv[i + 6]);
}
-}
+ free(argv);
-/*
- * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
- */
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: Adding fd %d to InputSet...",
+ job->status_buffer->fd);
-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 */
+ FD_SET(job->status_buffer->fd, InputSet);
+
+ cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.",
+ job->id);
+
+ return;
/*
- * Set the hold_until value and hold the job...
+ * If we get here, we need to abort the current job and close out all
+ * files and pipes...
*/
- cupsdLogMessage(CUPSD_LOG_DEBUG, "set_hold_until: hold_until = %d", (int)holdtime);
+ abort_job:
- job->state->values[0].integer = IPP_JOB_HELD;
- job->hold_until = holdtime;
+ for (slot = 0; slot < 2; slot ++)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: Closing filter pipes for slot %d "
+ "[ %d %d ]...",
+ slot, filterfds[slot][0], filterfds[slot][1]);
+ cupsdClosePipe(filterfds[slot]);
+ }
- /*
- * Update the job-hold-until attribute with a string representing GMT
- * time (HH:MM:SS)...
- */
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "start_job: Closing status pipes [ %d %d ]...",
+ job->status_pipes[0], job->status_pipes[1]);
+ cupsdClosePipe(job->status_pipes);
+ cupsdStatBufDelete(job->status_buffer);
- holddate = gmtime(&holdtime);
- snprintf(holdstr, sizeof(holdstr), "%d:%d:%d", holddate->tm_hour,
- holddate->tm_min, holddate->tm_sec);
+ cupsArrayDelete(filters);
- if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
- attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
+ if (remote_job)
+ {
+ for (i = 0; i < job->num_files; i ++)
+ free(argv[i + 6]);
+ }
- /*
- * Either add the attribute or update the value of the existing one
- */
+ free(argv);
- 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);
+ cupsdStopJob(job, 0);
+}
- cupsdSaveJob(job);
+
+/*
+ * 'unload_job()' - Unload a job from memory.
+ */
+
+static void
+unload_job(cupsd_job_t *job) /* I - Job */
+{
+ if (!job->attrs)
+ return;
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Unloading job %d...", job->id);
+
+ ippDelete(job->attrs);
+
+ job->attrs = NULL;
+ job->state = NULL;
+ job->sheets = NULL;
+ job->job_sheets = NULL;
}
/*
- * End of "$Id: job.c 4992 2006-01-26 17:50:58Z mike $".
+ * End of "$Id: job.c 5452 2006-04-22 22:17:32Z mike $".
*/