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