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