2
* "$Id: job.c 11147 2013-07-17 02:54:31Z msweet $"
4
* Job management routines for the CUPS scheduler.
6
* Copyright 2007-2012 by Apple Inc.
7
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
9
* These coded instructions, statements, and computer programs are the
10
* property of Apple Inc. and are protected by Federal copyright
11
* law. Distribution and use rights are outlined in the file "LICENSE.txt"
12
* which should have been included with this file. If this file is
13
* file is missing or damaged, see the license at "http://www.cups.org/".
17
* cupsdAddJob() - Add a new job to the job queue.
18
* cupsdCancelJobs() - Cancel all jobs for the given
20
* cupsdCheckJobs() - Check the pending jobs and start any if the
21
* destination is available.
22
* cupsdCleanJobs() - Clean out old jobs.
23
* cupsdContinueJob() - Continue printing with the next file in a
25
* cupsdDeleteJob() - Free all memory used by a job.
26
* cupsdFreeAllJobs() - Free all jobs from memory.
27
* cupsdFindJob() - Find the specified job.
28
* cupsdGetPrinterJobCount() - Get the number of pending, processing, or
29
* held jobs in a printer or class.
30
* cupsdGetUserJobCount() - Get the number of pending, processing, or
31
* held jobs for a user.
32
* cupsdLoadAllJobs() - Load all jobs from disk.
33
* cupsdLoadJob() - Load a single job.
34
* cupsdMoveJob() - Move the specified job to a different
36
* cupsdReleaseJob() - Release the specified job.
37
* cupsdRestartJob() - Restart the specified job.
38
* cupsdSaveAllJobs() - Save a summary of all jobs to disk.
39
* cupsdSaveJob() - Save a job to disk.
40
* cupsdSetJobHoldUntil() - Set the hold time for a job.
41
* cupsdSetJobPriority() - Set the priority of a job, moving it up/down
42
* in the list as needed.
43
* cupsdSetJobState() - Set the state of the specified print job.
44
* cupsdStopAllJobs() - Stop all print jobs.
45
* cupsdUnloadCompletedJobs() - Flush completed job history from memory.
46
* cupsdUpdateJobs() - Update the history/file files for all jobs.
47
* compare_active_jobs() - Compare the job IDs and priorities of two
49
* compare_jobs() - Compare the job IDs of two jobs.
50
* dump_job_history() - Dump any debug messages for a job.
51
* free_job_history() - Free any log history.
52
* finalize_job() - Cleanup after job filter processes and
54
* get_options() - Get a string containing the job options.
55
* ipp_length() - Compute the size of the buffer needed to hold
56
* the textual IPP attributes.
57
* load_job_cache() - Load jobs from the job.cache file.
58
* load_next_job_id() - Load the NextJobId value from the job.cache
60
* load_request_root() - Load jobs from the RequestRoot directory.
61
* remove_job_files() - Remove the document files for a job.
62
* remove_job_history() - Remove the control file for a job.
63
* set_time() - Set one of the "time-at-xyz" attributes.
64
* start_job() - Start a print job.
65
* stop_job() - Stop a print job.
66
* unload_job() - Unload a job from memory.
67
* update_job() - Read a status update from a job's filters.
68
* update_job_attrs() - Update the job-printer-* attributes.
72
* Include necessary headers...
77
#include <cups/backend.h>
80
# include <IOKit/pwr_mgt/IOPMLib.h>
81
# ifdef HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H
82
# include <IOKit/pwr_mgt/IOPMLibPrivate.h>
83
# endif /* HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H */
84
#endif /* __APPLE__ */
88
* Design Notes for Job Management
89
* -------------------------------
93
* pending Do nothing/check jobs
94
* pending-held Send SIGTERM to filters and backend
95
* processing Do nothing/start job
96
* stopped Send SIGKILL to filters and backend
97
* canceled Send SIGTERM to filters and backend
101
* Finalize clears the printer <-> job association, deletes the status
102
* buffer, closes all of the pipes, etc. and doesn't get run until all of
103
* the print processes are finished.
105
* UNLOADING OF JOBS (cupsdUnloadCompletedJobs)
107
* We unload the job attributes when they are not needed to reduce overall
108
* memory consumption. We don't unload jobs where job->state_value <
109
* IPP_JOB_STOPPED, job->printer != NULL, or job->access_time is recent.
111
* STARTING OF JOBS (start_job)
113
* When a job is started, a status buffer, several pipes, a security
114
* profile, and a backend process are created for the life of that job.
115
* These are shared for every file in a job. For remote print jobs, the
116
* IPP backend is provided with every file in the job and no filters are
119
* The job->printer member tracks which printer is printing a job, which
120
* can be different than the destination in job->dest for classes. The
121
* printer object also has a job pointer to track which job is being
124
* PRINTING OF JOB FILES (cupsdContinueJob)
126
* Each file in a job is filtered by 0 or more programs. After getting the
127
* list of filters needed and the total cost, the job is either passed or
128
* put back to the processing state until the current FilterLevel comes down
129
* enough to allow printing.
131
* If we can print, we build a string for the print options and run each of
132
* the filters, piping the output from one into the next.
134
* JOB STATUS UPDATES (update_job)
136
* The update_job function gets called whenever there are pending messages
137
* on the status pipe. These generally are updates to the marker-*,
138
* printer-state-message, or printer-state-reasons attributes. On EOF,
139
* finalize_job is called to clean up.
141
* FINALIZING JOBS (finalize_job)
143
* When all filters and the backend are done, we set the job state to
144
* completed (no errors), aborted (filter errors or abort-job policy),
145
* pending-held (auth required or retry-job policy), or pending
146
* (retry-current-job or stop-printer policies) as appropriate.
148
* Then we close the pipes and free the status buffers and profiles.
150
* JOB FILE COMPLETION (process_children in main.c)
152
* For multiple-file jobs, process_children (in main.c) sees that all
153
* filters have exited and calls in to print the next file if there are
154
* more files in the job, otherwise it waits for the backend to exit and
155
* update_job to do the cleanup.
163
static mime_filter_t gziptoany_filter =
165
NULL, /* Source type */
166
NULL, /* Destination type */
168
"gziptoany" /* Filter program to run */
176
static int compare_active_jobs(void *first, void *second, void *data);
177
static int compare_jobs(void *first, void *second, void *data);
178
static void dump_job_history(cupsd_job_t *job);
179
static void finalize_job(cupsd_job_t *job, int set_job_state);
180
static void free_job_history(cupsd_job_t *job);
181
static char *get_options(cupsd_job_t *job, int banner_page, char *copies,
182
size_t copies_size, char *title,
184
static size_t ipp_length(ipp_t *ipp);
185
static void load_job_cache(const char *filename);
186
static void load_next_job_id(const char *filename);
187
static void load_request_root(void);
188
static void remove_job_files(cupsd_job_t *job);
189
static void remove_job_history(cupsd_job_t *job);
190
static void set_time(cupsd_job_t *job, const char *name);
191
static void start_job(cupsd_job_t *job, cupsd_printer_t *printer);
192
static void stop_job(cupsd_job_t *job, cupsd_jobaction_t action);
193
static void unload_job(cupsd_job_t *job);
194
static void update_job(cupsd_job_t *job);
195
static void update_job_attrs(cupsd_job_t *job, int do_message);
199
* 'cupsdAddJob()' - Add a new job to the job queue.
202
cupsd_job_t * /* O - New job record */
203
cupsdAddJob(int priority, /* I - Job priority */
204
const char *dest) /* I - Job destination */
206
cupsd_job_t *job; /* New job record */
209
if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
212
job->id = NextJobId ++;
213
job->priority = priority;
214
job->back_pipes[0] = -1;
215
job->back_pipes[1] = -1;
216
job->print_pipes[0] = -1;
217
job->print_pipes[1] = -1;
218
job->side_pipes[0] = -1;
219
job->side_pipes[1] = -1;
220
job->status_pipes[0] = -1;
221
job->status_pipes[1] = -1;
223
cupsdSetString(&job->dest, dest);
226
* Add the new job to the "all jobs" and "active jobs" lists...
229
cupsArrayAdd(Jobs, job);
230
cupsArrayAdd(ActiveJobs, job);
237
* 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user.
241
cupsdCancelJobs(const char *dest, /* I - Destination to cancel */
242
const char *username, /* I - Username or NULL */
243
int purge) /* I - Purge jobs? */
245
cupsd_job_t *job; /* Current job */
248
for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
250
job = (cupsd_job_t *)cupsArrayNext(Jobs))
252
if ((!job->dest || !job->username) && !cupsdLoadJob(job))
255
if ((!dest || !strcmp(job->dest, dest)) &&
256
(!username || !strcmp(job->username, username)))
259
* Cancel all jobs matching this destination/user...
263
cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_PURGE,
264
"Job purged by user.");
265
else if (job->state_value < IPP_JOB_CANCELED)
266
cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT,
267
"Job canceled by user.");
276
* 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
283
cupsd_job_t *job; /* Current job in queue */
284
cupsd_printer_t *printer, /* Printer destination */
285
*pclass; /* Printer class destination */
286
ipp_attribute_t *attr; /* Job attribute */
287
time_t curtime; /* Current time */
290
curtime = time(NULL);
292
cupsdLogMessage(CUPSD_LOG_DEBUG2,
293
"cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d, "
294
"curtime=%ld", cupsArrayCount(ActiveJobs), Sleeping,
295
NeedReload, (long)curtime);
297
for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
299
job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
301
cupsdLogMessage(CUPSD_LOG_DEBUG2,
302
"cupsdCheckJobs: Job %d - dest=\"%s\", printer=%p, "
303
"state=%d, cancel_time=%ld, hold_until=%ld, kill_time=%ld, "
304
"pending_cost=%d, pending_timeout=%ld", job->id, job->dest,
305
job->printer, job->state_value, (long)job->cancel_time,
306
(long)job->hold_until, (long)job->kill_time,
307
job->pending_cost, (long)job->pending_timeout);
310
* Kill jobs if they are unresponsive...
313
if (job->kill_time && job->kill_time <= curtime)
315
cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Stopping unresponsive job.",
318
stop_job(job, CUPSD_JOB_FORCE);
323
* Cancel stuck jobs...
326
if (job->cancel_time && job->cancel_time <= curtime)
328
cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT,
329
"Canceling stuck job after %d seconds.", MaxJobTime);
334
* Start held jobs if they are ready...
337
if (job->state_value == IPP_JOB_HELD &&
339
job->hold_until < curtime)
341
if (job->pending_timeout)
344
* This job is pending; check that we don't have an active Send-Document
345
* operation in progress on any of the client connections, then timeout
346
* the job so we can start printing...
349
cupsd_client_t *con; /* Current client connection */
351
for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
353
con = (cupsd_client_t *)cupsArrayNext(Clients))
355
con->request->request.op.operation_id == IPP_SEND_DOCUMENT)
361
if (cupsdTimeoutJob(job))
365
cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
366
"Job submission timed out.");
370
* Continue jobs that are waiting on the FilterLimit...
373
if (job->pending_cost > 0 &&
374
((FilterLevel + job->pending_cost) < FilterLimit || FilterLevel == 0))
375
cupsdContinueJob(job);
378
* Start pending jobs if the destination is available...
381
if (job->state_value == IPP_JOB_PENDING && !NeedReload &&
382
#ifndef kIOPMAssertionTypeDenySystemSleep
384
#endif /* !kIOPMAssertionTypeDenySystemSleep */
385
!DoingShutdown && !job->printer)
387
printer = cupsdFindDest(job->dest);
390
while (printer && (printer->type & CUPS_PRINTER_CLASS))
393
* If the class is remote, just pass it to the remote server...
398
if (pclass->state == IPP_PRINTER_STOPPED)
400
else if (pclass->type & CUPS_PRINTER_REMOTE)
403
printer = cupsdFindAvailablePrinter(printer->name);
406
if (!printer && !pclass)
409
* Whoa, the printer and/or class for this destination went away;
413
cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
414
"Job aborted because the destination printer/class "
417
else if (printer && !printer->holding_new_jobs)
420
* See if the printer is available or remote and not printing a job;
421
* if so, start the job...
427
* Add/update a job-actual-printer-uri attribute for this job
428
* so that we know which printer actually printed the job...
431
if ((attr = ippFindAttribute(job->attrs, "job-actual-printer-uri",
432
IPP_TAG_URI)) != NULL)
433
cupsdSetString(&attr->values[0].string.text, printer->uri);
435
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI,
436
"job-actual-printer-uri", NULL, printer->uri);
439
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
442
if (!printer->job && printer->state == IPP_PRINTER_IDLE)
448
start_job(job, printer);
457
* 'cupsdCleanJobs()' - Clean out old jobs.
463
cupsd_job_t *job; /* Current job */
464
time_t curtime; /* Current time */
467
cupsdLogMessage(CUPSD_LOG_DEBUG2,
468
"cupsdCleanJobs: MaxJobs=%d, JobHistory=%d, JobFiles=%d",
469
MaxJobs, JobHistory, JobFiles);
471
if (MaxJobs <= 0 && JobHistory == INT_MAX && JobFiles == INT_MAX)
474
curtime = time(NULL);
475
JobHistoryUpdate = 0;
477
for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
479
job = (cupsd_job_t *)cupsArrayNext(Jobs))
481
if (job->state_value >= IPP_JOB_CANCELED && !job->printer)
484
* Expire old jobs (or job files)...
487
if ((MaxJobs > 0 && cupsArrayCount(Jobs) >= MaxJobs) ||
488
(job->history_time && job->history_time <= curtime))
490
cupsdLogJob(job, CUPSD_LOG_DEBUG, "Removing from history.");
491
cupsdDeleteJob(job, CUPSD_JOB_PURGE);
493
else if (job->file_time && job->file_time <= curtime)
495
cupsdLogJob(job, CUPSD_LOG_DEBUG, "Removing document files.");
496
remove_job_files(job);
498
if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
499
JobHistoryUpdate = job->history_time;
503
if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
504
JobHistoryUpdate = job->history_time;
506
if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate)
507
JobHistoryUpdate = job->file_time;
512
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCleanJobs: JobHistoryUpdate=%ld",
513
(long)JobHistoryUpdate);
518
* 'cupsdContinueJob()' - Continue printing with the next file in a job.
522
cupsdContinueJob(cupsd_job_t *job) /* I - Job */
524
int i; /* Looping var */
525
int slot; /* Pipe slot */
526
cups_array_t *filters = NULL,/* Filters for job */
527
*prefilters; /* Filters with prefilters */
528
mime_filter_t *filter, /* Current filter */
529
*prefilter, /* Prefilter */
530
port_monitor; /* Port monitor filter */
531
char scheme[255]; /* Device URI scheme */
532
ipp_attribute_t *attr; /* Current attribute */
533
const char *ptr, /* Pointer into value */
534
*abort_message; /* Abort message */
535
ipp_jstate_t abort_state = IPP_JOB_STOPPED;
536
/* New job state on abort */
537
struct stat backinfo; /* Backend file information */
538
int backroot; /* Run backend as root? */
539
int pid; /* Process ID of new filter process */
540
int banner_page; /* 1 if banner page, 0 otherwise */
541
int filterfds[2][2] = { { -1, -1 }, { -1, -1 } };
542
/* Pipes used between filters */
543
int envc; /* Number of environment variables */
544
struct stat fileinfo; /* Job file information */
545
char **argv = NULL, /* Filter command-line arguments */
546
filename[1024], /* Job filename */
547
command[1024], /* Full path to command */
548
jobid[255], /* Job ID string */
550
/* Job title string */
551
copies[255], /* # copies string */
552
*options, /* Options string */
554
/* Environment variables */
555
charset[255], /* CHARSET env variable */
556
class_name[255],/* CLASS env variable */
557
classification[1024],
558
/* CLASSIFICATION env variable */
560
/* CONTENT_TYPE env variable */
562
/* DEVICE_URI env variable */
563
final_content_type[1024] = "",
564
/* FINAL_CONTENT_TYPE env variable */
565
lang[255], /* LANG env variable */
568
/* APPLE_LANGUAGE env variable */
569
#endif /* __APPLE__ */
570
auth_info_required[255],
571
/* AUTH_INFO_REQUIRED env variable */
572
ppd[1024], /* PPD env variable */
574
/* PRINTER_INFO env variable */
575
printer_location[255],
576
/* PRINTER_LOCATION env variable */
578
/* PRINTER env variable */
579
*printer_state_reasons = NULL,
580
/* PRINTER_STATE_REASONS env var */
582
/* RIP_MAX_CACHE env variable */
585
cupsdLogMessage(CUPSD_LOG_DEBUG2,
586
"cupsdContinueJob(job=%p(%d)): current_file=%d, num_files=%d",
587
job, job->id, job->current_file, job->num_files);
590
* Figure out what filters are required to convert from
591
* the source to the destination type...
594
FilterLevel -= job->cost;
597
job->pending_cost = 0;
599
memset(job->filters, 0, sizeof(job->filters));
601
if (job->printer->raw)
604
* Remote jobs and raw queues go directly to the printer without
608
cupsdLogJob(job, CUPSD_LOG_DEBUG, "Sending job to queue tagged as raw...");
613
* Local jobs get filtered...
616
snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
617
job->id, job->current_file + 1);
618
if (stat(filename, &fileinfo))
619
fileinfo.st_size = 0;
621
filters = mimeFilter2(MimeDatabase, job->filetypes[job->current_file],
622
fileinfo.st_size, job->printer->filetype,
627
cupsdLogJob(job, CUPSD_LOG_ERROR,
628
"Unable to convert file %d to printable format.",
631
abort_message = "Aborting job because it cannot be printed.";
632
abort_state = IPP_JOB_ABORTED;
634
ippSetString(job->attrs, &job->reasons, 0, "document-unprintable-error");
639
* Figure out the final content type...
642
cupsdLogJob(job, CUPSD_LOG_DEBUG, "%d filters for job:",
643
cupsArrayCount(filters));
644
for (filter = (mime_filter_t *)cupsArrayFirst(filters);
646
filter = (mime_filter_t *)cupsArrayNext(filters))
647
cupsdLogJob(job, CUPSD_LOG_DEBUG, "%s (%s/%s to %s/%s, cost %d)",
649
filter->src ? filter->src->super : "???",
650
filter->src ? filter->src->type : "???",
651
filter->dst ? filter->dst->super : "???",
652
filter->dst ? filter->dst->type : "???",
655
if (!job->printer->remote)
657
for (filter = (mime_filter_t *)cupsArrayLast(filters);
658
filter && filter->dst;
659
filter = (mime_filter_t *)cupsArrayPrev(filters))
660
if (strcmp(filter->dst->super, "printer") ||
661
strcmp(filter->dst->type, job->printer->name))
664
if (filter && filter->dst)
666
if ((ptr = strchr(filter->dst->type, '/')) != NULL)
667
snprintf(final_content_type, sizeof(final_content_type),
668
"FINAL_CONTENT_TYPE=%s", ptr + 1);
670
snprintf(final_content_type, sizeof(final_content_type),
671
"FINAL_CONTENT_TYPE=%s/%s", filter->dst->super,
675
snprintf(final_content_type, sizeof(final_content_type),
676
"FINAL_CONTENT_TYPE=printer/%s", job->printer->name);
680
* Remove NULL ("-") filters...
683
for (filter = (mime_filter_t *)cupsArrayFirst(filters);
685
filter = (mime_filter_t *)cupsArrayNext(filters))
686
if (!strcmp(filter->filter, "-"))
687
cupsArrayRemove(filters, filter);
689
if (cupsArrayCount(filters) == 0)
691
cupsArrayDelete(filters);
696
* If this printer has any pre-filters, insert the required pre-filter
697
* in the filters array...
700
if (job->printer->prefiltertype && filters)
702
prefilters = cupsArrayNew(NULL, NULL);
704
for (filter = (mime_filter_t *)cupsArrayFirst(filters);
706
filter = (mime_filter_t *)cupsArrayNext(filters))
708
if ((prefilter = mimeFilterLookup(MimeDatabase, filter->src,
709
job->printer->prefiltertype)))
711
cupsArrayAdd(prefilters, prefilter);
712
job->cost += prefilter->cost;
715
cupsArrayAdd(prefilters, filter);
718
cupsArrayDelete(filters);
719
filters = prefilters;
724
* Set a minimum cost of 100 for all jobs so that FilterLimit
725
* works with raw queues and other low-cost paths.
732
* See if the filter cost is too high...
735
if ((FilterLevel + job->cost) > FilterLimit && FilterLevel > 0 &&
739
* Don't print this job quite yet...
742
cupsArrayDelete(filters);
744
cupsdLogJob(job, CUPSD_LOG_INFO,
745
"Holding because filter limit has been reached.");
746
cupsdLogJob(job, CUPSD_LOG_DEBUG2,
747
"cupsdContinueJob: file=%d, cost=%d, level=%d, limit=%d",
748
job->current_file, job->cost, FilterLevel,
751
job->pending_cost = job->cost;
756
FilterLevel += job->cost;
759
* Add decompression/raw filter as needed...
762
if ((!job->printer->raw && job->compressions[job->current_file]) ||
763
(!filters && !job->printer->remote &&
764
(job->num_files > 1 || !strncmp(job->printer->device_uri, "file:", 5))))
767
* Add gziptoany filter to the front of the list...
771
filters = cupsArrayNew(NULL, NULL);
773
if (!cupsArrayInsert(filters, &gziptoany_filter))
775
cupsdLogJob(job, CUPSD_LOG_DEBUG,
776
"Unable to add decompression filter - %s", strerror(errno));
778
cupsArrayDelete(filters);
780
abort_message = "Stopping job because the scheduler ran out of memory.";
787
* Add port monitor, if any...
790
if (job->printer->port_monitor)
793
* Add port monitor to the end of the list...
797
filters = cupsArrayNew(NULL, NULL);
799
port_monitor.src = NULL;
800
port_monitor.dst = NULL;
801
port_monitor.cost = 0;
803
snprintf(port_monitor.filter, sizeof(port_monitor.filter),
804
"%s/monitor/%s", ServerBin, job->printer->port_monitor);
806
if (!cupsArrayAdd(filters, &port_monitor))
808
cupsdLogJob(job, CUPSD_LOG_DEBUG,
809
"Unable to add port monitor - %s", strerror(errno));
811
abort_message = "Stopping job because the scheduler ran out of memory.";
818
* Make sure we don't go over the "MAX_FILTERS" limit...
821
if (cupsArrayCount(filters) > MAX_FILTERS)
823
cupsdLogJob(job, CUPSD_LOG_DEBUG,
824
"Too many filters (%d > %d), unable to print.",
825
cupsArrayCount(filters), MAX_FILTERS);
827
abort_message = "Aborting job because it needs too many filters to print.";
828
abort_state = IPP_JOB_ABORTED;
830
ippSetString(job->attrs, &job->reasons, 0, "document-unprintable-error");
836
* Determine if we are printing a banner page or not...
839
if (job->job_sheets == NULL)
841
cupsdLogJob(job, CUPSD_LOG_DEBUG, "No job-sheets attribute.");
842
if ((job->job_sheets =
843
ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
844
cupsdLogJob(job, CUPSD_LOG_DEBUG,
845
"... but someone added one without setting job_sheets.");
847
else if (job->job_sheets->num_values == 1)
848
cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-sheets=%s",
849
job->job_sheets->values[0].string.text);
851
cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-sheets=%s,%s",
852
job->job_sheets->values[0].string.text,
853
job->job_sheets->values[1].string.text);
855
if (job->printer->type & CUPS_PRINTER_REMOTE)
857
else if (job->job_sheets == NULL)
859
else if (_cups_strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
860
job->current_file == 0)
862
else if (job->job_sheets->num_values > 1 &&
863
_cups_strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
864
job->current_file == (job->num_files - 1))
869
if ((options = get_options(job, banner_page, copies, sizeof(copies), title,
870
sizeof(title))) == NULL)
872
abort_message = "Stopping job because the scheduler ran out of memory.";
878
* Build the command-line arguments for the filters. Each filter
879
* has 6 or 7 arguments:
887
* argv[6] = filename (optional; normally stdin)
889
* This allows legacy printer drivers that use the old System V
890
* printing interface to be used by CUPS.
892
* For remote jobs, we send all of the files in the argument list.
895
if (job->printer->remote)
896
argv = calloc(7 + job->num_files, sizeof(char *));
898
argv = calloc(8, sizeof(char *));
902
cupsdLogMessage(CUPSD_LOG_DEBUG, "Unable to allocate argument array - %s",
905
abort_message = "Stopping job because the scheduler ran out of memory.";
910
sprintf(jobid, "%d", job->id);
912
argv[0] = job->printer->name;
914
argv[2] = job->username;
919
if (job->printer->remote && job->num_files > 1)
921
for (i = 0; i < job->num_files; i ++)
923
snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
925
argv[6 + i] = strdup(filename);
930
snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
931
job->id, job->current_file + 1);
935
for (i = 0; argv[i]; i ++)
936
cupsdLogJob(job, CUPSD_LOG_DEBUG, "argv[%d]=\"%s\"", i, argv[i]);
939
* Create environment variable strings for the filters...
942
attr = ippFindAttribute(job->attrs, "attributes-natural-language",
946
strlcpy(apple_language, "APPLE_LANGUAGE=", sizeof(apple_language));
947
_cupsAppleLanguage(attr->values[0].string.text,
948
apple_language + 15, sizeof(apple_language) - 15);
949
#endif /* __APPLE__ */
951
switch (strlen(attr->values[0].string.text))
955
* This is an unknown or badly formatted language code; use
956
* the POSIX locale...
959
strlcpy(lang, "LANG=C", sizeof(lang));
964
* Just the language code (ll)...
967
snprintf(lang, sizeof(lang), "LANG=%s.UTF-8",
968
attr->values[0].string.text);
973
* Language and country code (ll-cc)...
976
snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF-8",
977
attr->values[0].string.text[0],
978
attr->values[0].string.text[1],
979
toupper(attr->values[0].string.text[3] & 255),
980
toupper(attr->values[0].string.text[4] & 255));
984
if ((attr = ippFindAttribute(job->attrs, "document-format",
985
IPP_TAG_MIMETYPE)) != NULL &&
986
(ptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
987
snprintf(charset, sizeof(charset), "CHARSET=%s", ptr + 8);
989
strlcpy(charset, "CHARSET=utf-8", sizeof(charset));
991
snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
992
job->filetypes[job->current_file]->super,
993
job->filetypes[job->current_file]->type);
994
snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s",
995
job->printer->device_uri);
996
snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot,
998
snprintf(printer_info, sizeof(printer_name), "PRINTER_INFO=%s",
999
job->printer->info ? job->printer->info : "");
1000
snprintf(printer_location, sizeof(printer_name), "PRINTER_LOCATION=%s",
1001
job->printer->location ? job->printer->location : "");
1002
snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", job->printer->name);
1003
if (job->printer->num_reasons > 0)
1005
char *psrptr; /* Pointer into PRINTER_STATE_REASONS */
1006
size_t psrlen; /* Size of PRINTER_STATE_REASONS */
1008
for (psrlen = 22, i = 0; i < job->printer->num_reasons; i ++)
1009
psrlen += strlen(job->printer->reasons[i]) + 1;
1011
if ((printer_state_reasons = malloc(psrlen)) != NULL)
1014
* All of these strcpy's are safe because we allocated the psr string...
1017
strlcpy(printer_state_reasons, "PRINTER_STATE_REASONS=", psrlen);
1018
for (psrptr = printer_state_reasons + 22, i = 0;
1019
i < job->printer->num_reasons;
1024
strlcpy(psrptr, job->printer->reasons[i],
1025
psrlen - (psrptr - printer_state_reasons));
1026
psrptr += strlen(psrptr);
1030
snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
1032
if (job->printer->num_auth_info_required == 1)
1033
snprintf(auth_info_required, sizeof(auth_info_required),
1034
"AUTH_INFO_REQUIRED=%s",
1035
job->printer->auth_info_required[0]);
1036
else if (job->printer->num_auth_info_required == 2)
1037
snprintf(auth_info_required, sizeof(auth_info_required),
1038
"AUTH_INFO_REQUIRED=%s,%s",
1039
job->printer->auth_info_required[0],
1040
job->printer->auth_info_required[1]);
1041
else if (job->printer->num_auth_info_required == 3)
1042
snprintf(auth_info_required, sizeof(auth_info_required),
1043
"AUTH_INFO_REQUIRED=%s,%s,%s",
1044
job->printer->auth_info_required[0],
1045
job->printer->auth_info_required[1],
1046
job->printer->auth_info_required[2]);
1047
else if (job->printer->num_auth_info_required == 4)
1048
snprintf(auth_info_required, sizeof(auth_info_required),
1049
"AUTH_INFO_REQUIRED=%s,%s,%s,%s",
1050
job->printer->auth_info_required[0],
1051
job->printer->auth_info_required[1],
1052
job->printer->auth_info_required[2],
1053
job->printer->auth_info_required[3]);
1055
strlcpy(auth_info_required, "AUTH_INFO_REQUIRED=none",
1056
sizeof(auth_info_required));
1058
envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
1060
envp[envc ++] = charset;
1061
envp[envc ++] = lang;
1063
envp[envc ++] = apple_language;
1064
#endif /* __APPLE__ */
1065
envp[envc ++] = ppd;
1066
envp[envc ++] = rip_max_cache;
1067
envp[envc ++] = content_type;
1068
envp[envc ++] = device_uri;
1069
envp[envc ++] = printer_info;
1070
envp[envc ++] = printer_location;
1071
envp[envc ++] = printer_name;
1072
envp[envc ++] = printer_state_reasons ? printer_state_reasons :
1073
"PRINTER_STATE_REASONS=none";
1074
envp[envc ++] = banner_page ? "CUPS_FILETYPE=job-sheet" :
1075
"CUPS_FILETYPE=document";
1077
if (final_content_type[0])
1078
envp[envc ++] = final_content_type;
1080
if (Classification && !banner_page)
1082
if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1083
IPP_TAG_NAME)) == NULL)
1084
snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1086
else if (attr->num_values > 1 &&
1087
strcmp(attr->values[1].string.text, "none") != 0)
1088
snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1089
attr->values[1].string.text);
1091
snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1092
attr->values[0].string.text);
1094
envp[envc ++] = classification;
1097
if (job->dtype & CUPS_PRINTER_CLASS)
1099
snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest);
1100
envp[envc ++] = class_name;
1103
envp[envc ++] = auth_info_required;
1106
i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
1108
if (job->auth_env[i])
1109
envp[envc ++] = job->auth_env[i];
1114
envp[envc ++] = job->auth_uid;
1118
for (i = 0; i < envc; i ++)
1119
if (!strncmp(envp[i], "AUTH_", 5))
1120
cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"AUTH_%c****\"", i,
1122
else if (strncmp(envp[i], "DEVICE_URI=", 11))
1123
cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"%s\"", i, envp[i]);
1125
cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"DEVICE_URI=%s\"", i,
1126
job->printer->sanitized_device_uri);
1128
if (job->printer->remote)
1129
job->current_file = job->num_files;
1131
job->current_file ++;
1134
* Now create processes for all of the filters...
1137
for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
1139
i ++, filter = (mime_filter_t *)cupsArrayNext(filters))
1141
if (filter->filter[0] != '/')
1142
snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
1145
strlcpy(command, filter->filter, sizeof(command));
1147
if (i < (cupsArrayCount(filters) - 1))
1149
if (cupsdOpenPipe(filterfds[slot]))
1151
abort_message = "Stopping job because the scheduler could not create "
1152
"the filter pipes.";
1159
if (job->current_file == 1 ||
1160
(job->printer->pc && job->printer->pc->single_file))
1162
if (strncmp(job->printer->device_uri, "file:", 5) != 0)
1164
if (cupsdOpenPipe(job->print_pipes))
1166
abort_message = "Stopping job because the scheduler could not "
1167
"create the backend pipes.";
1174
job->print_pipes[0] = -1;
1175
if (!strcmp(job->printer->device_uri, "file:/dev/null") ||
1176
!strcmp(job->printer->device_uri, "file:///dev/null"))
1177
job->print_pipes[1] = -1;
1180
if (!strncmp(job->printer->device_uri, "file:/dev/", 10))
1181
job->print_pipes[1] = open(job->printer->device_uri + 5,
1183
else if (!strncmp(job->printer->device_uri, "file:///dev/", 12))
1184
job->print_pipes[1] = open(job->printer->device_uri + 7,
1186
else if (!strncmp(job->printer->device_uri, "file:///", 8))
1187
job->print_pipes[1] = open(job->printer->device_uri + 7,
1188
O_WRONLY | O_CREAT | O_TRUNC, 0600);
1190
job->print_pipes[1] = open(job->printer->device_uri + 5,
1191
O_WRONLY | O_CREAT | O_TRUNC, 0600);
1193
if (job->print_pipes[1] < 0)
1195
abort_message = "Stopping job because the scheduler could not "
1196
"open the output file.";
1201
fcntl(job->print_pipes[1], F_SETFD,
1202
fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
1207
filterfds[slot][0] = job->print_pipes[0];
1208
filterfds[slot][1] = job->print_pipes[1];
1211
pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
1212
filterfds[slot][1], job->status_pipes[1],
1213
job->back_pipes[0], job->side_pipes[0], 0,
1214
job->profile, job, job->filters + i);
1216
cupsdClosePipe(filterfds[!slot]);
1220
cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
1221
filter->filter, strerror(errno));
1223
abort_message = "Stopping job because the scheduler could not execute a "
1229
cupsdLogJob(job, CUPSD_LOG_INFO, "Started filter %s (PID %d)", command,
1236
cupsArrayDelete(filters);
1240
* Finally, pipe the final output into a backend process if needed...
1243
if (strncmp(job->printer->device_uri, "file:", 5) != 0)
1245
if (job->current_file == 1 || job->printer->remote ||
1246
(job->printer->pc && job->printer->pc->single_file))
1248
sscanf(job->printer->device_uri, "%254[^:]", scheme);
1249
snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, scheme);
1252
* See if the backend needs to run as root...
1257
else if (stat(command, &backinfo))
1260
backroot = !(backinfo.st_mode & (S_IWGRP | S_IXGRP | S_IWOTH | S_IXOTH));
1262
argv[0] = job->printer->sanitized_device_uri;
1264
filterfds[slot][0] = -1;
1265
filterfds[slot][1] = -1;
1267
pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
1268
filterfds[slot][1], job->status_pipes[1],
1269
job->back_pipes[1], job->side_pipes[1],
1270
backroot, job->profile, job, &(job->backend));
1274
abort_message = "Stopping job because the sheduler could not execute "
1281
cupsdLogJob(job, CUPSD_LOG_INFO, "Started backend %s (PID %d)",
1286
if (job->current_file == job->num_files ||
1287
(job->printer->pc && job->printer->pc->single_file))
1288
cupsdClosePipe(job->print_pipes);
1290
if (job->current_file == job->num_files)
1292
cupsdClosePipe(job->back_pipes);
1293
cupsdClosePipe(job->side_pipes);
1295
close(job->status_pipes[1]);
1296
job->status_pipes[1] = -1;
1301
filterfds[slot][0] = -1;
1302
filterfds[slot][1] = -1;
1304
if (job->current_file == job->num_files ||
1305
(job->printer->pc && job->printer->pc->single_file))
1306
cupsdClosePipe(job->print_pipes);
1308
if (job->current_file == job->num_files)
1310
close(job->status_pipes[1]);
1311
job->status_pipes[1] = -1;
1315
cupsdClosePipe(filterfds[slot]);
1317
if (job->printer->remote && job->num_files > 1)
1319
for (i = 0; i < job->num_files; i ++)
1324
if (printer_state_reasons)
1325
free(printer_state_reasons);
1327
cupsdAddSelect(job->status_buffer->fd, (cupsd_selfunc_t)update_job, NULL,
1330
cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.",
1337
* If we get here, we need to abort the current job and close out all
1338
* files and pipes...
1343
FilterLevel -= job->cost;
1346
for (slot = 0; slot < 2; slot ++)
1347
cupsdClosePipe(filterfds[slot]);
1349
cupsArrayDelete(filters);
1353
if (job->printer->remote && job->num_files > 1)
1355
for (i = 0; i < job->num_files; i ++)
1362
if (printer_state_reasons)
1363
free(printer_state_reasons);
1365
cupsdClosePipe(job->print_pipes);
1366
cupsdClosePipe(job->back_pipes);
1367
cupsdClosePipe(job->side_pipes);
1369
cupsdRemoveSelect(job->status_pipes[0]);
1370
cupsdClosePipe(job->status_pipes);
1371
cupsdStatBufDelete(job->status_buffer);
1372
job->status_buffer = NULL;
1375
* Update the printer and job state.
1378
cupsdSetJobState(job, abort_state, CUPSD_JOB_DEFAULT, "%s", abort_message);
1379
cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0);
1380
update_job_attrs(job, 0);
1383
free_job_history(job);
1385
cupsArrayRemove(PrintingJobs, job);
1388
* Clear the printer <-> job association...
1391
job->printer->job = NULL;
1392
job->printer = NULL;
1397
* 'cupsdDeleteJob()' - Free all memory used by a job.
1401
cupsdDeleteJob(cupsd_job_t *job, /* I - Job */
1402
cupsd_jobaction_t action)/* I - Action */
1404
int i; /* Looping var */
1408
finalize_job(job, 1);
1410
if (action == CUPSD_JOB_PURGE)
1411
remove_job_history(job);
1413
cupsdClearString(&job->username);
1414
cupsdClearString(&job->dest);
1416
i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
1418
cupsdClearString(job->auth_env + i);
1419
cupsdClearString(&job->auth_uid);
1421
if (action == CUPSD_JOB_PURGE)
1422
remove_job_files(job);
1423
else if (job->num_files > 0)
1425
free(job->compressions);
1426
free(job->filetypes);
1432
free_job_history(job);
1436
cupsArrayRemove(Jobs, job);
1437
cupsArrayRemove(ActiveJobs, job);
1438
cupsArrayRemove(PrintingJobs, job);
1445
* 'cupsdFreeAllJobs()' - Free all jobs from memory.
1449
cupsdFreeAllJobs(void)
1451
cupsd_job_t *job; /* Current job */
1459
cupsdStopAllJobs(CUPSD_JOB_FORCE, 0);
1462
for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
1464
job = (cupsd_job_t *)cupsArrayNext(Jobs))
1465
cupsdDeleteJob(job, CUPSD_JOB_DEFAULT);
1467
cupsdReleaseSignals();
1472
* 'cupsdFindJob()' - Find the specified job.
1475
cupsd_job_t * /* O - Job data */
1476
cupsdFindJob(int id) /* I - Job ID */
1478
cupsd_job_t key; /* Search key */
1483
return ((cupsd_job_t *)cupsArrayFind(Jobs, &key));
1488
* 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
1489
* or held jobs in a printer or class.
1492
int /* O - Job count */
1493
cupsdGetPrinterJobCount(
1494
const char *dest) /* I - Printer or class name */
1496
int count; /* Job count */
1497
cupsd_job_t *job; /* Current job */
1500
for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
1502
job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
1503
if (job->dest && !_cups_strcasecmp(job->dest, dest))
1511
* 'cupsdGetUserJobCount()' - Get the number of pending, processing,
1512
* or held jobs for a user.
1515
int /* O - Job count */
1516
cupsdGetUserJobCount(
1517
const char *username) /* I - Username */
1519
int count; /* Job count */
1520
cupsd_job_t *job; /* Current job */
1523
for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
1525
job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
1526
if (!_cups_strcasecmp(job->username, username))
1534
* 'cupsdLoadAllJobs()' - Load all jobs from disk.
1538
cupsdLoadAllJobs(void)
1540
char filename[1024]; /* Full filename of job.cache file */
1541
struct stat fileinfo, /* Information on job.cache file */
1542
dirinfo; /* Information on RequestRoot dir */
1547
* Create the job arrays as needed...
1551
Jobs = cupsArrayNew(compare_jobs, NULL);
1554
ActiveJobs = cupsArrayNew(compare_active_jobs, NULL);
1557
PrintingJobs = cupsArrayNew(compare_jobs, NULL);
1560
* See whether the job.cache file is older than the RequestRoot directory...
1563
snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir);
1565
if (stat(filename, &fileinfo))
1567
fileinfo.st_mtime = 0;
1569
if (errno != ENOENT)
1570
cupsdLogMessage(CUPSD_LOG_ERROR,
1571
"Unable to get file information for \"%s\" - %s",
1572
filename, strerror(errno));
1575
if (stat(RequestRoot, &dirinfo))
1577
dirinfo.st_mtime = 0;
1579
if (errno != ENOENT)
1580
cupsdLogMessage(CUPSD_LOG_ERROR,
1581
"Unable to get directory information for \"%s\" - %s",
1582
RequestRoot, strerror(errno));
1586
* Load the most recent source for job data...
1589
if (dirinfo.st_mtime > fileinfo.st_mtime)
1591
load_request_root();
1593
load_next_job_id(filename);
1596
load_job_cache(filename);
1599
* Clean out old jobs as needed...
1602
if (MaxJobs > 0 && cupsArrayCount(Jobs) >= MaxJobs)
1608
* 'cupsdLoadJob()' - Load a single job.
1611
int /* O - 1 on success, 0 on failure */
1612
cupsdLoadJob(cupsd_job_t *job) /* I - Job */
1614
int i; /* Looping var */
1615
char jobfile[1024]; /* Job filename */
1616
cups_file_t *fp; /* Job file */
1617
int fileid; /* Current file ID */
1618
ipp_attribute_t *attr; /* Job attribute */
1619
const char *dest; /* Destination name */
1620
cupsd_printer_t *destptr; /* Pointer to destination */
1621
mime_type_t **filetypes; /* New filetypes array */
1622
int *compressions; /* New compressions array */
1627
if (job->state_value > IPP_JOB_STOPPED)
1628
job->access_time = time(NULL);
1633
if ((job->attrs = ippNew()) == NULL)
1635
cupsdLogJob(job, CUPSD_LOG_ERROR, "Ran out of memory for job attributes.");
1640
* Load job attributes...
1643
cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading attributes...", job->id);
1645
snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, job->id);
1646
if ((fp = cupsdOpenConfFile(jobfile)) == NULL)
1649
if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, job->attrs) != IPP_DATA)
1651
cupsdLogMessage(CUPSD_LOG_ERROR,
1652
"[Job %d] Unable to read job control file \"%s\".", job->id,
1661
* Copy attribute data to the job object...
1664
if (!ippFindAttribute(job->attrs, "time-at-creation", IPP_TAG_INTEGER))
1666
cupsdLogMessage(CUPSD_LOG_ERROR,
1667
"[Job %d] Missing or bad time-at-creation attribute in "
1668
"control file.", job->id);
1672
if ((job->state = ippFindAttribute(job->attrs, "job-state",
1673
IPP_TAG_ENUM)) == NULL)
1675
cupsdLogMessage(CUPSD_LOG_ERROR,
1676
"[Job %d] Missing or bad job-state attribute in control "
1681
job->state_value = (ipp_jstate_t)job->state->values[0].integer;
1683
job->history_time = 0;
1685
if (job->state_value >= IPP_JOB_CANCELED &&
1686
(attr = ippFindAttribute(job->attrs, "time-at-completed",
1687
IPP_TAG_INTEGER)) != NULL)
1689
if (JobHistory < INT_MAX)
1690
job->history_time = attr->values[0].integer + JobHistory;
1692
job->history_time = INT_MAX;
1694
if (job->history_time < time(NULL))
1695
goto error; /* Expired, remove from history */
1697
if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
1698
JobHistoryUpdate = job->history_time;
1700
if (JobFiles < INT_MAX)
1701
job->file_time = attr->values[0].integer + JobFiles;
1703
job->file_time = INT_MAX;
1705
if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate)
1706
JobHistoryUpdate = job->file_time;
1708
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdLoadJob: JobHistoryUpdate=%ld",
1709
(long)JobHistoryUpdate);
1714
if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
1715
IPP_TAG_URI)) == NULL)
1717
cupsdLogMessage(CUPSD_LOG_ERROR,
1718
"[Job %d] No job-printer-uri attribute in control file.",
1723
if ((dest = cupsdValidateDest(attr->values[0].string.text, &(job->dtype),
1726
cupsdLogMessage(CUPSD_LOG_ERROR,
1727
"[Job %d] Unable to queue job for destination \"%s\".",
1728
job->id, attr->values[0].string.text);
1732
cupsdSetString(&job->dest, dest);
1734
else if ((destptr = cupsdFindDest(job->dest)) == NULL)
1736
cupsdLogMessage(CUPSD_LOG_ERROR,
1737
"[Job %d] Unable to queue job for destination \"%s\".",
1738
job->id, job->dest);
1742
if ((job->reasons = ippFindAttribute(job->attrs, "job-state-reasons",
1743
IPP_TAG_KEYWORD)) == NULL)
1745
const char *reason; /* job-state-reason keyword */
1747
cupsdLogMessage(CUPSD_LOG_DEBUG,
1748
"[Job %d] Adding missing job-state-reasons attribute to "
1749
" control file.", job->id);
1751
switch (job->state_value)
1754
case IPP_JOB_PENDING :
1755
if (destptr->state == IPP_PRINTER_STOPPED)
1756
reason = "printer-stopped";
1762
if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1763
IPP_TAG_ZERO)) != NULL &&
1764
(attr->value_tag == IPP_TAG_NAME ||
1765
attr->value_tag == IPP_TAG_NAMELANG ||
1766
attr->value_tag == IPP_TAG_KEYWORD) &&
1767
strcmp(attr->values[0].string.text, "no-hold"))
1768
reason = "job-hold-until-specified";
1770
reason = "job-incoming";
1773
case IPP_JOB_PROCESSING :
1774
reason = "job-printing";
1777
case IPP_JOB_STOPPED :
1778
reason = "job-stopped";
1781
case IPP_JOB_CANCELED :
1782
reason = "job-canceled-by-user";
1785
case IPP_JOB_ABORTED :
1786
reason = "aborted-by-system";
1789
case IPP_JOB_COMPLETED :
1790
reason = "job-completed-successfully";
1794
job->reasons = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1795
"job-state-reasons", NULL, reason);
1797
else if (job->state_value == IPP_JOB_PENDING)
1799
if (destptr->state == IPP_PRINTER_STOPPED)
1800
ippSetString(job->attrs, &job->reasons, 0, "printer-stopped");
1802
ippSetString(job->attrs, &job->reasons, 0, "none");
1805
job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed",
1807
job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
1811
if ((attr = ippFindAttribute(job->attrs, "job-priority",
1812
IPP_TAG_INTEGER)) == NULL)
1814
cupsdLogMessage(CUPSD_LOG_ERROR,
1815
"[Job %d] Missing or bad job-priority attribute in "
1816
"control file.", job->id);
1820
job->priority = attr->values[0].integer;
1825
if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name",
1826
IPP_TAG_NAME)) == NULL)
1828
cupsdLogMessage(CUPSD_LOG_ERROR,
1829
"[Job %d] Missing or bad job-originating-user-name "
1830
"attribute in control file.", job->id);
1834
cupsdSetString(&job->username, attr->values[0].string.text);
1838
* Set the job hold-until time and state...
1841
if (job->state_value == IPP_JOB_HELD)
1843
if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1844
IPP_TAG_KEYWORD)) == NULL)
1845
attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
1848
cupsdSetJobHoldUntil(job, attr->values[0].string.text, CUPSD_JOB_DEFAULT);
1851
job->state->values[0].integer = IPP_JOB_PENDING;
1852
job->state_value = IPP_JOB_PENDING;
1855
else if (job->state_value == IPP_JOB_PROCESSING)
1857
job->state->values[0].integer = IPP_JOB_PENDING;
1858
job->state_value = IPP_JOB_PENDING;
1861
if (!job->num_files)
1864
* Find all the d##### files...
1867
for (fileid = 1; fileid < 10000; fileid ++)
1869
snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
1872
if (access(jobfile, 0))
1875
cupsdLogMessage(CUPSD_LOG_DEBUG,
1876
"[Job %d] Auto-typing document file \"%s\"...", job->id,
1879
if (fileid > job->num_files)
1881
if (job->num_files == 0)
1883
compressions = (int *)calloc(fileid, sizeof(int));
1884
filetypes = (mime_type_t **)calloc(fileid, sizeof(mime_type_t *));
1888
compressions = (int *)realloc(job->compressions,
1889
sizeof(int) * fileid);
1890
filetypes = (mime_type_t **)realloc(job->filetypes,
1891
sizeof(mime_type_t *) *
1896
job->compressions = compressions;
1899
job->filetypes = filetypes;
1901
if (!compressions || !filetypes)
1903
cupsdLogMessage(CUPSD_LOG_ERROR,
1904
"[Job %d] Ran out of memory for job file types.",
1907
ippDelete(job->attrs);
1910
if (job->compressions)
1912
free(job->compressions);
1913
job->compressions = NULL;
1918
free(job->filetypes);
1919
job->filetypes = NULL;
1926
job->num_files = fileid;
1929
job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, jobfile, NULL,
1930
job->compressions + fileid - 1);
1932
if (!job->filetypes[fileid - 1])
1933
job->filetypes[fileid - 1] = mimeType(MimeDatabase, "application",
1939
* Load authentication information as needed...
1942
if (job->state_value < IPP_JOB_STOPPED)
1944
snprintf(jobfile, sizeof(jobfile), "%s/a%05d", RequestRoot, job->id);
1947
i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
1949
cupsdClearString(job->auth_env + i);
1950
cupsdClearString(&job->auth_uid);
1952
if ((fp = cupsFileOpen(jobfile, "r")) != NULL)
1954
int bytes, /* Size of auth data */
1955
linenum = 1; /* Current line number */
1956
char line[65536], /* Line from file */
1957
*value, /* Value from line */
1958
data[65536]; /* Decoded data */
1961
if (cupsFileGets(fp, line, sizeof(line)) &&
1962
!strcmp(line, "CUPSD-AUTH-V3"))
1965
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1971
if (strcmp(line, "negotiate") && strcmp(line, "uid"))
1973
bytes = sizeof(data);
1974
httpDecode64_2(data, &bytes, value);
1978
* Assign environment variables...
1981
if (!strcmp(line, "uid"))
1983
cupsdSetStringf(&job->auth_uid, "AUTH_UID=%s", value);
1986
else if (i >= (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0])))
1989
if (!strcmp(line, "username"))
1990
cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s", data);
1991
else if (!strcmp(line, "domain"))
1992
cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s", data);
1993
else if (!strcmp(line, "password"))
1994
cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s", data);
1995
else if (!strcmp(line, "negotiate"))
1996
cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s", value);
2008
job->access_time = time(NULL);
2012
* If we get here then something bad happened...
2017
ippDelete(job->attrs);
2020
remove_job_history(job);
2021
remove_job_files(job);
2028
* 'cupsdMoveJob()' - Move the specified job to a different destination.
2032
cupsdMoveJob(cupsd_job_t *job, /* I - Job */
2033
cupsd_printer_t *p) /* I - Destination printer or class */
2035
ipp_attribute_t *attr; /* job-printer-uri attribute */
2036
const char *olddest; /* Old destination */
2037
cupsd_printer_t *oldp; /* Old pointer */
2041
* Don't move completed jobs...
2044
if (job->state_value > IPP_JOB_STOPPED)
2048
* Get the old destination...
2051
olddest = job->dest;
2054
oldp = job->printer;
2056
oldp = cupsdFindDest(olddest);
2059
* Change the destination information...
2062
if (job->state_value > IPP_JOB_HELD)
2063
cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
2064
"Stopping job prior to move.");
2066
cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, oldp, job,
2067
"Job #%d moved from %s to %s.", job->id, olddest,
2070
cupsdSetString(&job->dest, p->name);
2071
job->dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
2073
if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
2074
IPP_TAG_URI)) != NULL)
2075
cupsdSetString(&(attr->values[0].string.text), p->uri);
2077
cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job,
2078
"Job #%d moved from %s to %s.", job->id, olddest,
2082
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2087
* 'cupsdReleaseJob()' - Release the specified job.
2091
cupsdReleaseJob(cupsd_job_t *job) /* I - Job */
2093
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReleaseJob(job=%p(%d))", job,
2096
if (job->state_value == IPP_JOB_HELD)
2099
* Add trailing banner as needed...
2102
if (job->pending_timeout)
2103
cupsdTimeoutJob(job);
2105
cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
2106
"Job released by user.");
2112
* 'cupsdRestartJob()' - Restart the specified job.
2116
cupsdRestartJob(cupsd_job_t *job) /* I - Job */
2118
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRestartJob(job=%p(%d))", job,
2121
if (job->state_value == IPP_JOB_STOPPED || job->num_files)
2122
cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
2123
"Job restarted by user.");
2128
* 'cupsdSaveAllJobs()' - Save a summary of all jobs to disk.
2132
cupsdSaveAllJobs(void)
2134
int i; /* Looping var */
2135
cups_file_t *fp; /* job.cache file */
2136
char filename[1024], /* job.cache filename */
2137
temp[1024]; /* Temporary string */
2138
cupsd_job_t *job; /* Current job */
2139
time_t curtime; /* Current time */
2140
struct tm *curdate; /* Current date */
2143
snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir);
2144
if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
2147
cupsdLogMessage(CUPSD_LOG_INFO, "Saving job.cache...");
2150
* Write a small header to the file...
2153
curtime = time(NULL);
2154
curdate = localtime(&curtime);
2155
strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
2157
cupsFilePuts(fp, "# Job cache file for " CUPS_SVERSION "\n");
2158
cupsFilePrintf(fp, "# Written by cupsd\n", temp);
2159
cupsFilePrintf(fp, "NextJobId %d\n", NextJobId);
2162
* Write each job known to the system...
2165
for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
2167
job = (cupsd_job_t *)cupsArrayNext(Jobs))
2169
cupsFilePrintf(fp, "<Job %d>\n", job->id);
2170
cupsFilePrintf(fp, "State %d\n", job->state_value);
2171
cupsFilePrintf(fp, "Priority %d\n", job->priority);
2172
cupsFilePrintf(fp, "HoldUntil %d\n", (int)job->hold_until);
2173
cupsFilePrintf(fp, "Username %s\n", job->username);
2174
cupsFilePrintf(fp, "Destination %s\n", job->dest);
2175
cupsFilePrintf(fp, "DestType %d\n", job->dtype);
2176
cupsFilePrintf(fp, "NumFiles %d\n", job->num_files);
2177
for (i = 0; i < job->num_files; i ++)
2178
cupsFilePrintf(fp, "File %d %s/%s %d\n", i + 1, job->filetypes[i]->super,
2179
job->filetypes[i]->type, job->compressions[i]);
2180
cupsFilePuts(fp, "</Job>\n");
2183
cupsdCloseCreatedConfFile(fp, filename);
2188
* 'cupsdSaveJob()' - Save a job to disk.
2192
cupsdSaveJob(cupsd_job_t *job) /* I - Job */
2194
char filename[1024]; /* Job control filename */
2195
cups_file_t *fp; /* Job file */
2198
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSaveJob(job=%p(%d)): job->attrs=%p",
2199
job, job->id, job->attrs);
2201
snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id);
2203
if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm & 0600)) == NULL)
2206
fchown(cupsFileNumber(fp), RunUser, Group);
2208
job->attrs->state = IPP_IDLE;
2210
if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL,
2211
job->attrs) != IPP_DATA)
2213
cupsdLogMessage(CUPSD_LOG_ERROR,
2214
"[Job %d] Unable to write job control file.", job->id);
2219
if (!cupsdCloseCreatedConfFile(fp, filename))
2222
* Remove backup file and mark this job as clean...
2225
strlcat(filename, ".O", sizeof(filename));
2234
* 'cupsdSetJobHoldUntil()' - Set the hold time for a job.
2238
cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */
2239
const char *when, /* I - When to resume */
2240
int update)/* I - Update job-hold-until attr? */
2242
time_t curtime; /* Current time */
2243
struct tm *curdate; /* Current date */
2244
int hour; /* Hold hour */
2245
int minute; /* Hold minute */
2246
int second = 0; /* Hold second */
2249
cupsdLogMessage(CUPSD_LOG_DEBUG2,
2250
"cupsdSetJobHoldUntil(job=%p(%d), when=\"%s\", update=%d)",
2251
job, job->id, when, update);
2256
* Update the job-hold-until attribute...
2259
ipp_attribute_t *attr; /* job-hold-until attribute */
2261
if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
2262
IPP_TAG_KEYWORD)) == NULL)
2263
attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
2266
cupsdSetString(&(attr->values[0].string.text), when);
2268
attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2269
"job-hold-until", NULL, when);
2273
if (isdigit(when[0] & 255))
2274
attr->value_tag = IPP_TAG_NAME;
2276
attr->value_tag = IPP_TAG_KEYWORD;
2279
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2282
ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
2286
* Update the hold time...
2289
job->cancel_time = 0;
2291
if (!strcmp(when, "indefinite") || !strcmp(when, "auth-info-required"))
2294
* Hold indefinitely...
2297
job->hold_until = 0;
2299
if (MaxHoldTime > 0)
2300
job->cancel_time = time(NULL) + MaxHoldTime;
2302
else if (!strcmp(when, "day-time"))
2305
* Hold to 6am the next morning unless local time is < 6pm.
2308
curtime = time(NULL);
2309
curdate = localtime(&curtime);
2311
if (curdate->tm_hour < 18)
2312
job->hold_until = curtime;
2314
job->hold_until = curtime +
2315
((29 - curdate->tm_hour) * 60 + 59 -
2316
curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2318
else if (!strcmp(when, "evening") || !strcmp(when, "night"))
2321
* Hold to 6pm unless local time is > 6pm or < 6am.
2324
curtime = time(NULL);
2325
curdate = localtime(&curtime);
2327
if (curdate->tm_hour < 6 || curdate->tm_hour >= 18)
2328
job->hold_until = curtime;
2330
job->hold_until = curtime +
2331
((17 - curdate->tm_hour) * 60 + 59 -
2332
curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2334
else if (!strcmp(when, "second-shift"))
2337
* Hold to 4pm unless local time is > 4pm.
2340
curtime = time(NULL);
2341
curdate = localtime(&curtime);
2343
if (curdate->tm_hour >= 16)
2344
job->hold_until = curtime;
2346
job->hold_until = curtime +
2347
((15 - curdate->tm_hour) * 60 + 59 -
2348
curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2350
else if (!strcmp(when, "third-shift"))
2353
* Hold to 12am unless local time is < 8am.
2356
curtime = time(NULL);
2357
curdate = localtime(&curtime);
2359
if (curdate->tm_hour < 8)
2360
job->hold_until = curtime;
2362
job->hold_until = curtime +
2363
((23 - curdate->tm_hour) * 60 + 59 -
2364
curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2366
else if (!strcmp(when, "weekend"))
2369
* Hold to weekend unless we are in the weekend.
2372
curtime = time(NULL);
2373
curdate = localtime(&curtime);
2375
if (curdate->tm_wday == 0 || curdate->tm_wday == 6)
2376
job->hold_until = curtime;
2378
job->hold_until = curtime +
2379
(((5 - curdate->tm_wday) * 24 +
2380
(17 - curdate->tm_hour)) * 60 + 59 -
2381
curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2383
else if (sscanf(when, "%d:%d:%d", &hour, &minute, &second) >= 2)
2386
* Hold to specified GMT time (HH:MM or HH:MM:SS)...
2389
curtime = time(NULL);
2390
curdate = gmtime(&curtime);
2392
job->hold_until = curtime +
2393
((hour - curdate->tm_hour) * 60 + minute -
2394
curdate->tm_min) * 60 + second - curdate->tm_sec;
2397
* Hold until next day as needed...
2400
if (job->hold_until < curtime)
2401
job->hold_until += 24 * 60 * 60;
2404
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil: hold_until=%d",
2405
(int)job->hold_until);
2410
* 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
2411
* the list as needed.
2415
cupsdSetJobPriority(
2416
cupsd_job_t *job, /* I - Job ID */
2417
int priority) /* I - New priority (0 to 100) */
2419
ipp_attribute_t *attr; /* Job attribute */
2423
* Don't change completed jobs...
2426
if (job->state_value >= IPP_JOB_PROCESSING)
2430
* Set the new priority and re-add the job into the active list...
2433
cupsArrayRemove(ActiveJobs, job);
2435
job->priority = priority;
2437
if ((attr = ippFindAttribute(job->attrs, "job-priority",
2438
IPP_TAG_INTEGER)) != NULL)
2439
attr->values[0].integer = priority;
2441
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
2444
cupsArrayAdd(ActiveJobs, job);
2447
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2452
* 'cupsdSetJobState()' - Set the state of the specified print job.
2457
cupsd_job_t *job, /* I - Job to cancel */
2458
ipp_jstate_t newstate, /* I - New job state */
2459
cupsd_jobaction_t action, /* I - Action to take */
2460
const char *message, /* I - Message to log */
2461
...) /* I - Additional arguments as needed */
2463
int i; /* Looping var */
2464
ipp_jstate_t oldstate; /* Old state */
2465
char filename[1024]; /* Job filename */
2466
ipp_attribute_t *attr; /* Job attribute */
2469
cupsdLogMessage(CUPSD_LOG_DEBUG2,
2470
"cupsdSetJobState(job=%p(%d), state=%d, newstate=%d, "
2471
"action=%d, message=\"%s\")", job, job->id, job->state_value,
2472
newstate, action, message ? message : "(null)");
2476
* Make sure we have the job attributes...
2479
if (!cupsdLoadJob(job))
2483
* Don't do anything if the state is unchanged and we aren't purging the
2487
oldstate = job->state_value;
2488
if (newstate == oldstate && action != CUPSD_JOB_PURGE)
2492
* Stop any processes that are working on the current job...
2495
if (oldstate == IPP_JOB_PROCESSING)
2496
stop_job(job, action);
2499
* Set the new job state...
2502
job->state_value = newstate;
2505
job->state->values[0].integer = newstate;
2509
case IPP_JOB_PENDING :
2511
* Update job-hold-until as needed...
2514
if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
2515
IPP_TAG_KEYWORD)) == NULL)
2516
attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
2520
attr->value_tag = IPP_TAG_KEYWORD;
2521
cupsdSetString(&(attr->values[0].string.text), "no-hold");
2527
case IPP_JOB_ABORTED :
2528
case IPP_JOB_CANCELED :
2529
case IPP_JOB_COMPLETED :
2530
set_time(job, "time-at-completed");
2531
ippSetString(job->attrs, &job->reasons, 0, "processing-to-stop-point");
2536
* Log message as needed...
2541
char buffer[2048]; /* Message buffer */
2542
va_list ap; /* Pointer to additional arguments */
2544
va_start(ap, message);
2545
vsnprintf(buffer, sizeof(buffer), message, ap);
2548
if (newstate > IPP_JOB_STOPPED)
2549
cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, "%s", buffer);
2551
cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "%s", buffer);
2553
if (newstate == IPP_JOB_STOPPED || newstate == IPP_JOB_ABORTED)
2554
cupsdLogJob(job, CUPSD_LOG_ERROR, "%s", buffer);
2556
cupsdLogJob(job, CUPSD_LOG_INFO, "%s", buffer);
2560
* Handle post-state-change actions...
2565
case IPP_JOB_PROCESSING :
2567
* Add the job to the "printing" list...
2570
if (!cupsArrayFind(PrintingJobs, job))
2571
cupsArrayAdd(PrintingJobs, job);
2574
* Set the processing time...
2577
set_time(job, "time-at-processing");
2579
case IPP_JOB_PENDING :
2581
case IPP_JOB_STOPPED :
2583
* Make sure the job is in the active list...
2586
if (!cupsArrayFind(ActiveJobs, job))
2587
cupsArrayAdd(ActiveJobs, job);
2590
* Save the job state to disk...
2594
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2597
case IPP_JOB_ABORTED :
2598
case IPP_JOB_CANCELED :
2599
case IPP_JOB_COMPLETED :
2600
if (newstate == IPP_JOB_CANCELED)
2603
* Remove the job from the active list if there are no processes still
2607
for (i = 0; job->filters[i] < 0; i++);
2609
if (!job->filters[i] && job->backend <= 0)
2610
cupsArrayRemove(ActiveJobs, job);
2615
* Otherwise just remove the job from the active list immediately...
2618
cupsArrayRemove(ActiveJobs, job);
2622
* Expire job subscriptions since the job is now "completed"...
2625
cupsdExpireSubscriptions(NULL, job);
2629
* If we are going to sleep and the PrintingJobs count is now 0, allow the
2630
* sleep to happen immediately...
2633
if (Sleeping && cupsArrayCount(PrintingJobs) == 0)
2635
#endif /* __APPLE__ */
2638
* Remove any authentication data...
2641
snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
2642
if (cupsdRemoveFile(filename) && errno != ENOENT)
2643
cupsdLogMessage(CUPSD_LOG_ERROR,
2644
"Unable to remove authentication cache: %s",
2648
i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
2650
cupsdClearString(job->auth_env + i);
2652
cupsdClearString(&job->auth_uid);
2655
* Remove the print file for good if we aren't preserving jobs or
2659
if (!JobHistory || !JobFiles || action == CUPSD_JOB_PURGE)
2660
remove_job_files(job);
2662
if (JobHistory && action != CUPSD_JOB_PURGE)
2665
* Save job state info...
2669
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2671
else if (!job->printer)
2674
* Delete the job immediately if not actively printing...
2677
cupsdDeleteJob(job, CUPSD_JOB_PURGE);
2684
* Finalize the job immediately if we forced things...
2687
if (action >= CUPSD_JOB_FORCE && job && job->printer)
2688
finalize_job(job, 0);
2691
* Update the server "busy" state...
2694
cupsdSetBusyState();
2699
* 'cupsdStopAllJobs()' - Stop all print jobs.
2704
cupsd_jobaction_t action, /* I - Action */
2705
int kill_delay) /* I - Number of seconds before we kill */
2707
cupsd_job_t *job; /* Current job */
2710
DEBUG_puts("cupsdStopAllJobs()");
2712
for (job = (cupsd_job_t *)cupsArrayFirst(PrintingJobs);
2714
job = (cupsd_job_t *)cupsArrayNext(PrintingJobs))
2717
job->kill_time = time(NULL) + kill_delay;
2719
cupsdSetJobState(job, IPP_JOB_PENDING, action, NULL);
2725
* 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory.
2729
cupsdUnloadCompletedJobs(void)
2731
cupsd_job_t *job; /* Current job */
2732
time_t expire; /* Expiration time */
2735
expire = time(NULL) - 60;
2737
for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
2739
job = (cupsd_job_t *)cupsArrayNext(Jobs))
2740
if (job->attrs && job->state_value >= IPP_JOB_STOPPED && !job->printer &&
2741
job->access_time < expire)
2752
* 'cupsdUpdateJobs()' - Update the history/file files for all jobs.
2756
cupsdUpdateJobs(void)
2758
cupsd_job_t *job; /* Current job */
2759
time_t curtime; /* Current time */
2760
ipp_attribute_t *attr; /* time-at-completed attribute */
2763
curtime = time(NULL);
2764
JobHistoryUpdate = 0;
2766
for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
2768
job = (cupsd_job_t *)cupsArrayNext(Jobs))
2770
if (job->state_value >= IPP_JOB_CANCELED &&
2771
(attr = ippFindAttribute(job->attrs, "time-at-completed",
2772
IPP_TAG_INTEGER)) != NULL)
2775
* Update history/file expiration times...
2778
if (JobHistory < INT_MAX)
2779
job->history_time = attr->values[0].integer + JobHistory;
2781
job->history_time = INT_MAX;
2783
if (job->history_time < curtime)
2785
cupsdDeleteJob(job, CUPSD_JOB_PURGE);
2789
if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
2790
JobHistoryUpdate = job->history_time;
2792
if (JobFiles < INT_MAX)
2793
job->file_time = attr->values[0].integer + JobFiles;
2795
job->file_time = INT_MAX;
2797
if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate)
2798
JobHistoryUpdate = job->file_time;
2802
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdUpdateAllJobs: JobHistoryUpdate=%ld",
2803
(long)JobHistoryUpdate);
2808
* 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
2811
static int /* O - Difference */
2812
compare_active_jobs(void *first, /* I - First job */
2813
void *second, /* I - Second job */
2814
void *data) /* I - App data (not used) */
2816
int diff; /* Difference */
2821
if ((diff = ((cupsd_job_t *)second)->priority -
2822
((cupsd_job_t *)first)->priority) != 0)
2825
return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
2830
* 'compare_jobs()' - Compare the job IDs of two jobs.
2833
static int /* O - Difference */
2834
compare_jobs(void *first, /* I - First job */
2835
void *second, /* I - Second job */
2836
void *data) /* I - App data (not used) */
2840
return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
2845
* 'dump_job_history()' - Dump any debug messages for a job.
2849
dump_job_history(cupsd_job_t *job) /* I - Job */
2851
int i, /* Looping var */
2852
oldsize; /* Current MaxLogSize */
2853
struct tm *date; /* Date/time value */
2854
cupsd_joblog_t *message; /* Current message */
2855
char temp[2048], /* Log message */
2856
*ptr, /* Pointer into log message */
2857
start[256], /* Start time */
2858
end[256]; /* End time */
2859
cupsd_printer_t *printer; /* Printer for job */
2863
* See if we have anything to dump...
2870
* Disable log rotation temporarily...
2873
oldsize = MaxLogSize;
2877
* Copy the debug messages to the log...
2880
message = (cupsd_joblog_t *)cupsArrayFirst(job->history);
2881
date = localtime(&(message->time));
2882
strftime(start, sizeof(start), "%X", date);
2884
message = (cupsd_joblog_t *)cupsArrayLast(job->history);
2885
date = localtime(&(message->time));
2886
strftime(end, sizeof(end), "%X", date);
2888
snprintf(temp, sizeof(temp),
2889
"[Job %d] The following messages were recorded from %s to %s",
2890
job->id, start, end);
2891
cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2893
for (message = (cupsd_joblog_t *)cupsArrayFirst(job->history);
2895
message = (cupsd_joblog_t *)cupsArrayNext(job->history))
2896
cupsdWriteErrorLog(CUPSD_LOG_DEBUG, message->message);
2898
snprintf(temp, sizeof(temp), "[Job %d] End of messages", job->id);
2899
cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2902
* Log the printer state values...
2905
if ((printer = job->printer) == NULL)
2906
printer = cupsdFindDest(job->dest);
2910
snprintf(temp, sizeof(temp), "[Job %d] printer-state=%d(%s)", job->id,
2912
printer->state == IPP_PRINTER_IDLE ? "idle" :
2913
printer->state == IPP_PRINTER_PROCESSING ? "processing" :
2915
cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2917
snprintf(temp, sizeof(temp), "[Job %d] printer-state-message=\"%s\"",
2918
job->id, printer->state_message);
2919
cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2921
snprintf(temp, sizeof(temp), "[Job %d] printer-state-reasons=", job->id);
2922
ptr = temp + strlen(temp);
2923
if (printer->num_reasons == 0)
2924
strlcpy(ptr, "none", sizeof(temp) - (ptr - temp));
2928
i < printer->num_reasons && ptr < (temp + sizeof(temp) - 2);
2934
strlcpy(ptr, printer->reasons[i], sizeof(temp) - (ptr - temp));
2938
cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2942
* Restore log file rotation...
2945
MaxLogSize = oldsize;
2948
* Free all messages...
2951
free_job_history(job);
2956
* 'free_job_history()' - Free any log history.
2960
free_job_history(cupsd_job_t *job) /* I - Job */
2962
char *message; /* Current message */
2968
for (message = (char *)cupsArrayFirst(job->history);
2970
message = (char *)cupsArrayNext(job->history))
2973
cupsArrayDelete(job->history);
2974
job->history = NULL;
2979
* 'finalize_job()' - Cleanup after job filter processes and support data.
2983
finalize_job(cupsd_job_t *job, /* I - Job */
2984
int set_job_state) /* I - 1 = set the job state */
2986
ipp_pstate_t printer_state; /* New printer state value */
2987
ipp_jstate_t job_state; /* New job state value */
2988
const char *message; /* Message for job state */
2989
char buffer[1024]; /* Buffer for formatted messages */
2992
cupsdLogMessage(CUPSD_LOG_DEBUG2, "finalize_job(job=%p(%d))", job, job->id);
2995
* Clear the "connecting-to-device" reason, which is only valid when a printer
2996
* is processing, along with any remote printing job state...
2999
cupsdSetPrinterReasons(job->printer, "-connecting-to-device,"
3000
"cups-remote-pending,"
3001
"cups-remote-pending-held,"
3002
"cups-remote-processing,"
3003
"cups-remote-stopped,"
3004
"cups-remote-canceled,"
3005
"cups-remote-aborted,"
3006
"cups-remote-completed");
3009
* Similarly, clear the "offline-report" reason for non-USB devices since we
3010
* rarely have current information for network devices...
3013
if (strncmp(job->printer->device_uri, "usb:", 4) &&
3014
strncmp(job->printer->device_uri, "ippusb:", 7))
3015
cupsdSetPrinterReasons(job->printer, "-offline-report");
3018
* Free the security profile...
3021
cupsdDestroyProfile(job->profile);
3022
job->profile = NULL;
3025
* Clear the unresponsive job watchdog timers...
3028
job->cancel_time = 0;
3032
* Close pipes and status buffer...
3035
cupsdClosePipe(job->print_pipes);
3036
cupsdClosePipe(job->back_pipes);
3037
cupsdClosePipe(job->side_pipes);
3039
cupsdRemoveSelect(job->status_pipes[0]);
3040
cupsdClosePipe(job->status_pipes);
3041
cupsdStatBufDelete(job->status_buffer);
3042
job->status_buffer = NULL;
3045
* Process the exit status...
3048
if (job->printer->state == IPP_PRINTER_PROCESSING)
3049
printer_state = IPP_PRINTER_IDLE;
3051
printer_state = job->printer->state;
3053
switch (job_state = job->state_value)
3055
case IPP_JOB_PENDING :
3056
message = "Job paused.";
3060
message = "Job held.";
3064
case IPP_JOB_PROCESSING :
3065
case IPP_JOB_COMPLETED :
3066
job_state = IPP_JOB_COMPLETED;
3067
message = "Job completed.";
3070
ippSetString(job->attrs, &job->reasons, 0,
3071
"job-completed-successfully");
3074
case IPP_JOB_STOPPED :
3075
message = "Job stopped.";
3077
ippSetString(job->attrs, &job->reasons, 0, "job-stopped");
3080
case IPP_JOB_CANCELED :
3081
message = "Job canceled.";
3083
ippSetString(job->attrs, &job->reasons, 0, "job-canceled-by-user");
3086
case IPP_JOB_ABORTED :
3087
message = "Job aborted.";
3091
if (job->status < 0)
3094
* Backend had errors...
3097
int exit_code; /* Exit code from backend */
3100
* Convert the status to an exit code. Due to the way the W* macros are
3101
* implemented on MacOS X (bug?), we have to store the exit status in a
3102
* variable first and then convert...
3105
exit_code = -job->status;
3106
if (WIFEXITED(exit_code))
3107
exit_code = WEXITSTATUS(exit_code);
3110
ippSetString(job->attrs, &job->reasons, 0, "cups-backend-crashed");
3111
exit_code = job->status;
3114
cupsdLogJob(job, CUPSD_LOG_INFO, "Backend returned status %d (%s)",
3116
exit_code == CUPS_BACKEND_FAILED ? "failed" :
3117
exit_code == CUPS_BACKEND_AUTH_REQUIRED ?
3118
"authentication required" :
3119
exit_code == CUPS_BACKEND_HOLD ? "hold job" :
3120
exit_code == CUPS_BACKEND_STOP ? "stop printer" :
3121
exit_code == CUPS_BACKEND_CANCEL ? "cancel job" :
3122
exit_code == CUPS_BACKEND_RETRY ? "retry job later" :
3123
exit_code == CUPS_BACKEND_RETRY_CURRENT ? "retry job immediately" :
3124
exit_code < 0 ? "crashed" : "unknown");
3127
* Do what needs to be done...
3133
case CUPS_BACKEND_FAILED :
3135
* Backend failure, use the error-policy to determine how to
3139
if (job->dtype & CUPS_PRINTER_CLASS)
3142
* Queued on a class - mark the job as pending and we'll retry on
3143
* another printer...
3146
if (job_state == IPP_JOB_COMPLETED)
3148
job_state = IPP_JOB_PENDING;
3149
message = "Retrying job on another printer.";
3151
ippSetString(job->attrs, &job->reasons, 0,
3152
"resources-are-not-ready");
3155
else if (!strcmp(job->printer->error_policy, "retry-current-job"))
3158
* The error policy is "retry-current-job" - mark the job as pending
3159
* and we'll retry on the same printer...
3162
if (job_state == IPP_JOB_COMPLETED)
3164
job_state = IPP_JOB_PENDING;
3165
message = "Retrying job on same printer.";
3167
ippSetString(job->attrs, &job->reasons, 0, "none");
3170
else if ((job->printer->type & CUPS_PRINTER_FAX) ||
3171
!strcmp(job->printer->error_policy, "retry-job"))
3173
if (job_state == IPP_JOB_COMPLETED)
3176
* The job was queued on a fax or the error policy is "retry-job" -
3177
* hold the job if the number of retries is less than the
3178
* JobRetryLimit, otherwise abort the job.
3183
if (job->tries > JobRetryLimit && JobRetryLimit > 0)
3189
snprintf(buffer, sizeof(buffer),
3190
"Job aborted after %d unsuccessful attempts.",
3192
job_state = IPP_JOB_ABORTED;
3195
ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
3200
* Try again in N seconds...
3203
snprintf(buffer, sizeof(buffer),
3204
"Job held for %d seconds since it could not be sent.",
3207
job->hold_until = time(NULL) + JobRetryInterval;
3208
job_state = IPP_JOB_HELD;
3211
ippSetString(job->attrs, &job->reasons, 0,
3212
"resources-are-not-ready");
3216
else if (!strcmp(job->printer->error_policy, "abort-job") &&
3217
job_state == IPP_JOB_COMPLETED)
3219
job_state = IPP_JOB_ABORTED;
3220
message = "Job aborted due to backend errors; please consult "
3221
"the error_log file for details.";
3223
ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
3225
else if (job->state_value == IPP_JOB_PROCESSING)
3227
job_state = IPP_JOB_PENDING;
3228
printer_state = IPP_PRINTER_STOPPED;
3229
message = "Printer stopped due to backend errors; please "
3230
"consult the error_log file for details.";
3232
ippSetString(job->attrs, &job->reasons, 0, "none");
3236
case CUPS_BACKEND_CANCEL :
3241
if (job_state == IPP_JOB_COMPLETED)
3243
job_state = IPP_JOB_CANCELED;
3244
message = "Job canceled at printer.";
3246
ippSetString(job->attrs, &job->reasons, 0, "canceled-at-device");
3250
case CUPS_BACKEND_HOLD :
3251
if (job_state == IPP_JOB_COMPLETED)
3257
const char *reason = ippGetString(job->reasons, 0, NULL);
3259
cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-state-reasons=\"%s\"",
3262
if (!reason || strncmp(reason, "account-", 8))
3264
cupsdSetJobHoldUntil(job, "indefinite", 1);
3266
ippSetString(job->attrs, &job->reasons, 0,
3267
"job-hold-until-specified");
3268
message = "Job held indefinitely due to backend errors; please "
3269
"consult the error_log file for details.";
3271
else if (!strcmp(reason, "account-info-needed"))
3273
cupsdSetJobHoldUntil(job, "indefinite", 0);
3275
message = "Job held indefinitely - account information is "
3278
else if (!strcmp(reason, "account-closed"))
3280
cupsdSetJobHoldUntil(job, "indefinite", 0);
3282
message = "Job held indefinitely - account has been closed.";
3284
else if (!strcmp(reason, "account-limit-reached"))
3286
cupsdSetJobHoldUntil(job, "indefinite", 0);
3288
message = "Job held indefinitely - account limit has been "
3293
cupsdSetJobHoldUntil(job, "indefinite", 0);
3295
message = "Job held indefinitely - account authorization failed.";
3298
job_state = IPP_JOB_HELD;
3302
case CUPS_BACKEND_STOP :
3304
* Stop the printer...
3307
printer_state = IPP_PRINTER_STOPPED;
3308
message = "Printer stopped due to backend errors; please "
3309
"consult the error_log file for details.";
3311
if (job_state == IPP_JOB_COMPLETED)
3313
job_state = IPP_JOB_PENDING;
3315
ippSetString(job->attrs, &job->reasons, 0,
3316
"resources-are-not-ready");
3320
case CUPS_BACKEND_AUTH_REQUIRED :
3322
* Hold the job for authentication...
3325
if (job_state == IPP_JOB_COMPLETED)
3327
cupsdSetJobHoldUntil(job, "auth-info-required", 1);
3329
job_state = IPP_JOB_HELD;
3330
message = "Job held for authentication.";
3332
if (strncmp(job->reasons->values[0].string.text, "account-", 8))
3333
ippSetString(job->attrs, &job->reasons, 0,
3334
"cups-held-for-authentication");
3338
case CUPS_BACKEND_RETRY :
3339
if (job_state == IPP_JOB_COMPLETED)
3342
* Hold the job if the number of retries is less than the
3343
* JobRetryLimit, otherwise abort the job.
3348
if (job->tries > JobRetryLimit && JobRetryLimit > 0)
3354
snprintf(buffer, sizeof(buffer),
3355
"Job aborted after %d unsuccessful attempts.",
3357
job_state = IPP_JOB_ABORTED;
3360
ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
3365
* Try again in N seconds...
3368
snprintf(buffer, sizeof(buffer),
3369
"Job held for %d seconds since it could not be sent.",
3372
job->hold_until = time(NULL) + JobRetryInterval;
3373
job_state = IPP_JOB_HELD;
3376
ippSetString(job->attrs, &job->reasons, 0,
3377
"resources-are-not-ready");
3382
case CUPS_BACKEND_RETRY_CURRENT :
3384
* Mark the job as pending and retry on the same printer...
3387
if (job_state == IPP_JOB_COMPLETED)
3389
job_state = IPP_JOB_PENDING;
3390
message = "Retrying job on same printer.";
3392
ippSetString(job->attrs, &job->reasons, 0, "none");
3397
else if (job->status > 0)
3400
* Filter had errors; stop job...
3403
if (job_state == IPP_JOB_COMPLETED)
3405
job_state = IPP_JOB_STOPPED;
3406
message = "Job stopped due to filter errors; please consult the "
3407
"error_log file for details.";
3409
if (WIFSIGNALED(job->status))
3410
ippSetString(job->attrs, &job->reasons, 0, "cups-filter-crashed");
3412
ippSetString(job->attrs, &job->reasons, 0, "job-completed-with-errors");
3417
* Update the printer and job state.
3420
if (set_job_state && job_state != job->state_value)
3421
cupsdSetJobState(job, job_state, CUPSD_JOB_DEFAULT, "%s", message);
3423
cupsdSetPrinterState(job->printer, printer_state,
3424
printer_state == IPP_PRINTER_STOPPED);
3425
update_job_attrs(job, 0);
3430
(job->state_value == IPP_JOB_ABORTED ||
3431
job->state_value == IPP_JOB_STOPPED))
3432
dump_job_history(job);
3434
free_job_history(job);
3437
cupsArrayRemove(PrintingJobs, job);
3440
* Apply any PPD updates...
3443
if (job->num_keywords)
3445
if (cupsdUpdatePrinterPPD(job->printer, job->num_keywords, job->keywords))
3446
cupsdSetPrinterAttrs(job->printer);
3448
cupsFreeOptions(job->num_keywords, job->keywords);
3450
job->num_keywords = 0;
3451
job->keywords = NULL;
3455
* Clear the printer <-> job association...
3458
job->printer->job = NULL;
3459
job->printer = NULL;
3462
* Try printing another job...
3465
if (printer_state != IPP_PRINTER_STOPPED)
3471
* 'get_options()' - Get a string containing the job options.
3474
static char * /* O - Options string */
3475
get_options(cupsd_job_t *job, /* I - Job */
3476
int banner_page, /* I - Printing a banner page? */
3477
char *copies, /* I - Copies buffer */
3478
size_t copies_size, /* I - Size of copies buffer */
3479
char *title, /* I - Title buffer */
3480
size_t title_size) /* I - Size of title buffer */
3482
int i; /* Looping var */
3483
size_t newlength; /* New option buffer length */
3484
char *optptr, /* Pointer to options */
3485
*valptr; /* Pointer in value string */
3486
ipp_attribute_t *attr; /* Current attribute */
3487
_ppd_cache_t *pc; /* PPD cache and mapping data */
3488
int num_pwgppds; /* Number of PWG->PPD options */
3489
cups_option_t *pwgppds, /* PWG->PPD options */
3490
*pwgppd, /* Current PWG->PPD option */
3491
*preset; /* Current preset option */
3492
int print_color_mode,
3493
/* Output mode (if any) */
3494
print_quality; /* Print quality (if any) */
3495
const char *ppd; /* PPD option choice */
3496
int exact; /* Did we get an exact match? */
3497
static char *options = NULL;/* Full list of options */
3498
static size_t optlength = 0; /* Length of option buffer */
3502
* Building the options string is harder than it needs to be, but for the
3503
* moment we need to pass strings for command-line args and not IPP attribute
3506
* First build an options array for any PWG->PPD mapped option/choice pairs.
3509
pc = job->printer->pc;
3514
!ippFindAttribute(job->attrs,
3515
"com.apple.print.DocumentTicket.PMSpoolFormat",
3517
!ippFindAttribute(job->attrs, "APPrinterPreset", IPP_TAG_ZERO) &&
3518
(ippFindAttribute(job->attrs, "print-color-mode", IPP_TAG_ZERO) ||
3519
ippFindAttribute(job->attrs, "print-quality", IPP_TAG_ZERO)))
3522
* Map print-color-mode and print-quality to a preset...
3525
if ((attr = ippFindAttribute(job->attrs, "print-color-mode",
3526
IPP_TAG_KEYWORD)) != NULL &&
3527
!strcmp(attr->values[0].string.text, "monochrome"))
3528
print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
3530
print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
3532
if ((attr = ippFindAttribute(job->attrs, "print-quality",
3533
IPP_TAG_ENUM)) != NULL &&
3534
attr->values[0].integer >= IPP_QUALITY_DRAFT &&
3535
attr->values[0].integer <= IPP_QUALITY_HIGH)
3536
print_quality = attr->values[0].integer - IPP_QUALITY_DRAFT;
3538
print_quality = _PWG_PRINT_QUALITY_NORMAL;
3540
if (pc->num_presets[print_color_mode][print_quality] == 0)
3543
* Try to find a preset that works so that we maximize the chances of us
3544
* getting a good print using IPP attributes.
3547
if (pc->num_presets[print_color_mode][_PWG_PRINT_QUALITY_NORMAL] > 0)
3548
print_quality = _PWG_PRINT_QUALITY_NORMAL;
3549
else if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][print_quality] > 0)
3550
print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
3553
print_quality = _PWG_PRINT_QUALITY_NORMAL;
3554
print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
3558
if (pc->num_presets[print_color_mode][print_quality] > 0)
3561
* Copy the preset options as long as the corresponding names are not
3562
* already defined in the IPP request...
3565
for (i = pc->num_presets[print_color_mode][print_quality],
3566
preset = pc->presets[print_color_mode][print_quality];
3570
if (!ippFindAttribute(job->attrs, preset->name, IPP_TAG_ZERO))
3571
num_pwgppds = cupsAddOption(preset->name, preset->value, num_pwgppds,
3579
if (!ippFindAttribute(job->attrs, "InputSlot", IPP_TAG_ZERO) &&
3580
!ippFindAttribute(job->attrs, "HPPaperSource", IPP_TAG_ZERO))
3582
if ((ppd = _ppdCacheGetInputSlot(pc, job->attrs, NULL)) != NULL)
3583
num_pwgppds = cupsAddOption(pc->source_option, ppd, num_pwgppds,
3586
if (!ippFindAttribute(job->attrs, "MediaType", IPP_TAG_ZERO) &&
3587
(ppd = _ppdCacheGetMediaType(pc, job->attrs, NULL)) != NULL)
3588
num_pwgppds = cupsAddOption("MediaType", ppd, num_pwgppds, &pwgppds);
3590
if (!ippFindAttribute(job->attrs, "PageRegion", IPP_TAG_ZERO) &&
3591
!ippFindAttribute(job->attrs, "PageSize", IPP_TAG_ZERO) &&
3592
(ppd = _ppdCacheGetPageSize(pc, job->attrs, NULL, &exact)) != NULL)
3594
num_pwgppds = cupsAddOption("PageSize", ppd, num_pwgppds, &pwgppds);
3596
if (!ippFindAttribute(job->attrs, "media", IPP_TAG_ZERO))
3597
num_pwgppds = cupsAddOption("media", ppd, num_pwgppds, &pwgppds);
3600
if (!ippFindAttribute(job->attrs, "OutputBin", IPP_TAG_ZERO) &&
3601
(attr = ippFindAttribute(job->attrs, "output-bin",
3602
IPP_TAG_ZERO)) != NULL &&
3603
(attr->value_tag == IPP_TAG_KEYWORD ||
3604
attr->value_tag == IPP_TAG_NAME) &&
3605
(ppd = _ppdCacheGetOutputBin(pc, attr->values[0].string.text)) != NULL)
3608
* Map output-bin to OutputBin option...
3611
num_pwgppds = cupsAddOption("OutputBin", ppd, num_pwgppds, &pwgppds);
3614
if (pc->sides_option &&
3615
!ippFindAttribute(job->attrs, pc->sides_option, IPP_TAG_ZERO) &&
3616
(attr = ippFindAttribute(job->attrs, "sides", IPP_TAG_KEYWORD)) != NULL)
3619
* Map sides to duplex option...
3622
if (!strcmp(attr->values[0].string.text, "one-sided"))
3623
num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_1sided,
3624
num_pwgppds, &pwgppds);
3625
else if (!strcmp(attr->values[0].string.text, "two-sided-long-edge"))
3626
num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_long,
3627
num_pwgppds, &pwgppds);
3628
else if (!strcmp(attr->values[0].string.text, "two-sided-short-edge"))
3629
num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_short,
3630
num_pwgppds, &pwgppds);
3634
* Map finishings values...
3637
num_pwgppds = _ppdCacheGetFinishingOptions(pc, job->attrs,
3638
IPP_FINISHINGS_NONE, num_pwgppds,
3643
* Figure out how much room we need...
3646
newlength = ipp_length(job->attrs);
3648
for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++)
3649
newlength += 1 + strlen(pwgppd->name) + 1 + strlen(pwgppd->value);
3652
* Then allocate/reallocate the option buffer as needed...
3655
if (newlength == 0) /* This can never happen, but Clang */
3656
newlength = 1; /* thinks it can... */
3658
if (newlength > optlength || !options)
3661
optptr = malloc(newlength);
3663
optptr = realloc(options, newlength);
3667
cupsdLogJob(job, CUPSD_LOG_CRIT,
3668
"Unable to allocate " CUPS_LLFMT " bytes for option buffer.",
3669
CUPS_LLCAST newlength);
3674
optlength = newlength;
3678
* Now loop through the attributes and convert them to the textual
3679
* representation used by the filters...
3685
snprintf(title, title_size, "%s-%d", job->printer->name, job->id);
3686
strlcpy(copies, "1", copies_size);
3688
for (attr = job->attrs->attrs; attr != NULL; attr = attr->next)
3690
if (!strcmp(attr->name, "copies") &&
3691
attr->value_tag == IPP_TAG_INTEGER)
3694
* Don't use the # copies attribute if we are printing the job sheets...
3698
snprintf(copies, copies_size, "%d", attr->values[0].integer);
3700
else if (!strcmp(attr->name, "job-name") &&
3701
(attr->value_tag == IPP_TAG_NAME ||
3702
attr->value_tag == IPP_TAG_NAMELANG))
3703
strlcpy(title, attr->values[0].string.text, title_size);
3704
else if (attr->group_tag == IPP_TAG_JOB)
3707
* Filter out other unwanted attributes...
3710
if (attr->value_tag == IPP_TAG_NOVALUE ||
3711
attr->value_tag == IPP_TAG_MIMETYPE ||
3712
attr->value_tag == IPP_TAG_NAMELANG ||
3713
attr->value_tag == IPP_TAG_TEXTLANG ||
3714
(attr->value_tag == IPP_TAG_URI && strcmp(attr->name, "job-uuid") &&
3715
strcmp(attr->name, "job-authorization-uri")) ||
3716
attr->value_tag == IPP_TAG_URISCHEME ||
3717
attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */
3720
if (!strcmp(attr->name, "job-hold-until") ||
3721
!strcmp(attr->name, "job-id") ||
3722
!strcmp(attr->name, "job-k-octets") ||
3723
!strcmp(attr->name, "job-media-sheets") ||
3724
!strcmp(attr->name, "job-media-sheets-completed") ||
3725
!strcmp(attr->name, "job-state") ||
3726
!strcmp(attr->name, "job-state-reasons"))
3729
if (!strncmp(attr->name, "job-", 4) &&
3730
strcmp(attr->name, "job-account-id") &&
3731
strcmp(attr->name, "job-accounting-user-id") &&
3732
strcmp(attr->name, "job-authorization-uri") &&
3733
strcmp(attr->name, "job-billing") &&
3734
strcmp(attr->name, "job-impressions") &&
3735
strcmp(attr->name, "job-originating-host-name") &&
3736
strcmp(attr->name, "job-password") &&
3737
strcmp(attr->name, "job-password-encryption") &&
3738
strcmp(attr->name, "job-uuid") &&
3739
!(job->printer->type & CUPS_PRINTER_REMOTE))
3742
if ((!strcmp(attr->name, "job-impressions") ||
3743
!strcmp(attr->name, "page-label") ||
3744
!strcmp(attr->name, "page-border") ||
3745
!strncmp(attr->name, "number-up", 9) ||
3746
!strcmp(attr->name, "page-ranges") ||
3747
!strcmp(attr->name, "page-set") ||
3748
!_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
3749
!_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed") ||
3750
!_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings."
3751
"PMTotalSidesImaged..n.") ||
3752
!_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings."
3753
"PMTotalBeginPages..n.")) &&
3758
* Otherwise add them to the list...
3761
if (optptr > options)
3762
strlcat(optptr, " ", optlength - (optptr - options));
3764
if (attr->value_tag != IPP_TAG_BOOLEAN)
3766
strlcat(optptr, attr->name, optlength - (optptr - options));
3767
strlcat(optptr, "=", optlength - (optptr - options));
3770
for (i = 0; i < attr->num_values; i ++)
3773
strlcat(optptr, ",", optlength - (optptr - options));
3775
optptr += strlen(optptr);
3777
switch (attr->value_tag)
3779
case IPP_TAG_INTEGER :
3781
snprintf(optptr, optlength - (optptr - options),
3782
"%d", attr->values[i].integer);
3785
case IPP_TAG_BOOLEAN :
3786
if (!attr->values[i].boolean)
3787
strlcat(optptr, "no", optlength - (optptr - options));
3789
strlcat(optptr, attr->name,
3790
optlength - (optptr - options));
3793
case IPP_TAG_RANGE :
3794
if (attr->values[i].range.lower == attr->values[i].range.upper)
3795
snprintf(optptr, optlength - (optptr - options) - 1,
3796
"%d", attr->values[i].range.lower);
3798
snprintf(optptr, optlength - (optptr - options) - 1,
3799
"%d-%d", attr->values[i].range.lower,
3800
attr->values[i].range.upper);
3803
case IPP_TAG_RESOLUTION :
3804
snprintf(optptr, optlength - (optptr - options) - 1,
3805
"%dx%d%s", attr->values[i].resolution.xres,
3806
attr->values[i].resolution.yres,
3807
attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3811
case IPP_TAG_STRING :
3814
case IPP_TAG_KEYWORD :
3815
case IPP_TAG_CHARSET :
3816
case IPP_TAG_LANGUAGE :
3818
for (valptr = attr->values[i].string.text; *valptr;)
3820
if (strchr(" \t\n\\\'\"", *valptr))
3822
*optptr++ = *valptr++;
3829
break; /* anti-compiler-warning-code */
3833
optptr += strlen(optptr);
3838
* Finally loop through the PWG->PPD mapped options and add them...
3841
for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++)
3844
strlcpy(optptr, pwgppd->name, optlength - (optptr - options));
3845
optptr += strlen(optptr);
3847
strlcpy(optptr, pwgppd->value, optlength - (optptr - options));
3848
optptr += strlen(optptr);
3851
cupsFreeOptions(num_pwgppds, pwgppds);
3854
* Return the options string...
3862
* 'ipp_length()' - Compute the size of the buffer needed to hold
3863
* the textual IPP attributes.
3866
static size_t /* O - Size of attribute buffer */
3867
ipp_length(ipp_t *ipp) /* I - IPP request */
3869
size_t bytes; /* Number of bytes */
3870
int i; /* Looping var */
3871
ipp_attribute_t *attr; /* Current attribute */
3875
* Loop through all attributes...
3880
for (attr = ipp->attrs; attr != NULL; attr = attr->next)
3883
* Skip attributes that won't be sent to filters...
3886
if (attr->value_tag == IPP_TAG_NOVALUE ||
3887
attr->value_tag == IPP_TAG_MIMETYPE ||
3888
attr->value_tag == IPP_TAG_NAMELANG ||
3889
attr->value_tag == IPP_TAG_TEXTLANG ||
3890
attr->value_tag == IPP_TAG_URI ||
3891
attr->value_tag == IPP_TAG_URISCHEME)
3895
* Add space for a leading space and commas between each value.
3896
* For the first attribute, the leading space isn't used, so the
3897
* extra byte can be used as the nul terminator...
3900
bytes ++; /* " " separator */
3901
bytes += attr->num_values; /* "," separators */
3904
* Boolean attributes appear as "foo,nofoo,foo,nofoo", while
3905
* other attributes appear as "foo=value1,value2,...,valueN".
3908
if (attr->value_tag != IPP_TAG_BOOLEAN)
3909
bytes += strlen(attr->name);
3911
bytes += attr->num_values * strlen(attr->name);
3914
* Now add the size required for each value in the attribute...
3917
switch (attr->value_tag)
3919
case IPP_TAG_INTEGER :
3922
* Minimum value of a signed integer is -2147483647, or 11 digits.
3925
bytes += attr->num_values * 11;
3928
case IPP_TAG_BOOLEAN :
3930
* Add two bytes for each false ("no") value...
3933
for (i = 0; i < attr->num_values; i ++)
3934
if (!attr->values[i].boolean)
3938
case IPP_TAG_RANGE :
3940
* A range is two signed integers separated by a hyphen, or
3941
* 23 characters max.
3944
bytes += attr->num_values * 23;
3947
case IPP_TAG_RESOLUTION :
3949
* A resolution is two signed integers separated by an "x" and
3950
* suffixed by the units, or 26 characters max.
3953
bytes += attr->num_values * 26;
3956
case IPP_TAG_STRING :
3959
case IPP_TAG_KEYWORD :
3960
case IPP_TAG_CHARSET :
3961
case IPP_TAG_LANGUAGE :
3964
* Strings can contain characters that need quoting. We need
3965
* at least 2 * len + 2 characters to cover the quotes and
3966
* any backslashes in the string.
3969
for (i = 0; i < attr->num_values; i ++)
3970
bytes += 2 * strlen(attr->values[i].string.text) + 2;
3974
break; /* anti-compiler-warning-code */
3983
* 'load_job_cache()' - Load jobs from the job.cache file.
3987
load_job_cache(const char *filename) /* I - job.cache filename */
3989
cups_file_t *fp; /* job.cache file */
3990
char line[1024], /* Line buffer */
3991
*value; /* Value on line */
3992
int linenum; /* Line number in file */
3993
cupsd_job_t *job; /* Current job */
3994
int jobid; /* Job ID */
3995
char jobfile[1024]; /* Job filename */
3999
* Open the job.cache file...
4002
if ((fp = cupsdOpenConfFile(filename)) == NULL)
4004
load_request_root();
4009
* Read entries from the job cache file and create jobs as needed.
4012
cupsdLogMessage(CUPSD_LOG_INFO, "Loading job cache file \"%s\"...",
4018
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
4020
if (!_cups_strcasecmp(line, "NextJobId"))
4023
NextJobId = atoi(value);
4025
else if (!_cups_strcasecmp(line, "<Job"))
4029
cupsdLogMessage(CUPSD_LOG_ERROR, "Missing </Job> directive on line %d.",
4036
cupsdLogMessage(CUPSD_LOG_ERROR, "Missing job ID on line %d.", linenum);
4040
jobid = atoi(value);
4044
cupsdLogMessage(CUPSD_LOG_ERROR, "Bad job ID %d on line %d.", jobid,
4049
snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, jobid);
4050
if (access(jobfile, 0))
4052
snprintf(jobfile, sizeof(jobfile), "%s/c%05d.N", RequestRoot, jobid);
4053
if (access(jobfile, 0))
4055
cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Files have gone away.",
4061
job = calloc(1, sizeof(cupsd_job_t));
4064
cupsdLogMessage(CUPSD_LOG_EMERG,
4065
"[Job %d] Unable to allocate memory for job.", jobid);
4070
job->back_pipes[0] = -1;
4071
job->back_pipes[1] = -1;
4072
job->print_pipes[0] = -1;
4073
job->print_pipes[1] = -1;
4074
job->side_pipes[0] = -1;
4075
job->side_pipes[1] = -1;
4076
job->status_pipes[0] = -1;
4077
job->status_pipes[1] = -1;
4079
cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading from cache...",
4084
cupsdLogMessage(CUPSD_LOG_ERROR,
4085
"Missing <Job #> directive on line %d.", linenum);
4088
else if (!_cups_strcasecmp(line, "</Job>"))
4090
cupsArrayAdd(Jobs, job);
4092
if (job->state_value <= IPP_JOB_STOPPED && cupsdLoadJob(job))
4093
cupsArrayAdd(ActiveJobs, job);
4099
cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d.", linenum);
4102
else if (!_cups_strcasecmp(line, "State"))
4104
job->state_value = (ipp_jstate_t)atoi(value);
4106
if (job->state_value < IPP_JOB_PENDING)
4107
job->state_value = IPP_JOB_PENDING;
4108
else if (job->state_value > IPP_JOB_COMPLETED)
4109
job->state_value = IPP_JOB_COMPLETED;
4111
else if (!_cups_strcasecmp(line, "HoldUntil"))
4113
job->hold_until = atoi(value);
4115
else if (!_cups_strcasecmp(line, "Priority"))
4117
job->priority = atoi(value);
4119
else if (!_cups_strcasecmp(line, "Username"))
4121
cupsdSetString(&job->username, value);
4123
else if (!_cups_strcasecmp(line, "Destination"))
4125
cupsdSetString(&job->dest, value);
4127
else if (!_cups_strcasecmp(line, "DestType"))
4129
job->dtype = (cups_ptype_t)atoi(value);
4131
else if (!_cups_strcasecmp(line, "NumFiles"))
4133
job->num_files = atoi(value);
4135
if (job->num_files < 0)
4137
cupsdLogMessage(CUPSD_LOG_ERROR, "Bad NumFiles value %d on line %d.",
4138
job->num_files, linenum);
4143
if (job->num_files > 0)
4145
snprintf(jobfile, sizeof(jobfile), "%s/d%05d-001", RequestRoot,
4147
if (access(jobfile, 0))
4149
cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Data files have gone away.",
4155
job->filetypes = calloc(job->num_files, sizeof(mime_type_t *));
4156
job->compressions = calloc(job->num_files, sizeof(int));
4158
if (!job->filetypes || !job->compressions)
4160
cupsdLogMessage(CUPSD_LOG_EMERG,
4161
"[Job %d] Unable to allocate memory for %d files.",
4162
job->id, job->num_files);
4167
else if (!_cups_strcasecmp(line, "File"))
4169
int number, /* File number */
4170
compression; /* Compression value */
4171
char super[MIME_MAX_SUPER], /* MIME super type */
4172
type[MIME_MAX_TYPE]; /* MIME type */
4175
if (sscanf(value, "%d%*[ \t]%15[^/]/%255s%d", &number, super, type,
4178
cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File on line %d.", linenum);
4182
if (number < 1 || number > job->num_files)
4184
cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File number %d on line %d.",
4191
job->compressions[number] = compression;
4192
job->filetypes[number] = mimeType(MimeDatabase, super, type);
4194
if (!job->filetypes[number])
4197
* If the original MIME type is unknown, auto-type it!
4200
cupsdLogMessage(CUPSD_LOG_ERROR,
4201
"[Job %d] Unknown MIME type %s/%s for file %d.",
4202
job->id, super, type, number + 1);
4204
snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
4205
job->id, number + 1);
4206
job->filetypes[number] = mimeFileType(MimeDatabase, jobfile, NULL,
4207
job->compressions + number);
4210
* If that didn't work, assume it is raw...
4213
if (!job->filetypes[number])
4214
job->filetypes[number] = mimeType(MimeDatabase, "application",
4219
cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown %s directive on line %d.",
4225
cupsdLogMessage(CUPSD_LOG_ERROR,
4226
"Missing </Job> directive on line %d.", linenum);
4227
cupsdDeleteJob(job, CUPSD_JOB_PURGE);
4235
* 'load_next_job_id()' - Load the NextJobId value from the job.cache file.
4239
load_next_job_id(const char *filename) /* I - job.cache filename */
4241
cups_file_t *fp; /* job.cache file */
4242
char line[1024], /* Line buffer */
4243
*value; /* Value on line */
4244
int linenum; /* Line number in file */
4245
int next_job_id; /* NextJobId value from line */
4249
* Read the NextJobId directive from the job.cache file and use
4250
* the value (if any).
4253
if ((fp = cupsFileOpen(filename, "r")) == NULL)
4255
if (errno != ENOENT)
4256
cupsdLogMessage(CUPSD_LOG_ERROR,
4257
"Unable to open job cache file \"%s\": %s",
4258
filename, strerror(errno));
4263
cupsdLogMessage(CUPSD_LOG_INFO,
4264
"Loading NextJobId from job cache file \"%s\"...", filename);
4268
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
4270
if (!_cups_strcasecmp(line, "NextJobId"))
4274
next_job_id = atoi(value);
4276
if (next_job_id > NextJobId)
4277
NextJobId = next_job_id;
4288
* 'load_request_root()' - Load jobs from the RequestRoot directory.
4292
load_request_root(void)
4294
cups_dir_t *dir; /* Directory */
4295
cups_dentry_t *dent; /* Directory entry */
4296
cupsd_job_t *job; /* New job */
4300
* Open the requests directory...
4303
cupsdLogMessage(CUPSD_LOG_DEBUG, "Scanning %s for jobs...", RequestRoot);
4305
if ((dir = cupsDirOpen(RequestRoot)) == NULL)
4307
cupsdLogMessage(CUPSD_LOG_ERROR,
4308
"Unable to open spool directory \"%s\": %s",
4309
RequestRoot, strerror(errno));
4314
* Read all the c##### files...
4317
while ((dent = cupsDirRead(dir)) != NULL)
4318
if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c')
4321
* Allocate memory for the job...
4324
if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
4326
cupsdLogMessage(CUPSD_LOG_ERROR, "Ran out of memory for jobs.");
4332
* Assign the job ID...
4335
job->id = atoi(dent->filename + 1);
4336
job->back_pipes[0] = -1;
4337
job->back_pipes[1] = -1;
4338
job->print_pipes[0] = -1;
4339
job->print_pipes[1] = -1;
4340
job->side_pipes[0] = -1;
4341
job->side_pipes[1] = -1;
4342
job->status_pipes[0] = -1;
4343
job->status_pipes[1] = -1;
4345
if (job->id >= NextJobId)
4346
NextJobId = job->id + 1;
4352
if (cupsdLoadJob(job))
4355
* Insert the job into the array, sorting by job priority and ID...
4358
cupsArrayAdd(Jobs, job);
4360
if (job->state_value <= IPP_JOB_STOPPED)
4361
cupsArrayAdd(ActiveJobs, job);
4374
* 'remove_job_files()' - Remove the document files for a job.
4378
remove_job_files(cupsd_job_t *job) /* I - Job */
4380
int i; /* Looping var */
4381
char filename[1024]; /* Document filename */
4384
if (job->num_files <= 0)
4387
for (i = 1; i <= job->num_files; i ++)
4389
snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
4391
cupsdUnlinkOrRemoveFile(filename);
4394
free(job->filetypes);
4395
free(job->compressions);
4399
job->filetypes = NULL;
4400
job->compressions = NULL;
4402
LastEvent |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
4407
* 'remove_job_history()' - Remove the control file for a job.
4411
remove_job_history(cupsd_job_t *job) /* I - Job */
4413
char filename[1024]; /* Control filename */
4417
* Remove the job info file...
4420
snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot,
4422
cupsdUnlinkOrRemoveFile(filename);
4424
LastEvent |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
4429
* 'set_time()' - Set one of the "time-at-xyz" attributes.
4433
set_time(cupsd_job_t *job, /* I - Job to update */
4434
const char *name) /* I - Name of attribute */
4436
ipp_attribute_t *attr; /* Time attribute */
4437
time_t curtime; /* Current time */
4440
curtime = time(NULL);
4442
cupsdLogJob(job, CUPSD_LOG_DEBUG, "%s=%ld", name, (long)curtime);
4444
if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL)
4446
attr->value_tag = IPP_TAG_INTEGER;
4447
attr->values[0].integer = curtime;
4450
if (!strcmp(name, "time-at-completed"))
4452
if (JobHistory < INT_MAX && attr)
4453
job->history_time = attr->values[0].integer + JobHistory;
4455
job->history_time = INT_MAX;
4457
if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
4458
JobHistoryUpdate = job->history_time;
4460
if (JobFiles < INT_MAX && attr)
4461
job->file_time = attr->values[0].integer + JobFiles;
4463
job->file_time = INT_MAX;
4465
if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate)
4466
JobHistoryUpdate = job->file_time;
4468
cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_time: JobHistoryUpdate=%ld",
4469
(long)JobHistoryUpdate);
4475
* 'start_job()' - Start a print job.
4479
start_job(cupsd_job_t *job, /* I - Job ID */
4480
cupsd_printer_t *printer) /* I - Printer to print job */
4482
const char *filename; /* Support filename */
4485
cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job(job=%p(%d), printer=%p(%s))",
4486
job, job->id, printer, printer->name);
4489
* Make sure we have some files around before we try to print...
4492
if (job->num_files == 0)
4494
ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
4495
cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_DEFAULT,
4496
"Aborting job because it has no files.");
4501
* Update the printer and job state to "processing"...
4504
if (!cupsdLoadJob(job))
4507
if (!job->printer_message)
4508
job->printer_message = ippFindAttribute(job->attrs,
4509
"job-printer-state-message",
4511
if (job->printer_message)
4512
cupsdSetString(&(job->printer_message->values[0].string.text), "");
4514
ippSetString(job->attrs, &job->reasons, 0, "job-printing");
4515
cupsdSetJobState(job, IPP_JOB_PROCESSING, CUPSD_JOB_DEFAULT, NULL);
4516
cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
4517
cupsdSetPrinterReasons(printer, "-cups-remote-pending,"
4518
"cups-remote-pending-held,"
4519
"cups-remote-processing,"
4520
"cups-remote-stopped,"
4521
"cups-remote-canceled,"
4522
"cups-remote-aborted,"
4523
"cups-remote-completed");
4526
job->current_file = 0;
4528
job->history_time = 0;
4530
job->printer = printer;
4534
job->cancel_time = time(NULL) + MaxJobTime;
4536
job->cancel_time = 0;
4539
* Check for support files...
4542
cupsdSetPrinterReasons(job->printer, "-cups-missing-filter-warning,"
4543
"cups-insecure-filter-warning");
4547
for (filename = (const char *)cupsArrayFirst(printer->pc->support_files);
4549
filename = (const char *)cupsArrayNext(printer->pc->support_files))
4551
if (_cupsFileCheck(filename, _CUPS_FILE_CHECK_FILE, !RunUser,
4552
cupsdLogFCMessage, printer))
4558
* Setup the last exit status and security profiles...
4562
job->profile = cupsdCreateProfile(job->id);
4565
* Create the status pipes and buffer...
4568
if (cupsdOpenPipe(job->status_pipes))
4570
cupsdLogJob(job, CUPSD_LOG_DEBUG,
4571
"Unable to create job status pipes - %s.", strerror(errno));
4573
cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
4574
"Job stopped because the scheduler could not create the "
4575
"job status pipes.");
4577
cupsdDestroyProfile(job->profile);
4578
job->profile = NULL;
4582
job->status_buffer = cupsdStatBufNew(job->status_pipes[0], NULL);
4583
job->status_level = CUPSD_LOG_INFO;
4586
* Create the backchannel pipes and make them non-blocking...
4589
if (cupsdOpenPipe(job->back_pipes))
4591
cupsdLogJob(job, CUPSD_LOG_DEBUG,
4592
"Unable to create back-channel pipes - %s.", strerror(errno));
4594
cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
4595
"Job stopped because the scheduler could not create the "
4596
"back-channel pipes.");
4598
cupsdClosePipe(job->status_pipes);
4599
cupsdStatBufDelete(job->status_buffer);
4600
job->status_buffer = NULL;
4602
cupsdDestroyProfile(job->profile);
4603
job->profile = NULL;
4607
fcntl(job->back_pipes[0], F_SETFL,
4608
fcntl(job->back_pipes[0], F_GETFL) | O_NONBLOCK);
4609
fcntl(job->back_pipes[1], F_SETFL,
4610
fcntl(job->back_pipes[1], F_GETFL) | O_NONBLOCK);
4613
* Create the side-channel pipes and make them non-blocking...
4616
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, job->side_pipes))
4618
cupsdLogJob(job, CUPSD_LOG_DEBUG,
4619
"Unable to create side-channel pipes - %s.", strerror(errno));
4621
cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
4622
"Job stopped because the scheduler could not create the "
4623
"side-channel pipes.");
4625
cupsdClosePipe(job->back_pipes);
4627
cupsdClosePipe(job->status_pipes);
4628
cupsdStatBufDelete(job->status_buffer);
4629
job->status_buffer = NULL;
4631
cupsdDestroyProfile(job->profile);
4632
job->profile = NULL;
4636
fcntl(job->side_pipes[0], F_SETFL,
4637
fcntl(job->side_pipes[0], F_GETFL) | O_NONBLOCK);
4638
fcntl(job->side_pipes[1], F_SETFL,
4639
fcntl(job->side_pipes[1], F_GETFL) | O_NONBLOCK);
4641
fcntl(job->side_pipes[0], F_SETFD,
4642
fcntl(job->side_pipes[0], F_GETFD) | FD_CLOEXEC);
4643
fcntl(job->side_pipes[1], F_SETFD,
4644
fcntl(job->side_pipes[1], F_GETFD) | FD_CLOEXEC);
4647
* Now start the first file in the job...
4650
cupsdContinueJob(job);
4655
* 'stop_job()' - Stop a print job.
4659
stop_job(cupsd_job_t *job, /* I - Job */
4660
cupsd_jobaction_t action) /* I - Action */
4662
int i; /* Looping var */
4665
cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_job(job=%p(%d), action=%d)", job,
4668
FilterLevel -= job->cost;
4671
if (action == CUPSD_JOB_DEFAULT && !job->kill_time)
4672
job->kill_time = time(NULL) + JobKillDelay;
4673
else if (action >= CUPSD_JOB_FORCE)
4676
for (i = 0; job->filters[i]; i ++)
4677
if (job->filters[i] > 0)
4679
cupsdEndProcess(job->filters[i], action >= CUPSD_JOB_FORCE);
4681
if (action >= CUPSD_JOB_FORCE)
4682
job->filters[i] = -job->filters[i];
4685
if (job->backend > 0)
4687
cupsdEndProcess(job->backend, action >= CUPSD_JOB_FORCE);
4689
if (action >= CUPSD_JOB_FORCE)
4690
job->backend = -job->backend;
4693
if (action >= CUPSD_JOB_FORCE)
4696
* Clear job status...
4705
* 'unload_job()' - Unload a job from memory.
4709
unload_job(cupsd_job_t *job) /* I - Job */
4714
cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Unloading...", job->id);
4716
ippDelete(job->attrs);
4720
job->reasons = NULL;
4722
job->job_sheets = NULL;
4723
job->printer_message = NULL;
4724
job->printer_reasons = NULL;
4729
* 'update_job()' - Read a status update from a job's filters.
4733
update_job(cupsd_job_t *job) /* I - Job to check */
4735
int i; /* Looping var */
4736
int copies; /* Number of copies printed */
4737
char message[CUPSD_SB_BUFFER_SIZE],
4739
*ptr; /* Pointer update... */
4740
int loglevel, /* Log level for message */
4741
event = 0; /* Events? */
4742
static const char * const levels[] = /* Log levels */
4758
* Get the printer associated with this job; if the printer is stopped for
4759
* any reason then job->printer will be reset to NULL, so make sure we have
4760
* a valid pointer...
4763
while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
4764
message, sizeof(message))) != NULL)
4767
* Process page and printer state messages as needed...
4770
if (loglevel == CUPSD_LOG_PAGE)
4773
* Page message; send the message to the page_log file and update the
4774
* job sheet count...
4777
cupsdLogJob(job, CUPSD_LOG_DEBUG, "PAGE: %s", message);
4781
if (!_cups_strncasecmp(message, "total ", 6))
4784
* Got a total count of pages from a backend or filter...
4787
copies = atoi(message + 6);
4788
copies -= job->sheets->values[0].integer; /* Just track the delta */
4790
else if (!sscanf(message, "%*d%d", &copies))
4793
job->sheets->values[0].integer += copies;
4795
if (job->printer->page_limit)
4796
cupsdUpdateQuota(job->printer, job->username, copies, 0);
4799
cupsdLogPage(job, message);
4802
cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
4803
"Printed %d page(s).", job->sheets->values[0].integer);
4805
else if (loglevel == CUPSD_LOG_JOBSTATE)
4808
* Support "keyword" to set job-state-reasons to the specified keyword.
4809
* This is sufficient for the current paid printing stuff.
4812
cupsdLogJob(job, CUPSD_LOG_DEBUG, "JOBSTATE: %s", message);
4814
ippSetString(job->attrs, &job->reasons, 0, message);
4816
else if (loglevel == CUPSD_LOG_STATE)
4818
cupsdLogJob(job, CUPSD_LOG_DEBUG, "STATE: %s", message);
4820
if (!strcmp(message, "paused"))
4822
cupsdStopPrinter(job->printer, 1);
4825
else if (message[0] && cupsdSetPrinterReasons(job->printer, message))
4827
event |= CUPSD_EVENT_PRINTER_STATE;
4829
if (MaxJobTime > 0 && strstr(message, "connecting-to-device") != NULL)
4832
* Reset cancel time after connecting to the device...
4835
for (i = 0; i < job->printer->num_reasons; i ++)
4836
if (!strcmp(job->printer->reasons[i], "connecting-to-device"))
4839
if (i >= job->printer->num_reasons)
4840
job->cancel_time = time(NULL) + MaxJobTime;
4844
update_job_attrs(job, 0);
4846
else if (loglevel == CUPSD_LOG_ATTR)
4849
* Set attribute(s)...
4852
int num_attrs; /* Number of attributes */
4853
cups_option_t *attrs; /* Attributes */
4854
const char *attr; /* Attribute */
4856
cupsdLogJob(job, CUPSD_LOG_DEBUG, "ATTR: %s", message);
4858
num_attrs = cupsParseOptions(message, 0, &attrs);
4860
if ((attr = cupsGetOption("auth-info-default", num_attrs,
4863
job->printer->num_options = cupsAddOption("auth-info", attr,
4864
job->printer->num_options,
4865
&(job->printer->options));
4866
cupsdSetPrinterAttrs(job->printer);
4868
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4871
if ((attr = cupsGetOption("auth-info-required", num_attrs,
4874
cupsdSetAuthInfoRequired(job->printer, attr, NULL);
4875
cupsdSetPrinterAttrs(job->printer);
4877
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4880
if ((attr = cupsGetOption("job-media-progress", num_attrs,
4883
int progress = atoi(attr);
4886
if (progress >= 0 && progress <= 100)
4888
job->progress = progress;
4891
cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
4892
"Printing page %d, %d%%",
4893
job->sheets->values[0].integer, job->progress);
4897
if ((attr = cupsGetOption("printer-alert", num_attrs, attrs)) != NULL)
4899
cupsdSetString(&job->printer->alert, attr);
4900
event |= CUPSD_EVENT_PRINTER_STATE;
4903
if ((attr = cupsGetOption("printer-alert-description", num_attrs,
4906
cupsdSetString(&job->printer->alert_description, attr);
4907
event |= CUPSD_EVENT_PRINTER_STATE;
4910
if ((attr = cupsGetOption("marker-colors", num_attrs, attrs)) != NULL)
4912
cupsdSetPrinterAttr(job->printer, "marker-colors", (char *)attr);
4913
job->printer->marker_time = time(NULL);
4914
event |= CUPSD_EVENT_PRINTER_STATE;
4915
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4918
if ((attr = cupsGetOption("marker-levels", num_attrs, attrs)) != NULL)
4920
cupsdSetPrinterAttr(job->printer, "marker-levels", (char *)attr);
4921
job->printer->marker_time = time(NULL);
4922
event |= CUPSD_EVENT_PRINTER_STATE;
4923
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4926
if ((attr = cupsGetOption("marker-low-levels", num_attrs, attrs)) != NULL)
4928
cupsdSetPrinterAttr(job->printer, "marker-low-levels", (char *)attr);
4929
job->printer->marker_time = time(NULL);
4930
event |= CUPSD_EVENT_PRINTER_STATE;
4931
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4934
if ((attr = cupsGetOption("marker-high-levels", num_attrs, attrs)) != NULL)
4936
cupsdSetPrinterAttr(job->printer, "marker-high-levels", (char *)attr);
4937
job->printer->marker_time = time(NULL);
4938
event |= CUPSD_EVENT_PRINTER_STATE;
4939
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4942
if ((attr = cupsGetOption("marker-message", num_attrs, attrs)) != NULL)
4944
cupsdSetPrinterAttr(job->printer, "marker-message", (char *)attr);
4945
job->printer->marker_time = time(NULL);
4946
event |= CUPSD_EVENT_PRINTER_STATE;
4947
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4950
if ((attr = cupsGetOption("marker-names", num_attrs, attrs)) != NULL)
4952
cupsdSetPrinterAttr(job->printer, "marker-names", (char *)attr);
4953
job->printer->marker_time = time(NULL);
4954
event |= CUPSD_EVENT_PRINTER_STATE;
4955
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4958
if ((attr = cupsGetOption("marker-types", num_attrs, attrs)) != NULL)
4960
cupsdSetPrinterAttr(job->printer, "marker-types", (char *)attr);
4961
job->printer->marker_time = time(NULL);
4962
event |= CUPSD_EVENT_PRINTER_STATE;
4963
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4966
cupsFreeOptions(num_attrs, attrs);
4968
else if (loglevel == CUPSD_LOG_PPD)
4971
* Set attribute(s)...
4974
cupsdLogJob(job, CUPSD_LOG_DEBUG, "PPD: %s", message);
4976
job->num_keywords = cupsParseOptions(message, job->num_keywords,
4982
* Strip legacy message prefix...
4985
if (!strncmp(message, "recoverable:", 12))
4988
while (isspace(*ptr & 255))
4991
else if (!strncmp(message, "recovered:", 10))
4994
while (isspace(*ptr & 255))
5001
cupsdLogJob(job, loglevel, "%s", ptr);
5003
if (loglevel < CUPSD_LOG_DEBUG &&
5004
strcmp(job->printer->state_message, ptr))
5006
strlcpy(job->printer->state_message, ptr,
5007
sizeof(job->printer->state_message));
5009
event |= CUPSD_EVENT_PRINTER_STATE | CUPSD_EVENT_JOB_PROGRESS;
5011
if (loglevel <= job->status_level && job->status_level > CUPSD_LOG_ERROR)
5014
* Some messages show in the job-printer-state-message attribute...
5017
if (loglevel != CUPSD_LOG_NOTICE)
5018
job->status_level = loglevel;
5020
update_job_attrs(job, 1);
5022
cupsdLogJob(job, CUPSD_LOG_DEBUG,
5023
"Set job-printer-state-message to \"%s\", "
5025
job->printer_message->values[0].string.text,
5026
levels[job->status_level]);
5031
if (!strchr(job->status_buffer->buffer, '\n'))
5035
if (event & CUPSD_EVENT_JOB_PROGRESS)
5036
cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
5037
"%s", job->printer->state_message);
5038
if (event & CUPSD_EVENT_PRINTER_STATE)
5039
cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, job->printer, NULL,
5040
(job->printer->type & CUPS_PRINTER_CLASS) ?
5041
"Class \"%s\" state changed." :
5042
"Printer \"%s\" state changed.",
5043
job->printer->name);
5046
if (ptr == NULL && !job->status_buffer->bufused)
5049
* See if all of the filters and the backend have returned their
5053
for (i = 0; job->filters[i] < 0; i ++);
5055
if (job->filters[i])
5058
* EOF but we haven't collected the exit status of all filters...
5061
cupsdCheckProcess();
5065
if (job->current_file >= job->num_files && job->backend > 0)
5068
* EOF but we haven't collected the exit status of the backend...
5071
cupsdCheckProcess();
5076
* Handle the end of job stuff...
5079
finalize_job(job, 1);
5082
* Check for new jobs...
5091
* 'update_job_attrs()' - Update the job-printer-* attributes.
5095
update_job_attrs(cupsd_job_t *job, /* I - Job to update */
5096
int do_message)/* I - 1 = copy job-printer-state message */
5098
int i; /* Looping var */
5099
int num_reasons; /* Actual number of reasons */
5100
const char * const *reasons; /* Reasons */
5101
static const char *none = "none"; /* "none" reason */
5105
* Get/create the job-printer-state-* attributes...
5108
if (!job->printer_message)
5110
if ((job->printer_message = ippFindAttribute(job->attrs,
5111
"job-printer-state-message",
5112
IPP_TAG_TEXT)) == NULL)
5113
job->printer_message = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_TEXT,
5114
"job-printer-state-message",
5118
if (!job->printer_reasons)
5119
job->printer_reasons = ippFindAttribute(job->attrs,
5120
"job-printer-state-reasons",
5124
* Copy or clear the printer-state-message value as needed...
5127
if (job->state_value != IPP_JOB_PROCESSING &&
5128
job->status_level == CUPSD_LOG_INFO)
5130
cupsdSetString(&(job->printer_message->values[0].string.text), "");
5133
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
5135
else if (job->printer->state_message[0] && do_message)
5137
cupsdSetString(&(job->printer_message->values[0].string.text),
5138
job->printer->state_message);
5141
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
5145
* ... and the printer-state-reasons value...
5148
if (job->printer->num_reasons == 0)
5155
num_reasons = job->printer->num_reasons;
5156
reasons = (const char * const *)job->printer->reasons;
5159
if (!job->printer_reasons || job->printer_reasons->num_values != num_reasons)
5162
* Replace/create a job-printer-state-reasons attribute...
5165
ippDeleteAttribute(job->attrs, job->printer_reasons);
5167
job->printer_reasons = ippAddStrings(job->attrs,
5168
IPP_TAG_JOB, IPP_TAG_KEYWORD,
5169
"job-printer-state-reasons",
5170
num_reasons, NULL, NULL);
5175
* Don't bother clearing the reason strings if they are the same...
5178
for (i = 0; i < num_reasons; i ++)
5179
if (strcmp(job->printer_reasons->values[i].string.text, reasons[i]))
5182
if (i >= num_reasons)
5186
* Not the same, so free the current strings...
5189
for (i = 0; i < num_reasons; i ++)
5190
_cupsStrFree(job->printer_reasons->values[i].string.text);
5194
* Copy the reasons...
5197
for (i = 0; i < num_reasons; i ++)
5198
job->printer_reasons->values[i].string.text = _cupsStrAlloc(reasons[i]);
5201
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
5206
* End of "$Id: job.c 11147 2013-07-17 02:54:31Z msweet $".