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