]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/job.c
Merge changes from CUPS 1.5svn-r9717.
[thirdparty/cups.git] / scheduler / job.c
CommitLineData
ef416fc2 1/*
b19ccc9e 2 * "$Id: job.c 7902 2008-09-03 14:20:17Z mike $"
ef416fc2 3 *
7cf5915e 4 * Job management routines for the CUPS scheduler.
ef416fc2 5 *
0268488e 6 * Copyright 2007-2011 by Apple Inc.
f7deaa1a 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
ef416fc2 14 *
15 * Contents:
16 *
b9faaae1 17 * cupsdAddJob() - Add a new job to the job queue.
bd7854cb 18 * cupsdCancelJobs() - Cancel all jobs for the given
b9faaae1 19 * destination/user.
ed6e7faf
MS
20 * cupsdCheckJobs() - Check the pending jobs and start any if the
21 * destination is available.
ef416fc2 22 * cupsdCleanJobs() - Clean out old jobs.
b9faaae1 23 * cupsdContinueJob() - Continue printing with the next file in a job.
3dfe78b3 24 * cupsdDeleteJob() - Free all memory used by a job.
ef416fc2 25 * cupsdFreeAllJobs() - Free all jobs from memory.
26 * cupsdFindJob() - Find the specified job.
ed6e7faf
MS
27 * cupsdGetPrinterJobCount() - Get the number of pending, processing, or held
28 * jobs in a printer or class.
29 * cupsdGetUserJobCount() - Get the number of pending, processing, or held
30 * jobs for a user.
ef416fc2 31 * cupsdLoadAllJobs() - Load all jobs from disk.
b9faaae1 32 * cupsdLoadJob() - Load a single job.
ef416fc2 33 * cupsdMoveJob() - Move the specified job to a different
34 * destination.
35 * cupsdReleaseJob() - Release the specified job.
36 * cupsdRestartJob() - Restart the specified job.
bd7854cb 37 * cupsdSaveAllJobs() - Save a summary of all jobs to disk.
ef416fc2 38 * cupsdSaveJob() - Save a job to disk.
b9faaae1 39 * cupsdSetJobHoldUntil() - Set the hold time for a job.
ed6e7faf
MS
40 * cupsdSetJobPriority() - Set the priority of a job, moving it up/down
41 * in the list as needed.
b9faaae1 42 * cupsdSetJobState() - Set the state of the specified print job.
ef416fc2 43 * cupsdStopAllJobs() - Stop all print jobs.
bd7854cb 44 * cupsdUnloadCompletedJobs() - Flush completed job history from memory.
bd7854cb 45 * compare_active_jobs() - Compare the job IDs and priorities of two
46 * jobs.
ef416fc2 47 * compare_jobs() - Compare the job IDs of two jobs.
178cb736
MS
48 * dump_job_history() - Dump any debug messages for a job.
49 * free_job_history() - Free any log history.
b9faaae1
MS
50 * finalize_job() - Cleanup after job filter processes and support
51 * data.
52 * get_options() - Get a string containing the job options.
ed6e7faf
MS
53 * ipp_length() - Compute the size of the buffer needed to hold
54 * the textual IPP attributes.
bd7854cb 55 * load_job_cache() - Load jobs from the job.cache file.
ed6e7faf
MS
56 * load_next_job_id() - Load the NextJobId value from the job.cache
57 * file.
bd7854cb 58 * load_request_root() - Load jobs from the RequestRoot directory.
b9faaae1 59 * set_time() - Set one of the "time-at-xyz" attributes.
e1d6a774 60 * start_job() - Start a print job.
b9faaae1 61 * stop_job() - Stop a print job.
e1d6a774 62 * unload_job() - Unload a job from memory.
ed6e7faf 63 * update_job() - Read a status update from a job's filters.
bc44d920 64 * update_job_attrs() - Update the job-printer-* attributes.
ef416fc2 65 */
66
67/*
68 * Include necessary headers...
69 */
70
71#include "cupsd.h"
72#include <grp.h>
73#include <cups/backend.h>
74#include <cups/dir.h>
75
76
b9faaae1
MS
77/*
78 * Design Notes for Job Management
79 * -------------------------------
80 *
81 * STATE CHANGES
82 *
83 * pending Do nothing/check jobs
84 * pending-held Send SIGTERM to filters and backend
85 * processing Do nothing/start job
86 * stopped Send SIGKILL to filters and backend
87 * canceled Send SIGTERM to filters and backend
88 * aborted Finalize
89 * completed Finalize
90 *
91 * Finalize clears the printer <-> job association, deletes the status
92 * buffer, closes all of the pipes, etc. and doesn't get run until all of
93 * the print processes are finished.
94 *
95 * UNLOADING OF JOBS (cupsdUnloadCompletedJobs)
96 *
97 * We unload the job attributes when they are not needed to reduce overall
98 * memory consumption. We don't unload jobs where job->state_value <
99 * IPP_JOB_STOPPED, job->printer != NULL, or job->access_time is recent.
100 *
101 * STARTING OF JOBS (start_job)
102 *
103 * When a job is started, a status buffer, several pipes, a security
104 * profile, and a backend process are created for the life of that job.
105 * These are shared for every file in a job. For remote print jobs, the
106 * IPP backend is provided with every file in the job and no filters are
107 * run.
108 *
109 * The job->printer member tracks which printer is printing a job, which
110 * can be different than the destination in job->dest for classes. The
111 * printer object also has a job pointer to track which job is being
112 * printed.
113 *
114 * PRINTING OF JOB FILES (cupsdContinueJob)
115 *
116 * Each file in a job is filtered by 0 or more programs. After getting the
117 * list of filters needed and the total cost, the job is either passed or
118 * put back to the processing state until the current FilterLevel comes down
119 * enough to allow printing.
120 *
121 * If we can print, we build a string for the print options and run each of
122 * the filters, piping the output from one into the next.
123 *
124 * JOB STATUS UPDATES (update_job)
125 *
126 * The update_job function gets called whenever there are pending messages
127 * on the status pipe. These generally are updates to the marker-*,
128 * printer-state-message, or printer-state-reasons attributes. On EOF,
129 * finalize_job is called to clean up.
130 *
131 * FINALIZING JOBS (finalize_job)
132 *
133 * When all filters and the backend are done, we set the job state to
134 * completed (no errors), aborted (filter errors or abort-job policy),
135 * pending-held (auth required or retry-job policy), or pending
136 * (retry-current-job or stop-printer policies) as appropriate.
137 *
138 * Then we close the pipes and free the status buffers and profiles.
139 *
140 * JOB FILE COMPLETION (process_children in main.c)
141 *
142 * For multiple-file jobs, process_children (in main.c) sees that all
143 * filters have exited and calls in to print the next file if there are
144 * more files in the job, otherwise it waits for the backend to exit and
145 * update_job to do the cleanup.
146 */
147
148
ef416fc2 149/*
150 * Local globals...
151 */
152
153static mime_filter_t gziptoany_filter =
154 {
155 NULL, /* Source type */
156 NULL, /* Destination type */
157 0, /* Cost */
158 "gziptoany" /* Filter program to run */
159 };
160
161
162/*
163 * Local functions...
164 */
165
166static int compare_active_jobs(void *first, void *second, void *data);
167static int compare_jobs(void *first, void *second, void *data);
178cb736 168static void dump_job_history(cupsd_job_t *job);
ef55b745 169static void finalize_job(cupsd_job_t *job, int set_job_state);
178cb736 170static void free_job_history(cupsd_job_t *job);
b9faaae1
MS
171static char *get_options(cupsd_job_t *job, int banner_page, char *copies,
172 size_t copies_size, char *title,
173 size_t title_size);
c7017ecc 174static size_t ipp_length(ipp_t *ipp);
bd7854cb 175static void load_job_cache(const char *filename);
176static void load_next_job_id(const char *filename);
177static void load_request_root(void);
b9faaae1 178static void set_time(cupsd_job_t *job, const char *name);
e1d6a774 179static void start_job(cupsd_job_t *job, cupsd_printer_t *printer);
b9faaae1 180static void stop_job(cupsd_job_t *job, cupsd_jobaction_t action);
e1d6a774 181static void unload_job(cupsd_job_t *job);
f899b121 182static void update_job(cupsd_job_t *job);
4509bb49 183static void update_job_attrs(cupsd_job_t *job, int do_message);
ef416fc2 184
185
186/*
b9faaae1 187 * 'cupsdAddJob()' - Add a new job to the job queue.
ef416fc2 188 */
189
190cupsd_job_t * /* O - New job record */
191cupsdAddJob(int priority, /* I - Job priority */
192 const char *dest) /* I - Job destination */
193{
194 cupsd_job_t *job; /* New job record */
195
196
91c84a35
MS
197 if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
198 return (NULL);
ef416fc2 199
89d46774 200 job->id = NextJobId ++;
201 job->priority = priority;
202 job->back_pipes[0] = -1;
203 job->back_pipes[1] = -1;
204 job->print_pipes[0] = -1;
205 job->print_pipes[1] = -1;
f7deaa1a 206 job->side_pipes[0] = -1;
207 job->side_pipes[1] = -1;
89d46774 208 job->status_pipes[0] = -1;
209 job->status_pipes[1] = -1;
ef416fc2 210
211 cupsdSetString(&job->dest, dest);
212
213 /*
214 * Add the new job to the "all jobs" and "active jobs" lists...
215 */
216
217 cupsArrayAdd(Jobs, job);
218 cupsArrayAdd(ActiveJobs, job);
219
220 return (job);
221}
222
223
224/*
b9faaae1 225 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user.
ef416fc2 226 */
227
228void
229cupsdCancelJobs(const char *dest, /* I - Destination to cancel */
230 const char *username, /* I - Username or NULL */
231 int purge) /* I - Purge jobs? */
232{
233 cupsd_job_t *job; /* Current job */
234
235
236 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
237 job;
238 job = (cupsd_job_t *)cupsArrayNext(Jobs))
0a682745 239 {
b9faaae1 240 if ((!job->dest || !job->username) && !cupsdLoadJob(job))
0a682745
MS
241 continue;
242
b9faaae1
MS
243 if ((!dest || !strcmp(job->dest, dest)) &&
244 (!username || !strcmp(job->username, username)))
ef416fc2 245 {
246 /*
247 * Cancel all jobs matching this destination/user...
248 */
249
b9faaae1
MS
250 if (purge)
251 cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_PURGE,
252 "Job purged by user.");
41681883 253 else if (job->state_value < IPP_JOB_CANCELED)
b9faaae1
MS
254 cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT,
255 "Job canceled by user.");
ef416fc2 256 }
0a682745 257 }
ef416fc2 258
259 cupsdCheckJobs();
260}
261
262
263/*
264 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
265 * is available.
266 */
267
268void
269cupsdCheckJobs(void)
270{
271 cupsd_job_t *job; /* Current job in queue */
272 cupsd_printer_t *printer, /* Printer destination */
273 *pclass; /* Printer class destination */
ac884b6a 274 ipp_attribute_t *attr; /* Job attribute */
238c3832 275 time_t curtime; /* Current time */
ef416fc2 276
277
bd7854cb 278 cupsdLogMessage(CUPSD_LOG_DEBUG2,
279 "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d",
280 cupsArrayCount(ActiveJobs), Sleeping, NeedReload);
281
238c3832
MS
282 curtime = time(NULL);
283
ef416fc2 284 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
285 job;
286 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
287 {
238c3832
MS
288 /*
289 * Kill jobs if they are unresponsive...
290 */
291
292 if (job->kill_time && job->kill_time <= curtime)
293 {
f14324a7 294 cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Stopping unresponsive job!",
10d09e33
MS
295 job->id);
296
238c3832
MS
297 stop_job(job, CUPSD_JOB_FORCE);
298 continue;
299 }
300
ef416fc2 301 /*
302 * Start held jobs if they are ready...
303 */
304
bd7854cb 305 if (job->state_value == IPP_JOB_HELD &&
ef416fc2 306 job->hold_until &&
238c3832 307 job->hold_until < curtime)
bd7854cb 308 {
09a101d6 309 if (job->pending_timeout)
91c84a35 310 {
dfd5680b
MS
311 /*
312 * This job is pending; check that we don't have an active Send-Document
313 * operation in progress on any of the client connections, then timeout
314 * the job so we can start printing...
315 */
316
317 cupsd_client_t *con; /* Current client connection */
318
319
320 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
321 con;
322 con = (cupsd_client_t *)cupsArrayNext(Clients))
323 if (con->request &&
324 con->request->request.op.operation_id == IPP_SEND_DOCUMENT)
325 break;
326
327 if (con)
328 continue;
329
91c84a35
MS
330 if (cupsdTimeoutJob(job))
331 continue;
332 }
09a101d6 333
b9faaae1
MS
334 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
335 "Job submission timed out.");
bd7854cb 336 }
ef416fc2 337
e07d4801
MS
338 /*
339 * Continue jobs that are waiting on the FilterLimit...
340 */
341
342 if (job->pending_cost > 0 &&
343 ((FilterLevel + job->pending_cost) < FilterLimit || FilterLevel == 0))
344 cupsdContinueJob(job);
345
ef416fc2 346 /*
347 * Start pending jobs if the destination is available...
348 */
349
b9faaae1 350 if (job->state_value == IPP_JOB_PENDING && !NeedReload && !Sleeping &&
7cf5915e 351 !DoingShutdown && !job->printer)
ef416fc2 352 {
353 printer = cupsdFindDest(job->dest);
354 pclass = NULL;
355
356 while (printer &&
357 (printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS)))
358 {
359 /*
360 * If the class is remote, just pass it to the remote server...
361 */
362
363 pclass = printer;
364
c0e1af83 365 if (pclass->state == IPP_PRINTER_STOPPED)
b86bc4cf 366 printer = NULL;
c0e1af83 367 else if (pclass->type & CUPS_PRINTER_REMOTE)
368 break;
369 else
370 printer = cupsdFindAvailablePrinter(printer->name);
ef416fc2 371 }
372
373 if (!printer && !pclass)
374 {
375 /*
376 * Whoa, the printer and/or class for this destination went away;
377 * cancel the job...
378 */
379
b9faaae1
MS
380 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
381 "Job aborted because the destination printer/class "
382 "has gone away.");
ef416fc2 383 }
61cf44e2 384 else if (printer && !printer->holding_new_jobs)
ef416fc2 385 {
386 /*
387 * See if the printer is available or remote and not printing a job;
388 * if so, start the job...
389 */
390
391 if (pclass)
392 {
393 /*
394 * Add/update a job-actual-printer-uri attribute for this job
395 * so that we know which printer actually printed the job...
396 */
397
ef416fc2 398 if ((attr = ippFindAttribute(job->attrs, "job-actual-printer-uri",
399 IPP_TAG_URI)) != NULL)
400 cupsdSetString(&attr->values[0].string.text, printer->uri);
401 else
402 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI,
403 "job-actual-printer-uri", NULL, printer->uri);
3dfe78b3
MS
404
405 job->dirty = 1;
406 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 407 }
408
09a101d6 409 if ((!(printer->type & CUPS_PRINTER_DISCOVERED) && /* Printer is local */
61cf44e2 410 printer->state == IPP_PRINTER_IDLE) || /* and idle, OR */
09a101d6 411 ((printer->type & CUPS_PRINTER_DISCOVERED) && /* Printer is remote */
bd7854cb 412 !printer->job)) /* and not printing */
bc44d920 413 {
bc44d920 414 /*
415 * Start the job...
416 */
417
e1d6a774 418 start_job(job, printer);
bc44d920 419 }
ef416fc2 420 }
421 }
422 }
423}
424
425
426/*
427 * 'cupsdCleanJobs()' - Clean out old jobs.
428 */
429
430void
431cupsdCleanJobs(void)
432{
433 cupsd_job_t *job; /* Current job */
434
435
ef55b745 436 if (MaxJobs <= 0 && JobHistory)
ef416fc2 437 return;
438
439 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
ef55b745 440 job && (cupsArrayCount(Jobs) >= MaxJobs || !JobHistory);
ef416fc2 441 job = (cupsd_job_t *)cupsArrayNext(Jobs))
b9faaae1
MS
442 if (job->state_value >= IPP_JOB_CANCELED && !job->printer)
443 cupsdDeleteJob(job, CUPSD_JOB_PURGE);
ef416fc2 444}
445
446
3dfe78b3 447/*
b9faaae1 448 * 'cupsdContinueJob()' - Continue printing with the next file in a job.
3dfe78b3
MS
449 */
450
451void
b9faaae1 452cupsdContinueJob(cupsd_job_t *job) /* I - Job */
3dfe78b3 453{
b9faaae1
MS
454 int i; /* Looping var */
455 int slot; /* Pipe slot */
e07d4801 456 cups_array_t *filters = NULL,/* Filters for job */
b9faaae1
MS
457 *prefilters; /* Filters with prefilters */
458 mime_filter_t *filter, /* Current filter */
459 *prefilter, /* Prefilter */
460 port_monitor; /* Port monitor filter */
461 char scheme[255]; /* Device URI scheme */
462 ipp_attribute_t *attr; /* Current attribute */
463 const char *ptr, /* Pointer into value */
464 *abort_message; /* Abort message */
f11a948a 465 ipp_jstate_t abort_state = IPP_JOB_STOPPED;
e07d4801 466 /* New job state on abort */
b9faaae1
MS
467 struct stat backinfo; /* Backend file information */
468 int backroot; /* Run backend as root? */
469 int pid; /* Process ID of new filter process */
470 int banner_page; /* 1 if banner page, 0 otherwise */
e07d4801
MS
471 int filterfds[2][2] = { { -1, -1 }, { -1, -1 } };
472 /* Pipes used between filters */
b9faaae1 473 int envc; /* Number of environment variables */
e07d4801 474 char **argv = NULL, /* Filter command-line arguments */
b9faaae1
MS
475 filename[1024], /* Job filename */
476 command[1024], /* Full path to command */
477 jobid[255], /* Job ID string */
478 title[IPP_MAX_NAME],
479 /* Job title string */
480 copies[255], /* # copies string */
481 *options, /* Options string */
22c9029b 482 *envp[MAX_ENV + 20],
b9faaae1
MS
483 /* Environment variables */
484 charset[255], /* CHARSET env variable */
485 class_name[255],/* CLASS env variable */
486 classification[1024],
487 /* CLASSIFICATION env variable */
488 content_type[1024],
489 /* CONTENT_TYPE env variable */
490 device_uri[1024],
491 /* DEVICE_URI env variable */
492 final_content_type[1024],
493 /* FINAL_CONTENT_TYPE env variable */
494 lang[255], /* LANG env variable */
495#ifdef __APPLE__
496 apple_language[255],
497 /* APPLE_LANGUAGE env variable */
498#endif /* __APPLE__ */
22c9029b
MS
499 auth_info_required[255],
500 /* AUTH_INFO_REQUIRED env variable */
b9faaae1
MS
501 ppd[1024], /* PPD env variable */
502 printer_info[255],
503 /* PRINTER_INFO env variable */
504 printer_location[255],
505 /* PRINTER_LOCATION env variable */
506 printer_name[255],
507 /* PRINTER env variable */
508 rip_max_cache[255];
509 /* RIP_MAX_CACHE env variable */
510
511
512 cupsdLogMessage(CUPSD_LOG_DEBUG2,
513 "cupsdContinueJob(job=%p(%d)): current_file=%d, num_files=%d",
514 job, job->id, job->current_file, job->num_files);
3dfe78b3 515
3dfe78b3 516 /*
b9faaae1
MS
517 * Figure out what filters are required to convert from
518 * the source to the destination type...
3dfe78b3
MS
519 */
520
b9faaae1 521 FilterLevel -= job->cost;
3dfe78b3 522
e07d4801
MS
523 job->cost = 0;
524 job->pending_cost = 0;
525
526 memset(job->filters, 0, sizeof(job->filters));
527
3dfe78b3 528
b9faaae1 529 if (job->printer->raw)
3dfe78b3 530 {
b9faaae1
MS
531 /*
532 * Remote jobs and raw queues go directly to the printer without
533 * filtering...
534 */
3dfe78b3 535
b9faaae1 536 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Sending job to queue tagged as raw...");
b9faaae1
MS
537 }
538 else
539 {
540 /*
541 * Local jobs get filtered...
542 */
3dfe78b3 543
b9faaae1
MS
544 filters = mimeFilter(MimeDatabase, job->filetypes[job->current_file],
545 job->printer->filetype, &(job->cost));
ef416fc2 546
b9faaae1
MS
547 if (!filters)
548 {
549 cupsdLogJob(job, CUPSD_LOG_ERROR,
550 "Unable to convert file %d to printable format!",
551 job->current_file);
ef416fc2 552
e07d4801 553 abort_message = "Aborting job because it cannot be printed.";
f11a948a
MS
554 abort_state = IPP_JOB_ABORTED;
555
e07d4801 556 goto abort_job;
b9faaae1 557 }
ef416fc2 558
ef416fc2 559 /*
b9faaae1 560 * Remove NULL ("-") filters...
ef416fc2 561 */
562
b9faaae1
MS
563 for (filter = (mime_filter_t *)cupsArrayFirst(filters);
564 filter;
565 filter = (mime_filter_t *)cupsArrayNext(filters))
566 if (!strcmp(filter->filter, "-"))
567 cupsArrayRemove(filters, filter);
ef416fc2 568
b9faaae1
MS
569 if (cupsArrayCount(filters) == 0)
570 {
571 cupsArrayDelete(filters);
572 filters = NULL;
573 }
ef416fc2 574
b9faaae1
MS
575 /*
576 * If this printer has any pre-filters, insert the required pre-filter
577 * in the filters array...
578 */
ef416fc2 579
b9faaae1
MS
580 if (job->printer->prefiltertype && filters)
581 {
582 prefilters = cupsArrayNew(NULL, NULL);
583
584 for (filter = (mime_filter_t *)cupsArrayFirst(filters);
585 filter;
586 filter = (mime_filter_t *)cupsArrayNext(filters))
587 {
588 if ((prefilter = mimeFilterLookup(MimeDatabase, filter->src,
589 job->printer->prefiltertype)))
590 {
591 cupsArrayAdd(prefilters, prefilter);
592 job->cost += prefilter->cost;
593 }
ef416fc2 594
b9faaae1
MS
595 cupsArrayAdd(prefilters, filter);
596 }
e00b005a 597
b9faaae1
MS
598 cupsArrayDelete(filters);
599 filters = prefilters;
600 }
601 }
bc44d920 602
c168a833 603 /*
b9faaae1
MS
604 * Set a minimum cost of 100 for all jobs so that FilterLimit
605 * works with raw queues and other low-cost paths.
c168a833
MS
606 */
607
b9faaae1
MS
608 if (job->cost < 100)
609 job->cost = 100;
c168a833 610
745129be 611 /*
b9faaae1 612 * See if the filter cost is too high...
745129be
MS
613 */
614
b9faaae1
MS
615 if ((FilterLevel + job->cost) > FilterLimit && FilterLevel > 0 &&
616 FilterLimit > 0)
ef416fc2 617 {
618 /*
b9faaae1 619 * Don't print this job quite yet...
ef416fc2 620 */
621
b9faaae1 622 cupsArrayDelete(filters);
a74454a7 623
b9faaae1
MS
624 cupsdLogJob(job, CUPSD_LOG_INFO,
625 "Holding because filter limit has been reached.");
626 cupsdLogJob(job, CUPSD_LOG_DEBUG2,
627 "cupsdContinueJob: file=%d, cost=%d, level=%d, limit=%d",
628 job->current_file, job->cost, FilterLevel,
629 FilterLimit);
e07d4801
MS
630
631 job->pending_cost = job->cost;
632 job->cost = 0;
b9faaae1
MS
633 return;
634 }
a74454a7 635
b9faaae1 636 FilterLevel += job->cost;
a74454a7 637
b9faaae1
MS
638 /*
639 * Add decompression/raw filter as needed...
640 */
a74454a7 641
b9faaae1
MS
642 if ((!job->printer->raw && job->compressions[job->current_file]) ||
643 (!filters && !job->printer->remote &&
644 (job->num_files > 1 || !strncmp(job->printer->device_uri, "file:", 5))))
645 {
a74454a7 646 /*
b9faaae1 647 * Add gziptoany filter to the front of the list...
a74454a7 648 */
649
b9faaae1
MS
650 if (!filters)
651 filters = cupsArrayNew(NULL, NULL);
ef416fc2 652
b9faaae1
MS
653 if (!cupsArrayInsert(filters, &gziptoany_filter))
654 {
655 cupsdLogJob(job, CUPSD_LOG_DEBUG,
656 "Unable to add decompression filter - %s", strerror(errno));
ed486911 657
b9faaae1 658 cupsArrayDelete(filters);
ed486911 659
e07d4801 660 abort_message = "Stopping job because the scheduler ran out of memory.";
ed486911 661
e07d4801 662 goto abort_job;
b9faaae1
MS
663 }
664 }
ef416fc2 665
b9faaae1
MS
666 /*
667 * Add port monitor, if any...
668 */
ef416fc2 669
b9faaae1
MS
670 if (job->printer->port_monitor)
671 {
672 /*
673 * Add port monitor to the end of the list...
674 */
ef416fc2 675
b9faaae1
MS
676 if (!filters)
677 filters = cupsArrayNew(NULL, NULL);
ef416fc2 678
b9faaae1
MS
679 port_monitor.src = NULL;
680 port_monitor.dst = NULL;
681 port_monitor.cost = 0;
ef416fc2 682
b9faaae1
MS
683 snprintf(port_monitor.filter, sizeof(port_monitor.filter),
684 "%s/monitor/%s", ServerBin, job->printer->port_monitor);
ef416fc2 685
b9faaae1
MS
686 if (!cupsArrayAdd(filters, &port_monitor))
687 {
688 cupsdLogJob(job, CUPSD_LOG_DEBUG,
689 "Unable to add port monitor - %s", strerror(errno));
ef416fc2 690
e07d4801 691 abort_message = "Stopping job because the scheduler ran out of memory.";
07725fee 692
e07d4801 693 goto abort_job;
b9faaae1
MS
694 }
695 }
ef416fc2 696
b9faaae1
MS
697 /*
698 * Make sure we don't go over the "MAX_FILTERS" limit...
699 */
ef416fc2 700
b9faaae1
MS
701 if (cupsArrayCount(filters) > MAX_FILTERS)
702 {
703 cupsdLogJob(job, CUPSD_LOG_DEBUG,
704 "Too many filters (%d > %d), unable to print!",
705 cupsArrayCount(filters), MAX_FILTERS);
ef416fc2 706
e07d4801 707 abort_message = "Aborting job because it needs too many filters to print.";
f11a948a
MS
708 abort_state = IPP_JOB_ABORTED;
709
e07d4801 710 goto abort_job;
b9faaae1 711 }
07725fee 712
b9faaae1
MS
713 /*
714 * Determine if we are printing a banner page or not...
715 */
07725fee 716
b9faaae1
MS
717 if (job->job_sheets == NULL)
718 {
719 cupsdLogJob(job, CUPSD_LOG_DEBUG, "No job-sheets attribute.");
720 if ((job->job_sheets =
721 ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
722 cupsdLogJob(job, CUPSD_LOG_DEBUG,
723 "... but someone added one without setting job_sheets!");
724 }
725 else if (job->job_sheets->num_values == 1)
726 cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-sheets=%s",
727 job->job_sheets->values[0].string.text);
728 else
729 cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-sheets=%s,%s",
730 job->job_sheets->values[0].string.text,
731 job->job_sheets->values[1].string.text);
07725fee 732
b9faaae1
MS
733 if (job->printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
734 banner_page = 0;
735 else if (job->job_sheets == NULL)
736 banner_page = 0;
737 else if (strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
738 job->current_file == 0)
739 banner_page = 1;
740 else if (job->job_sheets->num_values > 1 &&
741 strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
742 job->current_file == (job->num_files - 1))
743 banner_page = 1;
744 else
745 banner_page = 0;
07725fee 746
b9faaae1
MS
747 if ((options = get_options(job, banner_page, copies, sizeof(copies), title,
748 sizeof(title))) == NULL)
749 {
e07d4801 750 abort_message = "Stopping job because the scheduler ran out of memory.";
ef416fc2 751
e07d4801 752 goto abort_job;
b9faaae1 753 }
ef416fc2 754
b9faaae1
MS
755 /*
756 * Build the command-line arguments for the filters. Each filter
757 * has 6 or 7 arguments:
758 *
759 * argv[0] = printer
760 * argv[1] = job ID
761 * argv[2] = username
762 * argv[3] = title
763 * argv[4] = # copies
764 * argv[5] = options
765 * argv[6] = filename (optional; normally stdin)
766 *
767 * This allows legacy printer drivers that use the old System V
768 * printing interface to be used by CUPS.
769 *
770 * For remote jobs, we send all of the files in the argument list.
771 */
07725fee 772
b9faaae1
MS
773 if (job->printer->remote)
774 argv = calloc(7 + job->num_files, sizeof(char *));
775 else
776 argv = calloc(8, sizeof(char *));
07725fee 777
b9faaae1
MS
778 if (!argv)
779 {
780 cupsdLogMessage(CUPSD_LOG_DEBUG, "Unable to allocate argument array - %s",
781 strerror(errno));
07725fee 782
e07d4801
MS
783 abort_message = "Stopping job because the scheduler ran out of memory.";
784
785 goto abort_job;
b9faaae1 786 }
ef416fc2 787
b9faaae1 788 sprintf(jobid, "%d", job->id);
ef416fc2 789
b9faaae1
MS
790 argv[0] = job->printer->name;
791 argv[1] = jobid;
792 argv[2] = job->username;
793 argv[3] = title;
794 argv[4] = copies;
795 argv[5] = options;
ef416fc2 796
b9faaae1 797 if (job->printer->remote && job->num_files > 1)
ef416fc2 798 {
b9faaae1
MS
799 for (i = 0; i < job->num_files; i ++)
800 {
801 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
802 job->id, i + 1);
803 argv[6 + i] = strdup(filename);
804 }
ef416fc2 805 }
806 else
807 {
b9faaae1
MS
808 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
809 job->id, job->current_file + 1);
810 argv[6] = filename;
ef416fc2 811 }
ef416fc2 812
b9faaae1
MS
813 for (i = 0; argv[i]; i ++)
814 cupsdLogJob(job, CUPSD_LOG_DEBUG, "argv[%d]=\"%s\"", i, argv[i]);
ef416fc2 815
b9faaae1
MS
816 /*
817 * Create environment variable strings for the filters...
818 */
bd7854cb 819
b9faaae1
MS
820 attr = ippFindAttribute(job->attrs, "attributes-natural-language",
821 IPP_TAG_LANGUAGE);
ef416fc2 822
b9faaae1
MS
823#ifdef __APPLE__
824 strcpy(apple_language, "APPLE_LANGUAGE=");
825 _cupsAppleLanguage(attr->values[0].string.text,
826 apple_language + 15, sizeof(apple_language) - 15);
827#endif /* __APPLE__ */
ef416fc2 828
b9faaae1 829 switch (strlen(attr->values[0].string.text))
ef416fc2 830 {
b9faaae1
MS
831 default :
832 /*
833 * This is an unknown or badly formatted language code; use
834 * the POSIX locale...
835 */
ef416fc2 836
b9faaae1
MS
837 strcpy(lang, "LANG=C");
838 break;
ef416fc2 839
b9faaae1
MS
840 case 2 :
841 /*
842 * Just the language code (ll)...
843 */
ef416fc2 844
1340db2d 845 snprintf(lang, sizeof(lang), "LANG=%s.UTF-8",
b9faaae1
MS
846 attr->values[0].string.text);
847 break;
ef416fc2 848
b9faaae1
MS
849 case 5 :
850 /*
851 * Language and country code (ll-cc)...
852 */
ef416fc2 853
1340db2d 854 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF-8",
b9faaae1
MS
855 attr->values[0].string.text[0],
856 attr->values[0].string.text[1],
857 toupper(attr->values[0].string.text[3] & 255),
858 toupper(attr->values[0].string.text[4] & 255));
859 break;
860 }
ef416fc2 861
b9faaae1
MS
862 if ((attr = ippFindAttribute(job->attrs, "document-format",
863 IPP_TAG_MIMETYPE)) != NULL &&
864 (ptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
865 snprintf(charset, sizeof(charset), "CHARSET=%s", ptr + 8);
866 else
867 strlcpy(charset, "CHARSET=utf-8", sizeof(charset));
ef416fc2 868
b9faaae1
MS
869 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
870 job->filetypes[job->current_file]->super,
871 job->filetypes[job->current_file]->type);
872 snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s",
873 job->printer->device_uri);
874 snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot,
875 job->printer->name);
876 snprintf(printer_info, sizeof(printer_name), "PRINTER_INFO=%s",
877 job->printer->info ? job->printer->info : "");
878 snprintf(printer_location, sizeof(printer_name), "PRINTER_LOCATION=%s",
879 job->printer->location ? job->printer->location : "");
880 snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", job->printer->name);
881 snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
ef416fc2 882
22c9029b
MS
883 if (job->printer->num_auth_info_required == 1)
884 snprintf(auth_info_required, sizeof(auth_info_required),
885 "AUTH_INFO_REQUIRED=%s",
886 job->printer->auth_info_required[0]);
887 else if (job->printer->num_auth_info_required == 2)
888 snprintf(auth_info_required, sizeof(auth_info_required),
889 "AUTH_INFO_REQUIRED=%s,%s",
890 job->printer->auth_info_required[0],
891 job->printer->auth_info_required[1]);
892 else if (job->printer->num_auth_info_required == 3)
893 snprintf(auth_info_required, sizeof(auth_info_required),
894 "AUTH_INFO_REQUIRED=%s,%s,%s",
895 job->printer->auth_info_required[0],
896 job->printer->auth_info_required[1],
897 job->printer->auth_info_required[2]);
898 else if (job->printer->num_auth_info_required == 4)
899 snprintf(auth_info_required, sizeof(auth_info_required),
900 "AUTH_INFO_REQUIRED=%s,%s,%s,%s",
901 job->printer->auth_info_required[0],
902 job->printer->auth_info_required[1],
903 job->printer->auth_info_required[2],
904 job->printer->auth_info_required[3]);
905 else
906 strlcpy(auth_info_required, "AUTH_INFO_REQUIRED=none",
907 sizeof(auth_info_required));
908
b9faaae1 909 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
ef416fc2 910
b9faaae1
MS
911 envp[envc ++] = charset;
912 envp[envc ++] = lang;
913#ifdef __APPLE__
914 envp[envc ++] = apple_language;
915#endif /* __APPLE__ */
916 envp[envc ++] = ppd;
917 envp[envc ++] = rip_max_cache;
918 envp[envc ++] = content_type;
919 envp[envc ++] = device_uri;
920 envp[envc ++] = printer_info;
921 envp[envc ++] = printer_location;
922 envp[envc ++] = printer_name;
923 envp[envc ++] = banner_page ? "CUPS_FILETYPE=job-sheet" :
924 "CUPS_FILETYPE=document";
ef416fc2 925
b9faaae1
MS
926 if (!job->printer->remote && !job->printer->raw)
927 {
928 filter = (mime_filter_t *)cupsArrayLast(filters);
ef416fc2 929
b9faaae1
MS
930 if (job->printer->port_monitor)
931 filter = (mime_filter_t *)cupsArrayPrev(filters);
ef416fc2 932
b9faaae1
MS
933 if (filter && filter->dst)
934 {
f14324a7 935 if ((ptr = strchr(filter->dst->type, '/')) != NULL)
c8fef167 936 snprintf(final_content_type, sizeof(final_content_type),
f14324a7 937 "FINAL_CONTENT_TYPE=%s", ptr + 1);
c8fef167
MS
938 else
939 snprintf(final_content_type, sizeof(final_content_type),
940 "FINAL_CONTENT_TYPE=%s/%s", filter->dst->super,
941 filter->dst->type);
b9faaae1
MS
942 envp[envc ++] = final_content_type;
943 }
944 }
ef416fc2 945
b9faaae1
MS
946 if (Classification && !banner_page)
947 {
948 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
949 IPP_TAG_NAME)) == NULL)
950 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
951 Classification);
952 else if (attr->num_values > 1 &&
953 strcmp(attr->values[1].string.text, "none") != 0)
954 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
955 attr->values[1].string.text);
956 else
957 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
958 attr->values[0].string.text);
ef416fc2 959
b9faaae1
MS
960 envp[envc ++] = classification;
961 }
ef416fc2 962
b9faaae1
MS
963 if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
964 {
965 snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest);
966 envp[envc ++] = class_name;
967 }
ef416fc2 968
22c9029b 969 envp[envc ++] = auth_info_required;
b9faaae1
MS
970 if (job->auth_username)
971 envp[envc ++] = job->auth_username;
972 if (job->auth_domain)
973 envp[envc ++] = job->auth_domain;
974 if (job->auth_password)
975 envp[envc ++] = job->auth_password;
07ed0e9a
MS
976 if (job->auth_uid)
977 envp[envc ++] = job->auth_uid;
ef416fc2 978
b9faaae1 979 envp[envc] = NULL;
ef416fc2 980
b9faaae1
MS
981 for (i = 0; i < envc; i ++)
982 if (!strncmp(envp[i], "AUTH_", 5))
983 cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"AUTH_%c****\"", i,
984 envp[i][5]);
985 else if (strncmp(envp[i], "DEVICE_URI=", 11))
986 cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"%s\"", i, envp[i]);
987 else
988 cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"DEVICE_URI=%s\"", i,
989 job->printer->sanitized_device_uri);
ef416fc2 990
b9faaae1
MS
991 if (job->printer->remote)
992 job->current_file = job->num_files;
993 else
994 job->current_file ++;
ef416fc2 995
b9faaae1
MS
996 /*
997 * Now create processes for all of the filters...
998 */
ef416fc2 999
82f97232
MS
1000 cupsdSetPrinterReasons(job->printer, "-cups-missing-filter-warning,"
1001 "cups-insecure-filter-warning");
1002
b9faaae1
MS
1003 for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
1004 filter;
1005 i ++, filter = (mime_filter_t *)cupsArrayNext(filters))
1006 {
1007 if (filter->filter[0] != '/')
1008 snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
1009 filter->filter);
1010 else
1011 strlcpy(command, filter->filter, sizeof(command));
ef416fc2 1012
b9faaae1
MS
1013 if (i < (cupsArrayCount(filters) - 1))
1014 {
1015 if (cupsdOpenPipe(filterfds[slot]))
1016 {
1017 abort_message = "Stopping job because the scheduler could not create "
1018 "the filter pipes.";
ef416fc2 1019
b9faaae1
MS
1020 goto abort_job;
1021 }
1022 }
1023 else
1024 {
82f97232
MS
1025 if (job->current_file == 1 ||
1026 (job->printer->pc && job->printer->pc->single_file))
b9faaae1
MS
1027 {
1028 if (strncmp(job->printer->device_uri, "file:", 5) != 0)
1029 {
1030 if (cupsdOpenPipe(job->print_pipes))
1031 {
1032 abort_message = "Stopping job because the scheduler could not "
1033 "create the backend pipes.";
ef416fc2 1034
b9faaae1
MS
1035 goto abort_job;
1036 }
1037 }
1038 else
1039 {
1040 job->print_pipes[0] = -1;
1041 if (!strcmp(job->printer->device_uri, "file:/dev/null") ||
1042 !strcmp(job->printer->device_uri, "file:///dev/null"))
1043 job->print_pipes[1] = -1;
1044 else
1045 {
1046 if (!strncmp(job->printer->device_uri, "file:/dev/", 10))
1047 job->print_pipes[1] = open(job->printer->device_uri + 5,
1048 O_WRONLY | O_EXCL);
1049 else if (!strncmp(job->printer->device_uri, "file:///dev/", 12))
1050 job->print_pipes[1] = open(job->printer->device_uri + 7,
1051 O_WRONLY | O_EXCL);
1052 else if (!strncmp(job->printer->device_uri, "file:///", 8))
1053 job->print_pipes[1] = open(job->printer->device_uri + 7,
1054 O_WRONLY | O_CREAT | O_TRUNC, 0600);
1055 else
1056 job->print_pipes[1] = open(job->printer->device_uri + 5,
1057 O_WRONLY | O_CREAT | O_TRUNC, 0600);
ef416fc2 1058
b9faaae1
MS
1059 if (job->print_pipes[1] < 0)
1060 {
1061 abort_message = "Stopping job because the scheduler could not "
1062 "open the output file.";
bd7854cb 1063
b9faaae1
MS
1064 goto abort_job;
1065 }
ef416fc2 1066
b9faaae1
MS
1067 fcntl(job->print_pipes[1], F_SETFD,
1068 fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
1069 }
1070 }
1071 }
ef416fc2 1072
b9faaae1
MS
1073 filterfds[slot][0] = job->print_pipes[0];
1074 filterfds[slot][1] = job->print_pipes[1];
1075 }
ef416fc2 1076
b9faaae1
MS
1077 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
1078 filterfds[slot][1], job->status_pipes[1],
1079 job->back_pipes[0], job->side_pipes[0], 0,
38e73f87 1080 job->profile, job, job->filters + i);
ef416fc2 1081
b9faaae1 1082 cupsdClosePipe(filterfds[!slot]);
ef416fc2 1083
b9faaae1
MS
1084 if (pid == 0)
1085 {
1086 cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
1087 filter->filter, strerror(errno));
3dfe78b3 1088
f11a948a 1089 abort_message = "Stopping job because the scheduler could not execute a "
b9faaae1 1090 "filter.";
ef416fc2 1091
b9faaae1
MS
1092 goto abort_job;
1093 }
ef416fc2 1094
b9faaae1
MS
1095 cupsdLogJob(job, CUPSD_LOG_INFO, "Started filter %s (PID %d)", command,
1096 pid);
bd7854cb 1097
b9faaae1
MS
1098 argv[6] = NULL;
1099 slot = !slot;
bd7854cb 1100 }
1101
b9faaae1
MS
1102 cupsArrayDelete(filters);
1103 filters = NULL;
ef416fc2 1104
1105 /*
b9faaae1 1106 * Finally, pipe the final output into a backend process if needed...
ef416fc2 1107 */
1108
b9faaae1 1109 if (strncmp(job->printer->device_uri, "file:", 5) != 0)
bd7854cb 1110 {
82f97232
MS
1111 if (job->current_file == 1 || job->printer->remote ||
1112 (job->printer->pc && job->printer->pc->single_file))
b9faaae1
MS
1113 {
1114 sscanf(job->printer->device_uri, "%254[^:]", scheme);
1115 snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, scheme);
ef416fc2 1116
b9faaae1
MS
1117 /*
1118 * See if the backend needs to run as root...
1119 */
ef416fc2 1120
b9faaae1
MS
1121 if (RunUser)
1122 backroot = 0;
1123 else if (stat(command, &backinfo))
1124 backroot = 0;
1125 else
1126 backroot = !(backinfo.st_mode & (S_IRWXG | S_IRWXO));
ef416fc2 1127
b9faaae1 1128 argv[0] = job->printer->sanitized_device_uri;
ef416fc2 1129
b9faaae1
MS
1130 filterfds[slot][0] = -1;
1131 filterfds[slot][1] = -1;
ef416fc2 1132
b9faaae1
MS
1133 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
1134 filterfds[slot][1], job->status_pipes[1],
1135 job->back_pipes[1], job->side_pipes[1],
38e73f87 1136 backroot, job->profile, job, &(job->backend));
ef416fc2 1137
b9faaae1
MS
1138 if (pid == 0)
1139 {
1140 abort_message = "Stopping job because the sheduler could not execute "
1141 "the backend.";
1142
1143 goto abort_job;
1144 }
1145 else
1146 {
1147 cupsdLogJob(job, CUPSD_LOG_INFO, "Started backend %s (PID %d)",
1148 command, pid);
1149 }
1150 }
ef416fc2 1151
82f97232
MS
1152 if (job->current_file == job->num_files ||
1153 (job->printer->pc && job->printer->pc->single_file))
1154 cupsdClosePipe(job->print_pipes);
1155
b9faaae1
MS
1156 if (job->current_file == job->num_files)
1157 {
b9faaae1
MS
1158 cupsdClosePipe(job->back_pipes);
1159 cupsdClosePipe(job->side_pipes);
ef416fc2 1160
b9faaae1
MS
1161 close(job->status_pipes[1]);
1162 job->status_pipes[1] = -1;
1163 }
1164 }
1165 else
bd7854cb 1166 {
b9faaae1
MS
1167 filterfds[slot][0] = -1;
1168 filterfds[slot][1] = -1;
ef416fc2 1169
82f97232
MS
1170 if (job->current_file == job->num_files ||
1171 (job->printer->pc && job->printer->pc->single_file))
b9faaae1
MS
1172 cupsdClosePipe(job->print_pipes);
1173
82f97232
MS
1174 if (job->current_file == job->num_files)
1175 {
b9faaae1
MS
1176 close(job->status_pipes[1]);
1177 job->status_pipes[1] = -1;
1178 }
bd7854cb 1179 }
ef416fc2 1180
b9faaae1
MS
1181 cupsdClosePipe(filterfds[slot]);
1182
1183 if (job->printer->remote && job->num_files > 1)
bd7854cb 1184 {
b9faaae1
MS
1185 for (i = 0; i < job->num_files; i ++)
1186 free(argv[i + 6]);
bd7854cb 1187 }
ef416fc2 1188
b9faaae1 1189 free(argv);
ef416fc2 1190
b9faaae1
MS
1191 cupsdAddSelect(job->status_buffer->fd, (cupsd_selfunc_t)update_job, NULL,
1192 job);
ef416fc2 1193
b9faaae1
MS
1194 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.",
1195 job->id);
ef416fc2 1196
b9faaae1 1197 return;
ef416fc2 1198
ef416fc2 1199
bd7854cb 1200 /*
b9faaae1
MS
1201 * If we get here, we need to abort the current job and close out all
1202 * files and pipes...
bd7854cb 1203 */
ef416fc2 1204
b9faaae1 1205 abort_job:
8b450588 1206
e07d4801
MS
1207 FilterLevel -= job->cost;
1208 job->cost = 0;
1209
b9faaae1
MS
1210 for (slot = 0; slot < 2; slot ++)
1211 cupsdClosePipe(filterfds[slot]);
1212
e07d4801
MS
1213 cupsArrayDelete(filters);
1214
1215 if (argv)
1216 {
1217 if (job->printer->remote && job->num_files > 1)
1218 {
1219 for (i = 0; i < job->num_files; i ++)
1220 free(argv[i + 6]);
1221 }
1222
1223 free(argv);
1224 }
1225
1226 cupsdClosePipe(job->print_pipes);
1227 cupsdClosePipe(job->back_pipes);
1228 cupsdClosePipe(job->side_pipes);
1229
1230 cupsdRemoveSelect(job->status_pipes[0]);
b9faaae1
MS
1231 cupsdClosePipe(job->status_pipes);
1232 cupsdStatBufDelete(job->status_buffer);
1233 job->status_buffer = NULL;
1234
e07d4801
MS
1235 /*
1236 * Update the printer and job state.
1237 */
b9faaae1 1238
e07d4801
MS
1239 cupsdSetJobState(job, abort_state, CUPSD_JOB_DEFAULT, "%s", abort_message);
1240 cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0);
1241 update_job_attrs(job, 0);
ef416fc2 1242
178cb736
MS
1243 if (job->history)
1244 free_job_history(job);
1245
e07d4801 1246 cupsArrayRemove(PrintingJobs, job);
ef416fc2 1247
e07d4801
MS
1248 /*
1249 * Clear the printer <-> job association...
1250 */
1251
1252 job->printer->job = NULL;
1253 job->printer = NULL;
b9faaae1 1254}
bd7854cb 1255
ef416fc2 1256
b9faaae1
MS
1257/*
1258 * 'cupsdDeleteJob()' - Free all memory used by a job.
1259 */
ef416fc2 1260
b9faaae1
MS
1261void
1262cupsdDeleteJob(cupsd_job_t *job, /* I - Job */
1263 cupsd_jobaction_t action)/* I - Action */
1264{
22c9029b
MS
1265 char filename[1024]; /* Job filename */
1266
1267
b9faaae1 1268 if (job->printer)
ef55b745 1269 finalize_job(job, 1);
ef416fc2 1270
b9faaae1 1271 if (action == CUPSD_JOB_PURGE)
bd7854cb 1272 {
1273 /*
b9faaae1 1274 * Remove the job info file...
bd7854cb 1275 */
ef416fc2 1276
b9faaae1
MS
1277 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot,
1278 job->id);
1279 unlink(filename);
bd7854cb 1280 }
ef416fc2 1281
b9faaae1
MS
1282 cupsdClearString(&job->username);
1283 cupsdClearString(&job->dest);
1284 cupsdClearString(&job->auth_username);
1285 cupsdClearString(&job->auth_domain);
1286 cupsdClearString(&job->auth_password);
07ed0e9a 1287 cupsdClearString(&job->auth_uid);
09a101d6 1288
b9faaae1
MS
1289 if (job->num_files > 0)
1290 {
1291 free(job->compressions);
1292 free(job->filetypes);
09a101d6 1293
22c9029b
MS
1294 while (job->num_files > 0)
1295 {
1296 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
1297 job->id, job->num_files);
1298 unlink(filename);
1299
1300 job->num_files --;
1301 }
09a101d6 1302 }
ed6e7faf 1303
178cb736
MS
1304 if (job->history)
1305 free_job_history(job);
1306
b9faaae1 1307 unload_job(job);
dfd5680b 1308
b9faaae1
MS
1309 cupsArrayRemove(Jobs, job);
1310 cupsArrayRemove(ActiveJobs, job);
1311 cupsArrayRemove(PrintingJobs, job);
dfd5680b 1312
b9faaae1 1313 free(job);
ef416fc2 1314}
1315
1316
1317/*
b9faaae1 1318 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
ef416fc2 1319 */
1320
1321void
b9faaae1 1322cupsdFreeAllJobs(void)
ef416fc2 1323{
b9faaae1 1324 cupsd_job_t *job; /* Current job */
ef416fc2 1325
ef416fc2 1326
b9faaae1 1327 if (!Jobs)
ef416fc2 1328 return;
1329
b9faaae1 1330 cupsdHoldSignals();
ef416fc2 1331
238c3832 1332 cupsdStopAllJobs(CUPSD_JOB_FORCE, 0);
b9faaae1 1333 cupsdSaveAllJobs();
e53920b9 1334
b9faaae1
MS
1335 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
1336 job;
1337 job = (cupsd_job_t *)cupsArrayNext(Jobs))
1338 cupsdDeleteJob(job, CUPSD_JOB_DEFAULT);
ef416fc2 1339
b9faaae1
MS
1340 cupsdReleaseSignals();
1341}
ef416fc2 1342
bd7854cb 1343
b9faaae1
MS
1344/*
1345 * 'cupsdFindJob()' - Find the specified job.
1346 */
e53920b9 1347
b9faaae1
MS
1348cupsd_job_t * /* O - Job data */
1349cupsdFindJob(int id) /* I - Job ID */
1350{
1351 cupsd_job_t key; /* Search key */
ef416fc2 1352
ef416fc2 1353
b9faaae1 1354 key.id = id;
e53920b9 1355
b9faaae1 1356 return ((cupsd_job_t *)cupsArrayFind(Jobs, &key));
ef416fc2 1357}
1358
1359
1360/*
b9faaae1
MS
1361 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
1362 * or held jobs in a printer or class.
ef416fc2 1363 */
1364
b9faaae1
MS
1365int /* O - Job count */
1366cupsdGetPrinterJobCount(
1367 const char *dest) /* I - Printer or class name */
ef416fc2 1368{
b9faaae1
MS
1369 int count; /* Job count */
1370 cupsd_job_t *job; /* Current job */
005dd1eb 1371
005dd1eb 1372
b9faaae1
MS
1373 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
1374 job;
1375 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
1376 if (job->dest && !strcasecmp(job->dest, dest))
1377 count ++;
ef416fc2 1378
b9faaae1 1379 return (count);
ef416fc2 1380}
1381
1382
1383/*
b9faaae1
MS
1384 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
1385 * or held jobs for a user.
ef416fc2 1386 */
1387
b9faaae1
MS
1388int /* O - Job count */
1389cupsdGetUserJobCount(
1390 const char *username) /* I - Username */
ef416fc2 1391{
b9faaae1
MS
1392 int count; /* Job count */
1393 cupsd_job_t *job; /* Current job */
07725fee 1394
07725fee 1395
b9faaae1
MS
1396 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
1397 job;
1398 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
1399 if (!strcasecmp(job->username, username))
1400 count ++;
07725fee 1401
b9faaae1 1402 return (count);
ef416fc2 1403}
1404
1405
1406/*
b9faaae1 1407 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
ef416fc2 1408 */
1409
1410void
b9faaae1 1411cupsdLoadAllJobs(void)
bd7854cb 1412{
b9faaae1
MS
1413 char filename[1024]; /* Full filename of job.cache file */
1414 struct stat fileinfo, /* Information on job.cache file */
1415 dirinfo; /* Information on RequestRoot dir */
bd7854cb 1416
bd7854cb 1417
bd7854cb 1418
1419 /*
b9faaae1 1420 * Create the job arrays as needed...
bd7854cb 1421 */
1422
b9faaae1
MS
1423 if (!Jobs)
1424 Jobs = cupsArrayNew(compare_jobs, NULL);
1425
1426 if (!ActiveJobs)
1427 ActiveJobs = cupsArrayNew(compare_active_jobs, NULL);
1428
1429 if (!PrintingJobs)
1430 PrintingJobs = cupsArrayNew(compare_jobs, NULL);
bd7854cb 1431
1432 /*
b9faaae1 1433 * See whether the job.cache file is older than the RequestRoot directory...
bd7854cb 1434 */
1435
b9faaae1 1436 snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir);
bd7854cb 1437
b9faaae1
MS
1438 if (stat(filename, &fileinfo))
1439 {
1440 fileinfo.st_mtime = 0;
1441
1442 if (errno != ENOENT)
1443 cupsdLogMessage(CUPSD_LOG_ERROR,
1444 "Unable to get file information for \"%s\" - %s",
1445 filename, strerror(errno));
1446 }
1447
1448 if (stat(RequestRoot, &dirinfo))
1449 {
1450 dirinfo.st_mtime = 0;
1451
1452 if (errno != ENOENT)
1453 cupsdLogMessage(CUPSD_LOG_ERROR,
1454 "Unable to get directory information for \"%s\" - %s",
1455 RequestRoot, strerror(errno));
1456 }
bd7854cb 1457
1458 /*
b9faaae1 1459 * Load the most recent source for job data...
bd7854cb 1460 */
1461
b9faaae1 1462 if (dirinfo.st_mtime > fileinfo.st_mtime)
bd7854cb 1463 {
b9faaae1
MS
1464 load_request_root();
1465
1466 load_next_job_id(filename);
bd7854cb 1467 }
b9faaae1
MS
1468 else
1469 load_job_cache(filename);
bd7854cb 1470
b9faaae1
MS
1471 /*
1472 * Clean out old jobs as needed...
1473 */
1474
1475 if (MaxJobs > 0 && cupsArrayCount(Jobs) >= MaxJobs)
1476 cupsdCleanJobs();
bd7854cb 1477}
1478
1479
1480/*
b9faaae1 1481 * 'cupsdLoadJob()' - Load a single job.
bd7854cb 1482 */
1483
b9faaae1
MS
1484int /* O - 1 on success, 0 on failure */
1485cupsdLoadJob(cupsd_job_t *job) /* I - Job */
ef416fc2 1486{
b9faaae1
MS
1487 char jobfile[1024]; /* Job filename */
1488 cups_file_t *fp; /* Job file */
1489 int fileid; /* Current file ID */
1490 ipp_attribute_t *attr; /* Job attribute */
1491 const char *dest; /* Destination name */
1492 cupsd_printer_t *destptr; /* Pointer to destination */
1493 mime_type_t **filetypes; /* New filetypes array */
1494 int *compressions; /* New compressions array */
ef416fc2 1495
1496
b9faaae1
MS
1497 if (job->attrs)
1498 {
1499 if (job->state_value > IPP_JOB_STOPPED)
1500 job->access_time = time(NULL);
bd7854cb 1501
b9faaae1
MS
1502 return (1);
1503 }
ef416fc2 1504
b9faaae1 1505 if ((job->attrs = ippNew()) == NULL)
ef416fc2 1506 {
b9faaae1
MS
1507 cupsdLogJob(job, CUPSD_LOG_ERROR, "Ran out of memory for job attributes!");
1508 return (0);
ef416fc2 1509 }
1510
b9faaae1
MS
1511 /*
1512 * Load job attributes...
1513 */
ef416fc2 1514
b9faaae1 1515 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading attributes...", job->id);
bd7854cb 1516
b9faaae1
MS
1517 snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, job->id);
1518 if ((fp = cupsFileOpen(jobfile, "r")) == NULL)
1519 {
ed6e7faf 1520 cupsdLogMessage(CUPSD_LOG_ERROR,
b9faaae1
MS
1521 "[Job %d] Unable to open job control file \"%s\" - %s!",
1522 job->id, jobfile, strerror(errno));
1523 goto error;
1524 }
ef416fc2 1525
b9faaae1
MS
1526 if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, job->attrs) != IPP_DATA)
1527 {
1528 cupsdLogMessage(CUPSD_LOG_ERROR,
1529 "[Job %d] Unable to read job control file \"%s\"!", job->id,
1530 jobfile);
1531 cupsFileClose(fp);
1532 goto error;
1533 }
ef416fc2 1534
b9faaae1 1535 cupsFileClose(fp);
ef416fc2 1536
b9faaae1
MS
1537 /*
1538 * Copy attribute data to the job object...
1539 */
ef416fc2 1540
b9faaae1 1541 if (!ippFindAttribute(job->attrs, "time-at-creation", IPP_TAG_INTEGER))
ef416fc2 1542 {
b9faaae1
MS
1543 cupsdLogMessage(CUPSD_LOG_ERROR,
1544 "[Job %d] Missing or bad time-at-creation attribute in "
1545 "control file!", job->id);
1546 goto error;
ef416fc2 1547 }
b9faaae1
MS
1548
1549 if ((job->state = ippFindAttribute(job->attrs, "job-state",
1550 IPP_TAG_ENUM)) == NULL)
ef416fc2 1551 {
b9faaae1
MS
1552 cupsdLogMessage(CUPSD_LOG_ERROR,
1553 "[Job %d] Missing or bad job-state attribute in control "
1554 "file!", job->id);
1555 goto error;
1556 }
ef416fc2 1557
b9faaae1 1558 job->state_value = (ipp_jstate_t)job->state->values[0].integer;
ef416fc2 1559
b9faaae1 1560 if (!job->dest)
ef416fc2 1561 {
b9faaae1
MS
1562 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
1563 IPP_TAG_URI)) == NULL)
1564 {
1565 cupsdLogMessage(CUPSD_LOG_ERROR,
1566 "[Job %d] No job-printer-uri attribute in control file!",
1567 job->id);
1568 goto error;
1569 }
ef416fc2 1570
b9faaae1
MS
1571 if ((dest = cupsdValidateDest(attr->values[0].string.text, &(job->dtype),
1572 &destptr)) == NULL)
1573 {
1574 cupsdLogMessage(CUPSD_LOG_ERROR,
1575 "[Job %d] Unable to queue job for destination \"%s\"!",
1576 job->id, attr->values[0].string.text);
1577 goto error;
1578 }
ef416fc2 1579
b9faaae1 1580 cupsdSetString(&job->dest, dest);
f7deaa1a 1581 }
b9faaae1 1582 else if ((destptr = cupsdFindDest(job->dest)) == NULL)
ef416fc2 1583 {
b9faaae1
MS
1584 cupsdLogMessage(CUPSD_LOG_ERROR,
1585 "[Job %d] Unable to queue job for destination \"%s\"!",
1586 job->id, job->dest);
1587 goto error;
1588 }
ef416fc2 1589
b9faaae1
MS
1590 job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed",
1591 IPP_TAG_INTEGER);
1592 job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
ef416fc2 1593
b9faaae1
MS
1594 if (!job->priority)
1595 {
1596 if ((attr = ippFindAttribute(job->attrs, "job-priority",
1597 IPP_TAG_INTEGER)) == NULL)
1598 {
1599 cupsdLogMessage(CUPSD_LOG_ERROR,
1600 "[Job %d] Missing or bad job-priority attribute in "
1601 "control file!", job->id);
1602 goto error;
1603 }
1604
1605 job->priority = attr->values[0].integer;
f7deaa1a 1606 }
b9faaae1
MS
1607
1608 if (!job->username)
ef416fc2 1609 {
b9faaae1
MS
1610 if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name",
1611 IPP_TAG_NAME)) == NULL)
1612 {
1613 cupsdLogMessage(CUPSD_LOG_ERROR,
1614 "[Job %d] Missing or bad job-originating-user-name "
1615 "attribute in control file!", job->id);
1616 goto error;
1617 }
ef416fc2 1618
b9faaae1
MS
1619 cupsdSetString(&job->username, attr->values[0].string.text);
1620 }
ef416fc2 1621
b9faaae1
MS
1622 /*
1623 * Set the job hold-until time and state...
1624 */
1625
1626 if (job->state_value == IPP_JOB_HELD)
1627 {
1628 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1629 IPP_TAG_KEYWORD)) == NULL)
1630 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
1631
1632 if (attr)
1633 cupsdSetJobHoldUntil(job, attr->values[0].string.text, CUPSD_JOB_DEFAULT);
ef416fc2 1634 else
b9faaae1
MS
1635 {
1636 job->state->values[0].integer = IPP_JOB_PENDING;
1637 job->state_value = IPP_JOB_PENDING;
1638 }
f7deaa1a 1639 }
b9faaae1
MS
1640 else if (job->state_value == IPP_JOB_PROCESSING)
1641 {
1642 job->state->values[0].integer = IPP_JOB_PENDING;
1643 job->state_value = IPP_JOB_PENDING;
1644 }
1645
1646 if (!job->num_files)
ef416fc2 1647 {
1648 /*
b9faaae1 1649 * Find all the d##### files...
ef416fc2 1650 */
1651
b9faaae1
MS
1652 for (fileid = 1; fileid < 10000; fileid ++)
1653 {
1654 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
1655 job->id, fileid);
ef416fc2 1656
b9faaae1
MS
1657 if (access(jobfile, 0))
1658 break;
1659
1660 cupsdLogMessage(CUPSD_LOG_DEBUG,
1661 "[Job %d] Auto-typing document file \"%s\"...", job->id,
1662 jobfile);
1663
1664 if (fileid > job->num_files)
1665 {
1666 if (job->num_files == 0)
1667 {
1668 compressions = (int *)calloc(fileid, sizeof(int));
1669 filetypes = (mime_type_t **)calloc(fileid, sizeof(mime_type_t *));
1670 }
1671 else
1672 {
1673 compressions = (int *)realloc(job->compressions,
1674 sizeof(int) * fileid);
1675 filetypes = (mime_type_t **)realloc(job->filetypes,
1676 sizeof(mime_type_t *) *
1677 fileid);
1678 }
1679
1680 if (!compressions || !filetypes)
1681 {
1682 cupsdLogMessage(CUPSD_LOG_ERROR,
1683 "[Job %d] Ran out of memory for job file types!",
1684 job->id);
ef55b745
MS
1685
1686 ippDelete(job->attrs);
1687 job->attrs = NULL;
1688
1689 if (compressions)
1690 free(compressions);
1691
1692 if (filetypes)
1693 free(filetypes);
1694
1695 if (job->compressions)
1696 {
1697 free(job->compressions);
1698 job->compressions = NULL;
1699 }
1700
1701 if (job->filetypes)
1702 {
1703 free(job->filetypes);
1704 job->filetypes = NULL;
1705 }
1706
1707 job->num_files = 0;
1708 return (0);
b9faaae1
MS
1709 }
1710
1711 job->compressions = compressions;
1712 job->filetypes = filetypes;
1713 job->num_files = fileid;
1714 }
1715
1716 job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, jobfile, NULL,
1717 job->compressions + fileid - 1);
1718
1719 if (!job->filetypes[fileid - 1])
1720 job->filetypes[fileid - 1] = mimeType(MimeDatabase, "application",
1721 "vnd.cups-raw");
1722 }
ef416fc2 1723 }
b9faaae1
MS
1724
1725 /*
1726 * Load authentication information as needed...
1727 */
1728
1729 if (job->state_value < IPP_JOB_STOPPED)
ef416fc2 1730 {
b9faaae1 1731 snprintf(jobfile, sizeof(jobfile), "%s/a%05d", RequestRoot, job->id);
ef416fc2 1732
b9faaae1
MS
1733 cupsdClearString(&job->auth_username);
1734 cupsdClearString(&job->auth_domain);
1735 cupsdClearString(&job->auth_password);
07ed0e9a 1736 cupsdClearString(&job->auth_uid);
ef416fc2 1737
b9faaae1
MS
1738 if ((fp = cupsFileOpen(jobfile, "r")) != NULL)
1739 {
1740 int i, /* Looping var */
1741 bytes; /* Size of auth data */
1742 char line[255], /* Line from file */
1743 data[255]; /* Decoded data */
ef416fc2 1744
ef416fc2 1745
b9faaae1
MS
1746 for (i = 0;
1747 i < destptr->num_auth_info_required &&
1748 cupsFileGets(fp, line, sizeof(line));
1749 i ++)
1750 {
1751 bytes = sizeof(data);
1752 httpDecode64_2(data, &bytes, line);
1753
1754 if (!strcmp(destptr->auth_info_required[i], "username"))
1755 cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s", data);
1756 else if (!strcmp(destptr->auth_info_required[i], "domain"))
1757 cupsdSetStringf(&job->auth_domain, "AUTH_DOMAIN=%s", data);
1758 else if (!strcmp(destptr->auth_info_required[i], "password"))
1759 cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s", data);
1760 }
1761
07ed0e9a
MS
1762 if (cupsFileGets(fp, line, sizeof(line)) && isdigit(line[0] & 255))
1763 cupsdSetStringf(&job->auth_uid, "AUTH_UID=%s", line);
1764
b9faaae1
MS
1765 cupsFileClose(fp);
1766 }
ef416fc2 1767 }
1768
b9faaae1
MS
1769 job->access_time = time(NULL);
1770 return (1);
1771
1772 /*
1773 * If we get here then something bad happened...
1774 */
1775
1776 error:
1777
1778 ippDelete(job->attrs);
1779 job->attrs = NULL;
ef55b745
MS
1780
1781 if (job->compressions)
1782 {
1783 free(job->compressions);
1784 job->compressions = NULL;
1785 }
1786
1787 if (job->filetypes)
1788 {
1789 free(job->filetypes);
1790 job->filetypes = NULL;
1791 }
1792
1793 job->num_files = 0;
1794
b9faaae1
MS
1795 unlink(jobfile);
1796
1797 return (0);
ef416fc2 1798}
1799
1800
1801/*
b9faaae1 1802 * 'cupsdMoveJob()' - Move the specified job to a different destination.
ef416fc2 1803 */
1804
1805void
b9faaae1
MS
1806cupsdMoveJob(cupsd_job_t *job, /* I - Job */
1807 cupsd_printer_t *p) /* I - Destination printer or class */
ef416fc2 1808{
b9faaae1
MS
1809 ipp_attribute_t *attr; /* job-printer-uri attribute */
1810 const char *olddest; /* Old destination */
1811 cupsd_printer_t *oldp; /* Old pointer */
ef416fc2 1812
1813
1814 /*
b9faaae1 1815 * Don't move completed jobs...
ef416fc2 1816 */
1817
b9faaae1 1818 if (job->state_value > IPP_JOB_STOPPED)
ef416fc2 1819 return;
1820
1821 /*
b9faaae1 1822 * Get the old destination...
ef416fc2 1823 */
1824
b9faaae1 1825 olddest = job->dest;
ef416fc2 1826
b9faaae1
MS
1827 if (job->printer)
1828 oldp = job->printer;
ef416fc2 1829 else
b9faaae1 1830 oldp = cupsdFindDest(olddest);
ef416fc2 1831
b9faaae1
MS
1832 /*
1833 * Change the destination information...
1834 */
1835
1836 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
1837 "Stopping job prior to move.");
1838
1839 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, oldp, job,
1840 "Job #%d moved from %s to %s.", job->id, olddest,
1841 p->name);
1842
1843 cupsdSetString(&job->dest, p->name);
1844 job->dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE |
1845 CUPS_PRINTER_IMPLICIT);
1846
1847 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
1848 IPP_TAG_URI)) != NULL)
1849 cupsdSetString(&(attr->values[0].string.text), p->uri);
1850
1851 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job,
1852 "Job #%d moved from %s to %s.", job->id, olddest,
1853 p->name);
ef416fc2 1854
3dfe78b3
MS
1855 job->dirty = 1;
1856 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 1857}
1858
1859
1860/*
b9faaae1 1861 * 'cupsdReleaseJob()' - Release the specified job.
ef416fc2 1862 */
1863
1864void
b9faaae1 1865cupsdReleaseJob(cupsd_job_t *job) /* I - Job */
ef416fc2 1866{
b9faaae1
MS
1867 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReleaseJob(job=%p(%d))", job,
1868 job->id);
ef416fc2 1869
b9faaae1
MS
1870 if (job->state_value == IPP_JOB_HELD)
1871 {
1872 /*
1873 * Add trailing banner as needed...
1874 */
ef416fc2 1875
b9faaae1
MS
1876 if (job->pending_timeout)
1877 cupsdTimeoutJob(job);
ef416fc2 1878
b9faaae1
MS
1879 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
1880 "Job released by user.");
1881 }
e1d6a774 1882}
ef416fc2 1883
ef416fc2 1884
e1d6a774 1885/*
b9faaae1 1886 * 'cupsdRestartJob()' - Restart the specified job.
e1d6a774 1887 */
ef416fc2 1888
e1d6a774 1889void
b9faaae1 1890cupsdRestartJob(cupsd_job_t *job) /* I - Job */
e1d6a774 1891{
b9faaae1
MS
1892 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRestartJob(job=%p(%d))", job,
1893 job->id);
ef416fc2 1894
b9faaae1
MS
1895 if (job->state_value == IPP_JOB_STOPPED || job->num_files)
1896 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
1897 "Job restarted by user.");
1898}
ef416fc2 1899
ef416fc2 1900
b9faaae1
MS
1901/*
1902 * 'cupsdSaveAllJobs()' - Save a summary of all jobs to disk.
1903 */
ef416fc2 1904
b9faaae1
MS
1905void
1906cupsdSaveAllJobs(void)
1907{
1908 int i; /* Looping var */
1909 cups_file_t *fp; /* Job cache file */
1910 char temp[1024]; /* Temporary string */
1911 cupsd_job_t *job; /* Current job */
1912 time_t curtime; /* Current time */
1913 struct tm *curdate; /* Current date */
ef416fc2 1914
ef416fc2 1915
b9faaae1
MS
1916 snprintf(temp, sizeof(temp), "%s/job.cache", CacheDir);
1917 if ((fp = cupsFileOpen(temp, "w")) == NULL)
ef416fc2 1918 {
b9faaae1
MS
1919 cupsdLogMessage(CUPSD_LOG_ERROR,
1920 "Unable to create job cache file \"%s\" - %s",
1921 temp, strerror(errno));
c168a833 1922 return;
b9faaae1 1923 }
c168a833 1924
b9faaae1 1925 cupsdLogMessage(CUPSD_LOG_INFO, "Saving job cache file \"%s\"...", temp);
a4924f6c 1926
b9faaae1
MS
1927 /*
1928 * Restrict access to the file...
1929 */
ef416fc2 1930
b9faaae1
MS
1931 fchown(cupsFileNumber(fp), getuid(), Group);
1932 fchmod(cupsFileNumber(fp), ConfigFilePerm);
bd7854cb 1933
b9faaae1
MS
1934 /*
1935 * Write a small header to the file...
1936 */
bd7854cb 1937
b9faaae1
MS
1938 curtime = time(NULL);
1939 curdate = localtime(&curtime);
1940 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
ef416fc2 1941
b9faaae1
MS
1942 cupsFilePuts(fp, "# Job cache file for " CUPS_SVERSION "\n");
1943 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
1944 cupsFilePrintf(fp, "NextJobId %d\n", NextJobId);
f7deaa1a 1945
b9faaae1
MS
1946 /*
1947 * Write each job known to the system...
1948 */
f7deaa1a 1949
b9faaae1
MS
1950 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
1951 job;
1952 job = (cupsd_job_t *)cupsArrayNext(Jobs))
ef416fc2 1953 {
b9faaae1
MS
1954 cupsFilePrintf(fp, "<Job %d>\n", job->id);
1955 cupsFilePrintf(fp, "State %d\n", job->state_value);
1956 cupsFilePrintf(fp, "Priority %d\n", job->priority);
8b116e60 1957 cupsFilePrintf(fp, "HoldUntil %d\n", (int)job->hold_until);
b9faaae1
MS
1958 cupsFilePrintf(fp, "Username %s\n", job->username);
1959 cupsFilePrintf(fp, "Destination %s\n", job->dest);
1960 cupsFilePrintf(fp, "DestType %d\n", job->dtype);
1961 cupsFilePrintf(fp, "NumFiles %d\n", job->num_files);
1962 for (i = 0; i < job->num_files; i ++)
1963 cupsFilePrintf(fp, "File %d %s/%s %d\n", i + 1, job->filetypes[i]->super,
1964 job->filetypes[i]->type, job->compressions[i]);
1965 cupsFilePuts(fp, "</Job>\n");
ef416fc2 1966 }
b9faaae1
MS
1967
1968 cupsFileClose(fp);
e1d6a774 1969}
ef416fc2 1970
ef416fc2 1971
e1d6a774 1972/*
b9faaae1 1973 * 'cupsdSaveJob()' - Save a job to disk.
e1d6a774 1974 */
ef416fc2 1975
e1d6a774 1976void
b9faaae1 1977cupsdSaveJob(cupsd_job_t *job) /* I - Job */
e1d6a774 1978{
b9faaae1
MS
1979 char filename[1024]; /* Job control filename */
1980 cups_file_t *fp; /* Job file */
3dfe78b3 1981
ef416fc2 1982
b9faaae1
MS
1983 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSaveJob(job=%p(%d)): job->attrs=%p",
1984 job, job->id, job->attrs);
ef416fc2 1985
b9faaae1 1986 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id);
ef416fc2 1987
b9faaae1
MS
1988 if ((fp = cupsFileOpen(filename, "w")) == NULL)
1989 {
1990 cupsdLogMessage(CUPSD_LOG_ERROR,
1991 "[Job %d] Unable to create job control file \"%s\" - %s.",
1992 job->id, filename, strerror(errno));
1993 return;
1994 }
ef416fc2 1995
b9faaae1
MS
1996 fchmod(cupsFileNumber(fp), 0600);
1997 fchown(cupsFileNumber(fp), RunUser, Group);
ef416fc2 1998
b9faaae1 1999 job->attrs->state = IPP_IDLE;
ef416fc2 2000
b9faaae1
MS
2001 if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL,
2002 job->attrs) != IPP_DATA)
2003 cupsdLogMessage(CUPSD_LOG_ERROR,
2004 "[Job %d] Unable to write job control file!", job->id);
ef416fc2 2005
b9faaae1 2006 cupsFileClose(fp);
ef416fc2 2007
b9faaae1 2008 job->dirty = 0;
e1d6a774 2009}
ef416fc2 2010
ef416fc2 2011
e1d6a774 2012/*
b9faaae1 2013 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job.
e1d6a774 2014 */
ef416fc2 2015
b9faaae1
MS
2016void
2017cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */
2018 const char *when, /* I - When to resume */
2019 int update)/* I - Update job-hold-until attr? */
e1d6a774 2020{
b9faaae1
MS
2021 time_t curtime; /* Current time */
2022 struct tm *curdate; /* Current date */
2023 int hour; /* Hold hour */
2024 int minute; /* Hold minute */
2025 int second = 0; /* Hold second */
ef416fc2 2026
ef416fc2 2027
b9faaae1
MS
2028 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2029 "cupsdSetJobHoldUntil(job=%p(%d), when=\"%s\", update=%d)",
2030 job, job->id, when, update);
ef416fc2 2031
b9faaae1 2032 if (update)
ef416fc2 2033 {
e1d6a774 2034 /*
b9faaae1 2035 * Update the job-hold-until attribute...
e1d6a774 2036 */
ef416fc2 2037
b9faaae1 2038 ipp_attribute_t *attr; /* job-hold-until attribute */
ef416fc2 2039
b9faaae1
MS
2040 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
2041 IPP_TAG_KEYWORD)) == NULL)
2042 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
ef416fc2 2043
b9faaae1
MS
2044 if (attr)
2045 cupsdSetString(&(attr->values[0].string.text), when);
2046 else
2047 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2048 "job-hold-until", NULL, when);
ef416fc2 2049
b9faaae1
MS
2050 if (attr)
2051 {
2052 if (isdigit(when[0] & 255))
2053 attr->value_tag = IPP_TAG_NAME;
2054 else
2055 attr->value_tag = IPP_TAG_KEYWORD;
2056
2057 job->dirty = 1;
2058 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2059 }
2060 }
ef416fc2 2061
b9faaae1
MS
2062 /*
2063 * Update the hold time...
2064 */
2065
2066 if (!strcmp(when, "indefinite") || !strcmp(when, "auth-info-required"))
2067 {
e1d6a774 2068 /*
b9faaae1 2069 * Hold indefinitely...
e1d6a774 2070 */
ef416fc2 2071
b9faaae1
MS
2072 job->hold_until = 0;
2073 }
2074 else if (!strcmp(when, "day-time"))
2075 {
e1d6a774 2076 /*
b9faaae1 2077 * Hold to 6am the next morning unless local time is < 6pm.
e1d6a774 2078 */
ef416fc2 2079
b9faaae1
MS
2080 curtime = time(NULL);
2081 curdate = localtime(&curtime);
ef416fc2 2082
b9faaae1
MS
2083 if (curdate->tm_hour < 18)
2084 job->hold_until = curtime;
2085 else
2086 job->hold_until = curtime +
2087 ((29 - curdate->tm_hour) * 60 + 59 -
2088 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2089 }
2090 else if (!strcmp(when, "evening") || !strcmp(when, "night"))
2091 {
2092 /*
2093 * Hold to 6pm unless local time is > 6pm or < 6am.
2094 */
ef416fc2 2095
b9faaae1
MS
2096 curtime = time(NULL);
2097 curdate = localtime(&curtime);
ef416fc2 2098
b9faaae1
MS
2099 if (curdate->tm_hour < 6 || curdate->tm_hour >= 18)
2100 job->hold_until = curtime;
2101 else
2102 job->hold_until = curtime +
2103 ((17 - curdate->tm_hour) * 60 + 59 -
2104 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2105 }
2106 else if (!strcmp(when, "second-shift"))
2107 {
2108 /*
2109 * Hold to 4pm unless local time is > 4pm.
2110 */
ef416fc2 2111
b9faaae1
MS
2112 curtime = time(NULL);
2113 curdate = localtime(&curtime);
ef416fc2 2114
b9faaae1
MS
2115 if (curdate->tm_hour >= 16)
2116 job->hold_until = curtime;
2117 else
2118 job->hold_until = curtime +
2119 ((15 - curdate->tm_hour) * 60 + 59 -
2120 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2121 }
2122 else if (!strcmp(when, "third-shift"))
2123 {
2124 /*
2125 * Hold to 12am unless local time is < 8am.
2126 */
ef416fc2 2127
b9faaae1
MS
2128 curtime = time(NULL);
2129 curdate = localtime(&curtime);
ef416fc2 2130
b9faaae1
MS
2131 if (curdate->tm_hour < 8)
2132 job->hold_until = curtime;
2133 else
2134 job->hold_until = curtime +
2135 ((23 - curdate->tm_hour) * 60 + 59 -
2136 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2137 }
2138 else if (!strcmp(when, "weekend"))
2139 {
2140 /*
2141 * Hold to weekend unless we are in the weekend.
2142 */
ef416fc2 2143
b9faaae1
MS
2144 curtime = time(NULL);
2145 curdate = localtime(&curtime);
ef416fc2 2146
b9faaae1
MS
2147 if (curdate->tm_wday == 0 || curdate->tm_wday == 6)
2148 job->hold_until = curtime;
2149 else
2150 job->hold_until = curtime +
2151 (((5 - curdate->tm_wday) * 24 +
2152 (17 - curdate->tm_hour)) * 60 + 59 -
2153 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2154 }
2155 else if (sscanf(when, "%d:%d:%d", &hour, &minute, &second) >= 2)
2156 {
2157 /*
2158 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
2159 */
bd7854cb 2160
b9faaae1
MS
2161 curtime = time(NULL);
2162 curdate = gmtime(&curtime);
2163
2164 job->hold_until = curtime +
2165 ((hour - curdate->tm_hour) * 60 + minute -
2166 curdate->tm_min) * 60 + second - curdate->tm_sec;
2167
2168 /*
2169 * Hold until next day as needed...
2170 */
2171
2172 if (job->hold_until < curtime)
2173 job->hold_until += 24 * 60 * 60;
ef416fc2 2174 }
2175
b9faaae1
MS
2176 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil: hold_until=%d",
2177 (int)job->hold_until);
e1d6a774 2178}
ef416fc2 2179
ef416fc2 2180
e1d6a774 2181/*
b9faaae1
MS
2182 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
2183 * the list as needed.
e1d6a774 2184 */
ef416fc2 2185
b9faaae1
MS
2186void
2187cupsdSetJobPriority(
2188 cupsd_job_t *job, /* I - Job ID */
2189 int priority) /* I - New priority (0 to 100) */
e1d6a774 2190{
b9faaae1 2191 ipp_attribute_t *attr; /* Job attribute */
ef416fc2 2192
ef416fc2 2193
e1d6a774 2194 /*
b9faaae1 2195 * Don't change completed jobs...
e1d6a774 2196 */
bd7854cb 2197
b9faaae1 2198 if (job->state_value >= IPP_JOB_PROCESSING)
ef416fc2 2199 return;
ef416fc2 2200
e1d6a774 2201 /*
b9faaae1 2202 * Set the new priority and re-add the job into the active list...
e1d6a774 2203 */
ef416fc2 2204
b9faaae1 2205 cupsArrayRemove(ActiveJobs, job);
ef416fc2 2206
b9faaae1 2207 job->priority = priority;
ef416fc2 2208
b9faaae1
MS
2209 if ((attr = ippFindAttribute(job->attrs, "job-priority",
2210 IPP_TAG_INTEGER)) != NULL)
2211 attr->values[0].integer = priority;
2212 else
2213 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
2214 priority);
ef416fc2 2215
b9faaae1 2216 cupsArrayAdd(ActiveJobs, job);
bd7854cb 2217
b9faaae1
MS
2218 job->dirty = 1;
2219 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2220}
bd7854cb 2221
ef416fc2 2222
b9faaae1
MS
2223/*
2224 * 'cupsdSetJobState()' - Set the state of the specified print job.
2225 */
e1d6a774 2226
b9faaae1
MS
2227void
2228cupsdSetJobState(
2229 cupsd_job_t *job, /* I - Job to cancel */
2230 ipp_jstate_t newstate, /* I - New job state */
2231 cupsd_jobaction_t action, /* I - Action to take */
2232 const char *message, /* I - Message to log */
2233 ...) /* I - Additional arguments as needed */
2234{
2235 int i; /* Looping var */
2236 ipp_jstate_t oldstate; /* Old state */
2237 char filename[1024]; /* Job filename */
2238 ipp_attribute_t *attr; /* Job attribute */
ef416fc2 2239
ef416fc2 2240
b9faaae1
MS
2241 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2242 "cupsdSetJobState(job=%p(%d), state=%d, newstate=%d, "
2243 "action=%d, message=\"%s\")", job, job->id, job->state_value,
2244 newstate, action, message ? message : "(null)");
ef416fc2 2245
bd7854cb 2246
b9faaae1
MS
2247 /*
2248 * Make sure we have the job attributes...
2249 */
e1d6a774 2250
b9faaae1
MS
2251 if (!cupsdLoadJob(job))
2252 return;
bd7854cb 2253
ba55dc12
MS
2254 /*
2255 * Don't do anything if the state is unchanged and we aren't purging the
2256 * job...
2257 */
ef416fc2 2258
ba55dc12
MS
2259 oldstate = job->state_value;
2260 if (newstate == oldstate && action != CUPSD_JOB_PURGE)
2261 return;
e1d6a774 2262
b9faaae1
MS
2263 /*
2264 * Stop any processes that are working on the current job...
2265 */
e1d6a774 2266
b9faaae1 2267 if (oldstate == IPP_JOB_PROCESSING)
ef55b745 2268 stop_job(job, action);
b9faaae1
MS
2269
2270 /*
2271 * Set the new job state...
2272 */
2273
2274 job->state->values[0].integer = newstate;
2275 job->state_value = newstate;
2276
2277 switch (newstate)
2278 {
2279 case IPP_JOB_PENDING :
2280 /*
2281 * Update job-hold-until as needed...
2282 */
2283
2284 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
2285 IPP_TAG_KEYWORD)) == NULL)
2286 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
2287
2288 if (attr)
ef416fc2 2289 {
b9faaae1
MS
2290 attr->value_tag = IPP_TAG_KEYWORD;
2291 cupsdSetString(&(attr->values[0].string.text), "no-hold");
e1d6a774 2292 }
ef416fc2 2293
b9faaae1
MS
2294 default :
2295 break;
ef416fc2 2296
b9faaae1
MS
2297 case IPP_JOB_ABORTED :
2298 case IPP_JOB_CANCELED :
2299 case IPP_JOB_COMPLETED :
2300 set_time(job, "time-at-completed");
2301 break;
2302 }
ef416fc2 2303
b9faaae1
MS
2304 /*
2305 * Log message as needed...
2306 */
ef416fc2 2307
b9faaae1
MS
2308 if (message)
2309 {
2310 char buffer[2048]; /* Message buffer */
2311 va_list ap; /* Pointer to additional arguments */
ef416fc2 2312
b9faaae1
MS
2313 va_start(ap, message);
2314 vsnprintf(buffer, sizeof(buffer), message, ap);
2315 va_end(ap);
bd7854cb 2316
b9faaae1
MS
2317 if (newstate > IPP_JOB_STOPPED)
2318 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, "%s", buffer);
2319 else
2320 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "%s", buffer);
2321
2322 if (newstate == IPP_JOB_STOPPED || newstate == IPP_JOB_ABORTED)
2323 cupsdLogJob(job, CUPSD_LOG_ERROR, "%s", buffer);
2324 else
2325 cupsdLogJob(job, CUPSD_LOG_INFO, "%s", buffer);
2326 }
2327
2328 /*
2329 * Handle post-state-change actions...
2330 */
2331
2332 switch (newstate)
2333 {
2334 case IPP_JOB_PROCESSING :
e1d6a774 2335 /*
b9faaae1 2336 * Add the job to the "printing" list...
e1d6a774 2337 */
bd7854cb 2338
b9faaae1
MS
2339 if (!cupsArrayFind(PrintingJobs, job))
2340 cupsArrayAdd(PrintingJobs, job);
ef416fc2 2341
b9faaae1
MS
2342 /*
2343 * Set the processing time...
2344 */
ef416fc2 2345
b9faaae1
MS
2346 set_time(job, "time-at-processing");
2347
2348 case IPP_JOB_PENDING :
2349 case IPP_JOB_HELD :
2350 case IPP_JOB_STOPPED :
e1d6a774 2351 /*
b9faaae1 2352 * Make sure the job is in the active list...
e1d6a774 2353 */
ef416fc2 2354
b9faaae1
MS
2355 if (!cupsArrayFind(ActiveJobs, job))
2356 cupsArrayAdd(ActiveJobs, job);
ef416fc2 2357
b9faaae1
MS
2358 /*
2359 * Save the job state to disk...
2360 */
ef416fc2 2361
b9faaae1
MS
2362 job->dirty = 1;
2363 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2364 break;
ef416fc2 2365
b9faaae1
MS
2366 case IPP_JOB_ABORTED :
2367 case IPP_JOB_CANCELED :
2368 case IPP_JOB_COMPLETED :
7a0cbd5e
MS
2369 if (newstate == IPP_JOB_CANCELED)
2370 {
2371 /*
2372 * Remove the job from the active list if there are no processes still
2373 * running for it...
2374 */
ef416fc2 2375
7a0cbd5e
MS
2376 for (i = 0; job->filters[i] < 0; i++);
2377
2378 if (!job->filters[i] && job->backend <= 0)
2379 cupsArrayRemove(ActiveJobs, job);
2380 }
2381 else
2382 {
2383 /*
2384 * Otherwise just remove the job from the active list immediately...
2385 */
2386
2387 cupsArrayRemove(ActiveJobs, job);
2388 }
ef416fc2 2389
b9faaae1 2390 /*
7a0cbd5e 2391 * Expire job subscriptions since the job is now "completed"...
b9faaae1 2392 */
ef416fc2 2393
7a0cbd5e 2394 cupsdExpireSubscriptions(NULL, job);
ef416fc2 2395
b9faaae1
MS
2396#ifdef __APPLE__
2397 /*
2398 * If we are going to sleep and the PrintingJobs count is now 0, allow the
2399 * sleep to happen immediately...
2400 */
ef416fc2 2401
b9faaae1
MS
2402 if (Sleeping && cupsArrayCount(PrintingJobs) == 0)
2403 cupsdAllowSleep();
2404#endif /* __APPLE__ */
ef416fc2 2405
b9faaae1
MS
2406 /*
2407 * Remove any authentication data...
2408 */
bd7854cb 2409
b9faaae1
MS
2410 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
2411 if (cupsdRemoveFile(filename) && errno != ENOENT)
2412 cupsdLogMessage(CUPSD_LOG_ERROR,
2413 "Unable to remove authentication cache: %s",
2414 strerror(errno));
bd7854cb 2415
b9faaae1
MS
2416 cupsdClearString(&job->auth_username);
2417 cupsdClearString(&job->auth_domain);
2418 cupsdClearString(&job->auth_password);
07ed0e9a 2419 cupsdClearString(&job->auth_uid);
ef416fc2 2420
b9faaae1
MS
2421 /*
2422 * Remove the print file for good if we aren't preserving jobs or
2423 * files...
2424 */
e1d6a774 2425
b9faaae1
MS
2426 if (!JobHistory || !JobFiles || action == CUPSD_JOB_PURGE)
2427 {
2428 for (i = 1; i <= job->num_files; i ++)
2429 {
2430 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
2431 job->id, i);
2432 unlink(filename);
2433 }
e1d6a774 2434
b9faaae1
MS
2435 if (job->num_files > 0)
2436 {
2437 free(job->filetypes);
2438 free(job->compressions);
ef416fc2 2439
b9faaae1
MS
2440 job->num_files = 0;
2441 job->filetypes = NULL;
2442 job->compressions = NULL;
2443 }
2444 }
ef416fc2 2445
b9faaae1
MS
2446 if (JobHistory && action != CUPSD_JOB_PURGE)
2447 {
2448 /*
2449 * Save job state info...
2450 */
e1d6a774 2451
b9faaae1
MS
2452 job->dirty = 1;
2453 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2454 }
ba55dc12
MS
2455 else if (!job->printer)
2456 {
2457 /*
2458 * Delete the job immediately if not actively printing...
2459 */
2460
2461 cupsdDeleteJob(job, CUPSD_JOB_PURGE);
2462 job = NULL;
2463 }
b9faaae1 2464 break;
e1d6a774 2465 }
2466
e07d4801
MS
2467 /*
2468 * Finalize the job immediately if we forced things...
2469 */
2470
ba55dc12 2471 if (action >= CUPSD_JOB_FORCE && job && job->printer)
ef55b745 2472 finalize_job(job, 0);
e07d4801 2473
e1d6a774 2474 /*
b9faaae1 2475 * Update the server "busy" state...
e1d6a774 2476 */
ef416fc2 2477
b9faaae1
MS
2478 cupsdSetBusyState();
2479}
e00b005a 2480
ef416fc2 2481
b9faaae1
MS
2482/*
2483 * 'cupsdStopAllJobs()' - Stop all print jobs.
2484 */
ef416fc2 2485
b9faaae1
MS
2486void
2487cupsdStopAllJobs(
238c3832
MS
2488 cupsd_jobaction_t action, /* I - Action */
2489 int kill_delay) /* I - Number of seconds before we kill */
b9faaae1
MS
2490{
2491 cupsd_job_t *job; /* Current job */
ef416fc2 2492
ef416fc2 2493
b9faaae1 2494 DEBUG_puts("cupsdStopAllJobs()");
ef416fc2 2495
b9faaae1
MS
2496 for (job = (cupsd_job_t *)cupsArrayFirst(PrintingJobs);
2497 job;
2498 job = (cupsd_job_t *)cupsArrayNext(PrintingJobs))
238c3832
MS
2499 {
2500 if (kill_delay)
2501 job->kill_time = time(NULL) + kill_delay;
2502
b9faaae1 2503 cupsdSetJobState(job, IPP_JOB_PENDING, action, NULL);
238c3832 2504 }
b9faaae1 2505}
bd7854cb 2506
bd7854cb 2507
b9faaae1
MS
2508/*
2509 * 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory.
2510 */
ef416fc2 2511
b9faaae1
MS
2512void
2513cupsdUnloadCompletedJobs(void)
2514{
2515 cupsd_job_t *job; /* Current job */
2516 time_t expire; /* Expiration time */
ef416fc2 2517
b9faaae1
MS
2518
2519 expire = time(NULL) - 60;
2520
2521 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
2522 job;
2523 job = (cupsd_job_t *)cupsArrayNext(Jobs))
2524 if (job->attrs && job->state_value >= IPP_JOB_STOPPED && !job->printer &&
2525 job->access_time < expire)
2526 {
2527 if (job->dirty)
2528 cupsdSaveJob(job);
2529
2530 unload_job(job);
2531 }
e1d6a774 2532}
ef416fc2 2533
ef416fc2 2534
e1d6a774 2535/*
b9faaae1 2536 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
e1d6a774 2537 */
ef416fc2 2538
b9faaae1
MS
2539static int /* O - Difference */
2540compare_active_jobs(void *first, /* I - First job */
2541 void *second, /* I - Second job */
2542 void *data) /* I - App data (not used) */
e1d6a774 2543{
b9faaae1 2544 int diff; /* Difference */
ef416fc2 2545
ef416fc2 2546
b9faaae1
MS
2547 if ((diff = ((cupsd_job_t *)second)->priority -
2548 ((cupsd_job_t *)first)->priority) != 0)
2549 return (diff);
2550 else
2551 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
e1d6a774 2552}
bd7854cb 2553
ef416fc2 2554
e1d6a774 2555/*
b9faaae1 2556 * 'compare_jobs()' - Compare the job IDs of two jobs.
e1d6a774 2557 */
ef416fc2 2558
b9faaae1
MS
2559static int /* O - Difference */
2560compare_jobs(void *first, /* I - First job */
2561 void *second, /* I - Second job */
2562 void *data) /* I - App data (not used) */
e1d6a774 2563{
b9faaae1
MS
2564 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
2565}
ef416fc2 2566
ef416fc2 2567
178cb736
MS
2568/*
2569 * 'dump_job_history()' - Dump any debug messages for a job.
2570 */
2571
2572static void
2573dump_job_history(cupsd_job_t *job) /* I - Job */
2574{
2575 int i, /* Looping var */
2576 oldsize; /* Current MaxLogSize */
2577 struct tm *date; /* Date/time value */
2578 cupsd_joblog_t *message; /* Current message */
2579 char temp[2048], /* Log message */
2580 *ptr, /* Pointer into log message */
2581 start[256], /* Start time */
2582 end[256]; /* End time */
2583 cupsd_printer_t *printer; /* Printer for job */
2584
2585
2586 /*
2587 * See if we have anything to dump...
2588 */
2589
2590 if (!job->history)
2591 return;
2592
2593 /*
2594 * Disable log rotation temporarily...
2595 */
2596
2597 oldsize = MaxLogSize;
2598 MaxLogSize = 0;
2599
2600 /*
2601 * Copy the debug messages to the log...
2602 */
2603
2604 message = (cupsd_joblog_t *)cupsArrayFirst(job->history);
2605 date = localtime(&(message->time));
2606 strftime(start, sizeof(start), "%X", date);
2607
2608 message = (cupsd_joblog_t *)cupsArrayLast(job->history);
2609 date = localtime(&(message->time));
2610 strftime(end, sizeof(end), "%X", date);
2611
2612 snprintf(temp, sizeof(temp),
2613 "[Job %d] The following messages were recorded from %s to %s",
2614 job->id, start, end);
2615 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2616
2617 for (message = (cupsd_joblog_t *)cupsArrayFirst(job->history);
2618 message;
2619 message = (cupsd_joblog_t *)cupsArrayNext(job->history))
2620 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, message->message);
2621
2622 snprintf(temp, sizeof(temp), "[Job %d] End of messages", job->id);
2623 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2624
2625 /*
2626 * Log the printer state values...
2627 */
2628
2629 if ((printer = job->printer) == NULL)
2630 printer = cupsdFindDest(job->dest);
2631
2632 if (printer)
2633 {
2634 snprintf(temp, sizeof(temp), "[Job %d] printer-state=%d(%s)", job->id,
2635 printer->state,
2636 printer->state == IPP_PRINTER_IDLE ? "idle" :
2637 printer->state == IPP_PRINTER_PROCESSING ? "processing" :
2638 "stopped");
2639 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2640
2641 snprintf(temp, sizeof(temp), "[Job %d] printer-state-message=\"%s\"",
2642 job->id, printer->state_message);
2643 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2644
2645 snprintf(temp, sizeof(temp), "[Job %d] printer-state-reasons=", job->id);
2646 ptr = temp + strlen(temp);
2647 if (printer->num_reasons == 0)
2648 strlcpy(ptr, "none", sizeof(temp) - (ptr - temp));
2649 else
2650 {
2651 for (i = 0;
2652 i < printer->num_reasons && ptr < (temp + sizeof(temp) - 2);
2653 i ++)
2654 {
2655 if (i)
2656 *ptr++ = ',';
2657
2658 strlcpy(ptr, printer->reasons[i], sizeof(temp) - (ptr - temp));
2659 ptr += strlen(ptr);
2660 }
2661 }
2662 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2663 }
2664
2665 /*
2666 * Restore log file rotation...
2667 */
2668
2669 MaxLogSize = oldsize;
2670
2671 /*
2672 * Free all messages...
2673 */
2674
2675 free_job_history(job);
2676}
2677
2678
2679/*
2680 * 'free_job_history()' - Free any log history.
2681 */
2682
2683static void
2684free_job_history(cupsd_job_t *job) /* I - Job */
2685{
2686 char *message; /* Current message */
2687
2688
2689 if (!job->history)
2690 return;
2691
2692 for (message = (char *)cupsArrayFirst(job->history);
2693 message;
2694 message = (char *)cupsArrayNext(job->history))
2695 free(message);
2696
2697 cupsArrayDelete(job->history);
2698 job->history = NULL;
2699}
2700
2701
b9faaae1
MS
2702/*
2703 * 'finalize_job()' - Cleanup after job filter processes and support data.
2704 */
ef416fc2 2705
b9faaae1 2706static void
ef55b745
MS
2707finalize_job(cupsd_job_t *job, /* I - Job */
2708 int set_job_state) /* I - 1 = set the job state */
b9faaae1
MS
2709{
2710 ipp_pstate_t printer_state; /* New printer state value */
2711 ipp_jstate_t job_state; /* New job state value */
2712 const char *message; /* Message for job state */
2713 char buffer[1024]; /* Buffer for formatted messages */
e00b005a 2714
b9faaae1
MS
2715
2716 cupsdLogMessage(CUPSD_LOG_DEBUG2, "finalize_job(job=%p(%d))", job, job->id);
ef416fc2 2717
e1d6a774 2718 /*
4d301e69 2719 * Clear the "connecting-to-device" reason, which is only valid when a printer
07ed0e9a 2720 * is processing, along with any remote printing job state...
e1d6a774 2721 */
ef416fc2 2722
07ed0e9a
MS
2723 cupsdSetPrinterReasons(job->printer, "-connecting-to-device,"
2724 "cups-remote-pending,"
2725 "cups-remote-pending-held,"
2726 "cups-remote-processing,"
2727 "cups-remote-stopped,"
2728 "cups-remote-canceled,"
2729 "cups-remote-aborted,"
2730 "cups-remote-completed");
ef416fc2 2731
e1d6a774 2732 /*
b9faaae1
MS
2733 * Similarly, clear the "offline-report" reason for non-USB devices since we
2734 * rarely have current information for network devices...
e1d6a774 2735 */
ef416fc2 2736
b9faaae1
MS
2737 if (strncmp(job->printer->device_uri, "usb:", 4))
2738 cupsdSetPrinterReasons(job->printer, "-offline-report");
ef416fc2 2739
b9faaae1
MS
2740 /*
2741 * Free the security profile...
2742 */
ef416fc2 2743
b9faaae1
MS
2744 cupsdDestroyProfile(job->profile);
2745 job->profile = NULL;
ef416fc2 2746
10d09e33
MS
2747 /*
2748 * Clear the unresponsive job watchdog timer...
2749 */
2750
2751 job->kill_time = 0;
2752
b9faaae1
MS
2753 /*
2754 * Close pipes and status buffer...
2755 */
ef416fc2 2756
b9faaae1
MS
2757 cupsdClosePipe(job->print_pipes);
2758 cupsdClosePipe(job->back_pipes);
2759 cupsdClosePipe(job->side_pipes);
ef416fc2 2760
e07d4801
MS
2761 cupsdRemoveSelect(job->status_pipes[0]);
2762 cupsdClosePipe(job->status_pipes);
b9faaae1
MS
2763 cupsdStatBufDelete(job->status_buffer);
2764 job->status_buffer = NULL;
ef416fc2 2765
e1d6a774 2766 /*
b9faaae1 2767 * Process the exit status...
e1d6a774 2768 */
ef416fc2 2769
1340db2d
MS
2770 if (job->printer->state == IPP_PRINTER_PROCESSING)
2771 printer_state = IPP_PRINTER_IDLE;
2772 else
2773 printer_state = job->printer->state;
2774
2775 switch (job_state = job->state_value)
2776 {
2777 case IPP_JOB_PENDING :
2778 message = "Job paused.";
2779 break;
2780
2781 case IPP_JOB_HELD :
2782 message = "Job held.";
2783 break;
2784
2785 default :
2786 case IPP_JOB_PROCESSING :
2787 case IPP_JOB_COMPLETED :
f11a948a
MS
2788 job_state = IPP_JOB_COMPLETED;
2789 message = "Job completed.";
1340db2d
MS
2790 break;
2791
2792 case IPP_JOB_STOPPED :
2793 message = "Job stopped.";
2794 break;
2795
2796 case IPP_JOB_CANCELED :
2797 message = "Job canceled.";
2798 break;
2799
2800 case IPP_JOB_ABORTED :
2801 message = "Job aborted.";
2802 break;
2803 }
ef416fc2 2804
b9faaae1 2805 if (job->status < 0)
e1d6a774 2806 {
2807 /*
b9faaae1 2808 * Backend had errors...
e1d6a774 2809 */
ef416fc2 2810
b9faaae1
MS
2811 int exit_code; /* Exit code from backend */
2812
ef416fc2 2813
e1d6a774 2814 /*
b9faaae1
MS
2815 * Convert the status to an exit code. Due to the way the W* macros are
2816 * implemented on MacOS X (bug?), we have to store the exit status in a
2817 * variable first and then convert...
e1d6a774 2818 */
ef416fc2 2819
b9faaae1
MS
2820 exit_code = -job->status;
2821 if (WIFEXITED(exit_code))
2822 exit_code = WEXITSTATUS(exit_code);
2823 else
2824 exit_code = job->status;
ef416fc2 2825
b9faaae1
MS
2826 cupsdLogJob(job, CUPSD_LOG_INFO, "Backend returned status %d (%s)",
2827 exit_code,
2828 exit_code == CUPS_BACKEND_FAILED ? "failed" :
2829 exit_code == CUPS_BACKEND_AUTH_REQUIRED ?
2830 "authentication required" :
2831 exit_code == CUPS_BACKEND_HOLD ? "hold job" :
2832 exit_code == CUPS_BACKEND_STOP ? "stop printer" :
2833 exit_code == CUPS_BACKEND_CANCEL ? "cancel job" :
2834 exit_code < 0 ? "crashed" : "unknown");
ef416fc2 2835
ef416fc2 2836 /*
b9faaae1 2837 * Do what needs to be done...
ef416fc2 2838 */
2839
b9faaae1 2840 switch (exit_code)
f7deaa1a 2841 {
b9faaae1
MS
2842 default :
2843 case CUPS_BACKEND_FAILED :
2844 /*
2845 * Backend failure, use the error-policy to determine how to
2846 * act...
2847 */
f7deaa1a 2848
b9faaae1
MS
2849 if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
2850 {
2851 /*
2852 * Queued on a class - mark the job as pending and we'll retry on
2853 * another printer...
2854 */
f7deaa1a 2855
8b116e60
MS
2856 if (job_state == IPP_JOB_COMPLETED)
2857 {
2858 job_state = IPP_JOB_PENDING;
2859 message = "Retrying job on another printer.";
2860 }
b9faaae1
MS
2861 }
2862 else if (!strcmp(job->printer->error_policy, "retry-current-job"))
2863 {
2864 /*
2865 * The error policy is "retry-current-job" - mark the job as pending
2866 * and we'll retry on the same printer...
2867 */
f7deaa1a 2868
8b116e60
MS
2869 if (job_state == IPP_JOB_COMPLETED)
2870 {
2871 job_state = IPP_JOB_PENDING;
2872 message = "Retrying job on same printer.";
2873 }
b9faaae1
MS
2874 }
2875 else if ((job->printer->type & CUPS_PRINTER_FAX) ||
2876 !strcmp(job->printer->error_policy, "retry-job"))
2877 {
8b116e60 2878 if (job_state == IPP_JOB_COMPLETED)
b9faaae1
MS
2879 {
2880 /*
8b116e60
MS
2881 * The job was queued on a fax or the error policy is "retry-job" -
2882 * hold the job if the number of retries is less than the
2883 * JobRetryLimit, otherwise abort the job.
b9faaae1 2884 */
ef416fc2 2885
8b116e60
MS
2886 job->tries ++;
2887
7cf5915e 2888 if (job->tries > JobRetryLimit && JobRetryLimit > 0)
8b116e60
MS
2889 {
2890 /*
2891 * Too many tries...
2892 */
2893
2894 snprintf(buffer, sizeof(buffer),
2895 "Job aborted after %d unsuccessful attempts.",
2896 JobRetryLimit);
2897 job_state = IPP_JOB_ABORTED;
2898 message = buffer;
2899 }
2900 else
2901 {
2902 /*
2903 * Try again in N seconds...
2904 */
ef416fc2 2905
8b116e60
MS
2906 snprintf(buffer, sizeof(buffer),
2907 "Job held for %d seconds since it could not be sent.",
2908 JobRetryInterval);
0268488e
MS
2909
2910 job->hold_until = time(NULL) + JobRetryInterval;
2911 job_state = IPP_JOB_HELD;
2912 message = buffer;
8b116e60
MS
2913 }
2914 }
b9faaae1 2915 }
8b116e60
MS
2916 else if (!strcmp(job->printer->error_policy, "abort-job") &&
2917 job_state == IPP_JOB_COMPLETED)
b9faaae1
MS
2918 {
2919 job_state = IPP_JOB_ABORTED;
2920 message = "Job aborted due to backend errors; please consult "
2921 "the error_log file for details.";
2922 }
f11a948a 2923 else if (job->state_value == IPP_JOB_PROCESSING)
b9faaae1 2924 {
f11a948a 2925 job_state = IPP_JOB_PENDING;
b9faaae1 2926 printer_state = IPP_PRINTER_STOPPED;
b9faaae1 2927 message = "Printer stopped due to backend errors; please "
8b116e60 2928 "consult the error_log file for details.";
b9faaae1
MS
2929 }
2930 break;
bd7854cb 2931
b9faaae1
MS
2932 case CUPS_BACKEND_CANCEL :
2933 /*
2934 * Abort the job...
2935 */
bd7854cb 2936
8b116e60
MS
2937 if (job_state == IPP_JOB_COMPLETED)
2938 {
2939 job_state = IPP_JOB_ABORTED;
2940 message = "Job aborted due to backend errors; please consult "
2941 "the error_log file for details.";
2942 }
b9faaae1 2943 break;
bd7854cb 2944
b9faaae1 2945 case CUPS_BACKEND_HOLD :
8b116e60
MS
2946 if (job_state == IPP_JOB_COMPLETED)
2947 {
2948 /*
2949 * Hold the job...
2950 */
bd7854cb 2951
8b116e60 2952 cupsdSetJobHoldUntil(job, "indefinite", 1);
bd7854cb 2953
8b116e60
MS
2954 job_state = IPP_JOB_HELD;
2955 message = "Job held indefinitely due to backend errors; please "
2956 "consult the error_log file for details.";
2957 }
b9faaae1 2958 break;
2abf387c 2959
b9faaae1
MS
2960 case CUPS_BACKEND_STOP :
2961 /*
2962 * Stop the printer...
2963 */
bd7854cb 2964
b9faaae1 2965 printer_state = IPP_PRINTER_STOPPED;
b9faaae1
MS
2966 message = "Printer stopped due to backend errors; please "
2967 "consult the error_log file for details.";
8b116e60
MS
2968
2969 if (job_state == IPP_JOB_COMPLETED)
2970 job_state = IPP_JOB_PENDING;
b9faaae1 2971 break;
bd7854cb 2972
b9faaae1
MS
2973 case CUPS_BACKEND_AUTH_REQUIRED :
2974 /*
2975 * Hold the job for authentication...
2976 */
bd7854cb 2977
8b116e60
MS
2978 if (job_state == IPP_JOB_COMPLETED)
2979 {
2980 cupsdSetJobHoldUntil(job, "auth-info-required", 1);
ef416fc2 2981
8b116e60
MS
2982 job_state = IPP_JOB_HELD;
2983 message = "Job held for authentication.";
2984 }
b9faaae1 2985 break;
22c9029b
MS
2986
2987 case CUPS_BACKEND_RETRY :
2988 if (job_state == IPP_JOB_COMPLETED)
2989 {
2990 /*
2991 * Hold the job if the number of retries is less than the
2992 * JobRetryLimit, otherwise abort the job.
2993 */
2994
2995 job->tries ++;
2996
2997 if (job->tries > JobRetryLimit && JobRetryLimit > 0)
2998 {
2999 /*
3000 * Too many tries...
3001 */
3002
3003 snprintf(buffer, sizeof(buffer),
3004 "Job aborted after %d unsuccessful attempts.",
3005 JobRetryLimit);
3006 job_state = IPP_JOB_ABORTED;
3007 message = buffer;
3008 }
3009 else
3010 {
3011 /*
3012 * Try again in N seconds...
3013 */
3014
3015 snprintf(buffer, sizeof(buffer),
3016 "Job held for %d seconds since it could not be sent.",
3017 JobRetryInterval);
3018
3019 job->hold_until = time(NULL) + JobRetryInterval;
3020 job_state = IPP_JOB_HELD;
3021 message = buffer;
3022 }
3023 }
3024 break;
3025
3026 case CUPS_BACKEND_RETRY_CURRENT :
3027 /*
3028 * Mark the job as pending and retry on the same printer...
3029 */
3030
3031 if (job_state == IPP_JOB_COMPLETED)
3032 {
3033 job_state = IPP_JOB_PENDING;
3034 message = "Retrying job on same printer.";
3035 }
3036 break;
e1d6a774 3037 }
3038 }
b9faaae1 3039 else if (job->status > 0)
ef416fc2 3040 {
3041 /*
b9faaae1 3042 * Filter had errors; stop job...
ef416fc2 3043 */
3044
8b116e60
MS
3045 if (job_state == IPP_JOB_COMPLETED)
3046 {
3047 job_state = IPP_JOB_STOPPED;
3048 message = "Job stopped due to filter errors; please consult the "
3049 "error_log file for details.";
3050 }
e1d6a774 3051 }
e00b005a 3052
3dfe78b3 3053 /*
b9faaae1 3054 * Update the printer and job state.
3dfe78b3
MS
3055 */
3056
ef55b745
MS
3057 if (job_state != job->state_value)
3058 cupsdSetJobState(job, job_state, CUPSD_JOB_DEFAULT, "%s", message);
3059
b9faaae1
MS
3060 cupsdSetPrinterState(job->printer, printer_state,
3061 printer_state == IPP_PRINTER_STOPPED);
3062 update_job_attrs(job, 0);
3dfe78b3 3063
178cb736
MS
3064 if (job->history)
3065 {
3066 if (job->status)
3067 dump_job_history(job);
3068 else
3069 free_job_history(job);
3070 }
3071
b9faaae1 3072 cupsArrayRemove(PrintingJobs, job);
3dfe78b3 3073
e1d6a774 3074 /*
b9faaae1 3075 * Clear the printer <-> job association...
e1d6a774 3076 */
ef416fc2 3077
b9faaae1
MS
3078 job->printer->job = NULL;
3079 job->printer = NULL;
ef416fc2 3080
e1d6a774 3081 /*
b9faaae1 3082 * Try printing another job...
e1d6a774 3083 */
ef416fc2 3084
b9faaae1
MS
3085 if (printer_state != IPP_PRINTER_STOPPED)
3086 cupsdCheckJobs();
3087}
ef416fc2 3088
ef416fc2 3089
b9faaae1
MS
3090/*
3091 * 'get_options()' - Get a string containing the job options.
3092 */
3093
3094static char * /* O - Options string */
3095get_options(cupsd_job_t *job, /* I - Job */
3096 int banner_page, /* I - Printing a banner page? */
3097 char *copies, /* I - Copies buffer */
3098 size_t copies_size, /* I - Size of copies buffer */
3099 char *title, /* I - Title buffer */
3100 size_t title_size) /* I - Size of title buffer */
3101{
3102 int i; /* Looping var */
c7017ecc 3103 size_t newlength; /* New option buffer length */
b9faaae1
MS
3104 char *optptr, /* Pointer to options */
3105 *valptr; /* Pointer in value string */
3106 ipp_attribute_t *attr; /* Current attribute */
f14324a7 3107 _ppd_cache_t *pc; /* PPD cache and mapping data */
c7017ecc
MS
3108 int num_pwgppds; /* Number of PWG->PPD options */
3109 cups_option_t *pwgppds, /* PWG->PPD options */
3110 *pwgppd, /* Current PWG->PPD option */
3111 *preset; /* Current preset option */
f14324a7
MS
3112 int print_color_mode,
3113 /* Output mode (if any) */
c7017ecc
MS
3114 print_quality; /* Print quality (if any) */
3115 const char *ppd; /* PPD option choice */
3116 int exact; /* Did we get an exact match? */
b9faaae1 3117 static char *options = NULL;/* Full list of options */
c7017ecc 3118 static size_t optlength = 0; /* Length of option buffer */
b9faaae1 3119
ef416fc2 3120
e1d6a774 3121 /*
c7017ecc
MS
3122 * Building the options string is harder than it needs to be, but for the
3123 * moment we need to pass strings for command-line args and not IPP attribute
3124 * pointers... :)
e1d6a774 3125 *
c7017ecc 3126 * First build an options array for any PWG->PPD mapped option/choice pairs.
e1d6a774 3127 */
ef416fc2 3128
f14324a7 3129 pc = job->printer->pc;
c7017ecc
MS
3130 num_pwgppds = 0;
3131 pwgppds = NULL;
ef416fc2 3132
f14324a7 3133 if (pc &&
7cf5915e
MS
3134 !ippFindAttribute(job->attrs,
3135 "com.apple.print.DocumentTicket.PMSpoolFormat",
3136 IPP_TAG_ZERO) &&
0268488e 3137 !ippFindAttribute(job->attrs, "APPrinterPreset", IPP_TAG_ZERO) &&
7cf5915e 3138 (ippFindAttribute(job->attrs, "output-mode", IPP_TAG_ZERO) ||
f14324a7 3139 ippFindAttribute(job->attrs, "print-color-mode", IPP_TAG_ZERO) ||
7cf5915e 3140 ippFindAttribute(job->attrs, "print-quality", IPP_TAG_ZERO)))
c7017ecc 3141 {
7cf5915e
MS
3142 /*
3143 * Map output-mode and print-quality to a preset...
3144 */
3145
f14324a7
MS
3146 if ((attr = ippFindAttribute(job->attrs, "print-color-mode",
3147 IPP_TAG_KEYWORD)) == NULL)
3148 attr = ippFindAttribute(job->attrs, "output-mode", IPP_TAG_KEYWORD);
3149
3150 if (attr && !strcmp(attr->values[0].string.text, "monochrome"))
3151 print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
c7017ecc 3152 else
f14324a7 3153 print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
c7017ecc
MS
3154
3155 if ((attr = ippFindAttribute(job->attrs, "print-quality",
10d09e33 3156 IPP_TAG_ENUM)) != NULL &&
c7017ecc
MS
3157 attr->values[0].integer >= IPP_QUALITY_DRAFT &&
3158 attr->values[0].integer <= IPP_QUALITY_HIGH)
3159 print_quality = attr->values[0].integer - IPP_QUALITY_DRAFT;
3160 else
030ae6a1 3161 print_quality = _PWG_PRINT_QUALITY_NORMAL;
c7017ecc 3162
f14324a7 3163 if (pc->num_presets[print_color_mode][print_quality] == 0)
c7017ecc
MS
3164 {
3165 /*
3166 * Try to find a preset that works so that we maximize the chances of us
3167 * getting a good print using IPP attributes.
3168 */
3169
f14324a7 3170 if (pc->num_presets[print_color_mode][_PWG_PRINT_QUALITY_NORMAL] > 0)
c7017ecc 3171 print_quality = _PWG_PRINT_QUALITY_NORMAL;
f14324a7
MS
3172 else if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][print_quality] > 0)
3173 print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
c7017ecc
MS
3174 else
3175 {
f14324a7
MS
3176 print_quality = _PWG_PRINT_QUALITY_NORMAL;
3177 print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
c7017ecc
MS
3178 }
3179 }
3180
f14324a7 3181 if (pc->num_presets[print_color_mode][print_quality] > 0)
c7017ecc
MS
3182 {
3183 /*
3184 * Copy the preset options as long as the corresponding names are not
3185 * already defined in the IPP request...
3186 */
3187
f14324a7
MS
3188 for (i = pc->num_presets[print_color_mode][print_quality],
3189 preset = pc->presets[print_color_mode][print_quality];
c7017ecc
MS
3190 i > 0;
3191 i --, preset ++)
3192 {
3193 if (!ippFindAttribute(job->attrs, preset->name, IPP_TAG_ZERO))
3194 num_pwgppds = cupsAddOption(preset->name, preset->value, num_pwgppds,
3195 &pwgppds);
3196 }
3197 }
7cf5915e 3198 }
c7017ecc 3199
f14324a7 3200 if (pc)
7cf5915e 3201 {
4220952d
MS
3202 if (!ippFindAttribute(job->attrs, "InputSlot", IPP_TAG_ZERO) &&
3203 !ippFindAttribute(job->attrs, "HPPaperSource", IPP_TAG_ZERO))
3204 {
f14324a7
MS
3205 if ((ppd = _ppdCacheGetInputSlot(pc, job->attrs, NULL)) != NULL)
3206 num_pwgppds = cupsAddOption(pc->source_option, ppd, num_pwgppds,
4220952d
MS
3207 &pwgppds);
3208 else if (!ippFindAttribute(job->attrs, "AP_D_InputSlot", IPP_TAG_ZERO))
3209 num_pwgppds = cupsAddOption("AP_D_InputSlot", "", num_pwgppds,
3210 &pwgppds);
3211 }
4220952d 3212 if (!ippFindAttribute(job->attrs, "MediaType", IPP_TAG_ZERO) &&
f14324a7 3213 (ppd = _ppdCacheGetMediaType(pc, job->attrs, NULL)) != NULL)
4220952d 3214 num_pwgppds = cupsAddOption("MediaType", ppd, num_pwgppds, &pwgppds);
c7017ecc 3215
4220952d
MS
3216 if (!ippFindAttribute(job->attrs, "PageRegion", IPP_TAG_ZERO) &&
3217 !ippFindAttribute(job->attrs, "PageSize", IPP_TAG_ZERO) &&
f14324a7 3218 (ppd = _ppdCacheGetPageSize(pc, job->attrs, NULL, &exact)) != NULL)
030ae6a1 3219 {
4220952d 3220 num_pwgppds = cupsAddOption("PageSize", ppd, num_pwgppds, &pwgppds);
c7017ecc 3221
030ae6a1
MS
3222 if (!ippFindAttribute(job->attrs, "media", IPP_TAG_ZERO))
3223 num_pwgppds = cupsAddOption("media", ppd, num_pwgppds, &pwgppds);
3224 }
3225
4220952d
MS
3226 if (!ippFindAttribute(job->attrs, "OutputBin", IPP_TAG_ZERO) &&
3227 (attr = ippFindAttribute(job->attrs, "output-bin",
3228 IPP_TAG_ZERO)) != NULL &&
3229 (attr->value_tag == IPP_TAG_KEYWORD ||
3230 attr->value_tag == IPP_TAG_NAME) &&
f14324a7 3231 (ppd = _ppdCacheGetOutputBin(pc, attr->values[0].string.text)) != NULL)
0268488e
MS
3232 {
3233 /*
3234 * Map output-bin to OutputBin option...
3235 */
3236
4220952d 3237 num_pwgppds = cupsAddOption("OutputBin", ppd, num_pwgppds, &pwgppds);
0268488e
MS
3238 }
3239
f14324a7
MS
3240 if (pc->sides_option &&
3241 !ippFindAttribute(job->attrs, pc->sides_option, IPP_TAG_ZERO) &&
0268488e
MS
3242 (attr = ippFindAttribute(job->attrs, "sides", IPP_TAG_KEYWORD)) != NULL)
3243 {
3244 /*
3245 * Map sides to duplex option...
3246 */
3247
3248 if (!strcmp(attr->values[0].string.text, "one-sided"))
f14324a7 3249 num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_1sided,
0268488e
MS
3250 num_pwgppds, &pwgppds);
3251 else if (!strcmp(attr->values[0].string.text, "two-sided-long-edge"))
f14324a7 3252 num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_long,
0268488e
MS
3253 num_pwgppds, &pwgppds);
3254 else if (!strcmp(attr->values[0].string.text, "two-sided-short-edge"))
f14324a7 3255 num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_short,
0268488e
MS
3256 num_pwgppds, &pwgppds);
3257 }
4220952d 3258 }
c7017ecc
MS
3259
3260 /*
3261 * Figure out how much room we need...
3262 */
3263
3264 newlength = ipp_length(job->attrs);
3265
3266 for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++)
3267 newlength += 1 + strlen(pwgppd->name) + 1 + strlen(pwgppd->value);
3268
3269 /*
3270 * Then allocate/reallocate the option buffer as needed...
3271 */
3272
3273 if (newlength > optlength || !options)
e1d6a774 3274 {
b9faaae1 3275 if (!options)
c7017ecc 3276 optptr = malloc(newlength);
e1d6a774 3277 else
c7017ecc 3278 optptr = realloc(options, newlength);
ef416fc2 3279
b9faaae1 3280 if (!optptr)
e1d6a774 3281 {
75bd9771 3282 cupsdLogJob(job, CUPSD_LOG_CRIT,
c7017ecc
MS
3283 "Unable to allocate " CUPS_LLFMT " bytes for option buffer!",
3284 CUPS_LLCAST newlength);
b9faaae1 3285 return (NULL);
e1d6a774 3286 }
ef416fc2 3287
e1d6a774 3288 options = optptr;
c7017ecc 3289 optlength = newlength;
e1d6a774 3290 }
ef416fc2 3291
e1d6a774 3292 /*
3293 * Now loop through the attributes and convert them to the textual
3294 * representation used by the filters...
3295 */
ef416fc2 3296
e1d6a774 3297 optptr = options;
3298 *optptr = '\0';
bd7854cb 3299
1340db2d
MS
3300 snprintf(title, title_size, "%s-%d", job->printer->name, job->id);
3301 strlcpy(copies, "1", copies_size);
bd7854cb 3302
e1d6a774 3303 for (attr = job->attrs->attrs; attr != NULL; attr = attr->next)
bd7854cb 3304 {
e1d6a774 3305 if (!strcmp(attr->name, "copies") &&
3306 attr->value_tag == IPP_TAG_INTEGER)
3307 {
3308 /*
3309 * Don't use the # copies attribute if we are printing the job sheets...
3310 */
bd7854cb 3311
e1d6a774 3312 if (!banner_page)
1340db2d 3313 snprintf(copies, copies_size, "%d", attr->values[0].integer);
e1d6a774 3314 }
3315 else if (!strcmp(attr->name, "job-name") &&
3316 (attr->value_tag == IPP_TAG_NAME ||
3317 attr->value_tag == IPP_TAG_NAMELANG))
1340db2d 3318 strlcpy(title, attr->values[0].string.text, title_size);
e1d6a774 3319 else if (attr->group_tag == IPP_TAG_JOB)
3320 {
3321 /*
3322 * Filter out other unwanted attributes...
3323 */
bd7854cb 3324
c7017ecc
MS
3325 if (attr->value_tag == IPP_TAG_NOVALUE ||
3326 attr->value_tag == IPP_TAG_MIMETYPE ||
e1d6a774 3327 attr->value_tag == IPP_TAG_NAMELANG ||
3328 attr->value_tag == IPP_TAG_TEXTLANG ||
3329 (attr->value_tag == IPP_TAG_URI && strcmp(attr->name, "job-uuid")) ||
3330 attr->value_tag == IPP_TAG_URISCHEME ||
3331 attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */
3332 continue;
bd7854cb 3333
c7017ecc 3334 if (!strcmp(attr->name, "job-hold-until"))
e1d6a774 3335 continue;
bd7854cb 3336
e1d6a774 3337 if (!strncmp(attr->name, "job-", 4) &&
e4572d57 3338 strcmp(attr->name, "job-billing") &&
5d6412a9 3339 strcmp(attr->name, "job-impressions") &&
c5571a1d 3340 strcmp(attr->name, "job-originating-host-name") &&
e4572d57 3341 strcmp(attr->name, "job-uuid") &&
b9faaae1 3342 !(job->printer->type & CUPS_PRINTER_REMOTE))
e1d6a774 3343 continue;
ef416fc2 3344
5d6412a9
MS
3345 if ((!strcmp(attr->name, "job-impressions") ||
3346 !strcmp(attr->name, "page-label") ||
e1d6a774 3347 !strcmp(attr->name, "page-border") ||
3348 !strncmp(attr->name, "number-up", 9) ||
b94498cf 3349 !strcmp(attr->name, "page-ranges") ||
e1d6a774 3350 !strcmp(attr->name, "page-set") ||
3351 !strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
db1f069b
MS
3352 !strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed") ||
3353 !strcasecmp(attr->name, "com.apple.print.PrintSettings."
3354 "PMTotalSidesImaged..n.") ||
3355 !strcasecmp(attr->name, "com.apple.print.PrintSettings."
3356 "PMTotalBeginPages..n.")) &&
e1d6a774 3357 banner_page)
3358 continue;
ef416fc2 3359
e1d6a774 3360 /*
3361 * Otherwise add them to the list...
3362 */
ef416fc2 3363
e1d6a774 3364 if (optptr > options)
3365 strlcat(optptr, " ", optlength - (optptr - options));
ef416fc2 3366
e1d6a774 3367 if (attr->value_tag != IPP_TAG_BOOLEAN)
3368 {
3369 strlcat(optptr, attr->name, optlength - (optptr - options));
3370 strlcat(optptr, "=", optlength - (optptr - options));
3371 }
ef416fc2 3372
e1d6a774 3373 for (i = 0; i < attr->num_values; i ++)
3374 {
3375 if (i)
3376 strlcat(optptr, ",", optlength - (optptr - options));
ef416fc2 3377
e1d6a774 3378 optptr += strlen(optptr);
ef416fc2 3379
e1d6a774 3380 switch (attr->value_tag)
3381 {
3382 case IPP_TAG_INTEGER :
3383 case IPP_TAG_ENUM :
3384 snprintf(optptr, optlength - (optptr - options),
3385 "%d", attr->values[i].integer);
3386 break;
ef416fc2 3387
e1d6a774 3388 case IPP_TAG_BOOLEAN :
3389 if (!attr->values[i].boolean)
3390 strlcat(optptr, "no", optlength - (optptr - options));
ef416fc2 3391
4220952d
MS
3392 strlcat(optptr, attr->name,
3393 optlength - (optptr - options));
3394 break;
3395
e1d6a774 3396 case IPP_TAG_RANGE :
3397 if (attr->values[i].range.lower == attr->values[i].range.upper)
3398 snprintf(optptr, optlength - (optptr - options) - 1,
3399 "%d", attr->values[i].range.lower);
3400 else
3401 snprintf(optptr, optlength - (optptr - options) - 1,
3402 "%d-%d", attr->values[i].range.lower,
3403 attr->values[i].range.upper);
3404 break;
ef416fc2 3405
e1d6a774 3406 case IPP_TAG_RESOLUTION :
3407 snprintf(optptr, optlength - (optptr - options) - 1,
3408 "%dx%d%s", attr->values[i].resolution.xres,
3409 attr->values[i].resolution.yres,
3410 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3411 "dpi" : "dpc");
3412 break;
ef416fc2 3413
e1d6a774 3414 case IPP_TAG_STRING :
3415 case IPP_TAG_TEXT :
3416 case IPP_TAG_NAME :
3417 case IPP_TAG_KEYWORD :
3418 case IPP_TAG_CHARSET :
3419 case IPP_TAG_LANGUAGE :
3420 case IPP_TAG_URI :
3421 for (valptr = attr->values[i].string.text; *valptr;)
3422 {
3423 if (strchr(" \t\n\\\'\"", *valptr))
3424 *optptr++ = '\\';
3425 *optptr++ = *valptr++;
3426 }
3427
3428 *optptr = '\0';
3429 break;
3430
3431 default :
3432 break; /* anti-compiler-warning-code */
3433 }
3434 }
3435
3436 optptr += strlen(optptr);
3437 }
3438 }
3439
c7017ecc
MS
3440 /*
3441 * Finally loop through the PWG->PPD mapped options and add them...
3442 */
3443
3444 for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++)
3445 {
3446 *optptr++ = ' ';
3447 strcpy(optptr, pwgppd->name);
3448 optptr += strlen(optptr);
3449 *optptr++ = '=';
3450 strcpy(optptr, pwgppd->value);
3451 optptr += strlen(optptr);
3452 }
3453
3454 cupsFreeOptions(num_pwgppds, pwgppds);
3455
3456 /*
3457 * Return the options string...
3458 */
b9faaae1
MS
3459
3460 return (options);
3461}
3462
3463
3464/*
3465 * 'ipp_length()' - Compute the size of the buffer needed to hold
3466 * the textual IPP attributes.
3467 */
3468
c7017ecc 3469static size_t /* O - Size of attribute buffer */
b9faaae1
MS
3470ipp_length(ipp_t *ipp) /* I - IPP request */
3471{
c7017ecc 3472 size_t bytes; /* Number of bytes */
b9faaae1
MS
3473 int i; /* Looping var */
3474 ipp_attribute_t *attr; /* Current attribute */
3475
3476
3477 /*
3478 * Loop through all attributes...
e1d6a774 3479 */
3480
b9faaae1 3481 bytes = 0;
e1d6a774 3482
b9faaae1 3483 for (attr = ipp->attrs; attr != NULL; attr = attr->next)
91c84a35 3484 {
b9faaae1
MS
3485 /*
3486 * Skip attributes that won't be sent to filters...
3487 */
91c84a35 3488
c7017ecc
MS
3489 if (attr->value_tag == IPP_TAG_NOVALUE ||
3490 attr->value_tag == IPP_TAG_MIMETYPE ||
b9faaae1
MS
3491 attr->value_tag == IPP_TAG_NAMELANG ||
3492 attr->value_tag == IPP_TAG_TEXTLANG ||
3493 attr->value_tag == IPP_TAG_URI ||
3494 attr->value_tag == IPP_TAG_URISCHEME)
3495 continue;
91c84a35 3496
b9faaae1
MS
3497 /*
3498 * Add space for a leading space and commas between each value.
3499 * For the first attribute, the leading space isn't used, so the
3500 * extra byte can be used as the nul terminator...
3501 */
e1d6a774 3502
b9faaae1
MS
3503 bytes ++; /* " " separator */
3504 bytes += attr->num_values; /* "," separators */
e1d6a774 3505
b9faaae1
MS
3506 /*
3507 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
3508 * other attributes appear as "foo=value1,value2,...,valueN".
3509 */
3510
3511 if (attr->value_tag != IPP_TAG_BOOLEAN)
3512 bytes += strlen(attr->name);
3513 else
3514 bytes += attr->num_values * strlen(attr->name);
3515
3516 /*
3517 * Now add the size required for each value in the attribute...
3518 */
3519
3520 switch (attr->value_tag)
ef416fc2 3521 {
b9faaae1
MS
3522 case IPP_TAG_INTEGER :
3523 case IPP_TAG_ENUM :
3524 /*
3525 * Minimum value of a signed integer is -2147483647, or 11 digits.
3526 */
3527
3528 bytes += attr->num_values * 11;
3529 break;
3530
3531 case IPP_TAG_BOOLEAN :
3532 /*
3533 * Add two bytes for each false ("no") value...
3534 */
3535
3536 for (i = 0; i < attr->num_values; i ++)
3537 if (!attr->values[i].boolean)
3538 bytes += 2;
3539 break;
3540
3541 case IPP_TAG_RANGE :
3542 /*
3543 * A range is two signed integers separated by a hyphen, or
3544 * 23 characters max.
3545 */
3546
3547 bytes += attr->num_values * 23;
3548 break;
3549
3550 case IPP_TAG_RESOLUTION :
3551 /*
3552 * A resolution is two signed integers separated by an "x" and
3553 * suffixed by the units, or 26 characters max.
3554 */
3555
3556 bytes += attr->num_values * 26;
3557 break;
3558
3559 case IPP_TAG_STRING :
3560 case IPP_TAG_TEXT :
3561 case IPP_TAG_NAME :
3562 case IPP_TAG_KEYWORD :
3563 case IPP_TAG_CHARSET :
3564 case IPP_TAG_LANGUAGE :
3565 case IPP_TAG_URI :
3566 /*
3567 * Strings can contain characters that need quoting. We need
3568 * at least 2 * len + 2 characters to cover the quotes and
3569 * any backslashes in the string.
3570 */
3571
3572 for (i = 0; i < attr->num_values; i ++)
3573 bytes += 2 * strlen(attr->values[i].string.text) + 2;
3574 break;
3575
3576 default :
3577 break; /* anti-compiler-warning-code */
e1d6a774 3578 }
3579 }
b9faaae1
MS
3580
3581 return (bytes);
3582}
3583
3584
3585/*
3586 * 'load_job_cache()' - Load jobs from the job.cache file.
3587 */
3588
3589static void
3590load_job_cache(const char *filename) /* I - job.cache filename */
3591{
3592 cups_file_t *fp; /* job.cache file */
3593 char line[1024], /* Line buffer */
3594 *value; /* Value on line */
3595 int linenum; /* Line number in file */
3596 cupsd_job_t *job; /* Current job */
3597 int jobid; /* Job ID */
3598 char jobfile[1024]; /* Job filename */
3599
3600
3601 /*
3602 * Open the job.cache file...
3603 */
3604
3605 if ((fp = cupsFileOpen(filename, "r")) == NULL)
e1d6a774 3606 {
b9faaae1
MS
3607 if (errno != ENOENT)
3608 cupsdLogMessage(CUPSD_LOG_ERROR,
3609 "Unable to open job cache file \"%s\": %s",
3610 filename, strerror(errno));
ef416fc2 3611
b9faaae1
MS
3612 load_request_root();
3613
3614 return;
3615 }
ef416fc2 3616
e1d6a774 3617 /*
b9faaae1 3618 * Read entries from the job cache file and create jobs as needed.
e1d6a774 3619 */
ef416fc2 3620
b9faaae1
MS
3621 cupsdLogMessage(CUPSD_LOG_INFO, "Loading job cache file \"%s\"...",
3622 filename);
ef416fc2 3623
b9faaae1
MS
3624 linenum = 0;
3625 job = NULL;
3626
3627 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
3628 {
3629 if (!strcasecmp(line, "NextJobId"))
3630 {
3631 if (value)
3632 NextJobId = atoi(value);
3633 }
3634 else if (!strcasecmp(line, "<Job"))
3635 {
3636 if (job)
3637 {
3638 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing </Job> directive on line %d!",
3639 linenum);
3640 continue;
3641 }
3642
3643 if (!value)
3644 {
3645 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing job ID on line %d!", linenum);
3646 continue;
3647 }
3648
3649 jobid = atoi(value);
3650
3651 if (jobid < 1)
3652 {
3653 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad job ID %d on line %d!", jobid,
3654 linenum);
3655 continue;
3656 }
3657
3658 snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, jobid);
3659 if (access(jobfile, 0))
3660 {
3661 cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Files have gone away!",
3662 jobid);
3663 continue;
3664 }
3665
3666 job = calloc(1, sizeof(cupsd_job_t));
3667 if (!job)
3668 {
3669 cupsdLogMessage(CUPSD_LOG_EMERG,
3670 "[Job %d] Unable to allocate memory for job!", jobid);
3671 break;
3672 }
3673
3674 job->id = jobid;
3675 job->back_pipes[0] = -1;
3676 job->back_pipes[1] = -1;
3677 job->print_pipes[0] = -1;
3678 job->print_pipes[1] = -1;
3679 job->side_pipes[0] = -1;
3680 job->side_pipes[1] = -1;
3681 job->status_pipes[0] = -1;
3682 job->status_pipes[1] = -1;
3683
3684 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading from cache...",
3685 job->id);
3686 }
3687 else if (!job)
3688 {
3689 cupsdLogMessage(CUPSD_LOG_ERROR,
3690 "Missing <Job #> directive on line %d!", linenum);
3691 continue;
3692 }
3693 else if (!strcasecmp(line, "</Job>"))
3694 {
3695 cupsArrayAdd(Jobs, job);
3696
f701418f
MS
3697 if (job->state_value <= IPP_JOB_STOPPED && cupsdLoadJob(job))
3698 cupsArrayAdd(ActiveJobs, job);
b9faaae1
MS
3699
3700 job = NULL;
3701 }
3702 else if (!value)
3703 {
3704 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d!", linenum);
3705 continue;
3706 }
3707 else if (!strcasecmp(line, "State"))
3708 {
3709 job->state_value = (ipp_jstate_t)atoi(value);
3710
3711 if (job->state_value < IPP_JOB_PENDING)
3712 job->state_value = IPP_JOB_PENDING;
3713 else if (job->state_value > IPP_JOB_COMPLETED)
3714 job->state_value = IPP_JOB_COMPLETED;
3715 }
8b116e60
MS
3716 else if (!strcasecmp(line, "HoldUntil"))
3717 {
3718 job->hold_until = atoi(value);
3719 }
b9faaae1
MS
3720 else if (!strcasecmp(line, "Priority"))
3721 {
3722 job->priority = atoi(value);
3723 }
3724 else if (!strcasecmp(line, "Username"))
3725 {
3726 cupsdSetString(&job->username, value);
3727 }
3728 else if (!strcasecmp(line, "Destination"))
3729 {
3730 cupsdSetString(&job->dest, value);
3731 }
3732 else if (!strcasecmp(line, "DestType"))
3733 {
3734 job->dtype = (cups_ptype_t)atoi(value);
3735 }
3736 else if (!strcasecmp(line, "NumFiles"))
3737 {
3738 job->num_files = atoi(value);
3739
3740 if (job->num_files < 0)
3741 {
3742 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad NumFiles value %d on line %d!",
3743 job->num_files, linenum);
3744 job->num_files = 0;
3745 continue;
3746 }
3747
3748 if (job->num_files > 0)
3749 {
3750 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-001", RequestRoot,
3751 job->id);
3752 if (access(jobfile, 0))
3753 {
3754 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Data files have gone away!",
3755 job->id);
3756 job->num_files = 0;
3757 continue;
3758 }
3759
3760 job->filetypes = calloc(job->num_files, sizeof(mime_type_t *));
3761 job->compressions = calloc(job->num_files, sizeof(int));
3762
3763 if (!job->filetypes || !job->compressions)
3764 {
3765 cupsdLogMessage(CUPSD_LOG_EMERG,
3766 "[Job %d] Unable to allocate memory for %d files!",
3767 job->id, job->num_files);
3768 break;
3769 }
3770 }
3771 }
3772 else if (!strcasecmp(line, "File"))
3773 {
3774 int number, /* File number */
3775 compression; /* Compression value */
3776 char super[MIME_MAX_SUPER], /* MIME super type */
3777 type[MIME_MAX_TYPE]; /* MIME type */
3778
3779
3780 if (sscanf(value, "%d%*[ \t]%15[^/]/%255s%d", &number, super, type,
3781 &compression) != 4)
3782 {
3783 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File on line %d!", linenum);
3784 continue;
3785 }
3786
3787 if (number < 1 || number > job->num_files)
3788 {
3789 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File number %d on line %d!",
3790 number, linenum);
3791 continue;
3792 }
0a682745 3793
b9faaae1 3794 number --;
ef416fc2 3795
b9faaae1
MS
3796 job->compressions[number] = compression;
3797 job->filetypes[number] = mimeType(MimeDatabase, super, type);
ef416fc2 3798
b9faaae1
MS
3799 if (!job->filetypes[number])
3800 {
e1d6a774 3801 /*
b9faaae1 3802 * If the original MIME type is unknown, auto-type it!
e1d6a774 3803 */
ef416fc2 3804
b9faaae1
MS
3805 cupsdLogMessage(CUPSD_LOG_ERROR,
3806 "[Job %d] Unknown MIME type %s/%s for file %d!",
3807 job->id, super, type, number + 1);
3808
3809 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
3810 job->id, number + 1);
3811 job->filetypes[number] = mimeFileType(MimeDatabase, jobfile, NULL,
3812 job->compressions + number);
ef416fc2 3813
e1d6a774 3814 /*
b9faaae1 3815 * If that didn't work, assume it is raw...
e1d6a774 3816 */
ef416fc2 3817
b9faaae1
MS
3818 if (!job->filetypes[number])
3819 job->filetypes[number] = mimeType(MimeDatabase, "application",
3820 "vnd.cups-raw");
3821 }
3822 }
3823 else
3824 cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown %s directive on line %d!",
3825 line, linenum);
ef416fc2 3826 }
3827
b9faaae1
MS
3828 cupsFileClose(fp);
3829}
ef416fc2 3830
3831
b9faaae1
MS
3832/*
3833 * 'load_next_job_id()' - Load the NextJobId value from the job.cache file.
3834 */
bd7854cb 3835
b9faaae1
MS
3836static void
3837load_next_job_id(const char *filename) /* I - job.cache filename */
3838{
3839 cups_file_t *fp; /* job.cache file */
3840 char line[1024], /* Line buffer */
3841 *value; /* Value on line */
3842 int linenum; /* Line number in file */
3843 int next_job_id; /* NextJobId value from line */
db0bd74a 3844
db0bd74a 3845
b9faaae1
MS
3846 /*
3847 * Read the NextJobId directive from the job.cache file and use
3848 * the value (if any).
3849 */
bd7854cb 3850
b9faaae1 3851 if ((fp = cupsFileOpen(filename, "r")) == NULL)
e1d6a774 3852 {
b9faaae1
MS
3853 if (errno != ENOENT)
3854 cupsdLogMessage(CUPSD_LOG_ERROR,
3855 "Unable to open job cache file \"%s\": %s",
3856 filename, strerror(errno));
bd7854cb 3857
b9faaae1 3858 return;
e1d6a774 3859 }
bd7854cb 3860
b9faaae1
MS
3861 cupsdLogMessage(CUPSD_LOG_INFO,
3862 "Loading NextJobId from job cache file \"%s\"...", filename);
3863
3864 linenum = 0;
3865
3866 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
bd7854cb 3867 {
b9faaae1
MS
3868 if (!strcasecmp(line, "NextJobId"))
3869 {
3870 if (value)
3871 {
3872 next_job_id = atoi(value);
3873
3874 if (next_job_id > NextJobId)
3875 NextJobId = next_job_id;
3876 }
3877 break;
3878 }
e1d6a774 3879 }
bd7854cb 3880
b9faaae1
MS
3881 cupsFileClose(fp);
3882}
09a101d6 3883
f7deaa1a 3884
b9faaae1
MS
3885/*
3886 * 'load_request_root()' - Load jobs from the RequestRoot directory.
3887 */
bd7854cb 3888
b9faaae1
MS
3889static void
3890load_request_root(void)
3891{
3892 cups_dir_t *dir; /* Directory */
3893 cups_dentry_t *dent; /* Directory entry */
3894 cupsd_job_t *job; /* New job */
e1d6a774 3895
bd7854cb 3896
3897 /*
b9faaae1 3898 * Open the requests directory...
bd7854cb 3899 */
3900
b9faaae1 3901 cupsdLogMessage(CUPSD_LOG_DEBUG, "Scanning %s for jobs...", RequestRoot);
bd7854cb 3902
b9faaae1 3903 if ((dir = cupsDirOpen(RequestRoot)) == NULL)
bd7854cb 3904 {
b9faaae1
MS
3905 cupsdLogMessage(CUPSD_LOG_ERROR,
3906 "Unable to open spool directory \"%s\": %s",
3907 RequestRoot, strerror(errno));
3908 return;
e1d6a774 3909 }
bd7854cb 3910
b9faaae1
MS
3911 /*
3912 * Read all the c##### files...
3913 */
bd7854cb 3914
b9faaae1
MS
3915 while ((dent = cupsDirRead(dir)) != NULL)
3916 if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c')
bd7854cb 3917 {
b9faaae1
MS
3918 /*
3919 * Allocate memory for the job...
3920 */
bd7854cb 3921
b9faaae1 3922 if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
bd7854cb 3923 {
b9faaae1
MS
3924 cupsdLogMessage(CUPSD_LOG_ERROR, "Ran out of memory for jobs!");
3925 cupsDirClose(dir);
3926 return;
3927 }
bd7854cb 3928
b9faaae1
MS
3929 /*
3930 * Assign the job ID...
3931 */
bd7854cb 3932
b9faaae1
MS
3933 job->id = atoi(dent->filename + 1);
3934 job->back_pipes[0] = -1;
3935 job->back_pipes[1] = -1;
3936 job->print_pipes[0] = -1;
3937 job->print_pipes[1] = -1;
3938 job->side_pipes[0] = -1;
3939 job->side_pipes[1] = -1;
3940 job->status_pipes[0] = -1;
3941 job->status_pipes[1] = -1;
bd7854cb 3942
b9faaae1
MS
3943 if (job->id >= NextJobId)
3944 NextJobId = job->id + 1;
ed486911 3945
b9faaae1
MS
3946 /*
3947 * Load the job...
3948 */
ed486911 3949
f701418f
MS
3950 if (cupsdLoadJob(job))
3951 {
3952 /*
3953 * Insert the job into the array, sorting by job priority and ID...
3954 */
bd7854cb 3955
f701418f 3956 cupsArrayAdd(Jobs, job);
e1d6a774 3957
f701418f
MS
3958 if (job->state_value <= IPP_JOB_STOPPED)
3959 cupsArrayAdd(ActiveJobs, job);
3960 else
3961 unload_job(job);
3962 }
bd7854cb 3963 }
bd7854cb 3964
b9faaae1
MS
3965 cupsDirClose(dir);
3966}
bd7854cb 3967
3968
b9faaae1
MS
3969/*
3970 * 'set_time()' - Set one of the "time-at-xyz" attributes.
3971 */
bd7854cb 3972
b9faaae1
MS
3973static void
3974set_time(cupsd_job_t *job, /* I - Job to update */
3975 const char *name) /* I - Name of attribute */
3976{
3977 ipp_attribute_t *attr; /* Time attribute */
bd7854cb 3978
bd7854cb 3979
b9faaae1
MS
3980 if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL)
3981 {
3982 attr->value_tag = IPP_TAG_INTEGER;
3983 attr->values[0].integer = time(NULL);
3984 }
3985}
bd7854cb 3986
e1d6a774 3987
b9faaae1
MS
3988/*
3989 * 'start_job()' - Start a print job.
3990 */
e1d6a774 3991
b9faaae1
MS
3992static void
3993start_job(cupsd_job_t *job, /* I - Job ID */
3994 cupsd_printer_t *printer) /* I - Printer to print job */
3995{
3996 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job(job=%p(%d), printer=%p(%s))",
3997 job, job->id, printer, printer->name);
bd7854cb 3998
b9faaae1
MS
3999 /*
4000 * Make sure we have some files around before we try to print...
4001 */
bd7854cb 4002
b9faaae1
MS
4003 if (job->num_files == 0)
4004 {
4005 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_DEFAULT,
4006 "Aborting job because it has no files.");
4007 return;
4008 }
bd7854cb 4009
b9faaae1
MS
4010 /*
4011 * Update the printer and job state to "processing"...
4012 */
bd7854cb 4013
b9faaae1
MS
4014 if (!cupsdLoadJob(job))
4015 return;
89d46774 4016
b9faaae1
MS
4017 cupsdSetJobState(job, IPP_JOB_PROCESSING, CUPSD_JOB_DEFAULT, NULL);
4018 cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
07ed0e9a
MS
4019 cupsdSetPrinterReasons(printer, "-cups-remote-pending,"
4020 "cups-remote-pending-held,"
4021 "cups-remote-processing,"
4022 "cups-remote-stopped,"
4023 "cups-remote-canceled,"
4024 "cups-remote-aborted,"
4025 "cups-remote-completed");
f7deaa1a 4026
1340db2d
MS
4027 job->cost = 0;
4028 job->current_file = 0;
4029 job->progress = 0;
4030 job->printer = printer;
4031 printer->job = job;
f7deaa1a 4032
b9faaae1
MS
4033 /*
4034 * Setup the last exit status and security profiles...
4035 */
89d46774 4036
b9faaae1
MS
4037 job->status = 0;
4038 job->profile = cupsdCreateProfile(job->id);
bd7854cb 4039
b9faaae1
MS
4040 /*
4041 * Create the status pipes and buffer...
4042 */
bd7854cb 4043
b9faaae1
MS
4044 if (cupsdOpenPipe(job->status_pipes))
4045 {
4046 cupsdLogJob(job, CUPSD_LOG_DEBUG,
4047 "Unable to create job status pipes - %s.", strerror(errno));
89d46774 4048
b9faaae1
MS
4049 cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
4050 "Job stopped because the scheduler could not create the "
4051 "job status pipes.");
89d46774 4052
b9faaae1
MS
4053 cupsdDestroyProfile(job->profile);
4054 job->profile = NULL;
4055 return;
e1d6a774 4056 }
bd7854cb 4057
b9faaae1
MS
4058 job->status_buffer = cupsdStatBufNew(job->status_pipes[0], NULL);
4059 job->status_level = CUPSD_LOG_INFO;
4060
38e73f87
MS
4061 if (job->printer_message)
4062 cupsdSetString(&(job->printer_message->values[0].string.text), "");
4063
b9faaae1
MS
4064 /*
4065 * Create the backchannel pipes and make them non-blocking...
4066 */
bd7854cb 4067
b9faaae1 4068 if (cupsdOpenPipe(job->back_pipes))
e1d6a774 4069 {
b9faaae1
MS
4070 cupsdLogJob(job, CUPSD_LOG_DEBUG,
4071 "Unable to create back-channel pipes - %s.", strerror(errno));
4072
4073 cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
4074 "Job stopped because the scheduler could not create the "
4075 "back-channel pipes.");
4076
4077 cupsdClosePipe(job->status_pipes);
4078 cupsdStatBufDelete(job->status_buffer);
4079 job->status_buffer = NULL;
4080
4081 cupsdDestroyProfile(job->profile);
4082 job->profile = NULL;
4083 return;
e1d6a774 4084 }
bd7854cb 4085
b9faaae1
MS
4086 fcntl(job->back_pipes[0], F_SETFL,
4087 fcntl(job->back_pipes[0], F_GETFL) | O_NONBLOCK);
4088 fcntl(job->back_pipes[1], F_SETFL,
4089 fcntl(job->back_pipes[1], F_GETFL) | O_NONBLOCK);
ef416fc2 4090
b9faaae1
MS
4091 /*
4092 * Create the side-channel pipes and make them non-blocking...
4093 */
ef416fc2 4094
b9faaae1
MS
4095 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, job->side_pipes))
4096 {
4097 cupsdLogJob(job, CUPSD_LOG_DEBUG,
4098 "Unable to create side-channel pipes - %s.", strerror(errno));
ef416fc2 4099
b9faaae1
MS
4100 cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
4101 "Job stopped because the scheduler could not create the "
4102 "side-channel pipes.");
ef416fc2 4103
b9faaae1
MS
4104 cupsdClosePipe(job->back_pipes);
4105
4106 cupsdClosePipe(job->status_pipes);
4107 cupsdStatBufDelete(job->status_buffer);
4108 job->status_buffer = NULL;
4109
4110 cupsdDestroyProfile(job->profile);
4111 job->profile = NULL;
4112 return;
4113 }
4114
4115 fcntl(job->side_pipes[0], F_SETFL,
4116 fcntl(job->side_pipes[0], F_GETFL) | O_NONBLOCK);
4117 fcntl(job->side_pipes[1], F_SETFL,
4118 fcntl(job->side_pipes[1], F_GETFL) | O_NONBLOCK);
ef416fc2 4119
7a0cbd5e
MS
4120 fcntl(job->side_pipes[0], F_SETFD,
4121 fcntl(job->side_pipes[0], F_GETFD) | FD_CLOEXEC);
4122 fcntl(job->side_pipes[1], F_SETFD,
4123 fcntl(job->side_pipes[1], F_GETFD) | FD_CLOEXEC);
4124
ef416fc2 4125 /*
b9faaae1 4126 * Now start the first file in the job...
ef416fc2 4127 */
4128
b9faaae1
MS
4129 cupsdContinueJob(job);
4130}
ef416fc2 4131
ef416fc2 4132
b9faaae1
MS
4133/*
4134 * 'stop_job()' - Stop a print job.
4135 */
ef416fc2 4136
b9faaae1
MS
4137static void
4138stop_job(cupsd_job_t *job, /* I - Job */
4139 cupsd_jobaction_t action) /* I - Action */
4140{
4141 int i; /* Looping var */
b94498cf 4142
ef416fc2 4143
b9faaae1
MS
4144 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_job(job=%p(%d), action=%d)", job,
4145 job->id, action);
ef416fc2 4146
b9faaae1
MS
4147 FilterLevel -= job->cost;
4148 job->cost = 0;
ef416fc2 4149
238c3832
MS
4150 if (action == CUPSD_JOB_DEFAULT && !job->kill_time)
4151 job->kill_time = time(NULL) + JobKillDelay;
ef55b745 4152 else if (action >= CUPSD_JOB_FORCE)
238c3832
MS
4153 job->kill_time = 0;
4154
b9faaae1
MS
4155 for (i = 0; job->filters[i]; i ++)
4156 if (job->filters[i] > 0)
ef55b745
MS
4157 {
4158 cupsdEndProcess(job->filters[i], action >= CUPSD_JOB_FORCE);
4159
4160 if (action >= CUPSD_JOB_FORCE)
4161 job->filters[i] = -job->filters[i];
4162 }
b9faaae1
MS
4163
4164 if (job->backend > 0)
ef55b745
MS
4165 {
4166 cupsdEndProcess(job->backend, action >= CUPSD_JOB_FORCE);
4167
4168 if (action >= CUPSD_JOB_FORCE)
4169 job->backend = -job->backend;
4170 }
4171
4172 if (action >= CUPSD_JOB_FORCE)
4173 {
4174 /*
4175 * Clear job status...
4176 */
4177
4178 job->status = 0;
4179 }
e1d6a774 4180}
ef416fc2 4181
e1d6a774 4182
4183/*
4184 * 'unload_job()' - Unload a job from memory.
4185 */
4186
4187static void
4188unload_job(cupsd_job_t *job) /* I - Job */
4189{
4190 if (!job->attrs)
4191 return;
4192
ed6e7faf 4193 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Unloading...", job->id);
e1d6a774 4194
4195 ippDelete(job->attrs);
4196
cc0d019f
MS
4197 job->attrs = NULL;
4198 job->state = NULL;
4199 job->sheets = NULL;
4200 job->job_sheets = NULL;
4201 job->printer_message = NULL;
4202 job->printer_reasons = NULL;
ef416fc2 4203}
4204
4205
4206/*
f899b121 4207 * 'update_job()' - Read a status update from a job's filters.
4208 */
4209
4210void
09a101d6 4211update_job(cupsd_job_t *job) /* I - Job to check */
f899b121 4212{
4213 int i; /* Looping var */
4214 int copies; /* Number of copies printed */
178cb736
MS
4215 char message[CUPSD_SB_BUFFER_SIZE],
4216 /* Message text */
f899b121 4217 *ptr; /* Pointer update... */
4218 int loglevel, /* Log level for message */
4219 event = 0; /* Events? */
f0ab5bff
MS
4220 static const char * const levels[] = /* Log levels */
4221 {
4222 "NONE",
4223 "EMERG",
4224 "ALERT",
4225 "CRIT",
4226 "ERROR",
4227 "WARN",
4228 "NOTICE",
4229 "INFO",
4230 "DEBUG",
4231 "DEBUG2"
4232 };
f899b121 4233
4234
f0ab5bff
MS
4235 /*
4236 * Get the printer associated with this job; if the printer is stopped for
4237 * any reason then job->printer will be reset to NULL, so make sure we have
4238 * a valid pointer...
4239 */
4240
f899b121 4241 while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
4242 message, sizeof(message))) != NULL)
4243 {
4244 /*
4245 * Process page and printer state messages as needed...
4246 */
4247
4248 if (loglevel == CUPSD_LOG_PAGE)
4249 {
4250 /*
4251 * Page message; send the message to the page_log file and update the
4252 * job sheet count...
4253 */
4254
75bd9771 4255 cupsdLogJob(job, CUPSD_LOG_DEBUG, "PAGE: %s", message);
dd1abb6b 4256
91c84a35 4257 if (job->sheets)
f899b121 4258 {
4259 if (!strncasecmp(message, "total ", 6))
4260 {
4261 /*
4262 * Got a total count of pages from a backend or filter...
4263 */
4264
4265 copies = atoi(message + 6);
4266 copies -= job->sheets->values[0].integer; /* Just track the delta */
4267 }
4268 else if (!sscanf(message, "%*d%d", &copies))
4269 copies = 1;
4270
4271 job->sheets->values[0].integer += copies;
4272
b9faaae1 4273 if (job->printer->page_limit)
ba55dc12 4274 cupsdUpdateQuota(job->printer, job->username, copies, 0);
f899b121 4275 }
4276
4277 cupsdLogPage(job, message);
4278
91c84a35 4279 if (job->sheets)
b9faaae1 4280 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
91c84a35 4281 "Printed %d page(s).", job->sheets->values[0].integer);
f899b121 4282 }
4283 else if (loglevel == CUPSD_LOG_STATE)
4284 {
75bd9771 4285 cupsdLogJob(job, CUPSD_LOG_DEBUG, "STATE: %s", message);
dd1abb6b 4286
09a101d6 4287 if (!strcmp(message, "paused"))
c24d2134 4288 {
b9faaae1 4289 cupsdStopPrinter(job->printer, 1);
c24d2134
MS
4290 return;
4291 }
acb056cb 4292 else if (cupsdSetPrinterReasons(job->printer, message))
d9bca400 4293 event |= CUPSD_EVENT_PRINTER_STATE;
bc44d920 4294
4509bb49 4295 update_job_attrs(job, 0);
f899b121 4296 }
4297 else if (loglevel == CUPSD_LOG_ATTR)
4298 {
4299 /*
4300 * Set attribute(s)...
4301 */
4302
4303 int num_attrs; /* Number of attributes */
4304 cups_option_t *attrs; /* Attributes */
4305 const char *attr; /* Attribute */
4306
4307
75bd9771 4308 cupsdLogJob(job, CUPSD_LOG_DEBUG, "ATTR: %s", message);
dd1abb6b 4309
f899b121 4310 num_attrs = cupsParseOptions(message, 0, &attrs);
4311
4312 if ((attr = cupsGetOption("auth-info-required", num_attrs,
4313 attrs)) != NULL)
7ff4fea9 4314 {
b9faaae1
MS
4315 cupsdSetAuthInfoRequired(job->printer, attr, NULL);
4316 cupsdSetPrinterAttrs(job->printer);
3dfe78b3 4317
b9faaae1 4318 if (job->printer->type & CUPS_PRINTER_DISCOVERED)
3dfe78b3
MS
4319 cupsdMarkDirty(CUPSD_DIRTY_REMOTE);
4320 else
4321 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
7ff4fea9 4322 }
f899b121 4323
9a4f8274
MS
4324 if ((attr = cupsGetOption("job-media-progress", num_attrs,
4325 attrs)) != NULL)
4326 {
4327 int progress = atoi(attr);
4328
4329
4330 if (progress >= 0 && progress <= 100)
4331 {
4332 job->progress = progress;
4333
4334 if (job->sheets)
b9faaae1 4335 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
9a4f8274
MS
4336 "Printing page %d, %d%%",
4337 job->sheets->values[0].integer, job->progress);
4338 }
4339 }
4340
323c5de1 4341 if ((attr = cupsGetOption("printer-alert", num_attrs, attrs)) != NULL)
4342 {
b9faaae1 4343 cupsdSetString(&job->printer->alert, attr);
d9bca400 4344 event |= CUPSD_EVENT_PRINTER_STATE;
323c5de1 4345 }
4346
4347 if ((attr = cupsGetOption("printer-alert-description", num_attrs,
4348 attrs)) != NULL)
4349 {
b9faaae1 4350 cupsdSetString(&job->printer->alert_description, attr);
d9bca400 4351 event |= CUPSD_EVENT_PRINTER_STATE;
323c5de1 4352 }
4353
5a738aea
MS
4354 if ((attr = cupsGetOption("marker-colors", num_attrs, attrs)) != NULL)
4355 {
b9faaae1
MS
4356 cupsdSetPrinterAttr(job->printer, "marker-colors", (char *)attr);
4357 job->printer->marker_time = time(NULL);
5a738aea 4358 event |= CUPSD_EVENT_PRINTER_STATE;
52f6f666 4359 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
5a738aea
MS
4360 }
4361
4362 if ((attr = cupsGetOption("marker-levels", num_attrs, attrs)) != NULL)
4363 {
b9faaae1
MS
4364 cupsdSetPrinterAttr(job->printer, "marker-levels", (char *)attr);
4365 job->printer->marker_time = time(NULL);
5a738aea 4366 event |= CUPSD_EVENT_PRINTER_STATE;
52f6f666 4367 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
5a738aea
MS
4368 }
4369
ed6e7faf
MS
4370 if ((attr = cupsGetOption("marker-low-levels", num_attrs, attrs)) != NULL)
4371 {
b9faaae1
MS
4372 cupsdSetPrinterAttr(job->printer, "marker-low-levels", (char *)attr);
4373 job->printer->marker_time = time(NULL);
ed6e7faf
MS
4374 event |= CUPSD_EVENT_PRINTER_STATE;
4375 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4376 }
4377
4378 if ((attr = cupsGetOption("marker-high-levels", num_attrs, attrs)) != NULL)
4379 {
b9faaae1
MS
4380 cupsdSetPrinterAttr(job->printer, "marker-high-levels", (char *)attr);
4381 job->printer->marker_time = time(NULL);
ed6e7faf
MS
4382 event |= CUPSD_EVENT_PRINTER_STATE;
4383 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4384 }
4385
75bd9771
MS
4386 if ((attr = cupsGetOption("marker-message", num_attrs, attrs)) != NULL)
4387 {
b9faaae1
MS
4388 cupsdSetPrinterAttr(job->printer, "marker-message", (char *)attr);
4389 job->printer->marker_time = time(NULL);
75bd9771 4390 event |= CUPSD_EVENT_PRINTER_STATE;
52f6f666 4391 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
75bd9771
MS
4392 }
4393
5a738aea
MS
4394 if ((attr = cupsGetOption("marker-names", num_attrs, attrs)) != NULL)
4395 {
b9faaae1
MS
4396 cupsdSetPrinterAttr(job->printer, "marker-names", (char *)attr);
4397 job->printer->marker_time = time(NULL);
5a738aea 4398 event |= CUPSD_EVENT_PRINTER_STATE;
52f6f666 4399 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
5a738aea
MS
4400 }
4401
4402 if ((attr = cupsGetOption("marker-types", num_attrs, attrs)) != NULL)
4403 {
b9faaae1
MS
4404 cupsdSetPrinterAttr(job->printer, "marker-types", (char *)attr);
4405 job->printer->marker_time = time(NULL);
5a738aea 4406 event |= CUPSD_EVENT_PRINTER_STATE;
52f6f666 4407 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
5a738aea
MS
4408 }
4409
f899b121 4410 cupsFreeOptions(num_attrs, attrs);
4411 }
c9fc04c6
MS
4412 else if (loglevel == CUPSD_LOG_PPD)
4413 {
4414 /*
4415 * Set attribute(s)...
4416 */
4417
4418 int num_keywords; /* Number of keywords */
4419 cups_option_t *keywords; /* Keywords */
4420
4421
75bd9771 4422 cupsdLogJob(job, CUPSD_LOG_DEBUG, "PPD: %s", message);
dd1abb6b 4423
c9fc04c6
MS
4424 num_keywords = cupsParseOptions(message, 0, &keywords);
4425
b9faaae1
MS
4426 if (cupsdUpdatePrinterPPD(job->printer, num_keywords, keywords))
4427 cupsdSetPrinterAttrs(job->printer);
c9fc04c6
MS
4428
4429 cupsFreeOptions(num_keywords, keywords);
4430 }
4d301e69 4431 else
f899b121 4432 {
4d301e69
MS
4433 /*
4434 * Strip legacy message prefix...
4435 */
f899b121 4436
4d301e69 4437 if (!strncmp(message, "recoverable:", 12))
d2354e63 4438 {
4d301e69
MS
4439 ptr = message + 12;
4440 while (isspace(*ptr & 255))
4441 ptr ++;
d2354e63 4442 }
4d301e69 4443 else if (!strncmp(message, "recovered:", 10))
acb056cb 4444 {
4d301e69
MS
4445 ptr = message + 10;
4446 while (isspace(*ptr & 255))
4447 ptr ++;
acb056cb 4448 }
4d301e69
MS
4449 else
4450 ptr = message;
4451
4452 cupsdLogJob(job, loglevel, "%s", ptr);
f899b121 4453
5180a04c
MS
4454 if (loglevel < CUPSD_LOG_DEBUG &&
4455 strcmp(job->printer->state_message, ptr))
1f0275e3 4456 {
4d301e69 4457 strlcpy(job->printer->state_message, ptr,
b9faaae1 4458 sizeof(job->printer->state_message));
4509bb49 4459
8b116e60 4460 event |= CUPSD_EVENT_PRINTER_STATE | CUPSD_EVENT_JOB_PROGRESS;
4509bb49 4461
229681c1 4462 if (loglevel <= job->status_level && job->status_level > CUPSD_LOG_ERROR)
1f0275e3
MS
4463 {
4464 /*
b9faaae1 4465 * Some messages show in the job-printer-state-message attribute...
1f0275e3 4466 */
09a101d6 4467
1f0275e3
MS
4468 if (loglevel != CUPSD_LOG_NOTICE)
4469 job->status_level = loglevel;
01ce6322 4470
1f0275e3 4471 update_job_attrs(job, 1);
f0ab5bff
MS
4472
4473 cupsdLogJob(job, CUPSD_LOG_DEBUG,
4474 "Set job-printer-state-message to \"%s\", "
4475 "current level=%s",
4476 job->printer_message->values[0].string.text,
4477 levels[job->status_level]);
1f0275e3 4478 }
75bd9771 4479 }
f899b121 4480 }
4481
4482 if (!strchr(job->status_buffer->buffer, '\n'))
4483 break;
4484 }
4485
f8b3a85b
MS
4486 if (event & CUPSD_EVENT_JOB_PROGRESS)
4487 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
4488 "%s", job->printer->state_message);
5180a04c 4489 if (event & CUPSD_EVENT_PRINTER_STATE)
b9faaae1
MS
4490 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, job->printer, NULL,
4491 (job->printer->type & CUPS_PRINTER_CLASS) ?
f899b121 4492 "Class \"%s\" state changed." :
4493 "Printer \"%s\" state changed.",
b9faaae1 4494 job->printer->name);
f899b121 4495
8b116e60 4496
f899b121 4497 if (ptr == NULL && !job->status_buffer->bufused)
4498 {
4499 /*
4500 * See if all of the filters and the backend have returned their
4501 * exit statuses.
4502 */
4503
4504 for (i = 0; job->filters[i] < 0; i ++);
4505
4506 if (job->filters[i])
d7871c8c
MS
4507 {
4508 /*
4509 * EOF but we haven't collected the exit status of all filters...
4510 */
4511
4512 cupsdCheckProcess();
f899b121 4513 return;
d7871c8c 4514 }
f899b121 4515
4516 if (job->current_file >= job->num_files && job->backend > 0)
d7871c8c
MS
4517 {
4518 /*
4519 * EOF but we haven't collected the exit status of the backend...
4520 */
4521
4522 cupsdCheckProcess();
f899b121 4523 return;
d7871c8c 4524 }
f899b121 4525
4526 /*
4527 * Handle the end of job stuff...
4528 */
4529
ef55b745 4530 finalize_job(job, 1);
b9faaae1
MS
4531
4532 /*
4533 * Check for new jobs...
4534 */
4535
4536 cupsdCheckJobs();
f899b121 4537 }
4538}
4539
4540
4541/*
bc44d920 4542 * 'update_job_attrs()' - Update the job-printer-* attributes.
4543 */
4544
4545void
4509bb49 4546update_job_attrs(cupsd_job_t *job, /* I - Job to update */
b9faaae1 4547 int do_message)/* I - 1 = copy job-printer-state message */
bc44d920 4548{
4549 int i; /* Looping var */
4550 int num_reasons; /* Actual number of reasons */
4551 const char * const *reasons; /* Reasons */
f0ab5bff 4552 static const char *none = "none"; /* "none" reason */
bc44d920 4553
4554
4555 /*
4556 * Get/create the job-printer-state-* attributes...
4557 */
4558
4559 if (!job->printer_message)
4560 {
4561 if ((job->printer_message = ippFindAttribute(job->attrs,
4562 "job-printer-state-message",
4563 IPP_TAG_TEXT)) == NULL)
4564 job->printer_message = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_TEXT,
4565 "job-printer-state-message",
4566 NULL, "");
4567 }
4568
4569 if (!job->printer_reasons)
4570 job->printer_reasons = ippFindAttribute(job->attrs,
4571 "job-printer-state-reasons",
4572 IPP_TAG_KEYWORD);
4573
bc44d920 4574 /*
b9faaae1 4575 * Copy or clear the printer-state-message value as needed...
bc44d920 4576 */
4577
b9faaae1
MS
4578 if (job->state_value != IPP_JOB_PROCESSING &&
4579 job->status_level == CUPSD_LOG_INFO)
4580 cupsdSetString(&(job->printer_message->values[0].string.text), "");
4581 else if (job->printer->state_message[0] && do_message)
bc44d920 4582 cupsdSetString(&(job->printer_message->values[0].string.text),
b9faaae1 4583 job->printer->state_message);
ef55b745 4584
bc44d920 4585 /*
4586 * ... and the printer-state-reasons value...
4587 */
4588
b9faaae1 4589 if (job->printer->num_reasons == 0)
bc44d920 4590 {
4591 num_reasons = 1;
f0ab5bff 4592 reasons = &none;
bc44d920 4593 }
4594 else
4595 {
b9faaae1
MS
4596 num_reasons = job->printer->num_reasons;
4597 reasons = (const char * const *)job->printer->reasons;
bc44d920 4598 }
4599
4600 if (!job->printer_reasons || job->printer_reasons->num_values != num_reasons)
4601 {
b9faaae1
MS
4602 /*
4603 * Replace/create a job-printer-state-reasons attribute...
4604 */
4605
bc44d920 4606 ippDeleteAttribute(job->attrs, job->printer_reasons);
4607
4608 job->printer_reasons = ippAddStrings(job->attrs,
4609 IPP_TAG_JOB, IPP_TAG_KEYWORD,
4610 "job-printer-state-reasons",
4611 num_reasons, NULL, NULL);
4612 }
b9faaae1 4613 else
d2354e63 4614 {
b9faaae1
MS
4615 /*
4616 * Don't bother clearing the reason strings if they are the same...
4617 */
4618
4619 for (i = 0; i < num_reasons; i ++)
4620 if (strcmp(job->printer_reasons->values[i].string.text, reasons[i]))
4621 break;
4622
4623 if (i >= num_reasons)
4624 return;
4625
4626 /*
4627 * Not the same, so free the current strings...
4628 */
4629
4630 for (i = 0; i < num_reasons; i ++)
d2354e63
MS
4631 _cupsStrFree(job->printer_reasons->values[i].string.text);
4632 }
bc44d920 4633
b9faaae1
MS
4634 /*
4635 * Copy the reasons...
4636 */
4637
bc44d920 4638 for (i = 0; i < num_reasons; i ++)
d2354e63 4639 job->printer_reasons->values[i].string.text = _cupsStrAlloc(reasons[i]);
bc44d920 4640}
4641
4642
4643/*
b19ccc9e 4644 * End of "$Id: job.c 7902 2008-09-03 14:20:17Z mike $".
ef416fc2 4645 */