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