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