/*
* "$Id: job.c 7902 2008-09-03 14:20:17Z mike $"
*
- * Job management routines for the Common UNIX Printing System (CUPS).
+ * Job management routines for the CUPS scheduler.
*
- * Copyright 2007-2009 by Apple Inc.
+ * Copyright 2007-2011 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
* compare_active_jobs() - Compare the job IDs and priorities of two
* jobs.
* compare_jobs() - Compare the job IDs of two jobs.
+ * dump_job_history() - Dump any debug messages for a job.
+ * free_job_history() - Free any log history.
* finalize_job() - Cleanup after job filter processes and support
* data.
* get_options() - Get a string containing the job options.
* load_next_job_id() - Load the NextJobId value from the job.cache
* file.
* load_request_root() - Load jobs from the RequestRoot directory.
- * set_hold_until() - Set the hold time and update job-hold-until
- * attribute.
* set_time() - Set one of the "time-at-xyz" attributes.
* start_job() - Start a print job.
* stop_job() - Stop a print job.
#include <grp.h>
#include <cups/backend.h>
#include <cups/dir.h>
+#ifdef __APPLE__
+# include <IOKit/pwr_mgt/IOPMLib.h>
+# ifdef HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H
+# include <IOKit/pwr_mgt/IOPMLibPrivate.h>
+# endif /* HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H */
+#endif /* __APPLE__ */
/*
static int compare_active_jobs(void *first, void *second, void *data);
static int compare_jobs(void *first, void *second, void *data);
-static void finalize_job(cupsd_job_t *job);
+static void dump_job_history(cupsd_job_t *job);
+static void finalize_job(cupsd_job_t *job, int set_job_state);
+static void free_job_history(cupsd_job_t *job);
static char *get_options(cupsd_job_t *job, int banner_page, char *copies,
size_t copies_size, char *title,
size_t title_size);
-static int ipp_length(ipp_t *ipp);
+static size_t 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_hold_until(cupsd_job_t *job, time_t holdtime);
static void set_time(cupsd_job_t *job, const char *name);
static void start_job(cupsd_job_t *job, cupsd_printer_t *printer);
static void stop_job(cupsd_job_t *job, cupsd_jobaction_t action);
if (purge)
cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_PURGE,
"Job purged by user.");
- else
+ else if (job->state_value < IPP_JOB_CANCELED)
cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT,
"Job canceled by user.");
}
if (job->kill_time && job->kill_time <= curtime)
{
+ cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Stopping unresponsive job!",
+ job->id);
+
stop_job(job, CUPSD_JOB_FORCE);
continue;
}
"Job submission timed out.");
}
+ /*
+ * Continue jobs that are waiting on the FilterLimit...
+ */
+
+ if (job->pending_cost > 0 &&
+ ((FilterLevel + job->pending_cost) < FilterLimit || FilterLevel == 0))
+ cupsdContinueJob(job);
+
/*
* Start pending jobs if the destination is available...
*/
- if (job->state_value == IPP_JOB_PENDING && !NeedReload && !Sleeping &&
- !job->printer)
+ if (job->state_value == IPP_JOB_PENDING && !NeedReload &&
+#ifndef kIOPMAssertionTypeDenySystemSleep
+ !Sleeping &&
+#endif /* !kIOPMAssertionTypeDenySystemSleep */
+ !DoingShutdown && !job->printer)
{
printer = cupsdFindDest(job->dest);
pclass = NULL;
cupsd_job_t *job; /* Current job */
- if (MaxJobs <= 0)
+ if (MaxJobs <= 0 && JobHistory)
return;
for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
- job && cupsArrayCount(Jobs) >= MaxJobs;
+ job && (cupsArrayCount(Jobs) >= MaxJobs || !JobHistory);
job = (cupsd_job_t *)cupsArrayNext(Jobs))
if (job->state_value >= IPP_JOB_CANCELED && !job->printer)
cupsdDeleteJob(job, CUPSD_JOB_PURGE);
{
int i; /* Looping var */
int slot; /* Pipe slot */
- cups_array_t *filters, /* Filters for job */
+ cups_array_t *filters = NULL,/* Filters for job */
*prefilters; /* Filters with prefilters */
mime_filter_t *filter, /* Current filter */
*prefilter, /* Prefilter */
ipp_attribute_t *attr; /* Current attribute */
const char *ptr, /* Pointer into value */
*abort_message; /* Abort message */
+ ipp_jstate_t abort_state = IPP_JOB_STOPPED;
+ /* New job state on abort */
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 filterfds[2][2] = { { -1, -1 }, { -1, -1 } };
+ /* Pipes used between filters */
int envc; /* Number of environment variables */
- char **argv, /* Filter command-line arguments */
+ struct stat fileinfo; /* Job file information */
+ char **argv = NULL, /* Filter command-line arguments */
filename[1024], /* Job filename */
command[1024], /* Full path to command */
jobid[255], /* Job ID string */
/* Job title string */
copies[255], /* # copies string */
*options, /* Options string */
- *envp[MAX_ENV + 19],
+ *envp[MAX_ENV + 21],
/* Environment variables */
charset[255], /* CHARSET env variable */
class_name[255],/* CLASS env variable */
apple_language[255],
/* APPLE_LANGUAGE env variable */
#endif /* __APPLE__ */
+ auth_info_required[255],
+ /* AUTH_INFO_REQUIRED env variable */
ppd[1024], /* PPD env variable */
printer_info[255],
/* PRINTER_INFO env variable */
/* PRINTER_LOCATION env variable */
printer_name[255],
/* PRINTER env variable */
+ *printer_state_reasons = NULL,
+ /* PRINTER_STATE_REASONS env var */
rip_max_cache[255];
/* RIP_MAX_CACHE env variable */
FilterLevel -= job->cost;
- filters = NULL;
+ job->cost = 0;
+ job->pending_cost = 0;
+
+ memset(job->filters, 0, sizeof(job->filters));
+
if (job->printer->raw)
{
*/
cupsdLogJob(job, CUPSD_LOG_DEBUG, "Sending job to queue tagged as raw...");
-
- filters = NULL;
}
else
{
* Local jobs get filtered...
*/
- filters = mimeFilter(MimeDatabase, job->filetypes[job->current_file],
- job->printer->filetype, &(job->cost));
+ snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
+ job->id, job->current_file + 1);
+ if (stat(filename, &fileinfo))
+ fileinfo.st_size = 0;
+
+ filters = mimeFilter2(MimeDatabase, job->filetypes[job->current_file],
+ fileinfo.st_size, job->printer->filetype,
+ &(job->cost));
if (!filters)
{
"Unable to convert file %d to printable format!",
job->current_file);
- job->current_file ++;
+ abort_message = "Aborting job because it cannot be printed.";
+ abort_state = IPP_JOB_ABORTED;
- if (job->current_file == job->num_files)
- cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_DEFAULT,
- "Aborting job because it cannot be printed.");
-
- return;
+ goto abort_job;
}
/*
"cupsdContinueJob: file=%d, cost=%d, level=%d, limit=%d",
job->current_file, job->cost, FilterLevel,
FilterLimit);
+
+ job->pending_cost = job->cost;
+ job->cost = 0;
return;
}
cupsArrayDelete(filters);
- cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
- "Stopping job because the scheduler ran out of "
- "memory.");
+ abort_message = "Stopping job because the scheduler ran out of memory.";
- FilterLevel -= job->cost;
- return;
+ goto abort_job;
}
}
cupsdLogJob(job, CUPSD_LOG_DEBUG,
"Unable to add port monitor - %s", strerror(errno));
- cupsArrayDelete(filters);
-
- cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
- "Stopping job because the scheduler ran out of "
- "memory.");
+ abort_message = "Stopping job because the scheduler ran out of memory.";
- FilterLevel -= job->cost;
- return;
+ goto abort_job;
}
}
"Too many filters (%d > %d), unable to print!",
cupsArrayCount(filters), MAX_FILTERS);
- cupsArrayDelete(filters);
- cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
- "Stopping job because it needs too many filters to "
- "print.");
+ abort_message = "Aborting job because it needs too many filters to print.";
+ abort_state = IPP_JOB_ABORTED;
- FilterLevel -= job->cost;
- return;
+ goto abort_job;
}
/*
banner_page = 0;
else if (job->job_sheets == NULL)
banner_page = 0;
- else if (strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
+ else if (_cups_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 &&
+ _cups_strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
job->current_file == (job->num_files - 1))
banner_page = 1;
else
if ((options = get_options(job, banner_page, copies, sizeof(copies), title,
sizeof(title))) == NULL)
{
- cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
- "Stopping job because the scheduler ran out of memory.");
- cupsArrayDelete(filters);
+ abort_message = "Stopping job because the scheduler ran out of memory.";
- FilterLevel -= job->cost;
- return;
+ goto abort_job;
}
/*
{
cupsdLogMessage(CUPSD_LOG_DEBUG, "Unable to allocate argument array - %s",
strerror(errno));
- cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
- "Stopping job because the scheduler ran out of memory.");
- cupsArrayDelete(filters);
- FilterLevel -= job->cost;
- return;
+ abort_message = "Stopping job because the scheduler ran out of memory.";
+
+ goto abort_job;
}
sprintf(jobid, "%d", job->id);
snprintf(printer_location, sizeof(printer_name), "PRINTER_LOCATION=%s",
job->printer->location ? job->printer->location : "");
snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", job->printer->name);
+ if (job->printer->num_reasons > 0)
+ {
+ char *psrptr; /* Pointer into PRINTER_STATE_REASONS */
+ size_t psrlen; /* Size of PRINTER_STATE_REASONS */
+
+ for (psrlen = 22, i = 0; i < job->printer->num_reasons; i ++)
+ psrlen += strlen(job->printer->reasons[i]) + 1;
+
+ if ((printer_state_reasons = malloc(psrlen)) != NULL)
+ {
+ /*
+ * All of these strcpy's are safe because we allocated the psr string...
+ */
+
+ strcpy(printer_state_reasons, "PRINTER_STATE_REASONS=");
+ for (psrptr = printer_state_reasons + 22, i = 0;
+ i < job->printer->num_reasons;
+ i ++)
+ {
+ if (i)
+ *psrptr++ = ',';
+ strcpy(psrptr, job->printer->reasons[i]);
+ psrptr += strlen(psrptr);
+ }
+ }
+ }
snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
+ if (job->printer->num_auth_info_required == 1)
+ snprintf(auth_info_required, sizeof(auth_info_required),
+ "AUTH_INFO_REQUIRED=%s",
+ job->printer->auth_info_required[0]);
+ else if (job->printer->num_auth_info_required == 2)
+ snprintf(auth_info_required, sizeof(auth_info_required),
+ "AUTH_INFO_REQUIRED=%s,%s",
+ job->printer->auth_info_required[0],
+ job->printer->auth_info_required[1]);
+ else if (job->printer->num_auth_info_required == 3)
+ snprintf(auth_info_required, sizeof(auth_info_required),
+ "AUTH_INFO_REQUIRED=%s,%s,%s",
+ job->printer->auth_info_required[0],
+ job->printer->auth_info_required[1],
+ job->printer->auth_info_required[2]);
+ else if (job->printer->num_auth_info_required == 4)
+ snprintf(auth_info_required, sizeof(auth_info_required),
+ "AUTH_INFO_REQUIRED=%s,%s,%s,%s",
+ job->printer->auth_info_required[0],
+ job->printer->auth_info_required[1],
+ job->printer->auth_info_required[2],
+ job->printer->auth_info_required[3]);
+ else
+ strlcpy(auth_info_required, "AUTH_INFO_REQUIRED=none",
+ sizeof(auth_info_required));
+
envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
envp[envc ++] = charset;
envp[envc ++] = printer_info;
envp[envc ++] = printer_location;
envp[envc ++] = printer_name;
+ envp[envc ++] = printer_state_reasons ? printer_state_reasons :
+ "PRINTER_STATE_REASONS=none";
envp[envc ++] = banner_page ? "CUPS_FILETYPE=job-sheet" :
"CUPS_FILETYPE=document";
if (filter && filter->dst)
{
- snprintf(final_content_type, sizeof(final_content_type),
- "FINAL_CONTENT_TYPE=%s/%s",
- filter->dst->super, filter->dst->type);
+ if ((ptr = strchr(filter->dst->type, '/')) != NULL)
+ snprintf(final_content_type, sizeof(final_content_type),
+ "FINAL_CONTENT_TYPE=%s", ptr + 1);
+ else
+ snprintf(final_content_type, sizeof(final_content_type),
+ "FINAL_CONTENT_TYPE=%s/%s", filter->dst->super,
+ filter->dst->type);
envp[envc ++] = final_content_type;
}
}
envp[envc ++] = class_name;
}
- if (job->auth_username)
- envp[envc ++] = job->auth_username;
- if (job->auth_domain)
- envp[envc ++] = job->auth_domain;
- if (job->auth_password)
- envp[envc ++] = job->auth_password;
+ envp[envc ++] = auth_info_required;
-#ifdef HAVE_GSSAPI
- if (job->ccname)
- envp[envc ++] = job->ccname;
-#endif /* HAVE_GSSAPI */
+ for (i = 0;
+ i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
+ i ++)
+ if (job->auth_env[i])
+ envp[envc ++] = job->auth_env[i];
+ else
+ break;
+
+ if (job->auth_uid)
+ envp[envc ++] = job->auth_uid;
envp[envc] = NULL;
* Now create processes for all of the filters...
*/
- filterfds[0][0] = -1;
- filterfds[0][1] = -1;
- filterfds[1][0] = -1;
- filterfds[1][1] = -1;
-
- memset(job->filters, 0, sizeof(job->filters));
+ cupsdSetPrinterReasons(job->printer, "-cups-missing-filter-warning,"
+ "cups-insecure-filter-warning");
for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
filter;
}
else
{
- if (job->current_file == 1)
+ if (job->current_file == 1 ||
+ (job->printer->pc && job->printer->pc->single_file))
{
if (strncmp(job->printer->device_uri, "file:", 5) != 0)
{
pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
filterfds[slot][1], job->status_pipes[1],
job->back_pipes[0], job->side_pipes[0], 0,
- job->profile, job->id, job->filters + i);
+ job->profile, job, job->filters + i);
cupsdClosePipe(filterfds[!slot]);
cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
filter->filter, strerror(errno));
- abort_message = "Stopped job because the scheduler could not execute a "
+ abort_message = "Stopping job because the scheduler could not execute a "
"filter.";
goto abort_job;
if (strncmp(job->printer->device_uri, "file:", 5) != 0)
{
- if (job->current_file == 1 || job->printer->remote)
+ if (job->current_file == 1 || job->printer->remote ||
+ (job->printer->pc && job->printer->pc->single_file))
{
sscanf(job->printer->device_uri, "%254[^:]", scheme);
snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, scheme);
pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
filterfds[slot][1], job->status_pipes[1],
job->back_pipes[1], job->side_pipes[1],
- backroot, job->profile, job->id, &(job->backend));
+ backroot, job->profile, job, &(job->backend));
if (pid == 0)
{
}
}
+ if (job->current_file == job->num_files ||
+ (job->printer->pc && job->printer->pc->single_file))
+ cupsdClosePipe(job->print_pipes);
+
if (job->current_file == job->num_files)
{
- cupsdClosePipe(job->print_pipes);
cupsdClosePipe(job->back_pipes);
cupsdClosePipe(job->side_pipes);
filterfds[slot][0] = -1;
filterfds[slot][1] = -1;
- if (job->current_file == job->num_files)
- {
+ if (job->current_file == job->num_files ||
+ (job->printer->pc && job->printer->pc->single_file))
cupsdClosePipe(job->print_pipes);
+ if (job->current_file == job->num_files)
+ {
close(job->status_pipes[1]);
job->status_pipes[1] = -1;
}
}
free(argv);
+ if (printer_state_reasons)
+ free(printer_state_reasons);
cupsdAddSelect(job->status_buffer->fd, (cupsd_selfunc_t)update_job, NULL,
job);
abort_job:
+ FilterLevel -= job->cost;
+ job->cost = 0;
+
for (slot = 0; slot < 2; slot ++)
cupsdClosePipe(filterfds[slot]);
+ cupsArrayDelete(filters);
+
+ if (argv)
+ {
+ if (job->printer->remote && job->num_files > 1)
+ {
+ for (i = 0; i < job->num_files; i ++)
+ free(argv[i + 6]);
+ }
+
+ free(argv);
+ }
+
+ if (printer_state_reasons)
+ free(printer_state_reasons);
+
+ cupsdClosePipe(job->print_pipes);
+ cupsdClosePipe(job->back_pipes);
+ cupsdClosePipe(job->side_pipes);
+
+ cupsdRemoveSelect(job->status_pipes[0]);
cupsdClosePipe(job->status_pipes);
cupsdStatBufDelete(job->status_buffer);
job->status_buffer = NULL;
- cupsArrayDelete(filters);
+ /*
+ * Update the printer and job state.
+ */
- if (job->printer->remote && job->num_files > 1)
- {
- for (i = 0; i < job->num_files; i ++)
- free(argv[i + 6]);
- }
+ cupsdSetJobState(job, abort_state, CUPSD_JOB_DEFAULT, "%s", abort_message);
+ cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0);
+ update_job_attrs(job, 0);
- free(argv);
+ if (job->history)
+ free_job_history(job);
+
+ cupsArrayRemove(PrintingJobs, job);
- cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT, "%s",
- abort_message);
+ /*
+ * Clear the printer <-> job association...
+ */
+
+ job->printer->job = NULL;
+ job->printer = NULL;
}
cupsdDeleteJob(cupsd_job_t *job, /* I - Job */
cupsd_jobaction_t action)/* I - Action */
{
+ int i; /* Looping var */
+ char filename[1024]; /* Job filename */
+
+
if (job->printer)
- finalize_job(job);
+ finalize_job(job, 1);
if (action == CUPSD_JOB_PURGE)
{
* Remove the job info file...
*/
- char filename[1024]; /* Job filename */
-
snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot,
job->id);
- unlink(filename);
+ if (Classification)
+ cupsdRemoveFile(filename);
+ else
+ unlink(filename);
}
cupsdClearString(&job->username);
cupsdClearString(&job->dest);
- cupsdClearString(&job->auth_username);
- cupsdClearString(&job->auth_domain);
- cupsdClearString(&job->auth_password);
-
-#ifdef HAVE_GSSAPI
- /*
- * Destroy the credential cache and clear the KRB5CCNAME env var string.
- */
-
- if (job->ccache)
- {
- krb5_cc_destroy(KerberosContext, job->ccache);
- job->ccache = NULL;
- }
-
- cupsdClearString(&job->ccname);
-#endif /* HAVE_GSSAPI */
+ for (i = 0;
+ i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
+ i ++)
+ cupsdClearString(job->auth_env + i);
+ cupsdClearString(&job->auth_uid);
if (job->num_files > 0)
{
free(job->compressions);
free(job->filetypes);
- job->num_files = 0;
+ if (action == CUPSD_JOB_PURGE)
+ {
+ while (job->num_files > 0)
+ {
+ snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
+ job->id, job->num_files);
+ if (Classification)
+ cupsdRemoveFile(filename);
+ else
+ unlink(filename);
+
+ job->num_files --;
+ }
+ }
+ else
+ job->num_files = 0;
}
+ if (job->history)
+ free_job_history(job);
+
unload_job(job);
cupsArrayRemove(Jobs, job);
for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
job;
job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
- if (job->dest && !strcasecmp(job->dest, dest))
+ if (job->dest && !_cups_strcasecmp(job->dest, dest))
count ++;
return (count);
for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
job;
job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
- if (!strcasecmp(job->username, username))
+ if (!_cups_strcasecmp(job->username, username))
count ++;
return (count);
int /* O - 1 on success, 0 on failure */
cupsdLoadJob(cupsd_job_t *job) /* I - Job */
{
+ int i; /* Looping var */
char jobfile[1024]; /* Job filename */
cups_file_t *fp; /* Job file */
int fileid; /* Current file ID */
snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, job->id);
if ((fp = cupsFileOpen(jobfile, "r")) == NULL)
{
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "[Job %d] Unable to open job control file \"%s\" - %s!",
- job->id, jobfile, strerror(errno));
- goto error;
+ char newfile[1024]; /* New job filename */
+
+ snprintf(newfile, sizeof(newfile), "%s/c%05d.N", RequestRoot, job->id);
+ if ((fp = cupsFileOpen(newfile, "r")) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "[Job %d] Unable to open job control file \"%s\": %s",
+ job->id, jobfile, strerror(errno));
+ goto error;
+ }
+
+ unlink(jobfile);
+ rename(newfile, jobfile);
}
if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, job->attrs) != IPP_DATA)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
- "[Job %d] Unable to read job control file \"%s\"!", job->id,
+ "[Job %d] Unable to read job control file \"%s\".", job->id,
jobfile);
cupsFileClose(fp);
goto error;
cupsdLogMessage(CUPSD_LOG_ERROR,
"[Job %d] Ran out of memory for job file types!",
job->id);
- return (1);
+
+ ippDelete(job->attrs);
+ job->attrs = NULL;
+
+ if (compressions)
+ free(compressions);
+
+ if (filetypes)
+ free(filetypes);
+
+ if (job->compressions)
+ {
+ free(job->compressions);
+ job->compressions = NULL;
+ }
+
+ if (job->filetypes)
+ {
+ free(job->filetypes);
+ job->filetypes = NULL;
+ }
+
+ job->num_files = 0;
+ return (0);
}
job->compressions = compressions;
{
snprintf(jobfile, sizeof(jobfile), "%s/a%05d", RequestRoot, job->id);
- cupsdClearString(&job->auth_username);
- cupsdClearString(&job->auth_domain);
- cupsdClearString(&job->auth_password);
+ for (i = 0;
+ i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
+ i ++)
+ cupsdClearString(job->auth_env + i);
+ cupsdClearString(&job->auth_uid);
if ((fp = cupsFileOpen(jobfile, "r")) != NULL)
{
- int i, /* Looping var */
- bytes; /* Size of auth data */
- char line[255], /* Line from file */
- data[255]; /* Decoded data */
+ int bytes; /* Size of auth data */
+ char line[65536], /* Line from file */
+ data[65536]; /* Decoded data */
for (i = 0;
i < destptr->num_auth_info_required &&
+ i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0])) &&
cupsFileGets(fp, line, sizeof(line));
i ++)
{
httpDecode64_2(data, &bytes, line);
if (!strcmp(destptr->auth_info_required[i], "username"))
- cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s", data);
+ cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s", data);
else if (!strcmp(destptr->auth_info_required[i], "domain"))
- cupsdSetStringf(&job->auth_domain, "AUTH_DOMAIN=%s", data);
+ cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s", data);
else if (!strcmp(destptr->auth_info_required[i], "password"))
- cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s", data);
+ cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s", data);
+ else if (!strcmp(destptr->auth_info_required[i], "negotiate"))
+ cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s", line);
}
+ if (cupsFileGets(fp, line, sizeof(line)) && isdigit(line[0] & 255))
+ cupsdSetStringf(&job->auth_uid, "AUTH_UID=%s", line);
+
cupsFileClose(fp);
}
}
ippDelete(job->attrs);
job->attrs = NULL;
- unlink(jobfile);
+
+ if (job->compressions)
+ {
+ free(job->compressions);
+ job->compressions = NULL;
+ }
+
+ if (job->filetypes)
+ {
+ free(job->filetypes);
+ job->filetypes = NULL;
+ }
+
+ job->num_files = 0;
+
+ if (Classification)
+ cupsdRemoveFile(jobfile);
+ else
+ unlink(jobfile);
return (0);
}
* Change the destination information...
*/
- cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
- "Stopping job prior to move.");
+ if (job->state_value > IPP_JOB_HELD)
+ cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
+ "Stopping job prior to move.");
cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, oldp, job,
"Job #%d moved from %s to %s.", job->id, olddest,
cupsdSaveAllJobs(void)
{
int i; /* Looping var */
- cups_file_t *fp; /* Job cache file */
- char temp[1024]; /* Temporary string */
+ cups_file_t *fp; /* job.cache file */
+ char filename[1024], /* job.cache filename */
+ 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));
+ snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir);
+ if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
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);
+ cupsdLogMessage(CUPSD_LOG_INFO, "Saving job.cache...");
/*
* Write a small header to the file...
cupsFilePuts(fp, "</Job>\n");
}
- cupsFileClose(fp);
+ cupsdCloseCreatedConfFile(fp, filename);
}
void
cupsdSaveJob(cupsd_job_t *job) /* I - Job */
{
- char filename[1024]; /* Job control filename */
+ char filename[1024], /* Job control filename */
+ newfile[1024]; /* New job control filename */
cups_file_t *fp; /* Job file */
job, job->id, job->attrs);
snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id);
+ snprintf(newfile, sizeof(newfile), "%s/c%05d.N", RequestRoot, job->id);
- if ((fp = cupsFileOpen(filename, "w")) == NULL)
+ if ((fp = cupsFileOpen(newfile, "w")) == NULL)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
- "[Job %d] Unable to create job control file \"%s\" - %s.",
- job->id, filename, strerror(errno));
+ "[Job %d] Unable to create job control file \"%s\": %s",
+ job->id, newfile, strerror(errno));
return;
}
if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL,
job->attrs) != IPP_DATA)
+ {
cupsdLogMessage(CUPSD_LOG_ERROR,
- "[Job %d] Unable to write job control file!", job->id);
-
- cupsFileClose(fp);
+ "[Job %d] Unable to write job control file.", job->id);
+ cupsFileClose(fp);
+ unlink(newfile);
+ return;
+ }
- job->dirty = 0;
+ if (cupsFileClose(fp))
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "[Job %d] Unable to close job control file: %s",
+ job->id, strerror(errno));
+ else
+ {
+ unlink(filename);
+ if (rename(newfile, filename))
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "[Job %d] Unable to finalize job control file: %s",
+ job->id, strerror(errno));
+ else
+ job->dirty = 0;
+ }
}
return;
/*
- * Don't do anything if the state is unchanged...
+ * Don't do anything if the state is unchanged and we aren't purging the
+ * job...
*/
- if (newstate == (oldstate = job->state_value))
+ oldstate = job->state_value;
+ if (newstate == oldstate && action != CUPSD_JOB_PURGE)
return;
/*
*/
if (oldstate == IPP_JOB_PROCESSING)
- stop_job(job, action != CUPSD_JOB_DEFAULT);
+ stop_job(job, action);
/*
* Set the new job state...
case IPP_JOB_ABORTED :
case IPP_JOB_CANCELED :
case IPP_JOB_COMPLETED :
- /*
- * Expire job subscriptions since the job is now "completed"...
- */
+ if (newstate == IPP_JOB_CANCELED)
+ {
+ /*
+ * Remove the job from the active list if there are no processes still
+ * running for it...
+ */
- cupsdExpireSubscriptions(NULL, job);
+ for (i = 0; job->filters[i] < 0; i++);
+
+ if (!job->filters[i] && job->backend <= 0)
+ cupsArrayRemove(ActiveJobs, job);
+ }
+ else
+ {
+ /*
+ * Otherwise just remove the job from the active list immediately...
+ */
+
+ cupsArrayRemove(ActiveJobs, job);
+ }
/*
- * Remove the job from the active list...
+ * Expire job subscriptions since the job is now "completed"...
*/
- cupsArrayRemove(ActiveJobs, job);
+ cupsdExpireSubscriptions(NULL, job);
#ifdef __APPLE__
/*
"Unable to remove authentication cache: %s",
strerror(errno));
- cupsdClearString(&job->auth_username);
- cupsdClearString(&job->auth_domain);
- cupsdClearString(&job->auth_password);
-
-#ifdef HAVE_GSSAPI
- /*
- * Destroy the credential cache and clear the KRB5CCNAME env var string.
- */
-
- if (job->ccache)
- {
- krb5_cc_destroy(KerberosContext, job->ccache);
- job->ccache = NULL;
- }
+ for (i = 0;
+ i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
+ i ++)
+ cupsdClearString(job->auth_env + i);
- cupsdClearString(&job->ccname);
-#endif /* HAVE_GSSAPI */
+ cupsdClearString(&job->auth_uid);
/*
* Remove the print file for good if we aren't preserving jobs or
{
snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
job->id, i);
- unlink(filename);
+ if (Classification)
+ cupsdRemoveFile(filename);
+ else
+ unlink(filename);
}
if (job->num_files > 0)
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
}
else if (!job->printer)
+ {
+ /*
+ * Delete the job immediately if not actively printing...
+ */
+
cupsdDeleteJob(job, CUPSD_JOB_PURGE);
+ job = NULL;
+ }
break;
}
+ /*
+ * Finalize the job immediately if we forced things...
+ */
+
+ if (action >= CUPSD_JOB_FORCE && job && job->printer)
+ finalize_job(job, 0);
+
/*
* Update the server "busy" state...
*/
int diff; /* Difference */
+ (void)data;
+
if ((diff = ((cupsd_job_t *)second)->priority -
((cupsd_job_t *)first)->priority) != 0)
return (diff);
void *second, /* I - Second job */
void *data) /* I - App data (not used) */
{
+ (void)data;
+
return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
}
+/*
+ * 'dump_job_history()' - Dump any debug messages for a job.
+ */
+
+static void
+dump_job_history(cupsd_job_t *job) /* I - Job */
+{
+ int i, /* Looping var */
+ oldsize; /* Current MaxLogSize */
+ struct tm *date; /* Date/time value */
+ cupsd_joblog_t *message; /* Current message */
+ char temp[2048], /* Log message */
+ *ptr, /* Pointer into log message */
+ start[256], /* Start time */
+ end[256]; /* End time */
+ cupsd_printer_t *printer; /* Printer for job */
+
+
+ /*
+ * See if we have anything to dump...
+ */
+
+ if (!job->history)
+ return;
+
+ /*
+ * Disable log rotation temporarily...
+ */
+
+ oldsize = MaxLogSize;
+ MaxLogSize = 0;
+
+ /*
+ * Copy the debug messages to the log...
+ */
+
+ message = (cupsd_joblog_t *)cupsArrayFirst(job->history);
+ date = localtime(&(message->time));
+ strftime(start, sizeof(start), "%X", date);
+
+ message = (cupsd_joblog_t *)cupsArrayLast(job->history);
+ date = localtime(&(message->time));
+ strftime(end, sizeof(end), "%X", date);
+
+ snprintf(temp, sizeof(temp),
+ "[Job %d] The following messages were recorded from %s to %s",
+ job->id, start, end);
+ cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
+
+ for (message = (cupsd_joblog_t *)cupsArrayFirst(job->history);
+ message;
+ message = (cupsd_joblog_t *)cupsArrayNext(job->history))
+ cupsdWriteErrorLog(CUPSD_LOG_DEBUG, message->message);
+
+ snprintf(temp, sizeof(temp), "[Job %d] End of messages", job->id);
+ cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
+
+ /*
+ * Log the printer state values...
+ */
+
+ if ((printer = job->printer) == NULL)
+ printer = cupsdFindDest(job->dest);
+
+ if (printer)
+ {
+ snprintf(temp, sizeof(temp), "[Job %d] printer-state=%d(%s)", job->id,
+ printer->state,
+ printer->state == IPP_PRINTER_IDLE ? "idle" :
+ printer->state == IPP_PRINTER_PROCESSING ? "processing" :
+ "stopped");
+ cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
+
+ snprintf(temp, sizeof(temp), "[Job %d] printer-state-message=\"%s\"",
+ job->id, printer->state_message);
+ cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
+
+ snprintf(temp, sizeof(temp), "[Job %d] printer-state-reasons=", job->id);
+ ptr = temp + strlen(temp);
+ if (printer->num_reasons == 0)
+ strlcpy(ptr, "none", sizeof(temp) - (ptr - temp));
+ else
+ {
+ for (i = 0;
+ i < printer->num_reasons && ptr < (temp + sizeof(temp) - 2);
+ i ++)
+ {
+ if (i)
+ *ptr++ = ',';
+
+ strlcpy(ptr, printer->reasons[i], sizeof(temp) - (ptr - temp));
+ ptr += strlen(ptr);
+ }
+ }
+ cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
+ }
+
+ /*
+ * Restore log file rotation...
+ */
+
+ MaxLogSize = oldsize;
+
+ /*
+ * Free all messages...
+ */
+
+ free_job_history(job);
+}
+
+
+/*
+ * 'free_job_history()' - Free any log history.
+ */
+
+static void
+free_job_history(cupsd_job_t *job) /* I - Job */
+{
+ char *message; /* Current message */
+
+
+ if (!job->history)
+ return;
+
+ for (message = (char *)cupsArrayFirst(job->history);
+ message;
+ message = (char *)cupsArrayNext(job->history))
+ free(message);
+
+ cupsArrayDelete(job->history);
+ job->history = NULL;
+}
+
+
/*
* 'finalize_job()' - Cleanup after job filter processes and support data.
*/
static void
-finalize_job(cupsd_job_t *job) /* I - Job */
+finalize_job(cupsd_job_t *job, /* I - Job */
+ int set_job_state) /* I - 1 = set the job state */
{
ipp_pstate_t printer_state; /* New printer state value */
ipp_jstate_t job_state; /* New job state value */
cupsdLogMessage(CUPSD_LOG_DEBUG2, "finalize_job(job=%p(%d))", job, job->id);
/*
- * Clear the "connecting-to-device" reason, which is only valid when a
- * printer is processing...
+ * Clear the "connecting-to-device" reason, which is only valid when a printer
+ * is processing, along with any remote printing job state...
*/
- cupsdSetPrinterReasons(job->printer, "-connecting-to-device");
+ cupsdSetPrinterReasons(job->printer, "-connecting-to-device,"
+ "cups-remote-pending,"
+ "cups-remote-pending-held,"
+ "cups-remote-processing,"
+ "cups-remote-stopped,"
+ "cups-remote-canceled,"
+ "cups-remote-aborted,"
+ "cups-remote-completed");
/*
* Similarly, clear the "offline-report" reason for non-USB devices since we
job->profile = NULL;
/*
- * Close pipes and status buffer...
+ * Clear the unresponsive job watchdog timer...
*/
- cupsdRemoveSelect(job->status_buffer->fd);
+ job->kill_time = 0;
+
+ /*
+ * Close pipes and status buffer...
+ */
cupsdClosePipe(job->print_pipes);
cupsdClosePipe(job->back_pipes);
cupsdClosePipe(job->side_pipes);
- cupsdClosePipe(job->status_pipes);
+ cupsdRemoveSelect(job->status_pipes[0]);
+ cupsdClosePipe(job->status_pipes);
cupsdStatBufDelete(job->status_buffer);
job->status_buffer = NULL;
default :
case IPP_JOB_PROCESSING :
case IPP_JOB_COMPLETED :
- job_state = IPP_JOB_COMPLETED;
- message = "Job completed.";
+ job_state = IPP_JOB_COMPLETED;
+ message = "Job completed.";
break;
case IPP_JOB_STOPPED :
int exit_code; /* Exit code from backend */
-
/*
* Convert the status to an exit code. Due to the way the W* macros are
* implemented on MacOS X (bug?), we have to store the exit status in a
job->tries ++;
- if (job->tries >= JobRetryLimit)
+ if (job->tries > JobRetryLimit && JobRetryLimit > 0)
{
/*
* Too many tries...
* Try again in N seconds...
*/
- set_hold_until(job, time(NULL) + JobRetryInterval);
-
snprintf(buffer, sizeof(buffer),
"Job held for %d seconds since it could not be sent.",
JobRetryInterval);
- job_state = IPP_JOB_HELD;
- message = buffer;
+
+ job->hold_until = time(NULL) + JobRetryInterval;
+ job_state = IPP_JOB_HELD;
+ message = buffer;
}
}
}
message = "Job aborted due to backend errors; please consult "
"the error_log file for details.";
}
- else
+ else if (job->state_value == IPP_JOB_PROCESSING)
{
+ job_state = IPP_JOB_PENDING;
printer_state = IPP_PRINTER_STOPPED;
message = "Printer stopped due to backend errors; please "
"consult the error_log file for details.";
-
- if (job_state == IPP_JOB_COMPLETED)
- job_state = IPP_JOB_PENDING;
}
break;
message = "Job held for authentication.";
}
break;
+
+ case CUPS_BACKEND_RETRY :
+ if (job_state == IPP_JOB_COMPLETED)
+ {
+ /*
+ * Hold the job if the number of retries is less than the
+ * JobRetryLimit, otherwise abort the job.
+ */
+
+ job->tries ++;
+
+ if (job->tries > JobRetryLimit && JobRetryLimit > 0)
+ {
+ /*
+ * Too many tries...
+ */
+
+ snprintf(buffer, sizeof(buffer),
+ "Job aborted after %d unsuccessful attempts.",
+ JobRetryLimit);
+ job_state = IPP_JOB_ABORTED;
+ message = buffer;
+ }
+ else
+ {
+ /*
+ * Try again in N seconds...
+ */
+
+ snprintf(buffer, sizeof(buffer),
+ "Job held for %d seconds since it could not be sent.",
+ JobRetryInterval);
+
+ job->hold_until = time(NULL) + JobRetryInterval;
+ job_state = IPP_JOB_HELD;
+ message = buffer;
+ }
+ }
+ break;
+
+ case CUPS_BACKEND_RETRY_CURRENT :
+ /*
+ * Mark the job as pending and retry on the same printer...
+ */
+
+ if (job_state == IPP_JOB_COMPLETED)
+ {
+ job_state = IPP_JOB_PENDING;
+ message = "Retrying job on same printer.";
+ }
+ break;
}
}
else if (job->status > 0)
* Update the printer and job state.
*/
- cupsdSetJobState(job, job_state, CUPSD_JOB_DEFAULT, "%s", message);
+ if (set_job_state && job_state != job->state_value)
+ cupsdSetJobState(job, job_state, CUPSD_JOB_DEFAULT, "%s", message);
+
cupsdSetPrinterState(job->printer, printer_state,
printer_state == IPP_PRINTER_STOPPED);
update_job_attrs(job, 0);
+ if (job->history)
+ {
+ if (job->status &&
+ (job->state_value == IPP_JOB_ABORTED ||
+ job->state_value == IPP_JOB_STOPPED))
+ dump_job_history(job);
+ else
+ free_job_history(job);
+ }
+
cupsArrayRemove(PrintingJobs, job);
/*
size_t title_size) /* I - Size of title buffer */
{
int i; /* Looping var */
+ size_t newlength; /* New option buffer length */
char *optptr, /* Pointer to options */
*valptr; /* Pointer in value string */
ipp_attribute_t *attr; /* Current attribute */
+ _ppd_cache_t *pc; /* PPD cache and mapping data */
+ int num_pwgppds; /* Number of PWG->PPD options */
+ cups_option_t *pwgppds, /* PWG->PPD options */
+ *pwgppd, /* Current PWG->PPD option */
+ *preset; /* Current preset option */
+ int print_color_mode,
+ /* Output mode (if any) */
+ print_quality; /* Print quality (if any) */
+ const char *ppd; /* PPD option choice */
+ int exact; /* Did we get an exact match? */
static char *options = NULL;/* Full list of options */
- static int optlength = 0; /* Length of option buffer */
+ static size_t optlength = 0; /* Length of option buffer */
/*
- * 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... :)
+ * 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...
+ * First build an options array for any PWG->PPD mapped option/choice pairs.
+ */
+
+ pc = job->printer->pc;
+ num_pwgppds = 0;
+ pwgppds = NULL;
+
+ if (pc &&
+ !ippFindAttribute(job->attrs,
+ "com.apple.print.DocumentTicket.PMSpoolFormat",
+ IPP_TAG_ZERO) &&
+ !ippFindAttribute(job->attrs, "APPrinterPreset", IPP_TAG_ZERO) &&
+ (ippFindAttribute(job->attrs, "output-mode", IPP_TAG_ZERO) ||
+ ippFindAttribute(job->attrs, "print-color-mode", IPP_TAG_ZERO) ||
+ ippFindAttribute(job->attrs, "print-quality", IPP_TAG_ZERO)))
+ {
+ /*
+ * Map output-mode and print-quality to a preset...
+ */
+
+ if ((attr = ippFindAttribute(job->attrs, "print-color-mode",
+ IPP_TAG_KEYWORD)) == NULL)
+ attr = ippFindAttribute(job->attrs, "output-mode", IPP_TAG_KEYWORD);
+
+ if (attr && !strcmp(attr->values[0].string.text, "monochrome"))
+ print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
+ else
+ print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
+
+ if ((attr = ippFindAttribute(job->attrs, "print-quality",
+ IPP_TAG_ENUM)) != NULL &&
+ attr->values[0].integer >= IPP_QUALITY_DRAFT &&
+ attr->values[0].integer <= IPP_QUALITY_HIGH)
+ print_quality = attr->values[0].integer - IPP_QUALITY_DRAFT;
+ else
+ print_quality = _PWG_PRINT_QUALITY_NORMAL;
+
+ if (pc->num_presets[print_color_mode][print_quality] == 0)
+ {
+ /*
+ * Try to find a preset that works so that we maximize the chances of us
+ * getting a good print using IPP attributes.
+ */
+
+ if (pc->num_presets[print_color_mode][_PWG_PRINT_QUALITY_NORMAL] > 0)
+ print_quality = _PWG_PRINT_QUALITY_NORMAL;
+ else if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][print_quality] > 0)
+ print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
+ else
+ {
+ print_quality = _PWG_PRINT_QUALITY_NORMAL;
+ print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
+ }
+ }
+
+ if (pc->num_presets[print_color_mode][print_quality] > 0)
+ {
+ /*
+ * Copy the preset options as long as the corresponding names are not
+ * already defined in the IPP request...
+ */
+
+ for (i = pc->num_presets[print_color_mode][print_quality],
+ preset = pc->presets[print_color_mode][print_quality];
+ i > 0;
+ i --, preset ++)
+ {
+ if (!ippFindAttribute(job->attrs, preset->name, IPP_TAG_ZERO))
+ num_pwgppds = cupsAddOption(preset->name, preset->value, num_pwgppds,
+ &pwgppds);
+ }
+ }
+ }
+
+ if (pc)
+ {
+ if (!ippFindAttribute(job->attrs, "InputSlot", IPP_TAG_ZERO) &&
+ !ippFindAttribute(job->attrs, "HPPaperSource", IPP_TAG_ZERO))
+ {
+ if ((ppd = _ppdCacheGetInputSlot(pc, job->attrs, NULL)) != NULL)
+ num_pwgppds = cupsAddOption(pc->source_option, ppd, num_pwgppds,
+ &pwgppds);
+ else if (!ippFindAttribute(job->attrs, "AP_D_InputSlot", IPP_TAG_ZERO))
+ num_pwgppds = cupsAddOption("AP_D_InputSlot", "", num_pwgppds,
+ &pwgppds);
+ }
+ if (!ippFindAttribute(job->attrs, "MediaType", IPP_TAG_ZERO) &&
+ (ppd = _ppdCacheGetMediaType(pc, job->attrs, NULL)) != NULL)
+ num_pwgppds = cupsAddOption("MediaType", ppd, num_pwgppds, &pwgppds);
+
+ if (!ippFindAttribute(job->attrs, "PageRegion", IPP_TAG_ZERO) &&
+ !ippFindAttribute(job->attrs, "PageSize", IPP_TAG_ZERO) &&
+ (ppd = _ppdCacheGetPageSize(pc, job->attrs, NULL, &exact)) != NULL)
+ {
+ num_pwgppds = cupsAddOption("PageSize", ppd, num_pwgppds, &pwgppds);
+
+ if (!ippFindAttribute(job->attrs, "media", IPP_TAG_ZERO))
+ num_pwgppds = cupsAddOption("media", ppd, num_pwgppds, &pwgppds);
+ }
+
+ if (!ippFindAttribute(job->attrs, "OutputBin", IPP_TAG_ZERO) &&
+ (attr = ippFindAttribute(job->attrs, "output-bin",
+ IPP_TAG_ZERO)) != NULL &&
+ (attr->value_tag == IPP_TAG_KEYWORD ||
+ attr->value_tag == IPP_TAG_NAME) &&
+ (ppd = _ppdCacheGetOutputBin(pc, attr->values[0].string.text)) != NULL)
+ {
+ /*
+ * Map output-bin to OutputBin option...
+ */
+
+ num_pwgppds = cupsAddOption("OutputBin", ppd, num_pwgppds, &pwgppds);
+ }
+
+ if (pc->sides_option &&
+ !ippFindAttribute(job->attrs, pc->sides_option, IPP_TAG_ZERO) &&
+ (attr = ippFindAttribute(job->attrs, "sides", IPP_TAG_KEYWORD)) != NULL)
+ {
+ /*
+ * Map sides to duplex option...
+ */
+
+ if (!strcmp(attr->values[0].string.text, "one-sided"))
+ num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_1sided,
+ num_pwgppds, &pwgppds);
+ else if (!strcmp(attr->values[0].string.text, "two-sided-long-edge"))
+ num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_long,
+ num_pwgppds, &pwgppds);
+ else if (!strcmp(attr->values[0].string.text, "two-sided-short-edge"))
+ num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_short,
+ num_pwgppds, &pwgppds);
+ }
+ }
+
+ /*
+ * Figure out how much room we need...
+ */
+
+ newlength = ipp_length(job->attrs);
+
+ for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++)
+ newlength += 1 + strlen(pwgppd->name) + 1 + strlen(pwgppd->value);
+
+ /*
+ * Then allocate/reallocate the option buffer as needed...
*/
- i = ipp_length(job->attrs);
+ if (newlength == 0) /* This can never happen, but Clang */
+ newlength = 1; /* thinks it can... */
- if (i > optlength || !options)
+ if (newlength > optlength || !options)
{
if (!options)
- optptr = malloc(i);
+ optptr = malloc(newlength);
else
- optptr = realloc(options, i);
+ optptr = realloc(options, newlength);
if (!optptr)
{
cupsdLogJob(job, CUPSD_LOG_CRIT,
- "Unable to allocate %d bytes for option buffer!", i);
+ "Unable to allocate " CUPS_LLFMT " bytes for option buffer!",
+ CUPS_LLCAST newlength);
return (NULL);
}
options = optptr;
- optlength = i;
+ optlength = newlength;
}
/*
* Filter out other unwanted attributes...
*/
- if (attr->value_tag == IPP_TAG_MIMETYPE ||
+ if (attr->value_tag == IPP_TAG_NOVALUE ||
+ 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_BEGIN_COLLECTION) /* Not yet supported */
continue;
- if (!strncmp(attr->name, "time-", 5))
+ if (!strcmp(attr->name, "job-hold-until"))
continue;
if (!strncmp(attr->name, "job-", 4) &&
!strncmp(attr->name, "number-up", 9) ||
!strcmp(attr->name, "page-ranges") ||
!strcmp(attr->name, "page-set") ||
- !strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
- !strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed") ||
- !strcasecmp(attr->name, "com.apple.print.PrintSettings."
+ !_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
+ !_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed") ||
+ !_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings."
"PMTotalSidesImaged..n.") ||
- !strcasecmp(attr->name, "com.apple.print.PrintSettings."
+ !_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings."
"PMTotalBeginPages..n.")) &&
banner_page)
continue;
if (!attr->values[i].boolean)
strlcat(optptr, "no", optlength - (optptr - options));
- case IPP_TAG_NOVALUE :
strlcat(optptr, attr->name,
optlength - (optptr - options));
break;
}
}
+ /*
+ * Finally loop through the PWG->PPD mapped options and add them...
+ */
+
+ for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++)
+ {
+ *optptr++ = ' ';
+ strcpy(optptr, pwgppd->name);
+ optptr += strlen(optptr);
+ *optptr++ = '=';
+ strcpy(optptr, pwgppd->value);
+ optptr += strlen(optptr);
+ }
+
+ cupsFreeOptions(num_pwgppds, pwgppds);
+
+ /*
+ * Return the options string...
+ */
return (options);
}
* the textual IPP attributes.
*/
-static int /* O - Size of attribute buffer */
+static size_t /* O - Size of attribute buffer */
ipp_length(ipp_t *ipp) /* I - IPP request */
{
- int bytes; /* Number of bytes */
+ size_t bytes; /* Number of bytes */
int i; /* Looping var */
ipp_attribute_t *attr; /* Current attribute */
* Skip attributes that won't be sent to filters...
*/
- if (attr->value_tag == IPP_TAG_MIMETYPE ||
+ if (attr->value_tag == IPP_TAG_NOVALUE ||
+ 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
* Open the job.cache file...
*/
- if ((fp = cupsFileOpen(filename, "r")) == NULL)
+ if ((fp = cupsdOpenConfFile(filename)) == NULL)
{
- if (errno != ENOENT)
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to open job cache file \"%s\": %s",
- filename, strerror(errno));
-
load_request_root();
-
return;
}
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
{
- if (!strcasecmp(line, "NextJobId"))
+ if (!_cups_strcasecmp(line, "NextJobId"))
{
if (value)
NextJobId = atoi(value);
}
- else if (!strcasecmp(line, "<Job"))
+ else if (!_cups_strcasecmp(line, "<Job"))
{
if (job)
{
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;
+ snprintf(jobfile, sizeof(jobfile), "%s/c%05d.N", 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));
"Missing <Job #> directive on line %d!", linenum);
continue;
}
- else if (!strcasecmp(line, "</Job>"))
+ else if (!_cups_strcasecmp(line, "</Job>"))
{
cupsArrayAdd(Jobs, job);
- if (job->state_value <= IPP_JOB_STOPPED)
- {
- cupsArrayAdd(ActiveJobs, job);
- cupsdLoadJob(job);
- }
+ if (job->state_value <= IPP_JOB_STOPPED && cupsdLoadJob(job))
+ cupsArrayAdd(ActiveJobs, job);
job = NULL;
}
cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d!", linenum);
continue;
}
- else if (!strcasecmp(line, "State"))
+ else if (!_cups_strcasecmp(line, "State"))
{
job->state_value = (ipp_jstate_t)atoi(value);
else if (job->state_value > IPP_JOB_COMPLETED)
job->state_value = IPP_JOB_COMPLETED;
}
- else if (!strcasecmp(line, "HoldUntil"))
+ else if (!_cups_strcasecmp(line, "HoldUntil"))
{
job->hold_until = atoi(value);
}
- else if (!strcasecmp(line, "Priority"))
+ else if (!_cups_strcasecmp(line, "Priority"))
{
job->priority = atoi(value);
}
- else if (!strcasecmp(line, "Username"))
+ else if (!_cups_strcasecmp(line, "Username"))
{
cupsdSetString(&job->username, value);
}
- else if (!strcasecmp(line, "Destination"))
+ else if (!_cups_strcasecmp(line, "Destination"))
{
cupsdSetString(&job->dest, value);
}
- else if (!strcasecmp(line, "DestType"))
+ else if (!_cups_strcasecmp(line, "DestType"))
{
job->dtype = (cups_ptype_t)atoi(value);
}
- else if (!strcasecmp(line, "NumFiles"))
+ else if (!_cups_strcasecmp(line, "NumFiles"))
{
job->num_files = atoi(value);
}
}
}
- else if (!strcasecmp(line, "File"))
+ else if (!_cups_strcasecmp(line, "File"))
{
int number, /* File number */
compression; /* Compression value */
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
{
- if (!strcasecmp(line, "NextJobId"))
+ if (!_cups_strcasecmp(line, "NextJobId"))
{
if (value)
{
* Load the job...
*/
- cupsdLoadJob(job);
-
- /*
- * Insert the job into the array, sorting by job priority and ID...
- */
+ if (cupsdLoadJob(job))
+ {
+ /*
+ * Insert the job into the array, sorting by job priority and ID...
+ */
- cupsArrayAdd(Jobs, job);
+ cupsArrayAdd(Jobs, job);
- if (job->state_value <= IPP_JOB_STOPPED)
- cupsArrayAdd(ActiveJobs, job);
- else
- unload_job(job);
+ if (job->state_value <= IPP_JOB_STOPPED)
+ cupsArrayAdd(ActiveJobs, job);
+ else
+ unload_job(job);
+ }
}
cupsDirClose(dir);
}
-/*
- * 'set_hold_until()' - Set the hold time and update job-hold-until attribute.
- */
-
-static void
-set_hold_until(cupsd_job_t *job, /* I - Job to update */
- time_t holdtime) /* I - Hold until time */
-{
- ipp_attribute_t *attr; /* job-hold-until attribute */
- struct tm *holddate; /* Hold date */
- char holdstr[64]; /* Hold time */
-
-
- /*
- * Set the hold_until value and hold the job...
- */
-
- cupsdLogMessage(CUPSD_LOG_DEBUG, "set_hold_until: hold_until = %d",
- (int)holdtime);
-
- job->state->values[0].integer = IPP_JOB_HELD;
- job->state_value = IPP_JOB_HELD;
- job->hold_until = holdtime;
-
- /*
- * Update the job-hold-until attribute with a string representing GMT
- * time (HH:MM:SS)...
- */
-
- holddate = gmtime(&holdtime);
- snprintf(holdstr, sizeof(holdstr), "%d:%d:%d", holddate->tm_hour,
- holddate->tm_min, holddate->tm_sec);
-
- if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
- IPP_TAG_KEYWORD)) == NULL)
- attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
-
- /*
- * Either add the attribute or update the value of the existing one
- */
-
- if (attr == NULL)
- ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-hold-until",
- NULL, holdstr);
- else
- cupsdSetString(&attr->values[0].string.text, holdstr);
-
- job->dirty = 1;
- cupsdMarkDirty(CUPSD_DIRTY_JOBS);
-}
-
-
/*
* 'set_time()' - Set one of the "time-at-xyz" attributes.
*/
if (!cupsdLoadJob(job))
return;
+ if (job->printer_message)
+ cupsdSetString(&(job->printer_message->values[0].string.text), "");
+
cupsdSetJobState(job, IPP_JOB_PROCESSING, CUPSD_JOB_DEFAULT, NULL);
cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
+ cupsdSetPrinterReasons(printer, "-cups-remote-pending,"
+ "cups-remote-pending-held,"
+ "cups-remote-processing,"
+ "cups-remote-stopped,"
+ "cups-remote-canceled,"
+ "cups-remote-aborted,"
+ "cups-remote-completed");
job->cost = 0;
job->current_file = 0;
fcntl(job->side_pipes[1], F_SETFL,
fcntl(job->side_pipes[1], F_GETFL) | O_NONBLOCK);
+ fcntl(job->side_pipes[0], F_SETFD,
+ fcntl(job->side_pipes[0], F_GETFD) | FD_CLOEXEC);
+ fcntl(job->side_pipes[1], F_SETFD,
+ fcntl(job->side_pipes[1], F_GETFD) | FD_CLOEXEC);
+
/*
* Now start the first file in the job...
*/
if (action == CUPSD_JOB_DEFAULT && !job->kill_time)
job->kill_time = time(NULL) + JobKillDelay;
- else if (action == CUPSD_JOB_FORCE)
+ else if (action >= CUPSD_JOB_FORCE)
job->kill_time = 0;
for (i = 0; job->filters[i]; i ++)
if (job->filters[i] > 0)
- cupsdEndProcess(job->filters[i], action == CUPSD_JOB_FORCE);
+ {
+ cupsdEndProcess(job->filters[i], action >= CUPSD_JOB_FORCE);
+
+ if (action >= CUPSD_JOB_FORCE)
+ job->filters[i] = -job->filters[i];
+ }
if (job->backend > 0)
- cupsdEndProcess(job->backend, action == CUPSD_JOB_FORCE);
+ {
+ cupsdEndProcess(job->backend, action >= CUPSD_JOB_FORCE);
+
+ if (action >= CUPSD_JOB_FORCE)
+ job->backend = -job->backend;
+ }
+
+ if (action >= CUPSD_JOB_FORCE)
+ {
+ /*
+ * Clear job status...
+ */
+
+ job->status = 0;
+ }
}
{
int i; /* Looping var */
int copies; /* Number of copies printed */
- char message[1024], /* Message text */
+ char message[CUPSD_SB_BUFFER_SIZE],
+ /* Message text */
*ptr; /* Pointer update... */
int loglevel, /* Log level for message */
event = 0; /* Events? */
if (job->sheets)
{
- if (!strncasecmp(message, "total ", 6))
+ if (!_cups_strncasecmp(message, "total ", 6))
{
/*
* Got a total count of pages from a backend or filter...
job->sheets->values[0].integer += copies;
if (job->printer->page_limit)
- {
- cupsd_quota_t *q = cupsdUpdateQuota(job->printer, job->username,
- copies, 0);
-
-#ifdef __APPLE__
- if (AppleQuotas && q->page_count == -3)
- {
- /*
- * Quota limit exceeded, cancel job in progress immediately...
- */
-
- cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT,
- "Canceled job because pages exceed user %s "
- "quota limit on printer %s (%s).",
- job->username, job->printer->name,
- job->printer->info);
- return;
- }
-#else
- (void)q;
-#endif /* __APPLE__ */
- }
+ cupsdUpdateQuota(job->printer, job->username, copies, 0);
}
cupsdLogPage(job, message);
cupsdStopPrinter(job->printer, 1);
return;
}
- else
- {
- cupsdSetPrinterReasons(job->printer, message);
- cupsdAddPrinterHistory(job->printer);
+ else if (cupsdSetPrinterReasons(job->printer, message))
event |= CUPSD_EVENT_PRINTER_STATE;
- }
update_job_attrs(job, 0);
}
cupsFreeOptions(num_keywords, keywords);
}
-#ifdef __APPLE__
- else if (!strncmp(message, "recoverable:", 12))
+ else
{
- ptr = message + 12;
- while (isspace(*ptr & 255))
- ptr ++;
+ /*
+ * Strip legacy message prefix...
+ */
- if (*ptr)
+ if (!strncmp(message, "recoverable:", 12))
{
- cupsdSetPrinterReasons(job->printer,
- "+com.apple.print.recoverable-warning");
- cupsdSetString(&(job->printer->recoverable), ptr);
- cupsdAddPrinterHistory(job->printer);
- event |= CUPSD_EVENT_PRINTER_STATE;
+ ptr = message + 12;
+ while (isspace(*ptr & 255))
+ ptr ++;
}
- }
- else if (!strncmp(message, "recovered:", 10))
- {
- cupsdSetPrinterReasons(job->printer,
- "-com.apple.print.recoverable-warning");
-
- ptr = message + 10;
- while (isspace(*ptr & 255))
- ptr ++;
+ else if (!strncmp(message, "recovered:", 10))
+ {
+ ptr = message + 10;
+ while (isspace(*ptr & 255))
+ ptr ++;
+ }
+ else
+ ptr = message;
- cupsdSetString(&(job->printer->recoverable), ptr);
- cupsdAddPrinterHistory(job->printer);
- event |= CUPSD_EVENT_PRINTER_STATE;
- }
-#endif /* __APPLE__ */
- else
- {
- cupsdLogJob(job, loglevel, "%s", message);
+ cupsdLogJob(job, loglevel, "%s", ptr);
- if (loglevel < CUPSD_LOG_DEBUG)
+ if (loglevel < CUPSD_LOG_DEBUG &&
+ strcmp(job->printer->state_message, ptr))
{
- strlcpy(job->printer->state_message, message,
+ strlcpy(job->printer->state_message, ptr,
sizeof(job->printer->state_message));
- cupsdAddPrinterHistory(job->printer);
event |= CUPSD_EVENT_PRINTER_STATE | CUPSD_EVENT_JOB_PROGRESS;
- if (loglevel < job->status_level)
+ if (loglevel <= job->status_level && job->status_level > CUPSD_LOG_ERROR)
{
/*
* Some messages show in the job-printer-state-message attribute...
break;
}
+ if (event & CUPSD_EVENT_JOB_PROGRESS)
+ cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
+ "%s", job->printer->state_message);
if (event & CUPSD_EVENT_PRINTER_STATE)
cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, job->printer, NULL,
(job->printer->type & CUPS_PRINTER_CLASS) ?
"Printer \"%s\" state changed.",
job->printer->name);
- if (event & CUPSD_EVENT_JOB_PROGRESS)
- cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
- "%s", job->printer->state_message);
if (ptr == NULL && !job->status_buffer->bufused)
{
* Handle the end of job stuff...
*/
- finalize_job(job);
+ finalize_job(job, 1);
/*
* Check for new jobs...
else if (job->printer->state_message[0] && do_message)
cupsdSetString(&(job->printer_message->values[0].string.text),
job->printer->state_message);
-
+
/*
* ... and the printer-state-reasons value...
*/