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