]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/job.c
f8edfdf46d9fbb343f69db61be5c2ccd9308e9eb
[thirdparty/cups.git] / scheduler / job.c
1 /*
2 * "$Id: job.c 5974 2006-09-20 18:42:37Z mike $"
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 char scheme[32], /* Scheme portion of URI */
927 username[64], /* Username portion of URI */
928 host[HTTP_MAX_HOST],
929 /* Host portion of URI */
930 resource[HTTP_MAX_URI];
931 /* Resource portion of URI */
932 int port; /* Port portion of URI */
933 const char *dest; /* Destination */
934 mime_type_t **filetypes; /* New filetypes array */
935 int *compressions; /* New compressions array */
936
937
938 if (job->attrs)
939 {
940 if (job->state_value > IPP_JOB_STOPPED)
941 job->access_time = time(NULL);
942
943 return;
944 }
945
946 if ((job->attrs = ippNew()) == NULL)
947 {
948 cupsdLogMessage(CUPSD_LOG_ERROR, "Ran out of memory for job attributes!");
949 return;
950 }
951
952 /*
953 * Load job attributes...
954 */
955
956 cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading attributes for job %d...",
957 job->id);
958
959 snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, job->id);
960 if ((fp = cupsFileOpen(jobfile, "r")) == NULL)
961 {
962 cupsdLogMessage(CUPSD_LOG_ERROR,
963 "Unable to open job control file \"%s\" - %s!",
964 jobfile, strerror(errno));
965 ippDelete(job->attrs);
966 job->attrs = NULL;
967 return;
968 }
969
970 if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, job->attrs) != IPP_DATA)
971 {
972 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to read job control file \"%s\"!",
973 jobfile);
974 cupsFileClose(fp);
975 ippDelete(job->attrs);
976 job->attrs = NULL;
977 unlink(jobfile);
978 return;
979 }
980
981 cupsFileClose(fp);
982
983 /*
984 * Copy attribute data to the job object...
985 */
986
987 if ((job->state = ippFindAttribute(job->attrs, "job-state",
988 IPP_TAG_ENUM)) == NULL)
989 {
990 cupsdLogMessage(CUPSD_LOG_ERROR,
991 "Missing or bad job-state attribute in control "
992 "file \"%s\"!",
993 jobfile);
994 ippDelete(job->attrs);
995 job->attrs = NULL;
996 unlink(jobfile);
997 return;
998 }
999
1000 job->state_value = (ipp_jstate_t)job->state->values[0].integer;
1001
1002 if (!job->dest)
1003 {
1004 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
1005 IPP_TAG_URI)) == NULL)
1006 {
1007 cupsdLogMessage(CUPSD_LOG_ERROR,
1008 "No job-printer-uri attribute in control file \"%s\"!",
1009 jobfile);
1010 ippDelete(job->attrs);
1011 job->attrs = NULL;
1012 unlink(jobfile);
1013 return;
1014 }
1015
1016 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, scheme,
1017 sizeof(scheme), username, sizeof(username), host,
1018 sizeof(host), &port, resource, sizeof(resource));
1019
1020 if ((dest = cupsdValidateDest(host, resource, &(job->dtype),
1021 NULL)) == NULL)
1022 {
1023 cupsdLogMessage(CUPSD_LOG_ERROR,
1024 "Unable to queue job for destination \"%s\"!",
1025 attr->values[0].string.text);
1026 ippDelete(job->attrs);
1027 job->attrs = NULL;
1028 unlink(jobfile);
1029 return;
1030 }
1031
1032 cupsdSetString(&job->dest, dest);
1033 }
1034
1035 job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed",
1036 IPP_TAG_INTEGER);
1037 job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
1038
1039 if (!job->priority)
1040 {
1041 if ((attr = ippFindAttribute(job->attrs, "job-priority",
1042 IPP_TAG_INTEGER)) == NULL)
1043 {
1044 cupsdLogMessage(CUPSD_LOG_ERROR,
1045 "Missing or bad job-priority attribute in control "
1046 "file \"%s\"!", jobfile);
1047 ippDelete(job->attrs);
1048 job->attrs = NULL;
1049 unlink(jobfile);
1050 return;
1051 }
1052
1053 job->priority = attr->values[0].integer;
1054 }
1055
1056 if (!job->username)
1057 {
1058 if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name",
1059 IPP_TAG_NAME)) == NULL)
1060 {
1061 cupsdLogMessage(CUPSD_LOG_ERROR,
1062 "Missing or bad job-originating-user-name attribute "
1063 "in control file \"%s\"!", jobfile);
1064 ippDelete(job->attrs);
1065 job->attrs = NULL;
1066 unlink(jobfile);
1067 return;
1068 }
1069
1070 cupsdSetString(&job->username, attr->values[0].string.text);
1071 }
1072
1073 /*
1074 * Set the job hold-until time and state...
1075 */
1076
1077 if (job->state_value == IPP_JOB_HELD)
1078 {
1079 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1080 IPP_TAG_KEYWORD)) == NULL)
1081 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
1082
1083 if (attr)
1084 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
1085 else
1086 {
1087 job->state->values[0].integer = IPP_JOB_PENDING;
1088 job->state_value = IPP_JOB_PENDING;
1089 }
1090 }
1091 else if (job->state_value == IPP_JOB_PROCESSING)
1092 {
1093 job->state->values[0].integer = IPP_JOB_PENDING;
1094 job->state_value = IPP_JOB_PENDING;
1095 }
1096
1097 if (!job->num_files)
1098 {
1099 /*
1100 * Find all the d##### files...
1101 */
1102
1103 for (fileid = 1; fileid < 10000; fileid ++)
1104 {
1105 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
1106 job->id, fileid);
1107
1108 if (access(jobfile, 0))
1109 break;
1110
1111 cupsdLogMessage(CUPSD_LOG_DEBUG, "Auto-typing document file \"%s\"...",
1112 jobfile);
1113
1114 if (fileid > job->num_files)
1115 {
1116 if (job->num_files == 0)
1117 {
1118 compressions = (int *)calloc(fileid, sizeof(int));
1119 filetypes = (mime_type_t **)calloc(fileid, sizeof(mime_type_t *));
1120 }
1121 else
1122 {
1123 compressions = (int *)realloc(job->compressions,
1124 sizeof(int) * fileid);
1125 filetypes = (mime_type_t **)realloc(job->filetypes,
1126 sizeof(mime_type_t *) *
1127 fileid);
1128 }
1129
1130 if (!compressions || !filetypes)
1131 {
1132 cupsdLogMessage(CUPSD_LOG_ERROR,
1133 "Ran out of memory for job file types!");
1134 return;
1135 }
1136
1137 job->compressions = compressions;
1138 job->filetypes = filetypes;
1139 job->num_files = fileid;
1140 }
1141
1142 job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, jobfile, NULL,
1143 job->compressions + fileid - 1);
1144
1145 if (!job->filetypes[fileid - 1])
1146 job->filetypes[fileid - 1] = mimeType(MimeDatabase, "application",
1147 "vnd.cups-raw");
1148 }
1149 }
1150
1151 job->access_time = time(NULL);
1152 }
1153
1154
1155 /*
1156 * 'cupsdMoveJob()' - Move the specified job to a different destination.
1157 */
1158
1159 void
1160 cupsdMoveJob(cupsd_job_t *job, /* I - Job */
1161 cupsd_printer_t *p) /* I - Destination printer or class */
1162 {
1163 ipp_attribute_t *attr; /* job-printer-uri attribute */
1164 const char *olddest; /* Old destination */
1165 cupsd_printer_t *oldp; /* Old pointer */
1166
1167
1168 /*
1169 * Don't move completed jobs...
1170 */
1171
1172 if (job->state_value > IPP_JOB_STOPPED)
1173 return;
1174
1175 /*
1176 * Get the old destination...
1177 */
1178
1179 olddest = job->dest;
1180
1181 if (job->printer)
1182 oldp = job->printer;
1183 else
1184 oldp = cupsdFindDest(olddest);
1185
1186 /*
1187 * Change the destination information...
1188 */
1189
1190 cupsdLoadJob(job);
1191
1192 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, oldp, job,
1193 "Job #%d moved from %s to %s.", job->id, olddest,
1194 p->name);
1195
1196 cupsdSetString(&job->dest, p->name);
1197 job->dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE |
1198 CUPS_PRINTER_IMPLICIT);
1199
1200 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
1201 IPP_TAG_URI)) != NULL)
1202 cupsdSetString(&(attr->values[0].string.text), p->uri);
1203
1204 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job,
1205 "Job #%d moved from %s to %s.", job->id, olddest,
1206 p->name);
1207
1208 cupsdSaveJob(job);
1209 }
1210
1211
1212 /*
1213 * 'cupsdReleaseJob()' - Release the specified job.
1214 */
1215
1216 void
1217 cupsdReleaseJob(cupsd_job_t *job) /* I - Job */
1218 {
1219 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReleaseJob: id = %d", job->id);
1220
1221 if (job->state_value == IPP_JOB_HELD)
1222 {
1223 DEBUG_puts("cupsdReleaseJob: setting state to pending...");
1224
1225 job->state->values[0].integer = IPP_JOB_PENDING;
1226 job->state_value = IPP_JOB_PENDING;
1227 cupsdSaveJob(job);
1228 cupsdCheckJobs();
1229 }
1230 }
1231
1232
1233 /*
1234 * 'cupsdRestartJob()' - Restart the specified job.
1235 */
1236
1237 void
1238 cupsdRestartJob(cupsd_job_t *job) /* I - Job */
1239 {
1240 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRestartJob: id = %d", job->id);
1241
1242 if (job->state_value == IPP_JOB_STOPPED || job->num_files)
1243 {
1244 ipp_jstate_t old_state; /* Old job state */
1245
1246
1247 cupsdLoadJob(job);
1248
1249 old_state = job->state_value;
1250
1251 job->tries = 0;
1252 job->state->values[0].integer = IPP_JOB_PENDING;
1253 job->state_value = IPP_JOB_PENDING;
1254
1255 cupsdSaveJob(job);
1256
1257 if (old_state > IPP_JOB_STOPPED)
1258 cupsArrayAdd(ActiveJobs, job);
1259
1260 cupsdCheckJobs();
1261 }
1262 }
1263
1264
1265 /*
1266 * 'cupsdSaveAllJobs()' - Save a summary of all jobs to disk.
1267 */
1268
1269 void
1270 cupsdSaveAllJobs(void)
1271 {
1272 int i; /* Looping var */
1273 cups_file_t *fp; /* Job cache file */
1274 char temp[1024]; /* Temporary string */
1275 cupsd_job_t *job; /* Current job */
1276 time_t curtime; /* Current time */
1277 struct tm *curdate; /* Current date */
1278
1279
1280 snprintf(temp, sizeof(temp), "%s/job.cache", CacheDir);
1281 if ((fp = cupsFileOpen(temp, "w")) == NULL)
1282 {
1283 cupsdLogMessage(CUPSD_LOG_ERROR,
1284 "Unable to create job cache file \"%s\" - %s",
1285 temp, strerror(errno));
1286 return;
1287 }
1288
1289 cupsdLogMessage(CUPSD_LOG_INFO, "Saving job cache file \"%s\"...", temp);
1290
1291 /*
1292 * Restrict access to the file...
1293 */
1294
1295 fchown(cupsFileNumber(fp), getuid(), Group);
1296 fchmod(cupsFileNumber(fp), ConfigFilePerm);
1297
1298 /*
1299 * Write a small header to the file...
1300 */
1301
1302 curtime = time(NULL);
1303 curdate = localtime(&curtime);
1304 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
1305
1306 cupsFilePuts(fp, "# Job cache file for " CUPS_SVERSION "\n");
1307 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
1308 cupsFilePrintf(fp, "NextJobId %d\n", NextJobId);
1309
1310 /*
1311 * Write each job known to the system...
1312 */
1313
1314 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
1315 job;
1316 job = (cupsd_job_t *)cupsArrayNext(Jobs))
1317 {
1318 cupsFilePrintf(fp, "<Job %d>\n", job->id);
1319 cupsFilePrintf(fp, "State %d\n", job->state_value);
1320 cupsFilePrintf(fp, "Priority %d\n", job->priority);
1321 cupsFilePrintf(fp, "Username %s\n", job->username);
1322 cupsFilePrintf(fp, "Destination %s\n", job->dest);
1323 cupsFilePrintf(fp, "DestType %d\n", job->dtype);
1324 cupsFilePrintf(fp, "NumFiles %d\n", job->num_files);
1325 for (i = 0; i < job->num_files; i ++)
1326 cupsFilePrintf(fp, "File %d %s/%s %d\n", i + 1, job->filetypes[i]->super,
1327 job->filetypes[i]->type, job->compressions[i]);
1328 cupsFilePuts(fp, "</Job>\n");
1329 }
1330
1331 cupsFileClose(fp);
1332 }
1333
1334
1335 /*
1336 * 'cupsdSaveJob()' - Save a job to disk.
1337 */
1338
1339 void
1340 cupsdSaveJob(cupsd_job_t *job) /* I - Job */
1341 {
1342 char filename[1024]; /* Job control filename */
1343 cups_file_t *fp; /* Job file */
1344
1345
1346 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSaveJob(job=%p(%d)): job->attrs=%p",
1347 job, job->id, job->attrs);
1348
1349 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id);
1350
1351 if ((fp = cupsFileOpen(filename, "w")) == NULL)
1352 {
1353 cupsdLogMessage(CUPSD_LOG_ERROR,
1354 "Unable to create job control file \"%s\" - %s.",
1355 filename, strerror(errno));
1356 return;
1357 }
1358
1359 fchmod(cupsFileNumber(fp), 0600);
1360 fchown(cupsFileNumber(fp), RunUser, Group);
1361
1362 job->attrs->state = IPP_IDLE;
1363
1364 if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL,
1365 job->attrs) != IPP_DATA)
1366 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to write job control file \"%s\"!",
1367 filename);
1368
1369 cupsFileClose(fp);
1370 }
1371
1372
1373 /*
1374 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job...
1375 */
1376
1377 void
1378 cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */
1379 const char *when) /* I - When to resume */
1380 {
1381 time_t curtime; /* Current time */
1382 struct tm *curdate; /* Current date */
1383 int hour; /* Hold hour */
1384 int minute; /* Hold minute */
1385 int second; /* Hold second */
1386
1387
1388 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil(%d, \"%s\")",
1389 job->id, when);
1390
1391 second = 0;
1392
1393 if (!strcmp(when, "indefinite") || !strcmp(when, "authenticated"))
1394 {
1395 /*
1396 * Hold indefinitely...
1397 */
1398
1399 job->hold_until = 0;
1400 }
1401 else if (!strcmp(when, "day-time"))
1402 {
1403 /*
1404 * Hold to 6am the next morning unless local time is < 6pm.
1405 */
1406
1407 curtime = time(NULL);
1408 curdate = localtime(&curtime);
1409
1410 if (curdate->tm_hour < 18)
1411 job->hold_until = curtime;
1412 else
1413 job->hold_until = curtime +
1414 ((29 - curdate->tm_hour) * 60 + 59 -
1415 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1416 }
1417 else if (!strcmp(when, "evening") || !strcmp(when, "night"))
1418 {
1419 /*
1420 * Hold to 6pm unless local time is > 6pm or < 6am.
1421 */
1422
1423 curtime = time(NULL);
1424 curdate = localtime(&curtime);
1425
1426 if (curdate->tm_hour < 6 || curdate->tm_hour >= 18)
1427 job->hold_until = curtime;
1428 else
1429 job->hold_until = curtime +
1430 ((17 - curdate->tm_hour) * 60 + 59 -
1431 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1432 }
1433 else if (!strcmp(when, "second-shift"))
1434 {
1435 /*
1436 * Hold to 4pm unless local time is > 4pm.
1437 */
1438
1439 curtime = time(NULL);
1440 curdate = localtime(&curtime);
1441
1442 if (curdate->tm_hour >= 16)
1443 job->hold_until = curtime;
1444 else
1445 job->hold_until = curtime +
1446 ((15 - curdate->tm_hour) * 60 + 59 -
1447 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1448 }
1449 else if (!strcmp(when, "third-shift"))
1450 {
1451 /*
1452 * Hold to 12am unless local time is < 8am.
1453 */
1454
1455 curtime = time(NULL);
1456 curdate = localtime(&curtime);
1457
1458 if (curdate->tm_hour < 8)
1459 job->hold_until = curtime;
1460 else
1461 job->hold_until = curtime +
1462 ((23 - curdate->tm_hour) * 60 + 59 -
1463 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1464 }
1465 else if (!strcmp(when, "weekend"))
1466 {
1467 /*
1468 * Hold to weekend unless we are in the weekend.
1469 */
1470
1471 curtime = time(NULL);
1472 curdate = localtime(&curtime);
1473
1474 if (curdate->tm_wday || curdate->tm_wday == 6)
1475 job->hold_until = curtime;
1476 else
1477 job->hold_until = curtime +
1478 (((5 - curdate->tm_wday) * 24 +
1479 (17 - curdate->tm_hour)) * 60 + 59 -
1480 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1481 }
1482 else if (sscanf(when, "%d:%d:%d", &hour, &minute, &second) >= 2)
1483 {
1484 /*
1485 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1486 */
1487
1488 curtime = time(NULL);
1489 curdate = gmtime(&curtime);
1490
1491 job->hold_until = curtime +
1492 ((hour - curdate->tm_hour) * 60 + minute -
1493 curdate->tm_min) * 60 + second - curdate->tm_sec;
1494
1495 /*
1496 * Hold until next day as needed...
1497 */
1498
1499 if (job->hold_until < curtime)
1500 job->hold_until += 24 * 60 * 60 * 60;
1501 }
1502
1503 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil: hold_until = %d",
1504 (int)job->hold_until);
1505 }
1506
1507
1508 /*
1509 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
1510 * the list as needed.
1511 */
1512
1513 void
1514 cupsdSetJobPriority(
1515 cupsd_job_t *job, /* I - Job ID */
1516 int priority) /* I - New priority (0 to 100) */
1517 {
1518 ipp_attribute_t *attr; /* Job attribute */
1519
1520
1521 /*
1522 * Don't change completed jobs...
1523 */
1524
1525 if (job->state_value >= IPP_JOB_PROCESSING)
1526 return;
1527
1528 /*
1529 * Set the new priority and re-add the job into the active list...
1530 */
1531
1532 cupsArrayRemove(ActiveJobs, job);
1533
1534 job->priority = priority;
1535
1536 if ((attr = ippFindAttribute(job->attrs, "job-priority",
1537 IPP_TAG_INTEGER)) != NULL)
1538 attr->values[0].integer = priority;
1539 else
1540 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
1541 priority);
1542
1543 cupsArrayAdd(ActiveJobs, job);
1544
1545 cupsdSaveJob(job);
1546 }
1547
1548
1549 /*
1550 * 'cupsdStopAllJobs()' - Stop all print jobs.
1551 */
1552
1553 void
1554 cupsdStopAllJobs(int force) /* I - 1 = Force all filters to stop */
1555 {
1556 cupsd_job_t *job; /* Current job */
1557
1558
1559 DEBUG_puts("cupsdStopAllJobs()");
1560
1561 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
1562 job;
1563 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
1564 if (job->state_value == IPP_JOB_PROCESSING)
1565 {
1566 cupsdStopJob(job, force);
1567 job->state->values[0].integer = IPP_JOB_PENDING;
1568 job->state_value = IPP_JOB_PENDING;
1569 }
1570 }
1571
1572
1573 /*
1574 * 'cupsdStopJob()' - Stop a print job.
1575 */
1576
1577 void
1578 cupsdStopJob(cupsd_job_t *job, /* I - Job */
1579 int force) /* I - 1 = Force all filters to stop */
1580 {
1581 int i; /* Looping var */
1582
1583
1584 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStopJob: id = %d, force = %d",
1585 job->id, force);
1586
1587 if (job->state_value != IPP_JOB_PROCESSING)
1588 return;
1589
1590 FilterLevel -= job->cost;
1591
1592 if (job->printer->state == IPP_PRINTER_PROCESSING)
1593 cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0);
1594
1595 job->state->values[0].integer = IPP_JOB_STOPPED;
1596 job->state_value = IPP_JOB_STOPPED;
1597 job->printer->job = NULL;
1598 job->printer = NULL;
1599
1600 job->current_file --;
1601
1602 for (i = 0; job->filters[i]; i ++)
1603 if (job->filters[i] > 0)
1604 {
1605 cupsdEndProcess(job->filters[i], force);
1606 job->filters[i] = 0;
1607 }
1608
1609 if (job->backend > 0)
1610 {
1611 cupsdEndProcess(job->backend, force);
1612 job->backend = 0;
1613 }
1614
1615 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1616 "cupsdStopJob: Closing print pipes [ %d %d ]...",
1617 job->print_pipes[0], job->print_pipes[1]);
1618
1619 cupsdClosePipe(job->print_pipes);
1620
1621 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1622 "cupsdStopJob: Closing back pipes [ %d %d ]...",
1623 job->back_pipes[0], job->back_pipes[1]);
1624
1625 cupsdClosePipe(job->back_pipes);
1626
1627 if (job->status_buffer)
1628 {
1629 /*
1630 * Close the pipe and clear the input bit.
1631 */
1632
1633 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1634 "cupsdStopJob: Removing fd %d from InputSet...",
1635 job->status_buffer->fd);
1636
1637 FD_CLR(job->status_buffer->fd, InputSet);
1638
1639 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1640 "cupsdStopJob: Closing status pipes [ %d %d ]...",
1641 job->status_pipes[0], job->status_pipes[1]);
1642
1643 cupsdClosePipe(job->status_pipes);
1644 cupsdStatBufDelete(job->status_buffer);
1645
1646 job->status_buffer = NULL;
1647 }
1648 }
1649
1650
1651 /*
1652 * 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory.
1653 */
1654
1655 void
1656 cupsdUnloadCompletedJobs(void)
1657 {
1658 cupsd_job_t *job; /* Current job */
1659 time_t expire; /* Expiration time */
1660
1661
1662 expire = time(NULL) - 60;
1663
1664 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
1665 job;
1666 job = (cupsd_job_t *)cupsArrayNext(Jobs))
1667 if (job->attrs && job->state_value >= IPP_JOB_STOPPED &&
1668 job->access_time < expire)
1669 unload_job(job);
1670 }
1671
1672
1673 /*
1674 * 'cupsdUpdateJob()' - Read a status update from a job's filters.
1675 */
1676
1677 void
1678 cupsdUpdateJob(cupsd_job_t *job) /* I - Job to check */
1679 {
1680 int i; /* Looping var */
1681 int copies; /* Number of copies printed */
1682 char message[1024], /* Message text */
1683 *ptr; /* Pointer update... */
1684 int loglevel, /* Log level for message */
1685 event = 0; /* Events? */
1686
1687
1688 while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
1689 message, sizeof(message))) != NULL)
1690 {
1691 /*
1692 * Process page and printer state messages as needed...
1693 */
1694
1695 if (loglevel == CUPSD_LOG_PAGE)
1696 {
1697 /*
1698 * Page message; send the message to the page_log file and update the
1699 * job sheet count...
1700 */
1701
1702 if (job->sheets != NULL)
1703 {
1704 if (!strncasecmp(message, "total ", 6))
1705 {
1706 /*
1707 * Got a total count of pages from a backend or filter...
1708 */
1709
1710 copies = atoi(message + 6);
1711 copies -= job->sheets->values[0].integer; /* Just track the delta */
1712 }
1713 else if (!sscanf(message, "%*d%d", &copies))
1714 copies = 1;
1715
1716 job->sheets->values[0].integer += copies;
1717
1718 if (job->printer->page_limit)
1719 cupsdUpdateQuota(job->printer, job->username, copies, 0);
1720 }
1721
1722 cupsdLogPage(job, message);
1723
1724 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
1725 "Printed %d page(s).", job->sheets->values[0].integer);
1726 }
1727 else if (loglevel == CUPSD_LOG_STATE)
1728 {
1729 cupsdSetPrinterReasons(job->printer, message);
1730 cupsdAddPrinterHistory(job->printer);
1731 event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
1732 }
1733 else if (loglevel == CUPSD_LOG_ATTR)
1734 {
1735 /*
1736 * Set attribute(s)...
1737 */
1738
1739 /**** TODO ****/
1740 }
1741 #ifdef __APPLE__
1742 else if (!strncmp(message, "recoverable:", 12))
1743 {
1744 cupsdSetPrinterReasons(job->printer,
1745 "+com.apple.print.recoverable-warning");
1746
1747 ptr = message + 12;
1748 while (isspace(*ptr & 255))
1749 ptr ++;
1750
1751 cupsdSetString(&job->printer->recoverable, ptr);
1752 cupsdAddPrinterHistory(job->printer);
1753 event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
1754 }
1755 else if (!strncmp(message, "recovered:", 10))
1756 {
1757 cupsdSetPrinterReasons(job->printer,
1758 "-com.apple.print.recoverable-warning");
1759
1760 ptr = message + 10;
1761 while (isspace(*ptr & 255))
1762 ptr ++;
1763
1764 cupsdSetString(&job->printer->recoverable, ptr);
1765 cupsdAddPrinterHistory(job->printer);
1766 event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
1767 }
1768 #endif /* __APPLE__ */
1769 else if (loglevel <= CUPSD_LOG_INFO)
1770 {
1771 /*
1772 * Some message to show in the printer-state-message attribute...
1773 */
1774
1775 strlcpy(job->printer->state_message, message,
1776 sizeof(job->printer->state_message));
1777 cupsdAddPrinterHistory(job->printer);
1778 }
1779
1780 if (!strchr(job->status_buffer->buffer, '\n'))
1781 break;
1782 }
1783
1784 if ((event & CUPSD_EVENT_PRINTER_STATE_CHANGED))
1785 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE_CHANGED, job->printer, NULL,
1786 (job->printer->type & CUPS_PRINTER_CLASS) ?
1787 "Class \"%s\" state changed." :
1788 "Printer \"%s\" state changed.",
1789 job->printer->name);
1790
1791 if (ptr == NULL && !job->status_buffer->bufused)
1792 {
1793 /*
1794 * See if all of the filters and the backend have returned their
1795 * exit statuses.
1796 */
1797
1798 for (i = 0; job->filters[i] < 0; i ++);
1799
1800 if (job->filters[i])
1801 return;
1802
1803 if (job->current_file >= job->num_files && job->backend > 0)
1804 return;
1805
1806 /*
1807 * Handle the end of job stuff...
1808 */
1809
1810 cupsdFinishJob(job);
1811 }
1812 }
1813
1814
1815 /*
1816 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
1817 */
1818
1819 static int /* O - Difference */
1820 compare_active_jobs(void *first, /* I - First job */
1821 void *second, /* I - Second job */
1822 void *data) /* I - App data (not used) */
1823 {
1824 int diff; /* Difference */
1825
1826
1827 if ((diff = ((cupsd_job_t *)second)->priority -
1828 ((cupsd_job_t *)first)->priority) != 0)
1829 return (diff);
1830 else
1831 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
1832 }
1833
1834
1835 /*
1836 * 'compare_jobs()' - Compare the job IDs of two jobs.
1837 */
1838
1839 static int /* O - Difference */
1840 compare_jobs(void *first, /* I - First job */
1841 void *second, /* I - Second job */
1842 void *data) /* I - App data (not used) */
1843 {
1844 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
1845 }
1846
1847
1848 /*
1849 * 'free_job()' - Free all memory used by a job.
1850 */
1851
1852 static void
1853 free_job(cupsd_job_t *job) /* I - Job */
1854 {
1855 cupsdClearString(&job->username);
1856 cupsdClearString(&job->dest);
1857
1858 if (job->num_files > 0)
1859 {
1860 free(job->compressions);
1861 free(job->filetypes);
1862 }
1863
1864 ippDelete(job->attrs);
1865
1866 free(job);
1867 }
1868
1869
1870 /*
1871 * 'ipp_length()' - Compute the size of the buffer needed to hold
1872 * the textual IPP attributes.
1873 */
1874
1875 static int /* O - Size of attribute buffer */
1876 ipp_length(ipp_t *ipp) /* I - IPP request */
1877 {
1878 int bytes; /* Number of bytes */
1879 int i; /* Looping var */
1880 ipp_attribute_t *attr; /* Current attribute */
1881
1882
1883 /*
1884 * Loop through all attributes...
1885 */
1886
1887 bytes = 0;
1888
1889 for (attr = ipp->attrs; attr != NULL; attr = attr->next)
1890 {
1891 /*
1892 * Skip attributes that won't be sent to filters...
1893 */
1894
1895 if (attr->value_tag == IPP_TAG_MIMETYPE ||
1896 attr->value_tag == IPP_TAG_NAMELANG ||
1897 attr->value_tag == IPP_TAG_TEXTLANG ||
1898 attr->value_tag == IPP_TAG_URI ||
1899 attr->value_tag == IPP_TAG_URISCHEME)
1900 continue;
1901
1902 if (strncmp(attr->name, "time-", 5) == 0)
1903 continue;
1904
1905 /*
1906 * Add space for a leading space and commas between each value.
1907 * For the first attribute, the leading space isn't used, so the
1908 * extra byte can be used as the nul terminator...
1909 */
1910
1911 bytes ++; /* " " separator */
1912 bytes += attr->num_values; /* "," separators */
1913
1914 /*
1915 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
1916 * other attributes appear as "foo=value1,value2,...,valueN".
1917 */
1918
1919 if (attr->value_tag != IPP_TAG_BOOLEAN)
1920 bytes += strlen(attr->name);
1921 else
1922 bytes += attr->num_values * strlen(attr->name);
1923
1924 /*
1925 * Now add the size required for each value in the attribute...
1926 */
1927
1928 switch (attr->value_tag)
1929 {
1930 case IPP_TAG_INTEGER :
1931 case IPP_TAG_ENUM :
1932 /*
1933 * Minimum value of a signed integer is -2147483647, or 11 digits.
1934 */
1935
1936 bytes += attr->num_values * 11;
1937 break;
1938
1939 case IPP_TAG_BOOLEAN :
1940 /*
1941 * Add two bytes for each false ("no") value...
1942 */
1943
1944 for (i = 0; i < attr->num_values; i ++)
1945 if (!attr->values[i].boolean)
1946 bytes += 2;
1947 break;
1948
1949 case IPP_TAG_RANGE :
1950 /*
1951 * A range is two signed integers separated by a hyphen, or
1952 * 23 characters max.
1953 */
1954
1955 bytes += attr->num_values * 23;
1956 break;
1957
1958 case IPP_TAG_RESOLUTION :
1959 /*
1960 * A resolution is two signed integers separated by an "x" and
1961 * suffixed by the units, or 26 characters max.
1962 */
1963
1964 bytes += attr->num_values * 26;
1965 break;
1966
1967 case IPP_TAG_STRING :
1968 case IPP_TAG_TEXT :
1969 case IPP_TAG_NAME :
1970 case IPP_TAG_KEYWORD :
1971 case IPP_TAG_CHARSET :
1972 case IPP_TAG_LANGUAGE :
1973 case IPP_TAG_URI :
1974 /*
1975 * Strings can contain characters that need quoting. We need
1976 * at least 2 * len + 2 characters to cover the quotes and
1977 * any backslashes in the string.
1978 */
1979
1980 for (i = 0; i < attr->num_values; i ++)
1981 bytes += 2 * strlen(attr->values[i].string.text) + 2;
1982 break;
1983
1984 default :
1985 break; /* anti-compiler-warning-code */
1986 }
1987 }
1988
1989 return (bytes);
1990 }
1991
1992
1993 /*
1994 * 'load_job_cache()' - Load jobs from the job.cache file.
1995 */
1996
1997 static void
1998 load_job_cache(const char *filename) /* I - job.cache filename */
1999 {
2000 cups_file_t *fp; /* job.cache file */
2001 char line[1024], /* Line buffer */
2002 *value; /* Value on line */
2003 int linenum; /* Line number in file */
2004 cupsd_job_t *job; /* Current job */
2005 int jobid; /* Job ID */
2006 char jobfile[1024]; /* Job filename */
2007
2008
2009 /*
2010 * Open the job.cache file...
2011 */
2012
2013 if ((fp = cupsFileOpen(filename, "r")) == NULL)
2014 {
2015 if (errno != ENOENT)
2016 cupsdLogMessage(CUPSD_LOG_ERROR,
2017 "Unable to open job cache file \"%s\": %s",
2018 filename, strerror(errno));
2019
2020 load_request_root();
2021
2022 return;
2023 }
2024
2025 /*
2026 * Read entries from the job cache file and create jobs as needed.
2027 */
2028
2029 cupsdLogMessage(CUPSD_LOG_INFO, "Loading job cache file \"%s\"...",
2030 filename);
2031
2032 linenum = 0;
2033 job = NULL;
2034
2035 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
2036 {
2037 if (!strcasecmp(line, "NextJobId"))
2038 {
2039 if (value)
2040 NextJobId = atoi(value);
2041 }
2042 else if (!strcasecmp(line, "<Job"))
2043 {
2044 if (job)
2045 {
2046 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing </Job> directive on line %d!",
2047 linenum);
2048 continue;
2049 }
2050
2051 if (!value)
2052 {
2053 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing job ID on line %d!", linenum);
2054 continue;
2055 }
2056
2057 jobid = atoi(value);
2058
2059 if (jobid < 1)
2060 {
2061 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad job ID %d on line %d!", jobid,
2062 linenum);
2063 continue;
2064 }
2065
2066 snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, jobid);
2067 if (access(jobfile, 0))
2068 {
2069 cupsdLogMessage(CUPSD_LOG_ERROR, "Job %d files have gone away!", jobid);
2070 continue;
2071 }
2072
2073 job = calloc(1, sizeof(cupsd_job_t));
2074 if (!job)
2075 {
2076 cupsdLogMessage(CUPSD_LOG_EMERG,
2077 "Unable to allocate memory for job %d!", jobid);
2078 break;
2079 }
2080
2081 job->id = jobid;
2082 job->back_pipes[0] = -1;
2083 job->back_pipes[1] = -1;
2084 job->print_pipes[0] = -1;
2085 job->print_pipes[1] = -1;
2086 job->status_pipes[0] = -1;
2087 job->status_pipes[1] = -1;
2088
2089 cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading job %d from cache...", job->id);
2090 }
2091 else if (!job)
2092 {
2093 cupsdLogMessage(CUPSD_LOG_ERROR,
2094 "Missing <Job #> directive on line %d!", linenum);
2095 continue;
2096 }
2097 else if (!strcasecmp(line, "</Job>"))
2098 {
2099 cupsArrayAdd(Jobs, job);
2100
2101 if (job->state_value <= IPP_JOB_STOPPED)
2102 {
2103 cupsArrayAdd(ActiveJobs, job);
2104 cupsdLoadJob(job);
2105 }
2106
2107 job = NULL;
2108 }
2109 else if (!value)
2110 {
2111 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d!", linenum);
2112 continue;
2113 }
2114 else if (!strcasecmp(line, "State"))
2115 {
2116 job->state_value = (ipp_jstate_t)atoi(value);
2117
2118 if (job->state_value < IPP_JOB_PENDING)
2119 job->state_value = IPP_JOB_PENDING;
2120 else if (job->state_value > IPP_JOB_COMPLETED)
2121 job->state_value = IPP_JOB_COMPLETED;
2122 }
2123 else if (!strcasecmp(line, "Priority"))
2124 {
2125 job->priority = atoi(value);
2126 }
2127 else if (!strcasecmp(line, "Username"))
2128 {
2129 cupsdSetString(&job->username, value);
2130 }
2131 else if (!strcasecmp(line, "Destination"))
2132 {
2133 cupsdSetString(&job->dest, value);
2134 }
2135 else if (!strcasecmp(line, "DestType"))
2136 {
2137 job->dtype = (cups_ptype_t)atoi(value);
2138 }
2139 else if (!strcasecmp(line, "NumFiles"))
2140 {
2141 job->num_files = atoi(value);
2142
2143 if (job->num_files < 0)
2144 {
2145 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad NumFiles value %d on line %d!",
2146 job->num_files, linenum);
2147 job->num_files = 0;
2148 continue;
2149 }
2150
2151 if (job->num_files > 0)
2152 {
2153 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-001", RequestRoot,
2154 job->id);
2155 if (access(jobfile, 0))
2156 {
2157 cupsdLogMessage(CUPSD_LOG_INFO,
2158 "Data files for job %d have gone away!", job->id);
2159 job->num_files = 0;
2160 continue;
2161 }
2162
2163 job->filetypes = calloc(job->num_files, sizeof(mime_type_t *));
2164 job->compressions = calloc(job->num_files, sizeof(int));
2165
2166 if (!job->filetypes || !job->compressions)
2167 {
2168 cupsdLogMessage(CUPSD_LOG_EMERG,
2169 "Unable to allocate memory for %d files!",
2170 job->num_files);
2171 break;
2172 }
2173 }
2174 }
2175 else if (!strcasecmp(line, "File"))
2176 {
2177 int number, /* File number */
2178 compression; /* Compression value */
2179 char super[MIME_MAX_SUPER], /* MIME super type */
2180 type[MIME_MAX_TYPE]; /* MIME type */
2181
2182
2183 if (sscanf(value, "%d%*[ \t]%15[^/]/%255s%d", &number, super, type,
2184 &compression) != 4)
2185 {
2186 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File on line %d!", linenum);
2187 continue;
2188 }
2189
2190 if (number < 1 || number > job->num_files)
2191 {
2192 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File number %d on line %d!",
2193 number, linenum);
2194 continue;
2195 }
2196
2197 number --;
2198
2199 job->compressions[number] = compression;
2200 job->filetypes[number] = mimeType(MimeDatabase, super, type);
2201
2202 if (!job->filetypes[number])
2203 {
2204 /*
2205 * If the original MIME type is unknown, auto-type it!
2206 */
2207
2208 cupsdLogMessage(CUPSD_LOG_ERROR,
2209 "Unknown MIME type %s/%s for file %d of job %d!",
2210 super, type, number + 1, job->id);
2211
2212 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
2213 job->id, number + 1);
2214 job->filetypes[number] = mimeFileType(MimeDatabase, jobfile, NULL,
2215 job->compressions + number);
2216
2217 /*
2218 * If that didn't work, assume it is raw...
2219 */
2220
2221 if (!job->filetypes[number])
2222 job->filetypes[number] = mimeType(MimeDatabase, "application",
2223 "vnd.cups-raw");
2224 }
2225 }
2226 else
2227 cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown %s directive on line %d!",
2228 line, linenum);
2229 }
2230
2231 cupsFileClose(fp);
2232 }
2233
2234
2235 /*
2236 * 'load_next_job_id()' - Load the NextJobId value from the job.cache file.
2237 */
2238
2239 static void
2240 load_next_job_id(const char *filename) /* I - job.cache filename */
2241 {
2242 cups_file_t *fp; /* job.cache file */
2243 char line[1024], /* Line buffer */
2244 *value; /* Value on line */
2245 int linenum; /* Line number in file */
2246 int next_job_id; /* NextJobId value from line */
2247
2248
2249 /*
2250 * Read the NextJobId directive from the job.cache file and use
2251 * the value (if any).
2252 */
2253
2254 if ((fp = cupsFileOpen(filename, "r")) == NULL)
2255 {
2256 if (errno != ENOENT)
2257 cupsdLogMessage(CUPSD_LOG_ERROR,
2258 "Unable to open job cache file \"%s\": %s",
2259 filename, strerror(errno));
2260
2261 return;
2262 }
2263
2264 cupsdLogMessage(CUPSD_LOG_INFO,
2265 "Loading NextJobId from job cache file \"%s\"...", filename);
2266
2267 linenum = 0;
2268
2269 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
2270 {
2271 if (!strcasecmp(line, "NextJobId"))
2272 {
2273 if (value)
2274 {
2275 next_job_id = atoi(value);
2276
2277 if (next_job_id > NextJobId)
2278 NextJobId = next_job_id;
2279 }
2280 break;
2281 }
2282 }
2283
2284 cupsFileClose(fp);
2285 }
2286
2287
2288 /*
2289 * 'load_request_root()' - Load jobs from the RequestRoot directory.
2290 */
2291
2292 static void
2293 load_request_root(void)
2294 {
2295 cups_dir_t *dir; /* Directory */
2296 cups_dentry_t *dent; /* Directory entry */
2297 cupsd_job_t *job; /* New job */
2298
2299
2300 /*
2301 * Open the requests directory...
2302 */
2303
2304 cupsdLogMessage(CUPSD_LOG_DEBUG, "Scanning %s for jobs...", RequestRoot);
2305
2306 if ((dir = cupsDirOpen(RequestRoot)) == NULL)
2307 {
2308 cupsdLogMessage(CUPSD_LOG_ERROR,
2309 "Unable to open spool directory \"%s\": %s",
2310 RequestRoot, strerror(errno));
2311 return;
2312 }
2313
2314 /*
2315 * Read all the c##### files...
2316 */
2317
2318 while ((dent = cupsDirRead(dir)) != NULL)
2319 if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c')
2320 {
2321 /*
2322 * Allocate memory for the job...
2323 */
2324
2325 if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
2326 {
2327 cupsdLogMessage(CUPSD_LOG_ERROR, "Ran out of memory for jobs!");
2328 cupsDirClose(dir);
2329 return;
2330 }
2331
2332 /*
2333 * Assign the job ID...
2334 */
2335
2336 job->id = atoi(dent->filename + 1);
2337 job->back_pipes[0] = -1;
2338 job->back_pipes[1] = -1;
2339 job->print_pipes[0] = -1;
2340 job->print_pipes[1] = -1;
2341 job->status_pipes[0] = -1;
2342 job->status_pipes[1] = -1;
2343
2344 if (job->id >= NextJobId)
2345 NextJobId = job->id + 1;
2346
2347 /*
2348 * Load the job...
2349 */
2350
2351 cupsdLoadJob(job);
2352
2353 /*
2354 * Insert the job into the array, sorting by job priority and ID...
2355 */
2356
2357 cupsArrayAdd(Jobs, job);
2358
2359 if (job->state_value <= IPP_JOB_STOPPED)
2360 cupsArrayAdd(ActiveJobs, job);
2361 else
2362 unload_job(job);
2363 }
2364
2365 cupsDirClose(dir);
2366 }
2367
2368
2369 /*
2370 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2371 */
2372
2373 static void
2374 set_time(cupsd_job_t *job, /* I - Job to update */
2375 const char *name) /* I - Name of attribute */
2376 {
2377 ipp_attribute_t *attr; /* Time attribute */
2378
2379
2380 if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL)
2381 {
2382 attr->value_tag = IPP_TAG_INTEGER;
2383 attr->values[0].integer = time(NULL);
2384 }
2385 }
2386
2387
2388 /*
2389 * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
2390 */
2391
2392 static void
2393 set_hold_until(cupsd_job_t *job, /* I - Job to update */
2394 time_t holdtime) /* I - Hold until time */
2395 {
2396 ipp_attribute_t *attr; /* job-hold-until attribute */
2397 struct tm *holddate; /* Hold date */
2398 char holdstr[64]; /* Hold time */
2399
2400
2401 /*
2402 * Set the hold_until value and hold the job...
2403 */
2404
2405 cupsdLogMessage(CUPSD_LOG_DEBUG, "set_hold_until: hold_until = %d",
2406 (int)holdtime);
2407
2408 job->state->values[0].integer = IPP_JOB_HELD;
2409 job->state_value = IPP_JOB_HELD;
2410 job->hold_until = holdtime;
2411
2412 /*
2413 * Update the job-hold-until attribute with a string representing GMT
2414 * time (HH:MM:SS)...
2415 */
2416
2417 holddate = gmtime(&holdtime);
2418 snprintf(holdstr, sizeof(holdstr), "%d:%d:%d", holddate->tm_hour,
2419 holddate->tm_min, holddate->tm_sec);
2420
2421 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
2422 IPP_TAG_KEYWORD)) == NULL)
2423 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
2424
2425 /*
2426 * Either add the attribute or update the value of the existing one
2427 */
2428
2429 if (attr == NULL)
2430 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2431 "job-hold-until", NULL, holdstr);
2432 else
2433 cupsdSetString(&attr->values[0].string.text, holdstr);
2434
2435 cupsdSaveJob(job);
2436 }
2437
2438
2439 /*
2440 * 'start_job()' - Start a print job.
2441 */
2442
2443 static void
2444 start_job(cupsd_job_t *job, /* I - Job ID */
2445 cupsd_printer_t *printer) /* I - Printer to print job */
2446 {
2447 int i; /* Looping var */
2448 int slot; /* Pipe slot */
2449 cups_array_t *filters; /* Filters for job */
2450 mime_filter_t *filter, /* Current filter */
2451 port_monitor; /* Port monitor filter */
2452 char method[255], /* Method for output */
2453 *optptr, /* Pointer to options */
2454 *valptr; /* Pointer in value string */
2455 ipp_attribute_t *attr; /* Current attribute */
2456 struct stat backinfo; /* Backend file information */
2457 int backroot; /* Run backend as root? */
2458 int pid; /* Process ID of new filter process */
2459 int banner_page; /* 1 if banner page, 0 otherwise */
2460 int filterfds[2][2];/* Pipes used between filters */
2461 int envc; /* Number of environment variables */
2462 char **argv, /* Filter command-line arguments */
2463 sani_uri[1024], /* Sanitized DEVICE_URI env var */
2464 filename[1024], /* Job filename */
2465 command[1024], /* Full path to command */
2466 jobid[255], /* Job ID string */
2467 title[IPP_MAX_NAME],
2468 /* Job title string */
2469 copies[255], /* # copies string */
2470 *envp[MAX_ENV + 11],
2471 /* Environment variables */
2472 charset[255], /* CHARSET env variable */
2473 class_name[255],/* CLASS env variable */
2474 classification[1024],
2475 /* CLASSIFICATION env variable */
2476 content_type[1024],
2477 /* CONTENT_TYPE env variable */
2478 device_uri[1024],
2479 /* DEVICE_URI env variable */
2480 final_content_type[1024],
2481 /* FINAL_CONTENT_TYPE env variable */
2482 lang[255], /* LANG env variable */
2483 ppd[1024], /* PPD env variable */
2484 printer_name[255],
2485 /* PRINTER env variable */
2486 rip_max_cache[255];
2487 /* RIP_MAX_CACHE env variable */
2488 static char *options = NULL;/* Full list of options */
2489 static int optlength = 0; /* Length of option buffer */
2490
2491
2492 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job: id = %d, file = %d/%d",
2493 job->id, job->current_file, job->num_files);
2494
2495 if (job->num_files == 0)
2496 {
2497 cupsdLogMessage(CUPSD_LOG_ERROR, "Job ID %d has no files! Canceling it!",
2498 job->id);
2499
2500 cupsdCancelJob(job, 0, IPP_JOB_ABORTED);
2501 return;
2502 }
2503
2504 if (printer->raw && !strncmp(printer->device_uri, "file:", 5))
2505 {
2506 cupsdLogMessage(CUPSD_LOG_ERROR,
2507 "Job ID %d cannot be printed to raw queue pointing to "
2508 "a file!",
2509 job->id);
2510
2511 strlcpy(printer->state_message, "Raw printers cannot use file: devices!",
2512 sizeof(printer->state_message));
2513 cupsdStopPrinter(printer, 1);
2514 cupsdAddPrinterHistory(printer);
2515 return;
2516 }
2517
2518 /*
2519 * Figure out what filters are required to convert from
2520 * the source to the destination type...
2521 */
2522
2523 filters = NULL;
2524 job->cost = 0;
2525
2526 if (printer->raw)
2527 {
2528 /*
2529 * Remote jobs and raw queues go directly to the printer without
2530 * filtering...
2531 */
2532
2533 cupsdLogMessage(CUPSD_LOG_DEBUG,
2534 "[Job %d] Sending job to queue tagged as raw...", job->id);
2535
2536 filters = NULL;
2537 }
2538 else
2539 {
2540 /*
2541 * Local jobs get filtered...
2542 */
2543
2544 filters = mimeFilter(MimeDatabase, job->filetypes[job->current_file],
2545 printer->filetype, &(job->cost));
2546
2547 if (!filters)
2548 {
2549 cupsdLogMessage(CUPSD_LOG_ERROR,
2550 "Unable to convert file %d to printable format for "
2551 "job %d!",
2552 job->current_file, job->id);
2553 cupsdLogMessage(CUPSD_LOG_INFO,
2554 "Hint: Do you have ESP Ghostscript installed?");
2555
2556 if (LogLevel < CUPSD_LOG_DEBUG)
2557 cupsdLogMessage(CUPSD_LOG_INFO,
2558 "Hint: Try setting the LogLevel to \"debug\".");
2559
2560 job->current_file ++;
2561
2562 if (job->current_file == job->num_files)
2563 cupsdCancelJob(job, 0, IPP_JOB_ABORTED);
2564
2565 return;
2566 }
2567
2568 /*
2569 * Remove NULL ("-") filters...
2570 */
2571
2572 for (filter = (mime_filter_t *)cupsArrayFirst(filters);
2573 filter;
2574 filter = (mime_filter_t *)cupsArrayNext(filters))
2575 if (!strcmp(filter->filter, "-"))
2576 cupsArrayRemove(filters, filter);
2577
2578 if (cupsArrayCount(filters) == 0)
2579 {
2580 cupsArrayDelete(filters);
2581 filters = NULL;
2582 }
2583 }
2584
2585 /*
2586 * Set a minimum cost of 100 for all jobs so that FilterLimit
2587 * works with raw queues and other low-cost paths.
2588 */
2589
2590 if (job->cost < 100)
2591 job->cost = 100;
2592
2593 /*
2594 * See if the filter cost is too high...
2595 */
2596
2597 if ((FilterLevel + job->cost) > FilterLimit && FilterLevel > 0 &&
2598 FilterLimit > 0)
2599 {
2600 /*
2601 * Don't print this job quite yet...
2602 */
2603
2604 cupsArrayDelete(filters);
2605
2606 cupsdLogMessage(CUPSD_LOG_INFO,
2607 "Holding job %d because filter limit has been reached.",
2608 job->id);
2609 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2610 "start_job: id=%d, file=%d, cost=%d, level=%d, "
2611 "limit=%d",
2612 job->id, job->current_file, job->cost, FilterLevel,
2613 FilterLimit);
2614 return;
2615 }
2616
2617 FilterLevel += job->cost;
2618
2619 /*
2620 * Add decompression filters, if any...
2621 */
2622
2623 if (!printer->raw && job->compressions[job->current_file])
2624 {
2625 /*
2626 * Add gziptoany filter to the front of the list...
2627 */
2628
2629 if (!cupsArrayInsert(filters, &gziptoany_filter))
2630 {
2631 cupsdLogMessage(CUPSD_LOG_ERROR,
2632 "Unable to add decompression filter - %s",
2633 strerror(errno));
2634
2635 cupsArrayDelete(filters);
2636
2637 job->current_file ++;
2638
2639 if (job->current_file == job->num_files)
2640 cupsdCancelJob(job, 0, IPP_JOB_ABORTED);
2641
2642 return;
2643 }
2644 }
2645
2646 /*
2647 * Add port monitor, if any...
2648 */
2649
2650 if (printer->port_monitor)
2651 {
2652 /*
2653 * Add port monitor to the end of the list...
2654 */
2655
2656 if (!cupsArrayAdd(filters, &port_monitor))
2657 {
2658 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add port monitor - %s",
2659 strerror(errno));
2660
2661 cupsArrayDelete(filters);
2662
2663 job->current_file ++;
2664
2665 if (job->current_file == job->num_files)
2666 cupsdCancelJob(job, 0, IPP_JOB_ABORTED);
2667
2668 return;
2669 }
2670
2671 snprintf(port_monitor.filter, sizeof(port_monitor.filter),
2672 "%s/monitor/%s", ServerBin, printer->port_monitor);
2673 }
2674
2675 /*
2676 * Update the printer and job state to "processing"...
2677 */
2678
2679 job->state->values[0].integer = IPP_JOB_PROCESSING;
2680 job->state_value = IPP_JOB_PROCESSING;
2681 job->status = 0;
2682 job->printer = printer;
2683 printer->job = job;
2684 cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
2685
2686 if (job->current_file == 0)
2687 {
2688 /*
2689 * Set the processing time...
2690 */
2691
2692 set_time(job, "time-at-processing");
2693
2694 /*
2695 * Create the backchannel pipes and make them non-blocking...
2696 */
2697
2698 cupsdOpenPipe(job->back_pipes);
2699
2700 fcntl(job->back_pipes[0], F_SETFL,
2701 fcntl(job->back_pipes[0], F_GETFL) | O_NONBLOCK);
2702
2703 fcntl(job->back_pipes[1], F_SETFL,
2704 fcntl(job->back_pipes[1], F_GETFL) | O_NONBLOCK);
2705 }
2706
2707 /*
2708 * Determine if we are printing a banner page or not...
2709 */
2710
2711 if (job->job_sheets == NULL)
2712 {
2713 cupsdLogMessage(CUPSD_LOG_DEBUG, "No job-sheets attribute.");
2714 if ((job->job_sheets =
2715 ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
2716 cupsdLogMessage(CUPSD_LOG_DEBUG,
2717 "... but someone added one without setting job_sheets!");
2718 }
2719 else if (job->job_sheets->num_values == 1)
2720 cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s",
2721 job->job_sheets->values[0].string.text);
2722 else
2723 cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s,%s",
2724 job->job_sheets->values[0].string.text,
2725 job->job_sheets->values[1].string.text);
2726
2727 if (printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
2728 banner_page = 0;
2729 else if (job->job_sheets == NULL)
2730 banner_page = 0;
2731 else if (strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
2732 job->current_file == 0)
2733 banner_page = 1;
2734 else if (job->job_sheets->num_values > 1 &&
2735 strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
2736 job->current_file == (job->num_files - 1))
2737 banner_page = 1;
2738 else
2739 banner_page = 0;
2740
2741 cupsdLogMessage(CUPSD_LOG_DEBUG, "banner_page = %d", banner_page);
2742
2743 /*
2744 * Building the options string is harder than it needs to be, but
2745 * for the moment we need to pass strings for command-line args and
2746 * not IPP attribute pointers... :)
2747 *
2748 * First allocate/reallocate the option buffer as needed...
2749 */
2750
2751 i = ipp_length(job->attrs);
2752
2753 if (i > optlength)
2754 {
2755 if (optlength == 0)
2756 optptr = malloc(i);
2757 else
2758 optptr = realloc(options, i);
2759
2760 if (optptr == NULL)
2761 {
2762 cupsdLogMessage(CUPSD_LOG_CRIT,
2763 "Unable to allocate %d bytes for option buffer for "
2764 "job %d!", i, job->id);
2765
2766 cupsArrayDelete(filters);
2767
2768 FilterLevel -= job->cost;
2769
2770 cupsdCancelJob(job, 0, IPP_JOB_ABORTED);
2771 return;
2772 }
2773
2774 options = optptr;
2775 optlength = i;
2776 }
2777
2778 /*
2779 * Now loop through the attributes and convert them to the textual
2780 * representation used by the filters...
2781 */
2782
2783 optptr = options;
2784 *optptr = '\0';
2785
2786 snprintf(title, sizeof(title), "%s-%d", printer->name, job->id);
2787 strcpy(copies, "1");
2788
2789 for (attr = job->attrs->attrs; attr != NULL; attr = attr->next)
2790 {
2791 if (!strcmp(attr->name, "copies") &&
2792 attr->value_tag == IPP_TAG_INTEGER)
2793 {
2794 /*
2795 * Don't use the # copies attribute if we are printing the job sheets...
2796 */
2797
2798 if (!banner_page)
2799 sprintf(copies, "%d", attr->values[0].integer);
2800 }
2801 else if (!strcmp(attr->name, "job-name") &&
2802 (attr->value_tag == IPP_TAG_NAME ||
2803 attr->value_tag == IPP_TAG_NAMELANG))
2804 strlcpy(title, attr->values[0].string.text, sizeof(title));
2805 else if (attr->group_tag == IPP_TAG_JOB)
2806 {
2807 /*
2808 * Filter out other unwanted attributes...
2809 */
2810
2811 if (attr->value_tag == IPP_TAG_MIMETYPE ||
2812 attr->value_tag == IPP_TAG_NAMELANG ||
2813 attr->value_tag == IPP_TAG_TEXTLANG ||
2814 (attr->value_tag == IPP_TAG_URI && strcmp(attr->name, "job-uuid")) ||
2815 attr->value_tag == IPP_TAG_URISCHEME ||
2816 attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */
2817 continue;
2818
2819 if (!strncmp(attr->name, "time-", 5))
2820 continue;
2821
2822 if (!strncmp(attr->name, "job-", 4) && strcmp(attr->name, "job-uuid") &&
2823 !(printer->type & CUPS_PRINTER_REMOTE))
2824 continue;
2825
2826 if (!strncmp(attr->name, "job-", 4) &&
2827 strcmp(attr->name, "job-uuid") &&
2828 strcmp(attr->name, "job-billing") &&
2829 strcmp(attr->name, "job-sheets") &&
2830 strcmp(attr->name, "job-hold-until") &&
2831 strcmp(attr->name, "job-priority"))
2832 continue;
2833
2834 if ((!strcmp(attr->name, "page-label") ||
2835 !strcmp(attr->name, "page-border") ||
2836 !strncmp(attr->name, "number-up", 9) ||
2837 !strcmp(attr->name, "page-set") ||
2838 !strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
2839 !strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed")) &&
2840 banner_page)
2841 continue;
2842
2843 /*
2844 * Otherwise add them to the list...
2845 */
2846
2847 if (optptr > options)
2848 strlcat(optptr, " ", optlength - (optptr - options));
2849
2850 if (attr->value_tag != IPP_TAG_BOOLEAN)
2851 {
2852 strlcat(optptr, attr->name, optlength - (optptr - options));
2853 strlcat(optptr, "=", optlength - (optptr - options));
2854 }
2855
2856 for (i = 0; i < attr->num_values; i ++)
2857 {
2858 if (i)
2859 strlcat(optptr, ",", optlength - (optptr - options));
2860
2861 optptr += strlen(optptr);
2862
2863 switch (attr->value_tag)
2864 {
2865 case IPP_TAG_INTEGER :
2866 case IPP_TAG_ENUM :
2867 snprintf(optptr, optlength - (optptr - options),
2868 "%d", attr->values[i].integer);
2869 break;
2870
2871 case IPP_TAG_BOOLEAN :
2872 if (!attr->values[i].boolean)
2873 strlcat(optptr, "no", optlength - (optptr - options));
2874
2875 case IPP_TAG_NOVALUE :
2876 strlcat(optptr, attr->name,
2877 optlength - (optptr - options));
2878 break;
2879
2880 case IPP_TAG_RANGE :
2881 if (attr->values[i].range.lower == attr->values[i].range.upper)
2882 snprintf(optptr, optlength - (optptr - options) - 1,
2883 "%d", attr->values[i].range.lower);
2884 else
2885 snprintf(optptr, optlength - (optptr - options) - 1,
2886 "%d-%d", attr->values[i].range.lower,
2887 attr->values[i].range.upper);
2888 break;
2889
2890 case IPP_TAG_RESOLUTION :
2891 snprintf(optptr, optlength - (optptr - options) - 1,
2892 "%dx%d%s", attr->values[i].resolution.xres,
2893 attr->values[i].resolution.yres,
2894 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
2895 "dpi" : "dpc");
2896 break;
2897
2898 case IPP_TAG_STRING :
2899 case IPP_TAG_TEXT :
2900 case IPP_TAG_NAME :
2901 case IPP_TAG_KEYWORD :
2902 case IPP_TAG_CHARSET :
2903 case IPP_TAG_LANGUAGE :
2904 case IPP_TAG_URI :
2905 for (valptr = attr->values[i].string.text; *valptr;)
2906 {
2907 if (strchr(" \t\n\\\'\"", *valptr))
2908 *optptr++ = '\\';
2909 *optptr++ = *valptr++;
2910 }
2911
2912 *optptr = '\0';
2913 break;
2914
2915 default :
2916 break; /* anti-compiler-warning-code */
2917 }
2918 }
2919
2920 optptr += strlen(optptr);
2921 }
2922 }
2923
2924 /*
2925 * Build the command-line arguments for the filters. Each filter
2926 * has 6 or 7 arguments:
2927 *
2928 * argv[0] = printer
2929 * argv[1] = job ID
2930 * argv[2] = username
2931 * argv[3] = title
2932 * argv[4] = # copies
2933 * argv[5] = options
2934 * argv[6] = filename (optional; normally stdin)
2935 *
2936 * This allows legacy printer drivers that use the old System V
2937 * printing interface to be used by CUPS.
2938 *
2939 * For remote jobs, we send all of the files in the argument list.
2940 */
2941
2942 if (printer->remote && job->num_files > 1)
2943 argv = calloc(7 + job->num_files, sizeof(char *));
2944 else
2945 argv = calloc(8, sizeof(char *));
2946
2947 sprintf(jobid, "%d", job->id);
2948
2949 argv[0] = printer->name;
2950 argv[1] = jobid;
2951 argv[2] = job->username;
2952 argv[3] = title;
2953 argv[4] = copies;
2954 argv[5] = options;
2955
2956 if (printer->remote && job->num_files > 1)
2957 {
2958 for (i = 0; i < job->num_files; i ++)
2959 {
2960 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
2961 job->id, i + 1);
2962 argv[6 + i] = strdup(filename);
2963 }
2964 }
2965 else
2966 {
2967 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
2968 job->id, job->current_file + 1);
2969 argv[6] = filename;
2970 }
2971
2972 for (i = 0; argv[i]; i ++)
2973 cupsdLogMessage(CUPSD_LOG_DEBUG,
2974 "[Job %d] argv[%d]=\"%s\"", job->id, i, argv[i]);
2975
2976 /*
2977 * Create environment variable strings for the filters...
2978 */
2979
2980 attr = ippFindAttribute(job->attrs, "attributes-natural-language",
2981 IPP_TAG_LANGUAGE);
2982
2983 switch (strlen(attr->values[0].string.text))
2984 {
2985 default :
2986 /*
2987 * This is an unknown or badly formatted language code; use
2988 * the POSIX locale...
2989 */
2990
2991 strcpy(lang, "LANG=C");
2992 break;
2993
2994 case 2 :
2995 /*
2996 * Just the language code (ll)...
2997 */
2998
2999 snprintf(lang, sizeof(lang), "LANG=%s",
3000 attr->values[0].string.text);
3001 break;
3002
3003 case 5 :
3004 /*
3005 * Language and country code (ll-cc)...
3006 */
3007
3008 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c",
3009 attr->values[0].string.text[0],
3010 attr->values[0].string.text[1],
3011 toupper(attr->values[0].string.text[3] & 255),
3012 toupper(attr->values[0].string.text[4] & 255));
3013 break;
3014 }
3015
3016 attr = ippFindAttribute(job->attrs, "document-format",
3017 IPP_TAG_MIMETYPE);
3018 if (attr != NULL &&
3019 (optptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
3020 snprintf(charset, sizeof(charset), "CHARSET=%s", optptr + 8);
3021 else
3022 {
3023 attr = ippFindAttribute(job->attrs, "attributes-charset",
3024 IPP_TAG_CHARSET);
3025 snprintf(charset, sizeof(charset), "CHARSET=%s",
3026 attr->values[0].string.text);
3027 }
3028
3029 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
3030 job->filetypes[job->current_file]->super,
3031 job->filetypes[job->current_file]->type);
3032 snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s",
3033 printer->device_uri);
3034 cupsdSanitizeURI(printer->device_uri, sani_uri, sizeof(sani_uri));
3035 snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot, printer->name);
3036 snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", printer->name);
3037 snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
3038
3039 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
3040
3041 envp[envc ++] = charset;
3042 envp[envc ++] = lang;
3043 envp[envc ++] = ppd;
3044 envp[envc ++] = rip_max_cache;
3045 envp[envc ++] = content_type;
3046 envp[envc ++] = device_uri;
3047 envp[envc ++] = printer_name;
3048
3049 if (!printer->remote &&
3050 (filter = (mime_filter_t *)cupsArrayLast(filters)) != NULL)
3051 {
3052 snprintf(final_content_type, sizeof(final_content_type),
3053 "FINAL_CONTENT_TYPE=%s/%s",
3054 filter->dst->super, filter->dst->type);
3055 envp[envc ++] = final_content_type;
3056 }
3057
3058 if (Classification && !banner_page)
3059 {
3060 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
3061 IPP_TAG_NAME)) == NULL)
3062 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
3063 Classification);
3064 else if (attr->num_values > 1 &&
3065 strcmp(attr->values[1].string.text, "none") != 0)
3066 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
3067 attr->values[1].string.text);
3068 else
3069 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
3070 attr->values[0].string.text);
3071
3072 envp[envc ++] = classification;
3073 }
3074
3075 if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
3076 {
3077 snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest);
3078 envp[envc ++] = class_name;
3079 }
3080
3081 envp[envc] = NULL;
3082
3083 for (i = 0; i < envc; i ++)
3084 if (strncmp(envp[i], "DEVICE_URI=", 11))
3085 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] envp[%d]=\"%s\"",
3086 job->id, i, envp[i]);
3087 else
3088 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] envp[%d]=\"DEVICE_URI=%s\"",
3089 job->id, i, sani_uri);
3090
3091 if (printer->remote)
3092 job->current_file = job->num_files;
3093 else
3094 job->current_file ++;
3095
3096 /*
3097 * Now create processes for all of the filters...
3098 */
3099
3100 filterfds[0][0] = -1;
3101 filterfds[0][1] = -1;
3102 filterfds[1][0] = -1;
3103 filterfds[1][1] = -1;
3104
3105 if (!job->status_buffer)
3106 {
3107 if (cupsdOpenPipe(job->status_pipes))
3108 {
3109 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create job status pipes - %s.",
3110 strerror(errno));
3111 snprintf(printer->state_message, sizeof(printer->state_message),
3112 "Unable to create status pipes - %s.", strerror(errno));
3113
3114 cupsdAddPrinterHistory(printer);
3115
3116 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3117 "Job canceled because the server could not create the job "
3118 "status pipes.");
3119
3120 goto abort_job;
3121 }
3122
3123 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job: status_pipes = [ %d %d ]",
3124 job->status_pipes[0], job->status_pipes[1]);
3125
3126 job->status_buffer = cupsdStatBufNew(job->status_pipes[0], "[Job %d]",
3127 job->id);
3128 }
3129
3130 job->status = 0;
3131 memset(job->filters, 0, sizeof(job->filters));
3132
3133 for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
3134 filter;
3135 i ++, filter = (mime_filter_t *)cupsArrayNext(filters))
3136 {
3137 if (filter->filter[0] != '/')
3138 snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
3139 filter->filter);
3140 else
3141 strlcpy(command, filter->filter, sizeof(command));
3142
3143 if (i < (cupsArrayCount(filters) - 1))
3144 {
3145 if (cupsdOpenPipe(filterfds[slot]))
3146 {
3147 cupsdLogMessage(CUPSD_LOG_ERROR,
3148 "Unable to create job filter pipes - %s.",
3149 strerror(errno));
3150 snprintf(printer->state_message, sizeof(printer->state_message),
3151 "Unable to create filter pipes - %s.", strerror(errno));
3152 cupsdAddPrinterHistory(printer);
3153
3154 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3155 "Job canceled because the server could not create the "
3156 "filter pipes.");
3157
3158 goto abort_job;
3159 }
3160 }
3161 else
3162 {
3163 if (job->current_file == 1)
3164 {
3165 if (strncmp(printer->device_uri, "file:", 5) != 0)
3166 {
3167 if (cupsdOpenPipe(job->print_pipes))
3168 {
3169 cupsdLogMessage(CUPSD_LOG_ERROR,
3170 "Unable to create job backend pipes - %s.",
3171 strerror(errno));
3172 snprintf(printer->state_message, sizeof(printer->state_message),
3173 "Unable to create backend pipes - %s.", strerror(errno));
3174 cupsdAddPrinterHistory(printer);
3175
3176 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3177 "Job canceled because the server could not create "
3178 "the backend pipes.");
3179
3180 goto abort_job;
3181 }
3182 }
3183 else
3184 {
3185 job->print_pipes[0] = -1;
3186 if (!strcmp(printer->device_uri, "file:/dev/null") ||
3187 !strcmp(printer->device_uri, "file:///dev/null"))
3188 job->print_pipes[1] = -1;
3189 else
3190 {
3191 if (!strncmp(printer->device_uri, "file:/dev/", 10))
3192 job->print_pipes[1] = open(printer->device_uri + 5,
3193 O_WRONLY | O_EXCL);
3194 else if (!strncmp(printer->device_uri, "file:///dev/", 12))
3195 job->print_pipes[1] = open(printer->device_uri + 7,
3196 O_WRONLY | O_EXCL);
3197 else if (!strncmp(printer->device_uri, "file:///", 8))
3198 job->print_pipes[1] = open(printer->device_uri + 7,
3199 O_WRONLY | O_CREAT | O_TRUNC, 0600);
3200 else
3201 job->print_pipes[1] = open(printer->device_uri + 5,
3202 O_WRONLY | O_CREAT | O_TRUNC, 0600);
3203
3204 if (job->print_pipes[1] < 0)
3205 {
3206 cupsdLogMessage(CUPSD_LOG_ERROR,
3207 "Unable to open output file \"%s\" - %s.",
3208 printer->device_uri, strerror(errno));
3209 snprintf(printer->state_message, sizeof(printer->state_message),
3210 "Unable to open output file \"%s\" - %s.",
3211 printer->device_uri, strerror(errno));
3212
3213 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3214 "Job canceled because the server could not open the "
3215 "output file.");
3216
3217 goto abort_job;
3218 }
3219
3220 fcntl(job->print_pipes[1], F_SETFD,
3221 fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
3222 }
3223 }
3224
3225 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3226 "start_job: print_pipes = [ %d %d ]",
3227 job->print_pipes[0], job->print_pipes[1]);
3228 }
3229
3230 filterfds[slot][0] = job->print_pipes[0];
3231 filterfds[slot][1] = job->print_pipes[1];
3232 }
3233
3234 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job: filter=\"%s\"",
3235 command);
3236 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3237 "start_job: filterfds[%d]=[ %d %d ]",
3238 slot, filterfds[slot][0], filterfds[slot][1]);
3239
3240 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
3241 filterfds[slot][1], job->status_pipes[1],
3242 job->back_pipes[0], 0, job->filters + i);
3243
3244 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3245 "start_job: Closing filter pipes for slot %d "
3246 "[ %d %d ]...",
3247 !slot, filterfds[!slot][0], filterfds[!slot][1]);
3248
3249 cupsdClosePipe(filterfds[!slot]);
3250
3251 if (pid == 0)
3252 {
3253 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
3254 filter->filter, strerror(errno));
3255 snprintf(printer->state_message, sizeof(printer->state_message),
3256 "Unable to start filter \"%s\" - %s.",
3257 filter->filter, strerror(errno));
3258
3259 cupsdAddPrinterHistory(printer);
3260
3261 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3262 "Job canceled because the server could not execute a "
3263 "filter.");
3264
3265 goto abort_job;
3266 }
3267
3268 cupsdLogMessage(CUPSD_LOG_INFO, "Started filter %s (PID %d) for job %d.",
3269 command, pid, job->id);
3270
3271 argv[6] = NULL;
3272 slot = !slot;
3273 }
3274
3275 cupsArrayDelete(filters);
3276
3277 /*
3278 * Finally, pipe the final output into a backend process if needed...
3279 */
3280
3281 if (strncmp(printer->device_uri, "file:", 5) != 0)
3282 {
3283 if (job->current_file == 1)
3284 {
3285 sscanf(printer->device_uri, "%254[^:]", method);
3286 snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, method);
3287
3288 /*
3289 * See if the backend needs to run as root...
3290 */
3291
3292 if (RunUser)
3293 backroot = 0;
3294 else if (stat(command, &backinfo))
3295 backroot = 0;
3296 else
3297 backroot = !(backinfo.st_mode & (S_IRWXG | S_IRWXO));
3298
3299 argv[0] = sani_uri;
3300
3301 filterfds[slot][0] = -1;
3302 filterfds[slot][1] = -1;
3303
3304 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job: backend=\"%s\"",
3305 command);
3306 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3307 "start_job: filterfds[%d] = [ %d %d ]",
3308 slot, filterfds[slot][0], filterfds[slot][1]);
3309
3310 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
3311 filterfds[slot][1], job->status_pipes[1],
3312 job->back_pipes[1], backroot,
3313 &(job->backend));
3314
3315 if (pid == 0)
3316 {
3317 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start backend \"%s\" - %s.",
3318 method, strerror(errno));
3319 snprintf(printer->state_message, sizeof(printer->state_message),
3320 "Unable to start backend \"%s\" - %s.", method,
3321 strerror(errno));
3322
3323 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3324 "Job canceled because the server could not execute "
3325 "the backend.");
3326
3327 goto abort_job;
3328 }
3329 else
3330 {
3331 cupsdLogMessage(CUPSD_LOG_INFO,
3332 "Started backend %s (PID %d) for job %d.",
3333 command, pid, job->id);
3334 }
3335 }
3336
3337 if (job->current_file == job->num_files)
3338 {
3339 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3340 "start_job: Closing print pipes [ %d %d ]...",
3341 job->print_pipes[0], job->print_pipes[1]);
3342
3343 cupsdClosePipe(job->print_pipes);
3344
3345 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3346 "start_job: Closing back pipes [ %d %d ]...",
3347 job->back_pipes[0], job->back_pipes[1]);
3348
3349 cupsdClosePipe(job->back_pipes);
3350
3351 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3352 "start_job: Closing status output pipe %d...",
3353 job->status_pipes[1]);
3354
3355 close(job->status_pipes[1]);
3356 job->status_pipes[1] = -1;
3357 }
3358 }
3359 else
3360 {
3361 filterfds[slot][0] = -1;
3362 filterfds[slot][1] = -1;
3363
3364 if (job->current_file == job->num_files)
3365 {
3366 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3367 "start_job: Closing print pipes [ %d %d ]...",
3368 job->print_pipes[0], job->print_pipes[1]);
3369
3370 cupsdClosePipe(job->print_pipes);
3371
3372 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3373 "start_job: Closing status output pipe %d...",
3374 job->status_pipes[1]);
3375
3376 close(job->status_pipes[1]);
3377 job->status_pipes[1] = -1;
3378 }
3379 }
3380
3381 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3382 "start_job: Closing filter pipes for slot %d "
3383 "[ %d %d ]...",
3384 slot, filterfds[slot][0], filterfds[slot][1]);
3385 cupsdClosePipe(filterfds[slot]);
3386
3387 if (printer->remote && job->num_files > 1)
3388 {
3389 for (i = 0; i < job->num_files; i ++)
3390 free(argv[i + 6]);
3391 }
3392
3393 free(argv);
3394
3395 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3396 "start_job: Adding fd %d to InputSet...",
3397 job->status_buffer->fd);
3398
3399 FD_SET(job->status_buffer->fd, InputSet);
3400
3401 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.",
3402 job->id);
3403
3404 return;
3405
3406
3407 /*
3408 * If we get here, we need to abort the current job and close out all
3409 * files and pipes...
3410 */
3411
3412 abort_job:
3413
3414 for (slot = 0; slot < 2; slot ++)
3415 {
3416 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3417 "start_job: Closing filter pipes for slot %d "
3418 "[ %d %d ]...",
3419 slot, filterfds[slot][0], filterfds[slot][1]);
3420 cupsdClosePipe(filterfds[slot]);
3421 }
3422
3423 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3424 "start_job: Closing status pipes [ %d %d ]...",
3425 job->status_pipes[0], job->status_pipes[1]);
3426 cupsdClosePipe(job->status_pipes);
3427 cupsdStatBufDelete(job->status_buffer);
3428
3429 cupsArrayDelete(filters);
3430
3431 if (printer->remote && job->num_files > 1)
3432 {
3433 for (i = 0; i < job->num_files; i ++)
3434 free(argv[i + 6]);
3435 }
3436
3437 free(argv);
3438
3439 cupsdStopJob(job, 0);
3440 }
3441
3442
3443 /*
3444 * 'unload_job()' - Unload a job from memory.
3445 */
3446
3447 static void
3448 unload_job(cupsd_job_t *job) /* I - Job */
3449 {
3450 if (!job->attrs)
3451 return;
3452
3453 cupsdLogMessage(CUPSD_LOG_DEBUG, "Unloading job %d...", job->id);
3454
3455 ippDelete(job->attrs);
3456
3457 job->attrs = NULL;
3458 job->state = NULL;
3459 job->sheets = NULL;
3460 job->job_sheets = NULL;
3461 }
3462
3463
3464 /*
3465 * End of "$Id: job.c 5974 2006-09-20 18:42:37Z mike $".
3466 */