]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/job.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / job.c
1 /*
2 * "$Id: job.c 4906 2006-01-10 20:53:28Z mike $"
3 *
4 * Job management routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2005 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 destination/user...
29 * cupsdCheckJobs() - Check the pending jobs and start any if the
30 * destination is available.
31 * cupsdCleanJobs() - Clean out old jobs.
32 * cupsdFreeAllJobs() - Free all jobs from memory.
33 * cupsdFindJob() - Find the specified job.
34 * cupsdGetPrinterJobCount() - Get the number of pending, processing,
35 * or held jobs in a printer or class.
36 * cupsdGetUserJobCount() - Get the number of pending, processing,
37 * or held jobs for a user.
38 * cupsdHoldJob() - Hold the specified job.
39 * cupsdLoadAllJobs() - Load all jobs from disk.
40 * cupsdMoveJob() - Move the specified job to a different
41 * destination.
42 * cupsdReleaseJob() - Release the specified job.
43 * cupsdRestartJob() - Restart the specified job.
44 * cupsdSaveJob() - Save a job to disk.
45 * cupsdSetJobHoldUntil() - Set the hold time for a job...
46 * cupsdSetJobPriority() - Set the priority of a job, moving it up/down
47 * in the list as needed.
48 * cupsdStartJob() - Start a print job.
49 * cupsdStopAllJobs() - Stop all print jobs.
50 * cupsdStopJob() - Stop a print job.
51 * cupsdUpdateJob() - Read a status update from a job's filters.
52 * compare_active_jobs() - Compare the job IDs and priorities of two jobs.
53 * compare_jobs() - Compare the job IDs of two jobs.
54 * ipp_length() - Compute the size of the buffer needed to hold
55 * the textual IPP attributes.
56 * set_hold_until() - Set the hold time and update job-hold-until attribute.
57 */
58
59 /*
60 * Include necessary headers...
61 */
62
63 #include "cupsd.h"
64 #include <grp.h>
65 #include <cups/backend.h>
66 #include <cups/dir.h>
67
68
69 /*
70 * Local globals...
71 */
72
73 static mime_filter_t gziptoany_filter =
74 {
75 NULL, /* Source type */
76 NULL, /* Destination type */
77 0, /* Cost */
78 "gziptoany" /* Filter program to run */
79 };
80
81
82 /*
83 * Local functions...
84 */
85
86 static int compare_active_jobs(void *first, void *second, void *data);
87 static int compare_jobs(void *first, void *second, void *data);
88 static int ipp_length(ipp_t *ipp);
89 static void set_time(cupsd_job_t *job, const char *name);
90 static void set_hold_until(cupsd_job_t *job, time_t holdtime);
91
92
93 /*
94 * 'cupsdAddJob()' - Add a new job to the job queue...
95 */
96
97 cupsd_job_t * /* O - New job record */
98 cupsdAddJob(int priority, /* I - Job priority */
99 const char *dest) /* I - Job destination */
100 {
101 cupsd_job_t *job; /* New job record */
102
103
104 job = calloc(sizeof(cupsd_job_t), 1);
105
106 job->id = NextJobId ++;
107 job->priority = priority;
108 job->back_pipes[0] = -1;
109 job->back_pipes[1] = -1;
110 job->print_pipes[0] = -1;
111 job->print_pipes[1] = -1;
112
113 cupsdSetString(&job->dest, dest);
114
115 /*
116 * Add the new job to the "all jobs" and "active jobs" lists...
117 */
118
119 cupsArrayAdd(Jobs, job);
120 cupsArrayAdd(ActiveJobs, job);
121
122 return (job);
123 }
124
125
126 /*
127 * 'cupsdCancelJob()' - Cancel the specified print job.
128 */
129
130 void
131 cupsdCancelJob(cupsd_job_t *job, /* I - Job to cancel */
132 int purge) /* I - Purge jobs? */
133 {
134 int i; /* Looping var */
135 char filename[1024]; /* Job filename */
136
137
138 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCancelJob: id = %d", job->id);
139
140 /*
141 * Remove the job from the active list...
142 */
143
144 cupsArrayRemove(ActiveJobs, job);
145
146 /*
147 * Stop any processes that are working on the current job...
148 */
149
150 if (job->state->values[0].integer == IPP_JOB_PROCESSING)
151 cupsdStopJob(job, 0);
152
153 cupsArrayRemove(ActiveJobs, job);
154
155 job->state->values[0].integer = IPP_JOB_CANCELLED;
156
157 set_time(job, "time-at-completed");
158
159 cupsdExpireSubscriptions(NULL, job);
160
161 /*
162 * Remove any authentication data...
163 */
164
165 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot,
166 job->id);
167 unlink(filename);
168
169 /*
170 * Remove the print file for good if we aren't preserving jobs or
171 * files...
172 */
173
174 job->current_file = 0;
175
176 if (!JobHistory || !JobFiles || purge ||
177 (job->dtype & CUPS_PRINTER_REMOTE))
178 for (i = 1; i <= job->num_files; i ++)
179 {
180 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
181 job->id, i);
182 unlink(filename);
183 }
184
185 if (JobHistory && !purge && !(job->dtype & CUPS_PRINTER_REMOTE))
186 {
187 /*
188 * Save job state info...
189 */
190
191 cupsdSaveJob(job);
192 }
193 else
194 {
195 /*
196 * Remove the job info file...
197 */
198
199 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot,
200 job->id);
201 unlink(filename);
202
203 /*
204 * Remove the job from the "all jobs" list...
205 */
206
207 cupsArrayRemove(Jobs, job);
208
209 /*
210 * Free all memory used...
211 */
212
213 if (job->attrs != NULL)
214 ippDelete(job->attrs);
215
216 if (job->num_files > 0)
217 {
218 free(job->compressions);
219 free(job->filetypes);
220 }
221
222 cupsdClearString(&job->username);
223 cupsdClearString(&job->dest);
224
225 free(job);
226 }
227 }
228
229
230 /*
231 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user...
232 */
233
234 void
235 cupsdCancelJobs(const char *dest, /* I - Destination to cancel */
236 const char *username, /* I - Username or NULL */
237 int purge) /* I - Purge jobs? */
238 {
239 cupsd_job_t *job; /* Current job */
240
241
242 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
243 job;
244 job = (cupsd_job_t *)cupsArrayNext(Jobs))
245 if ((dest == NULL || !strcmp(job->dest, dest)) &&
246 (username == NULL || !strcmp(job->username, username)))
247 {
248 /*
249 * Cancel all jobs matching this destination/user...
250 */
251
252 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
253 purge ? "Job purged." : "Job canceled.");
254
255 cupsdCancelJob(job, purge);
256 }
257
258 cupsdCheckJobs();
259 }
260
261
262 /*
263 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
264 * is available.
265 */
266
267 void
268 cupsdCheckJobs(void)
269 {
270 cupsd_job_t *job; /* Current job in queue */
271 cupsd_printer_t *printer, /* Printer destination */
272 *pclass; /* Printer class destination */
273
274
275 DEBUG_puts("cupsdCheckJobs()");
276
277 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
278 job;
279 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
280 {
281 /*
282 * Start held jobs if they are ready...
283 */
284
285 if (job->state->values[0].integer == IPP_JOB_HELD &&
286 job->hold_until &&
287 job->hold_until < time(NULL))
288 job->state->values[0].integer = IPP_JOB_PENDING;
289
290 /*
291 * Start pending jobs if the destination is available...
292 */
293
294 if (job->state->values[0].integer == IPP_JOB_PENDING && !NeedReload)
295 {
296 printer = cupsdFindDest(job->dest);
297 pclass = NULL;
298
299 while (printer &&
300 (printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS)))
301 {
302 /*
303 * If the class is remote, just pass it to the remote server...
304 */
305
306 pclass = printer;
307
308 if (!(pclass->type & CUPS_PRINTER_REMOTE))
309 {
310 if (pclass->state != IPP_PRINTER_STOPPED)
311 printer = cupsdFindAvailablePrinter(job->dest);
312 else
313 printer = NULL;
314 }
315 }
316
317 if (!printer && !pclass)
318 {
319 /*
320 * Whoa, the printer and/or class for this destination went away;
321 * cancel the job...
322 */
323
324 cupsdLogMessage(CUPSD_LOG_WARN,
325 "Printer/class %s has gone away; cancelling job %d!",
326 job->dest, job->id);
327
328 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
329 "Job canceled because the destination printer/class has gone away.");
330
331 cupsdCancelJob(job, 1);
332 }
333 else if (printer)
334 {
335 /*
336 * See if the printer is available or remote and not printing a job;
337 * if so, start the job...
338 */
339
340 if (pclass)
341 {
342 /*
343 * Add/update a job-actual-printer-uri attribute for this job
344 * so that we know which printer actually printed the job...
345 */
346
347 ipp_attribute_t *attr; /* job-actual-printer-uri attribute */
348
349
350 if ((attr = ippFindAttribute(job->attrs, "job-actual-printer-uri",
351 IPP_TAG_URI)) != NULL)
352 cupsdSetString(&attr->values[0].string.text, printer->uri);
353 else
354 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI,
355 "job-actual-printer-uri", NULL, printer->uri);
356 }
357
358 if (printer->state == IPP_PRINTER_IDLE || /* Printer is idle */
359 ((printer->type & CUPS_PRINTER_REMOTE) && /* Printer is remote */
360 !printer->job)) /* and not printing a job */
361 cupsdStartJob(job, printer);
362 }
363 }
364 }
365 }
366
367
368 /*
369 * 'cupsdCleanJobs()' - Clean out old jobs.
370 */
371
372 void
373 cupsdCleanJobs(void)
374 {
375 cupsd_job_t *job; /* Current job */
376
377
378 if (!MaxJobs)
379 return;
380
381 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
382 job && cupsArrayCount(Jobs) >= MaxJobs;
383 job = (cupsd_job_t *)cupsArrayNext(Jobs))
384 if (job->state->values[0].integer >= IPP_JOB_CANCELLED)
385 cupsdCancelJob(job, 1);
386 }
387
388
389 /*
390 * 'cupsdFinishJob()' - Finish a job.
391 */
392
393 void
394 cupsdFinishJob(cupsd_job_t *job) /* I - Job */
395 {
396 int job_history; /* Did cupsdCancelJob() keep the job? */
397 cupsd_printer_t *printer; /* Current printer */
398
399
400 cupsdLogMessage(CUPSD_LOG_DEBUG,
401 "cupsdFinishJob: job %d, file %d is complete.",
402 job->id, job->current_file - 1);
403
404 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFinishJob: job->status is %d",
405 job->status);
406
407 if (job->status_buffer && job->current_file >= job->num_files)
408 {
409 /*
410 * Close the pipe and clear the input bit.
411 */
412
413 cupsdLogMessage(CUPSD_LOG_DEBUG2,
414 "cupsdFinishJob: Removing fd %d from InputSet...",
415 job->status_buffer->fd);
416
417 FD_CLR(job->status_buffer->fd, InputSet);
418
419 cupsdLogMessage(CUPSD_LOG_DEBUG2,
420 "cupsdFinishJob: Closing status input pipe %d...",
421 job->status_buffer->fd);
422
423 cupsdStatBufDelete(job->status_buffer);
424
425 job->status_buffer = NULL;
426 }
427
428 if (job->status < 0)
429 {
430 /*
431 * Backend had errors; stop it...
432 */
433
434 printer = job->printer;
435
436 switch (-job->status)
437 {
438 default :
439 case CUPS_BACKEND_FAILED :
440 /*
441 * Backend failure, use the error-policy to determine how to
442 * act...
443 */
444
445 cupsdStopJob(job, 0);
446 job->state->values[0].integer = IPP_JOB_PENDING;
447 cupsdSaveJob(job);
448
449 /*
450 * If the job was queued to a class, try requeuing it... For
451 * faxes and retry-job queues, hold the current job for 5 minutes.
452 */
453
454 if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
455 cupsdCheckJobs();
456 else if ((printer->type & CUPS_PRINTER_FAX) ||
457 !strcmp(printer->error_policy, "retry-job"))
458 {
459 /*
460 * See how many times we've tried to send the job; if more than
461 * the limit, cancel the job.
462 */
463
464 job->tries ++;
465
466 if (job->tries >= JobRetryLimit)
467 {
468 /*
469 * Too many tries...
470 */
471
472 cupsdLogMessage(CUPSD_LOG_ERROR,
473 "Canceling job %d since it could not be sent after %d tries.",
474 job->id, JobRetryLimit);
475
476 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
477 "Job canceled since it could not be sent after %d tries.",
478 JobRetryLimit);
479
480 cupsdCancelJob(job, 0);
481 }
482 else
483 {
484 /*
485 * Try again in N seconds...
486 */
487
488 set_hold_until(job, time(NULL) + JobRetryInterval);
489 }
490 }
491 else if (!strcmp(printer->error_policy, "abort-job"))
492 cupsdCancelJob(job, 0);
493 break;
494
495 case CUPS_BACKEND_CANCEL :
496 /*
497 * Cancel the job...
498 */
499
500 cupsdCancelJob(job, 0);
501 break;
502
503 case CUPS_BACKEND_HOLD :
504 /*
505 * Hold the job...
506 */
507
508 cupsdStopJob(job, 0);
509 cupsdSetJobHoldUntil(job, "indefinite");
510 cupsdSaveJob(job);
511 break;
512
513 case CUPS_BACKEND_STOP :
514 /*
515 * Stop the printer...
516 */
517
518 cupsdStopJob(job, 0);
519 cupsdSaveJob(job);
520 cupsdSetPrinterState(printer, IPP_PRINTER_STOPPED, 1);
521 break;
522
523 case CUPS_BACKEND_AUTH_REQUIRED :
524 cupsdStopJob(job, 0);
525 cupsdSetJobHoldUntil(job, "authenticated");
526 cupsdSaveJob(job);
527
528 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, printer, job,
529 "Authentication is required for job %d.", job->id);
530 break;
531 }
532
533 /*
534 * Try printing another job...
535 */
536
537 cupsdCheckJobs();
538 }
539 else if (job->status > 0)
540 {
541 /*
542 * Filter had errors; cancel it...
543 */
544
545 if (job->current_file < job->num_files)
546 cupsdStartJob(job, job->printer);
547 else
548 {
549 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
550 "Job aborted due to filter errors; please consult the "
551 "error_log file for details.");
552
553 job_history = JobHistory && !(job->dtype & CUPS_PRINTER_REMOTE);
554
555 cupsdCancelJob(job, 0);
556
557 if (job_history)
558 {
559 job->state->values[0].integer = IPP_JOB_ABORTED;
560 cupsdSaveJob(job);
561 }
562
563 cupsdCheckJobs();
564 }
565 }
566 else
567 {
568 /*
569 * Job printed successfully; cancel it...
570 */
571
572 if (job->current_file < job->num_files)
573 {
574 FilterLevel -= job->cost;
575 cupsdStartJob(job, job->printer);
576 }
577 else
578 {
579 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
580 "Job completed successfully.");
581
582 job_history = JobHistory && !(job->dtype & CUPS_PRINTER_REMOTE);
583
584 cupsdCancelJob(job, 0);
585
586 if (job_history)
587 {
588 job->state->values[0].integer = IPP_JOB_COMPLETED;
589 cupsdSaveJob(job);
590 }
591
592 cupsdCheckJobs();
593 }
594 }
595 }
596
597
598 /*
599 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
600 */
601
602 void
603 cupsdFreeAllJobs(void)
604 {
605 cupsd_job_t *job; /* Current job */
606
607
608 cupsdHoldSignals();
609
610 cupsdStopAllJobs();
611
612 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
613 job;
614 job = (cupsd_job_t *)cupsArrayNext(Jobs))
615 {
616 cupsArrayRemove(Jobs, job);
617 cupsArrayRemove(ActiveJobs, job);
618
619 ippDelete(job->attrs);
620
621 if (job->num_files > 0)
622 {
623 free(job->compressions);
624 free(job->filetypes);
625 }
626
627 free(job);
628 }
629
630 cupsdReleaseSignals();
631 }
632
633
634 /*
635 * 'cupsdFindJob()' - Find the specified job.
636 */
637
638 cupsd_job_t * /* O - Job data */
639 cupsdFindJob(int id) /* I - Job ID */
640 {
641 cupsd_job_t key; /* Search key */
642
643
644 key.id = id;
645
646 return ((cupsd_job_t *)cupsArrayFind(Jobs, &key));
647 }
648
649
650 /*
651 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
652 * or held jobs in a printer or class.
653 */
654
655 int /* O - Job count */
656 cupsdGetPrinterJobCount(
657 const char *dest) /* I - Printer or class name */
658 {
659 int count; /* Job count */
660 cupsd_job_t *job; /* Current job */
661
662
663 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
664 job;
665 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
666 if (!strcasecmp(job->dest, dest))
667 count ++;
668
669 return (count);
670 }
671
672
673 /*
674 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
675 * or held jobs for a user.
676 */
677
678 int /* O - Job count */
679 cupsdGetUserJobCount(
680 const char *username) /* I - Username */
681 {
682 int count; /* Job count */
683 cupsd_job_t *job; /* Current job */
684
685
686 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
687 job;
688 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
689 if (!strcasecmp(job->username, username))
690 count ++;
691
692 return (count);
693 }
694
695
696 /*
697 * 'cupsdHoldJob()' - Hold the specified job.
698 */
699
700 void
701 cupsdHoldJob(cupsd_job_t *job) /* I - Job data */
702 {
703 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdHoldJob: id = %d", job->id);
704
705 if (job->state->values[0].integer == IPP_JOB_PROCESSING)
706 cupsdStopJob(job, 0);
707
708 DEBUG_puts("cupsdHoldJob: setting state to held...");
709
710 job->state->values[0].integer = IPP_JOB_HELD;
711
712 cupsdSaveJob(job);
713
714 cupsdCheckJobs();
715 }
716
717
718 /*
719 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
720 */
721
722 void
723 cupsdLoadAllJobs(void)
724 {
725 cups_dir_t *dir; /* Directory */
726 cups_dentry_t *dent; /* Directory entry */
727 char filename[1024]; /* Full filename of job file */
728 int fd; /* File descriptor */
729 cupsd_job_t *job; /* New job */
730 int jobid, /* Current job ID */
731 fileid; /* Current file ID */
732 ipp_attribute_t *attr; /* Job attribute */
733 char method[HTTP_MAX_URI],
734 /* Method portion of URI */
735 username[HTTP_MAX_URI],
736 /* Username portion of URI */
737 host[HTTP_MAX_URI],
738 /* Host portion of URI */
739 resource[HTTP_MAX_URI];
740 /* Resource portion of URI */
741 int port; /* Port portion of URI */
742 const char *dest; /* Destination */
743 mime_type_t **filetypes; /* New filetypes array */
744 int *compressions; /* New compressions array */
745
746
747 /*
748 * First create the job lists...
749 */
750
751 if (!Jobs)
752 Jobs = cupsArrayNew(compare_jobs, NULL);
753
754 if (!ActiveJobs)
755 ActiveJobs = cupsArrayNew(compare_active_jobs, NULL);
756
757 /*
758 * Then open the requests directory...
759 */
760
761 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdLoadAllJobs: Scanning %s...",
762 RequestRoot);
763
764 if ((dir = cupsDirOpen(RequestRoot)) == NULL)
765 {
766 cupsdLogMessage(CUPSD_LOG_ERROR,
767 "cupsdLoadAllJobs: Unable to open spool directory %s: %s",
768 RequestRoot, strerror(errno));
769 return;
770 }
771
772 /*
773 * Read all the c##### files...
774 */
775
776 while ((dent = cupsDirRead(dir)) != NULL)
777 if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c')
778 {
779 /*
780 * Allocate memory for the job...
781 */
782
783 if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
784 {
785 cupsdLogMessage(CUPSD_LOG_ERROR,
786 "cupsdLoadAllJobs: Ran out of memory for jobs!");
787 cupsDirClose(dir);
788 return;
789 }
790
791 if ((job->attrs = ippNew()) == NULL)
792 {
793 free(job);
794 cupsdLogMessage(CUPSD_LOG_ERROR,
795 "cupsdLoadAllJobs: Ran out of memory for job attributes!");
796 cupsDirClose(dir);
797 return;
798 }
799
800 /*
801 * Assign the job ID...
802 */
803
804 job->id = atoi(dent->filename + 1);
805 job->back_pipes[0] = -1;
806 job->back_pipes[1] = -1;
807 job->print_pipes[0] = -1;
808 job->print_pipes[1] = -1;
809
810 cupsdLogMessage(CUPSD_LOG_DEBUG,
811 "cupsdLoadAllJobs: Loading attributes for job %d...",
812 job->id);
813
814 if (job->id >= NextJobId)
815 NextJobId = job->id + 1;
816
817 /*
818 * Load the job control file...
819 */
820
821 snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->filename);
822 if ((fd = open(filename, O_RDONLY)) < 0)
823 {
824 cupsdLogMessage(CUPSD_LOG_ERROR,
825 "cupsdLoadAllJobs: Unable to open job control file \"%s\" - %s!",
826 filename, strerror(errno));
827 ippDelete(job->attrs);
828 free(job);
829 unlink(filename);
830 continue;
831 }
832 else
833 {
834 if (ippReadFile(fd, job->attrs) != IPP_DATA)
835 {
836 cupsdLogMessage(CUPSD_LOG_ERROR,
837 "cupsdLoadAllJobs: Unable to read job control file \"%s\"!",
838 filename);
839 close(fd);
840 ippDelete(job->attrs);
841 free(job);
842 unlink(filename);
843 continue;
844 }
845
846 close(fd);
847 }
848
849 if ((job->state = ippFindAttribute(job->attrs, "job-state", IPP_TAG_ENUM)) == NULL)
850 {
851 cupsdLogMessage(CUPSD_LOG_ERROR,
852 "cupsdLoadAllJobs: Missing or bad job-state attribute in control file \"%s\"!",
853 filename);
854 ippDelete(job->attrs);
855 free(job);
856 unlink(filename);
857 continue;
858 }
859
860 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", IPP_TAG_URI)) == NULL)
861 {
862 cupsdLogMessage(CUPSD_LOG_ERROR,
863 "cupsdLoadAllJobs: No job-printer-uri attribute in control file \"%s\"!",
864 filename);
865 ippDelete(job->attrs);
866 free(job);
867 unlink(filename);
868 continue;
869 }
870
871 httpSeparateURI(attr->values[0].string.text, method, sizeof(method),
872 username, sizeof(username), host, sizeof(host), &port,
873 resource, sizeof(resource));
874
875 if ((dest = cupsdValidateDest(host, resource, &(job->dtype),
876 NULL)) == NULL)
877 {
878 cupsdLogMessage(CUPSD_LOG_ERROR,
879 "cupsdLoadAllJobs: Unable to queue job for destination \"%s\"!",
880 attr->values[0].string.text);
881 ippDelete(job->attrs);
882 free(job);
883 unlink(filename);
884 continue;
885 }
886
887 cupsdSetString(&job->dest, dest);
888
889 job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed",
890 IPP_TAG_INTEGER);
891 job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
892
893 if ((attr = ippFindAttribute(job->attrs, "job-priority", IPP_TAG_INTEGER)) == NULL)
894 {
895 cupsdLogMessage(CUPSD_LOG_ERROR,
896 "cupsdLoadAllJobs: Missing or bad job-priority attribute in control file \"%s\"!",
897 filename);
898 ippDelete(job->attrs);
899 free(job);
900 unlink(filename);
901 continue;
902 }
903 job->priority = attr->values[0].integer;
904
905 if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name", IPP_TAG_NAME)) == NULL)
906 {
907 cupsdLogMessage(CUPSD_LOG_ERROR,
908 "cupsdLoadAllJobs: Missing or bad job-originating-user-name attribute in control file \"%s\"!",
909 filename);
910 ippDelete(job->attrs);
911 free(job);
912 unlink(filename);
913 continue;
914 }
915 cupsdSetString(&job->username, attr->values[0].string.text);
916
917 /*
918 * Insert the job into the array, sorting by job priority and ID...
919 */
920
921 cupsArrayAdd(Jobs, job);
922 if (job->state->values[0].integer < IPP_JOB_STOPPED)
923 cupsArrayAdd(ActiveJobs,job);
924
925 /*
926 * Set the job hold-until time and state...
927 */
928
929 if (job->state->values[0].integer == IPP_JOB_HELD)
930 {
931 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
932 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
933
934 if (attr == NULL)
935 job->state->values[0].integer = IPP_JOB_PENDING;
936 else
937 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
938 }
939 else if (job->state->values[0].integer == IPP_JOB_PROCESSING)
940 job->state->values[0].integer = IPP_JOB_PENDING;
941 }
942
943 /*
944 * Read all the d##### files...
945 */
946
947 cupsDirRewind(dir);
948
949 while ((dent = cupsDirRead(dir)) != NULL)
950 if (strlen(dent->filename) > 7 && dent->filename[0] == 'd' &&
951 strchr(dent->filename, '-'))
952 {
953 /*
954 * Find the job...
955 */
956
957 jobid = atoi(dent->filename + 1);
958 fileid = atoi(strchr(dent->filename, '-') + 1);
959
960 cupsdLogMessage(CUPSD_LOG_DEBUG,
961 "cupsdLoadAllJobs: Auto-typing document file %s...",
962 dent->filename);
963
964 snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->filename);
965
966 if ((job = cupsdFindJob(jobid)) == NULL)
967 {
968 cupsdLogMessage(CUPSD_LOG_ERROR,
969 "cupsdLoadAllJobs: Orphaned print file \"%s\"!",
970 filename);
971 unlink(filename);
972 continue;
973 }
974
975 if (fileid > job->num_files)
976 {
977 if (job->num_files == 0)
978 {
979 compressions = (int *)calloc(fileid, sizeof(int));
980 filetypes = (mime_type_t **)calloc(fileid, sizeof(mime_type_t *));
981 }
982 else
983 {
984 compressions = (int *)realloc(job->compressions,
985 sizeof(int) * fileid);
986 filetypes = (mime_type_t **)realloc(job->filetypes,
987 sizeof(mime_type_t *) * fileid);
988 }
989
990 if (compressions == NULL || filetypes == NULL)
991 {
992 cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdLoadAllJobs: Ran out of memory for job file types!");
993 continue;
994 }
995
996 job->compressions = compressions;
997 job->filetypes = filetypes;
998 job->num_files = fileid;
999 }
1000
1001 job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, filename,
1002 job->compressions + fileid - 1);
1003
1004 if (job->filetypes[fileid - 1] == NULL)
1005 job->filetypes[fileid - 1] = mimeType(MimeDatabase, "application",
1006 "vnd.cups-raw");
1007 }
1008
1009 cupsDirClose(dir);
1010
1011 /*
1012 * Clean out old jobs as needed...
1013 */
1014
1015 cupsdCleanJobs();
1016 }
1017
1018
1019 /*
1020 * 'cupsdMoveJob()' - Move the specified job to a different destination.
1021 */
1022
1023 void
1024 cupsdMoveJob(cupsd_job_t *job, /* I - Job */
1025 const char *dest) /* I - Destination */
1026 {
1027 ipp_attribute_t *attr; /* job-printer-uri attribute */
1028 cupsd_printer_t *p; /* Destination printer or class */
1029
1030
1031 /*
1032 * Find the printer...
1033 */
1034
1035 if ((p = cupsdFindDest(dest)) == NULL)
1036 return;
1037
1038 /*
1039 * Don't move completed jobs...
1040 */
1041
1042 if (job->state->values[0].integer >= IPP_JOB_PROCESSING)
1043 return;
1044
1045 /*
1046 * Change the destination information...
1047 */
1048
1049 cupsdSetString(&job->dest, dest);
1050 job->dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE |
1051 CUPS_PRINTER_IMPLICIT);
1052
1053 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", IPP_TAG_URI)) != NULL)
1054 cupsdSetString(&(attr->values[0].string.text), p->uri);
1055
1056 cupsdSaveJob(job);
1057 }
1058
1059
1060 /*
1061 * 'cupsdReleaseJob()' - Release the specified job.
1062 */
1063
1064 void
1065 cupsdReleaseJob(cupsd_job_t *job) /* I - Job */
1066 {
1067 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReleaseJob: id = %d", job->id);
1068
1069 if (job->state->values[0].integer == IPP_JOB_HELD)
1070 {
1071 DEBUG_puts("cupsdReleaseJob: setting state to pending...");
1072
1073 job->state->values[0].integer = IPP_JOB_PENDING;
1074 cupsdSaveJob(job);
1075 cupsdCheckJobs();
1076 }
1077 }
1078
1079
1080 /*
1081 * 'cupsdRestartJob()' - Restart the specified job.
1082 */
1083
1084 void
1085 cupsdRestartJob(cupsd_job_t *job) /* I - Job */
1086 {
1087 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdRestartJob: id = %d", job->id);
1088
1089 if (job->state->values[0].integer == IPP_JOB_STOPPED || JobFiles)
1090 {
1091 job->tries = 0;
1092 job->state->values[0].integer = IPP_JOB_PENDING;
1093 cupsdSaveJob(job);
1094 cupsdCheckJobs();
1095 }
1096 }
1097
1098
1099 /*
1100 * 'cupsdSaveJob()' - Save a job to disk.
1101 */
1102
1103 void
1104 cupsdSaveJob(cupsd_job_t *job) /* I - Job */
1105 {
1106 char filename[1024]; /* Job control filename */
1107 int fd; /* File descriptor */
1108
1109
1110 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id);
1111
1112 if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
1113 {
1114 cupsdLogMessage(CUPSD_LOG_ERROR,
1115 "cupsdSaveJob: Unable to create job control file \"%s\" - %s.",
1116 filename, strerror(errno));
1117 return;
1118 }
1119
1120 fchmod(fd, 0600);
1121 fchown(fd, RunUser, Group);
1122
1123 ippWriteFile(fd, job->attrs);
1124
1125 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSaveJob: Closing file %d...", fd);
1126
1127 close(fd);
1128 }
1129
1130
1131 /*
1132 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job...
1133 */
1134
1135 void
1136 cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */
1137 const char *when) /* I - When to resume */
1138 {
1139 time_t curtime; /* Current time */
1140 struct tm *curdate; /* Current date */
1141 int hour; /* Hold hour */
1142 int minute; /* Hold minute */
1143 int second; /* Hold second */
1144
1145
1146 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSetJobHoldUntil(%d, \"%s\")",
1147 job->id, when);
1148
1149 second = 0;
1150
1151 if (!strcmp(when, "indefinite") || !strcmp(when, "authenticated"))
1152 {
1153 /*
1154 * Hold indefinitely...
1155 */
1156
1157 job->hold_until = 0;
1158 }
1159 else if (!strcmp(when, "day-time"))
1160 {
1161 /*
1162 * Hold to 6am the next morning unless local time is < 6pm.
1163 */
1164
1165 curtime = time(NULL);
1166 curdate = localtime(&curtime);
1167
1168 if (curdate->tm_hour < 18)
1169 job->hold_until = curtime;
1170 else
1171 job->hold_until = curtime +
1172 ((29 - curdate->tm_hour) * 60 + 59 -
1173 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1174 }
1175 else if (!strcmp(when, "evening") || strcmp(when, "night"))
1176 {
1177 /*
1178 * Hold to 6pm unless local time is > 6pm or < 6am.
1179 */
1180
1181 curtime = time(NULL);
1182 curdate = localtime(&curtime);
1183
1184 if (curdate->tm_hour < 6 || curdate->tm_hour >= 18)
1185 job->hold_until = curtime;
1186 else
1187 job->hold_until = curtime +
1188 ((17 - curdate->tm_hour) * 60 + 59 -
1189 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1190 }
1191 else if (!strcmp(when, "second-shift"))
1192 {
1193 /*
1194 * Hold to 4pm unless local time is > 4pm.
1195 */
1196
1197 curtime = time(NULL);
1198 curdate = localtime(&curtime);
1199
1200 if (curdate->tm_hour >= 16)
1201 job->hold_until = curtime;
1202 else
1203 job->hold_until = curtime +
1204 ((15 - curdate->tm_hour) * 60 + 59 -
1205 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1206 }
1207 else if (!strcmp(when, "third-shift"))
1208 {
1209 /*
1210 * Hold to 12am unless local time is < 8am.
1211 */
1212
1213 curtime = time(NULL);
1214 curdate = localtime(&curtime);
1215
1216 if (curdate->tm_hour < 8)
1217 job->hold_until = curtime;
1218 else
1219 job->hold_until = curtime +
1220 ((23 - curdate->tm_hour) * 60 + 59 -
1221 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1222 }
1223 else if (!strcmp(when, "weekend"))
1224 {
1225 /*
1226 * Hold to weekend unless we are in the weekend.
1227 */
1228
1229 curtime = time(NULL);
1230 curdate = localtime(&curtime);
1231
1232 if (curdate->tm_wday || curdate->tm_wday == 6)
1233 job->hold_until = curtime;
1234 else
1235 job->hold_until = curtime +
1236 (((5 - curdate->tm_wday) * 24 +
1237 (17 - curdate->tm_hour)) * 60 + 59 -
1238 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1239 }
1240 else if (sscanf(when, "%d:%d:%d", &hour, &minute, &second) >= 2)
1241 {
1242 /*
1243 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1244 */
1245
1246 curtime = time(NULL);
1247 curdate = gmtime(&curtime);
1248
1249 job->hold_until = curtime +
1250 ((hour - curdate->tm_hour) * 60 + minute -
1251 curdate->tm_min) * 60 + second - curdate->tm_sec;
1252
1253 /*
1254 * Hold until next day as needed...
1255 */
1256
1257 if (job->hold_until < curtime)
1258 job->hold_until += 24 * 60 * 60 * 60;
1259 }
1260
1261 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSetJobHoldUntil: hold_until = %d",
1262 (int)job->hold_until);
1263 }
1264
1265
1266 /*
1267 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
1268 * the list as needed.
1269 */
1270
1271 void
1272 cupsdSetJobPriority(
1273 cupsd_job_t *job, /* I - Job ID */
1274 int priority) /* I - New priority (0 to 100) */
1275 {
1276 ipp_attribute_t *attr; /* Job attribute */
1277
1278
1279 /*
1280 * Don't change completed jobs...
1281 */
1282
1283 if (job->state->values[0].integer >= IPP_JOB_PROCESSING)
1284 return;
1285
1286 /*
1287 * Set the new priority and re-add the job into the active list...
1288 */
1289
1290 cupsArrayRemove(ActiveJobs, job);
1291
1292 job->priority = priority;
1293
1294 if ((attr = ippFindAttribute(job->attrs, "job-priority", IPP_TAG_INTEGER)) != NULL)
1295 attr->values[0].integer = priority;
1296 else
1297 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
1298 priority);
1299
1300 cupsArrayAdd(ActiveJobs, job);
1301
1302 cupsdSaveJob(job);
1303 }
1304
1305
1306 /*
1307 * 'cupsdStartJob()' - Start a print job.
1308 */
1309
1310 void
1311 cupsdStartJob(cupsd_job_t *job, /* I - Job ID */
1312 cupsd_printer_t *printer) /* I - Printer to print job */
1313 {
1314 int i; /* Looping var */
1315 int slot; /* Pipe slot */
1316 int num_filters; /* Number of filters for job */
1317 mime_filter_t *filters; /* Filters for job */
1318 char method[255], /* Method for output */
1319 *optptr, /* Pointer to options */
1320 *valptr; /* Pointer in value string */
1321 ipp_attribute_t *attr; /* Current attribute */
1322 int pid; /* Process ID of new filter process */
1323 int banner_page; /* 1 if banner page, 0 otherwise */
1324 int statusfds[2], /* Pipes used between the filters and scheduler */
1325 filterfds[2][2];/* Pipes used between the filters */
1326 int envc; /* Number of environment variables */
1327 char *argv[8], /* Filter command-line arguments */
1328 sani_uri[1024], /* Sanitized DEVICE_URI env var */
1329 filename[1024], /* Job filename */
1330 command[1024], /* Full path to filter/backend command */
1331 jobid[255], /* Job ID string */
1332 title[IPP_MAX_NAME],
1333 /* Job title string */
1334 copies[255], /* # copies string */
1335 *envp[100], /* Environment variables */
1336 charset[255], /* CHARSET environment variable */
1337 class_name[255],/* CLASS environment variable */
1338 classification[1024],
1339 /* CLASSIFICATION environment variable */
1340 content_type[1024],
1341 /* CONTENT_TYPE environment variable */
1342 device_uri[1024],
1343 /* DEVICE_URI environment variable */
1344 lang[255], /* LANG environment variable */
1345 ppd[1024], /* PPD environment variable */
1346 printer_name[255],
1347 /* PRINTER environment variable */
1348 rip_max_cache[255];
1349 /* RIP_MAX_CACHE environment variable */
1350 static char *options = NULL;/* Full list of options */
1351 static int optlength = 0; /* Length of option buffer */
1352
1353
1354 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob() id = %d, file = %d/%d",
1355 job->id, job->current_file, job->num_files);
1356
1357 if (job->num_files == 0)
1358 {
1359 cupsdLogMessage(CUPSD_LOG_ERROR, "Job ID %d has no files! Cancelling it!",
1360 job->id);
1361
1362 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
1363 "Job canceled because it has no files.");
1364
1365 cupsdCancelJob(job, 0);
1366 return;
1367 }
1368
1369 /*
1370 * Figure out what filters are required to convert from
1371 * the source to the destination type...
1372 */
1373
1374 num_filters = 0;
1375 job->cost = 0;
1376
1377 if (printer->raw)
1378 {
1379 /*
1380 * Remote jobs and raw queues go directly to the printer without
1381 * filtering...
1382 */
1383
1384 cupsdLogMessage(CUPSD_LOG_DEBUG,
1385 "cupsdStartJob: Sending job to queue tagged as raw...");
1386
1387 filters = NULL;
1388 }
1389 else
1390 {
1391 /*
1392 * Local jobs get filtered...
1393 */
1394
1395 filters = mimeFilter(MimeDatabase, job->filetypes[job->current_file],
1396 printer->filetype, &num_filters, MAX_FILTERS - 1);
1397
1398 if (num_filters == 0)
1399 {
1400 cupsdLogMessage(CUPSD_LOG_ERROR,
1401 "Unable to convert file %d to printable format for job %d!",
1402 job->current_file, job->id);
1403 cupsdLogMessage(CUPSD_LOG_INFO,
1404 "Hint: Do you have ESP Ghostscript installed?");
1405
1406 if (LogLevel < CUPSD_LOG_DEBUG)
1407 cupsdLogMessage(CUPSD_LOG_INFO,
1408 "Hint: Try setting the LogLevel to \"debug\".");
1409
1410 job->current_file ++;
1411
1412 if (job->current_file == job->num_files)
1413 {
1414 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
1415 "Job canceled because it has no files that can be printed.");
1416
1417 cupsdCancelJob(job, 0);
1418 }
1419
1420 return;
1421 }
1422
1423 /*
1424 * Remove NULL ("-") filters...
1425 */
1426
1427 for (i = 0; i < num_filters;)
1428 if (strcmp(filters[i].filter, "-") == 0)
1429 {
1430 num_filters --;
1431 if (i < num_filters)
1432 memcpy(filters + i, filters + i + 1,
1433 (num_filters - i) * sizeof(mime_filter_t));
1434 }
1435 else
1436 i ++;
1437
1438 if (num_filters == 0)
1439 {
1440 free(filters);
1441 filters = NULL;
1442 }
1443 else
1444 {
1445 /*
1446 * Compute filter cost...
1447 */
1448
1449 for (i = 0; i < num_filters; i ++)
1450 job->cost += filters[i].cost;
1451 }
1452 }
1453
1454 /*
1455 * See if the filter cost is too high...
1456 */
1457
1458 if ((FilterLevel + job->cost) > FilterLimit && FilterLevel > 0 &&
1459 FilterLimit > 0)
1460 {
1461 /*
1462 * Don't print this job quite yet...
1463 */
1464
1465 if (filters != NULL)
1466 free(filters);
1467
1468 cupsdLogMessage(CUPSD_LOG_INFO,
1469 "Holding job %d because filter limit has been reached.",
1470 job->id);
1471 cupsdLogMessage(CUPSD_LOG_DEBUG,
1472 "cupsdStartJob: id=%d, file=%d, cost=%d, level=%d, limit=%d",
1473 job->id, job->current_file, job->cost, FilterLevel,
1474 FilterLimit);
1475 return;
1476 }
1477
1478 FilterLevel += job->cost;
1479
1480 /*
1481 * Add decompression filters, if any...
1482 */
1483
1484 if (job->compressions[job->current_file])
1485 {
1486 /*
1487 * Add gziptoany filter to the front of the list...
1488 */
1489
1490 mime_filter_t *temp_filters;
1491
1492 if (num_filters == 0)
1493 temp_filters = malloc(sizeof(mime_filter_t));
1494 else
1495 temp_filters = realloc(filters,
1496 sizeof(mime_filter_t) * (num_filters + 1));
1497
1498 if (temp_filters == NULL)
1499 {
1500 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add decompression filter - %s",
1501 strerror(errno));
1502
1503 if (filters != NULL)
1504 free(filters);
1505
1506 job->current_file ++;
1507
1508 if (job->current_file == job->num_files)
1509 {
1510 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
1511 "Job canceled because the print file could not be decompressed.");
1512
1513 cupsdCancelJob(job, 0);
1514 }
1515
1516 return;
1517 }
1518
1519 filters = temp_filters;
1520 memmove(filters + 1, filters, num_filters * sizeof(mime_filter_t));
1521 *filters = gziptoany_filter;
1522 num_filters ++;
1523 }
1524
1525 /*
1526 * Add port monitor, if any...
1527 */
1528
1529 if (printer->port_monitor)
1530 {
1531 /*
1532 * Add port monitor to the end of the list...
1533 */
1534
1535 mime_filter_t *temp_filters;
1536
1537 if (num_filters == 0)
1538 temp_filters = malloc(sizeof(mime_filter_t));
1539 else
1540 temp_filters = realloc(filters,
1541 sizeof(mime_filter_t) * (num_filters + 1));
1542
1543 if (temp_filters == NULL)
1544 {
1545 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add port monitor - %s",
1546 strerror(errno));
1547
1548 if (filters != NULL)
1549 free(filters);
1550
1551 job->current_file ++;
1552
1553 if (job->current_file == job->num_files)
1554 {
1555 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
1556 "Job canceled because the port monitor could not be added.");
1557
1558 cupsdCancelJob(job, 0);
1559 }
1560
1561 return;
1562 }
1563
1564 filters = temp_filters;
1565 memset(filters + num_filters, 0, sizeof(mime_filter_t));
1566 snprintf(filters[num_filters].filter, sizeof(filters[num_filters].filter),
1567 "%s/monitor/%s", ServerBin, printer->port_monitor);
1568 num_filters ++;
1569 }
1570
1571 /*
1572 * Update the printer and job state to "processing"...
1573 */
1574
1575 job->state->values[0].integer = IPP_JOB_PROCESSING;
1576 job->status = 0;
1577 job->printer = printer;
1578 printer->job = job;
1579 cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
1580
1581 if (job->current_file == 0)
1582 {
1583 set_time(job, "time-at-processing");
1584 cupsdOpenPipe(job->back_pipes);
1585 }
1586
1587 /*
1588 * Determine if we are printing a banner page or not...
1589 */
1590
1591 if (job->job_sheets == NULL)
1592 {
1593 cupsdLogMessage(CUPSD_LOG_DEBUG, "No job-sheets attribute.");
1594 if ((job->job_sheets =
1595 ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
1596 cupsdLogMessage(CUPSD_LOG_DEBUG,
1597 "... but someone added one without setting job_sheets!");
1598 }
1599 else if (job->job_sheets->num_values == 1)
1600 cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s",
1601 job->job_sheets->values[0].string.text);
1602 else
1603 cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s,%s",
1604 job->job_sheets->values[0].string.text,
1605 job->job_sheets->values[1].string.text);
1606
1607 if (printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
1608 banner_page = 0;
1609 else if (job->job_sheets == NULL)
1610 banner_page = 0;
1611 else if (strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
1612 job->current_file == 0)
1613 banner_page = 1;
1614 else if (job->job_sheets->num_values > 1 &&
1615 strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
1616 job->current_file == (job->num_files - 1))
1617 banner_page = 1;
1618 else
1619 banner_page = 0;
1620
1621 cupsdLogMessage(CUPSD_LOG_DEBUG, "banner_page = %d", banner_page);
1622
1623 /*
1624 * Building the options string is harder than it needs to be, but
1625 * for the moment we need to pass strings for command-line args and
1626 * not IPP attribute pointers... :)
1627 *
1628 * First allocate/reallocate the option buffer as needed...
1629 */
1630
1631 i = ipp_length(job->attrs);
1632
1633 if (i > optlength)
1634 {
1635 if (optlength == 0)
1636 optptr = malloc(i);
1637 else
1638 optptr = realloc(options, i);
1639
1640 if (optptr == NULL)
1641 {
1642 cupsdLogMessage(CUPSD_LOG_CRIT,
1643 "cupsdStartJob: Unable to allocate %d bytes for option buffer for job %d!",
1644 i, job->id);
1645
1646 if (filters != NULL)
1647 free(filters);
1648
1649 FilterLevel -= job->cost;
1650
1651 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
1652 "Job canceled because the server ran out of memory.");
1653
1654 cupsdCancelJob(job, 0);
1655 return;
1656 }
1657
1658 options = optptr;
1659 optlength = i;
1660 }
1661
1662 /*
1663 * Now loop through the attributes and convert them to the textual
1664 * representation used by the filters...
1665 */
1666
1667 optptr = options;
1668 *optptr = '\0';
1669
1670 snprintf(title, sizeof(title), "%s-%d", printer->name, job->id);
1671 strcpy(copies, "1");
1672
1673 for (attr = job->attrs->attrs; attr != NULL; attr = attr->next)
1674 {
1675 if (strcmp(attr->name, "copies") == 0 &&
1676 attr->value_tag == IPP_TAG_INTEGER)
1677 {
1678 /*
1679 * Don't use the # copies attribute if we are printing the job sheets...
1680 */
1681
1682 if (!banner_page)
1683 sprintf(copies, "%d", attr->values[0].integer);
1684 }
1685 else if (strcmp(attr->name, "job-name") == 0 &&
1686 (attr->value_tag == IPP_TAG_NAME ||
1687 attr->value_tag == IPP_TAG_NAMELANG))
1688 strlcpy(title, attr->values[0].string.text, sizeof(title));
1689 else if (attr->group_tag == IPP_TAG_JOB)
1690 {
1691 /*
1692 * Filter out other unwanted attributes...
1693 */
1694
1695 if (attr->value_tag == IPP_TAG_MIMETYPE ||
1696 attr->value_tag == IPP_TAG_NAMELANG ||
1697 attr->value_tag == IPP_TAG_TEXTLANG ||
1698 attr->value_tag == IPP_TAG_URI ||
1699 attr->value_tag == IPP_TAG_URISCHEME ||
1700 attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */
1701 continue;
1702
1703 if (strncmp(attr->name, "time-", 5) == 0)
1704 continue;
1705
1706 if (strncmp(attr->name, "job-", 4) == 0 &&
1707 !(printer->type & CUPS_PRINTER_REMOTE))
1708 continue;
1709
1710 if (strncmp(attr->name, "job-", 4) == 0 &&
1711 strcmp(attr->name, "job-billing") != 0 &&
1712 strcmp(attr->name, "job-sheets") != 0 &&
1713 strcmp(attr->name, "job-hold-until") != 0 &&
1714 strcmp(attr->name, "job-priority") != 0)
1715 continue;
1716
1717 if ((strcmp(attr->name, "page-label") == 0 ||
1718 strcmp(attr->name, "page-border") == 0 ||
1719 strncmp(attr->name, "number-up", 9) == 0 ||
1720 strcmp(attr->name, "page-set") == 0) &&
1721 banner_page)
1722 continue;
1723
1724 /*
1725 * Otherwise add them to the list...
1726 */
1727
1728 if (optptr > options)
1729 strlcat(optptr, " ", optlength - (optptr - options));
1730
1731 if (attr->value_tag != IPP_TAG_BOOLEAN)
1732 {
1733 strlcat(optptr, attr->name, optlength - (optptr - options));
1734 strlcat(optptr, "=", optlength - (optptr - options));
1735 }
1736
1737 for (i = 0; i < attr->num_values; i ++)
1738 {
1739 if (i)
1740 strlcat(optptr, ",", optlength - (optptr - options));
1741
1742 optptr += strlen(optptr);
1743
1744 switch (attr->value_tag)
1745 {
1746 case IPP_TAG_INTEGER :
1747 case IPP_TAG_ENUM :
1748 snprintf(optptr, optlength - (optptr - options),
1749 "%d", attr->values[i].integer);
1750 break;
1751
1752 case IPP_TAG_BOOLEAN :
1753 if (!attr->values[i].boolean)
1754 strlcat(optptr, "no", optlength - (optptr - options));
1755
1756 case IPP_TAG_NOVALUE :
1757 strlcat(optptr, attr->name,
1758 optlength - (optptr - options));
1759 break;
1760
1761 case IPP_TAG_RANGE :
1762 if (attr->values[i].range.lower == attr->values[i].range.upper)
1763 snprintf(optptr, optlength - (optptr - options) - 1,
1764 "%d", attr->values[i].range.lower);
1765 else
1766 snprintf(optptr, optlength - (optptr - options) - 1,
1767 "%d-%d", attr->values[i].range.lower,
1768 attr->values[i].range.upper);
1769 break;
1770
1771 case IPP_TAG_RESOLUTION :
1772 snprintf(optptr, optlength - (optptr - options) - 1,
1773 "%dx%d%s", attr->values[i].resolution.xres,
1774 attr->values[i].resolution.yres,
1775 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
1776 "dpi" : "dpc");
1777 break;
1778
1779 case IPP_TAG_STRING :
1780 case IPP_TAG_TEXT :
1781 case IPP_TAG_NAME :
1782 case IPP_TAG_KEYWORD :
1783 case IPP_TAG_CHARSET :
1784 case IPP_TAG_LANGUAGE :
1785 for (valptr = attr->values[i].string.text; *valptr;)
1786 {
1787 if (strchr(" \t\n\\\'\"", *valptr))
1788 *optptr++ = '\\';
1789 *optptr++ = *valptr++;
1790 }
1791
1792 *optptr = '\0';
1793 break;
1794
1795 default :
1796 break; /* anti-compiler-warning-code */
1797 }
1798 }
1799
1800 optptr += strlen(optptr);
1801 }
1802 }
1803
1804 /*
1805 * Build the command-line arguments for the filters. Each filter
1806 * has 6 or 7 arguments:
1807 *
1808 * argv[0] = printer
1809 * argv[1] = job ID
1810 * argv[2] = username
1811 * argv[3] = title
1812 * argv[4] = # copies
1813 * argv[5] = options
1814 * argv[6] = filename (optional; normally stdin)
1815 *
1816 * This allows legacy printer drivers that use the old System V
1817 * printing interface to be used by CUPS.
1818 */
1819
1820 sprintf(jobid, "%d", job->id);
1821 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
1822 job->id, job->current_file + 1);
1823
1824 argv[0] = printer->name;
1825 argv[1] = jobid;
1826 argv[2] = job->username;
1827 argv[3] = title;
1828 argv[4] = copies;
1829 argv[5] = options;
1830 argv[6] = filename;
1831 argv[7] = NULL;
1832
1833 cupsdLogMessage(CUPSD_LOG_DEBUG,
1834 "cupsdStartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1835 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
1836
1837 /*
1838 * Create environment variable strings for the filters...
1839 */
1840
1841 attr = ippFindAttribute(job->attrs, "attributes-natural-language",
1842 IPP_TAG_LANGUAGE);
1843
1844 switch (strlen(attr->values[0].string.text))
1845 {
1846 default :
1847 /*
1848 * This is an unknown or badly formatted language code; use
1849 * the POSIX locale...
1850 */
1851
1852 strcpy(lang, "LANG=C");
1853 break;
1854
1855 case 2 :
1856 /*
1857 * Just the language code (ll)...
1858 */
1859
1860 snprintf(lang, sizeof(lang), "LANG=%s",
1861 attr->values[0].string.text);
1862 break;
1863
1864 case 5 :
1865 /*
1866 * Language and country code (ll-cc)...
1867 */
1868
1869 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c",
1870 attr->values[0].string.text[0],
1871 attr->values[0].string.text[1],
1872 toupper(attr->values[0].string.text[3] & 255),
1873 toupper(attr->values[0].string.text[4] & 255));
1874 break;
1875 }
1876
1877 attr = ippFindAttribute(job->attrs, "document-format",
1878 IPP_TAG_MIMETYPE);
1879 if (attr != NULL &&
1880 (optptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
1881 snprintf(charset, sizeof(charset), "CHARSET=%s", optptr + 8);
1882 else
1883 {
1884 attr = ippFindAttribute(job->attrs, "attributes-charset",
1885 IPP_TAG_CHARSET);
1886 snprintf(charset, sizeof(charset), "CHARSET=%s",
1887 attr->values[0].string.text);
1888 }
1889
1890 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
1891 job->filetypes[job->current_file]->super,
1892 job->filetypes[job->current_file]->type);
1893 snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s", printer->device_uri);
1894 cupsdSanitizeURI(printer->device_uri, sani_uri, sizeof(sani_uri));
1895 snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot, printer->name);
1896 snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", printer->name);
1897 snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
1898
1899 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
1900
1901 envp[envc ++] = charset;
1902 envp[envc ++] = lang;
1903 envp[envc ++] = ppd;
1904 envp[envc ++] = rip_max_cache;
1905 envp[envc ++] = content_type;
1906 envp[envc ++] = device_uri;
1907 envp[envc ++] = printer_name;
1908
1909 if (Classification && !banner_page)
1910 {
1911 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1912 IPP_TAG_NAME)) == NULL)
1913 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1914 Classification);
1915 else if (attr->num_values > 1 &&
1916 strcmp(attr->values[1].string.text, "none") != 0)
1917 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1918 attr->values[1].string.text);
1919 else
1920 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1921 attr->values[0].string.text);
1922
1923 envp[envc ++] = classification;
1924 }
1925
1926 if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
1927 {
1928 snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest);
1929 envp[envc ++] = class_name;
1930 }
1931
1932 envp[envc] = NULL;
1933
1934 for (i = 0; i < envc; i ++)
1935 if (strncmp(envp[i], "DEVICE_URI=", 11))
1936 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: envp[%d]=\"%s\"",
1937 i, envp[i]);
1938 else
1939 cupsdLogMessage(CUPSD_LOG_DEBUG,
1940 "cupsdStartJob: envp[%d]=\"DEVICE_URI=%s\"", i, sani_uri);
1941
1942 job->current_file ++;
1943
1944 /*
1945 * Now create processes for all of the filters...
1946 */
1947
1948 if (cupsdOpenPipe(statusfds))
1949 {
1950 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create job status pipes - %s.",
1951 strerror(errno));
1952 snprintf(printer->state_message, sizeof(printer->state_message),
1953 "Unable to create status pipes - %s.", strerror(errno));
1954
1955 cupsdAddPrinterHistory(printer);
1956
1957 if (filters != NULL)
1958 free(filters);
1959
1960 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
1961 "Job canceled because the server could not create the job status pipes.");
1962
1963 cupsdCancelJob(job, 0);
1964 return;
1965 }
1966
1967 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: statusfds = [ %d %d ]",
1968 statusfds[0], statusfds[1]);
1969
1970 #ifdef FD_CLOEXEC
1971 fcntl(statusfds[0], F_SETFD, FD_CLOEXEC);
1972 fcntl(statusfds[1], F_SETFD, FD_CLOEXEC);
1973 #endif /* FD_CLOEXEC */
1974
1975 job->status_buffer = cupsdStatBufNew(statusfds[0], "[Job %d]",
1976 job->id);
1977 job->status = 0;
1978 memset(job->filters, 0, sizeof(job->filters));
1979
1980 filterfds[1][0] = open("/dev/null", O_RDONLY);
1981 filterfds[1][1] = -1;
1982
1983 if (filterfds[1][0] < 0)
1984 {
1985 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"/dev/null\" - %s.",
1986 strerror(errno));
1987 snprintf(printer->state_message, sizeof(printer->state_message),
1988 "Unable to open \"/dev/null\" - %s.", strerror(errno));
1989
1990 cupsdAddPrinterHistory(printer);
1991
1992 if (filters != NULL)
1993 free(filters);
1994
1995 cupsdClosePipe(statusfds);
1996 cupsdCancelJob(job, 0);
1997 return;
1998 }
1999
2000 fcntl(filterfds[1][0], F_SETFD, fcntl(filterfds[1][0], F_GETFD) | FD_CLOEXEC);
2001
2002 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2003 1, filterfds[1][0], filterfds[1][1]);
2004
2005 for (i = 0, slot = 0; i < num_filters; i ++)
2006 {
2007 if (filters[i].filter[0] != '/')
2008 snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
2009 filters[i].filter);
2010 else
2011 strlcpy(command, filters[i].filter, sizeof(command));
2012
2013 if (i < (num_filters - 1))
2014 {
2015 if (cupsdOpenPipe(filterfds[slot]))
2016 {
2017 cupsdLogMessage(CUPSD_LOG_ERROR,
2018 "Unable to create job filter pipes - %s.",
2019 strerror(errno));
2020 snprintf(printer->state_message, sizeof(printer->state_message),
2021 "Unable to create filter pipes - %s.", strerror(errno));
2022 cupsdAddPrinterHistory(printer);
2023
2024 if (filters != NULL)
2025 free(filters);
2026
2027 cupsdClosePipe(statusfds);
2028 cupsdClosePipe(filterfds[!slot]);
2029
2030 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2031 "Job canceled because the server could not create the filter pipes.");
2032
2033 cupsdCancelJob(job, 0);
2034 return;
2035 }
2036 }
2037 else
2038 {
2039 if (job->current_file == 1)
2040 {
2041 if (strncmp(printer->device_uri, "file:", 5) != 0)
2042 {
2043 if (cupsdOpenPipe(job->print_pipes))
2044 {
2045 cupsdLogMessage(CUPSD_LOG_ERROR,
2046 "Unable to create job backend pipes - %s.",
2047 strerror(errno));
2048 snprintf(printer->state_message, sizeof(printer->state_message),
2049 "Unable to create backend pipes - %s.", strerror(errno));
2050 cupsdAddPrinterHistory(printer);
2051
2052 if (filters != NULL)
2053 free(filters);
2054
2055 cupsdClosePipe(statusfds);
2056 cupsdClosePipe(filterfds[!slot]);
2057
2058 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2059 "Job canceled because the server could not create the backend pipes.");
2060
2061 cupsdCancelJob(job, 0);
2062 return;
2063 }
2064 }
2065 else
2066 {
2067 job->print_pipes[0] = -1;
2068 if (!strncmp(printer->device_uri, "file:/dev/", 10) &&
2069 strcmp(printer->device_uri, "file:/dev/null"))
2070 job->print_pipes[1] = open(printer->device_uri + 5,
2071 O_WRONLY | O_EXCL);
2072 else if (!strncmp(printer->device_uri, "file:///dev/", 12) &&
2073 strcmp(printer->device_uri, "file:///dev/null"))
2074 job->print_pipes[1] = open(printer->device_uri + 7,
2075 O_WRONLY | O_EXCL);
2076 else
2077 job->print_pipes[1] = open(printer->device_uri + 5,
2078 O_WRONLY | O_CREAT | O_TRUNC, 0600);
2079
2080 if (job->print_pipes[1] < 0)
2081 {
2082 cupsdLogMessage(CUPSD_LOG_ERROR,
2083 "Unable to open output file \"%s\" - %s.",
2084 printer->device_uri, strerror(errno));
2085 snprintf(printer->state_message, sizeof(printer->state_message),
2086 "Unable to open output file \"%s\" - %s.",
2087 printer->device_uri, strerror(errno));
2088
2089 cupsdAddPrinterHistory(printer);
2090
2091 if (filters != NULL)
2092 free(filters);
2093
2094 cupsdClosePipe(statusfds);
2095 cupsdClosePipe(filterfds[!slot]);
2096
2097 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2098 "Job canceled because the server could not open the output file.");
2099
2100 cupsdCancelJob(job, 0);
2101 return;
2102 }
2103
2104 fcntl(job->print_pipes[1], F_SETFD,
2105 fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
2106 }
2107
2108 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartJob: print_pipes = [ %d %d ]",
2109 job->print_pipes[0], job->print_pipes[1]);
2110 }
2111
2112 filterfds[slot][0] = job->print_pipes[0];
2113 filterfds[slot][1] = job->print_pipes[1];
2114 }
2115
2116 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filter = \"%s\"", command);
2117 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2118 slot, filterfds[slot][0], filterfds[slot][1]);
2119
2120 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
2121 filterfds[slot][1], statusfds[1],
2122 job->back_pipes[0], 0, job->filters + i);
2123
2124 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2125 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2126 !slot, filterfds[!slot][0], filterfds[!slot][1]);
2127
2128 cupsdClosePipe(filterfds[!slot]);
2129
2130 if (pid == 0)
2131 {
2132 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
2133 filters[i].filter, strerror(errno));
2134 snprintf(printer->state_message, sizeof(printer->state_message),
2135 "Unable to start filter \"%s\" - %s.",
2136 filters[i].filter, strerror(errno));
2137
2138 cupsdAddPrinterHistory(printer);
2139
2140 if (filters != NULL)
2141 free(filters);
2142
2143 cupsdAddPrinterHistory(printer);
2144
2145 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2146 "Job canceled because the server could not execute a filter.");
2147
2148 cupsdCancelJob(job, 0);
2149 return;
2150 }
2151
2152 cupsdLogMessage(CUPSD_LOG_INFO, "Started filter %s (PID %d) for job %d.",
2153 command, pid, job->id);
2154
2155 argv[6] = NULL;
2156 slot = !slot;
2157 }
2158
2159 if (filters != NULL)
2160 free(filters);
2161
2162 /*
2163 * Finally, pipe the final output into a backend process if needed...
2164 */
2165
2166 if (strncmp(printer->device_uri, "file:", 5) != 0)
2167 {
2168 if (job->current_file == 1)
2169 {
2170 sscanf(printer->device_uri, "%254[^:]", method);
2171 snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, method);
2172
2173 argv[0] = sani_uri;
2174
2175 filterfds[slot][0] = -1;
2176 filterfds[slot][1] = open("/dev/null", O_WRONLY);
2177
2178 if (filterfds[slot][1] < 0)
2179 {
2180 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"/dev/null\" - %s.",
2181 strerror(errno));
2182 snprintf(printer->state_message, sizeof(printer->state_message),
2183 "Unable to open \"/dev/null\" - %s.", strerror(errno));
2184
2185 cupsdAddPrinterHistory(printer);
2186
2187 if (filters != NULL)
2188 free(filters);
2189
2190 cupsdClosePipe(statusfds);
2191
2192 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2193 "Job canceled because the server could not open a file.");
2194
2195 cupsdCancelJob(job, 0);
2196 return;
2197 }
2198
2199 fcntl(filterfds[slot][1], F_SETFD,
2200 fcntl(filterfds[slot][1], F_GETFD) | FD_CLOEXEC);
2201
2202 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: backend = \"%s\"",
2203 command);
2204 cupsdLogMessage(CUPSD_LOG_DEBUG,
2205 "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2206 slot, filterfds[slot][0], filterfds[slot][1]);
2207
2208 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
2209 filterfds[slot][1], statusfds[1],
2210 job->back_pipes[1], 1,
2211 &(job->backend));
2212
2213 if (pid == 0)
2214 {
2215 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start backend \"%s\" - %s.",
2216 method, strerror(errno));
2217 snprintf(printer->state_message, sizeof(printer->state_message),
2218 "Unable to start backend \"%s\" - %s.", method, strerror(errno));
2219
2220 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2221 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2222 job->print_pipes[0], job->print_pipes[1]);
2223
2224 cupsdClosePipe(job->print_pipes);
2225
2226 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2227 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2228 job->back_pipes[0], job->back_pipes[1]);
2229
2230 cupsdClosePipe(job->back_pipes);
2231
2232 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2233 "Job canceled because the server could not execute the backend.");
2234
2235 cupsdCancelJob(job, 0);
2236 return;
2237 }
2238 else
2239 {
2240 cupsdLogMessage(CUPSD_LOG_INFO,
2241 "Started backend %s (PID %d) for job %d.",
2242 command, pid, job->id);
2243 }
2244 }
2245
2246 if (job->current_file == job->num_files)
2247 {
2248 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2249 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2250 job->print_pipes[0], job->print_pipes[1]);
2251
2252 cupsdClosePipe(job->print_pipes);
2253
2254 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2255 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2256 job->back_pipes[0], job->back_pipes[1]);
2257
2258 cupsdClosePipe(job->back_pipes);
2259 }
2260 }
2261 else
2262 {
2263 filterfds[slot][0] = -1;
2264 filterfds[slot][1] = -1;
2265
2266 if (job->current_file == job->num_files)
2267 {
2268 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2269 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2270 job->print_pipes[0], job->print_pipes[1]);
2271
2272 cupsdClosePipe(job->print_pipes);
2273 }
2274 }
2275
2276 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2277 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2278 slot, filterfds[slot][0], filterfds[slot][1]);
2279
2280 cupsdClosePipe(filterfds[slot]);
2281
2282 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2283 "cupsdStartJob: Closing status output pipe %d...",
2284 statusfds[1]);
2285
2286 close(statusfds[1]);
2287
2288 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2289 "cupsdStartJob: Adding fd %d to InputSet...",
2290 job->status_buffer->fd);
2291
2292 FD_SET(job->status_buffer->fd, InputSet);
2293 }
2294
2295
2296 /*
2297 * 'cupsdStopAllJobs()' - Stop all print jobs.
2298 */
2299
2300 void
2301 cupsdStopAllJobs(void)
2302 {
2303 cupsd_job_t *job; /* Current job */
2304
2305
2306 DEBUG_puts("cupsdStopAllJobs()");
2307
2308 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
2309 job;
2310 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
2311 if (job->state->values[0].integer == IPP_JOB_PROCESSING)
2312 {
2313 cupsdStopJob(job, 1);
2314 job->state->values[0].integer = IPP_JOB_PENDING;
2315 }
2316 }
2317
2318
2319 /*
2320 * 'cupsdStopJob()' - Stop a print job.
2321 */
2322
2323 void
2324 cupsdStopJob(cupsd_job_t *job, /* I - Job */
2325 int force) /* I - 1 = Force all filters to stop */
2326 {
2327 int i; /* Looping var */
2328
2329
2330 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopJob: id = %d, force = %d",
2331 job->id, force);
2332
2333 if (job->state->values[0].integer != IPP_JOB_PROCESSING)
2334 return;
2335
2336 FilterLevel -= job->cost;
2337
2338 if (job->status < 0 &&
2339 !(job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)) &&
2340 !(job->printer->type & CUPS_PRINTER_FAX) &&
2341 !strcmp(job->printer->error_policy, "stop-printer"))
2342 cupsdSetPrinterState(job->printer, IPP_PRINTER_STOPPED, 1);
2343 else if (job->printer->state != IPP_PRINTER_STOPPED)
2344 cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0);
2345
2346 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopJob: printer state is %d",
2347 job->printer->state);
2348
2349 job->state->values[0].integer = IPP_JOB_STOPPED;
2350 job->printer->job = NULL;
2351 job->printer = NULL;
2352
2353 job->current_file --;
2354
2355 for (i = 0; job->filters[i]; i ++)
2356 if (job->filters[i] > 0)
2357 {
2358 cupsdEndProcess(job->filters[i], force);
2359 job->filters[i] = 0;
2360 }
2361
2362 if (job->backend > 0)
2363 {
2364 cupsdEndProcess(job->backend, force);
2365 job->backend = 0;
2366 }
2367
2368 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2369 "cupsdStopJob: Closing print pipes [ %d %d ]...",
2370 job->print_pipes[0], job->print_pipes[1]);
2371
2372 cupsdClosePipe(job->print_pipes);
2373
2374 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2375 "cupsdStopJob: Closing back pipes [ %d %d ]...",
2376 job->back_pipes[0], job->back_pipes[1]);
2377
2378 cupsdClosePipe(job->back_pipes);
2379
2380 if (job->status_buffer)
2381 {
2382 /*
2383 * Close the pipe and clear the input bit.
2384 */
2385
2386 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2387 "cupsdStopJob: Removing fd %d from InputSet...",
2388 job->status_buffer->fd);
2389
2390 FD_CLR(job->status_buffer->fd, InputSet);
2391
2392 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2393 "cupsdStopJob: Closing status input pipe %d...",
2394 job->status_buffer->fd);
2395
2396 cupsdStatBufDelete(job->status_buffer);
2397
2398 job->status_buffer = NULL;
2399 }
2400 }
2401
2402
2403 /*
2404 * 'cupsdUpdateJob()' - Read a status update from a job's filters.
2405 */
2406
2407 void
2408 cupsdUpdateJob(cupsd_job_t *job) /* I - Job to check */
2409 {
2410 int i; /* Looping var */
2411 int copies; /* Number of copies printed */
2412 char message[1024], /* Message text */
2413 *ptr; /* Pointer update... */
2414 int loglevel; /* Log level for message */
2415
2416
2417 while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
2418 message, sizeof(message))) != NULL)
2419 {
2420 /*
2421 * Process page and printer state messages as needed...
2422 */
2423
2424 if (loglevel == CUPSD_LOG_PAGE)
2425 {
2426 /*
2427 * Page message; send the message to the page_log file and update the
2428 * job sheet count...
2429 */
2430
2431 if (job->sheets != NULL)
2432 {
2433 if (!strncasecmp(message, "total ", 6))
2434 {
2435 /*
2436 * Got a total count of pages from a backend or filter...
2437 */
2438
2439 copies = atoi(message + 6);
2440 copies -= job->sheets->values[0].integer; /* Just track the delta */
2441 }
2442 else if (!sscanf(message, "%*d%d", &copies))
2443 copies = 1;
2444
2445 job->sheets->values[0].integer += copies;
2446
2447 if (job->printer->page_limit)
2448 cupsdUpdateQuota(job->printer, job->username, copies, 0);
2449 }
2450
2451 cupsdLogPage(job, message);
2452
2453 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
2454 "Printed %d page(s).", job->sheets->values[0].integer);
2455 }
2456 else if (loglevel == CUPSD_LOG_STATE)
2457 cupsdSetPrinterReasons(job->printer, message);
2458 else if (loglevel == CUPSD_LOG_ATTR)
2459 {
2460 /*
2461 * Set attribute(s)...
2462 */
2463
2464 /**** TODO ****/
2465 }
2466
2467 if (!strchr(job->status_buffer->buffer, '\n'))
2468 break;
2469 }
2470
2471 if (ptr == NULL)
2472 {
2473 /*
2474 * See if all of the filters and the backend have returned their
2475 * exit statuses.
2476 */
2477
2478 for (i = 0; job->filters[i] < 0; i ++);
2479
2480 if (job->filters[i])
2481 return;
2482
2483 if (job->current_file >= job->num_files && job->backend > 0)
2484 return;
2485
2486 /*
2487 * Handle the end of job stuff...
2488 */
2489
2490 cupsdFinishJob(job);
2491 }
2492 }
2493
2494
2495 /*
2496 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
2497 */
2498
2499 static int /* O - Difference */
2500 compare_active_jobs(void *first, /* I - First job */
2501 void *second, /* I - Second job */
2502 void *data) /* I - App data (not used) */
2503 {
2504 int diff; /* Difference */
2505
2506
2507 if ((diff = ((cupsd_job_t *)first)->priority - ((cupsd_job_t *)second)->priority) != 0)
2508 return (diff);
2509 else
2510 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
2511 }
2512
2513
2514 /*
2515 * 'compare_jobs()' - Compare the job IDs of two jobs.
2516 */
2517
2518 static int /* O - Difference */
2519 compare_jobs(void *first, /* I - First job */
2520 void *second, /* I - Second job */
2521 void *data) /* I - App data (not used) */
2522 {
2523 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
2524 }
2525
2526
2527 /*
2528 * 'ipp_length()' - Compute the size of the buffer needed to hold
2529 * the textual IPP attributes.
2530 */
2531
2532 int /* O - Size of buffer to hold IPP attributes */
2533 ipp_length(ipp_t *ipp) /* I - IPP request */
2534 {
2535 int bytes; /* Number of bytes */
2536 int i; /* Looping var */
2537 ipp_attribute_t *attr; /* Current attribute */
2538
2539
2540 /*
2541 * Loop through all attributes...
2542 */
2543
2544 bytes = 0;
2545
2546 for (attr = ipp->attrs; attr != NULL; attr = attr->next)
2547 {
2548 /*
2549 * Skip attributes that won't be sent to filters...
2550 */
2551
2552 if (attr->value_tag == IPP_TAG_MIMETYPE ||
2553 attr->value_tag == IPP_TAG_NAMELANG ||
2554 attr->value_tag == IPP_TAG_TEXTLANG ||
2555 attr->value_tag == IPP_TAG_URI ||
2556 attr->value_tag == IPP_TAG_URISCHEME)
2557 continue;
2558
2559 if (strncmp(attr->name, "time-", 5) == 0)
2560 continue;
2561
2562 /*
2563 * Add space for a leading space and commas between each value.
2564 * For the first attribute, the leading space isn't used, so the
2565 * extra byte can be used as the nul terminator...
2566 */
2567
2568 bytes ++; /* " " separator */
2569 bytes += attr->num_values; /* "," separators */
2570
2571 /*
2572 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
2573 * other attributes appear as "foo=value1,value2,...,valueN".
2574 */
2575
2576 if (attr->value_tag != IPP_TAG_BOOLEAN)
2577 bytes += strlen(attr->name);
2578 else
2579 bytes += attr->num_values * strlen(attr->name);
2580
2581 /*
2582 * Now add the size required for each value in the attribute...
2583 */
2584
2585 switch (attr->value_tag)
2586 {
2587 case IPP_TAG_INTEGER :
2588 case IPP_TAG_ENUM :
2589 /*
2590 * Minimum value of a signed integer is -2147483647, or 11 digits.
2591 */
2592
2593 bytes += attr->num_values * 11;
2594 break;
2595
2596 case IPP_TAG_BOOLEAN :
2597 /*
2598 * Add two bytes for each false ("no") value...
2599 */
2600
2601 for (i = 0; i < attr->num_values; i ++)
2602 if (!attr->values[i].boolean)
2603 bytes += 2;
2604 break;
2605
2606 case IPP_TAG_RANGE :
2607 /*
2608 * A range is two signed integers separated by a hyphen, or
2609 * 23 characters max.
2610 */
2611
2612 bytes += attr->num_values * 23;
2613 break;
2614
2615 case IPP_TAG_RESOLUTION :
2616 /*
2617 * A resolution is two signed integers separated by an "x" and
2618 * suffixed by the units, or 26 characters max.
2619 */
2620
2621 bytes += attr->num_values * 26;
2622 break;
2623
2624 case IPP_TAG_STRING :
2625 case IPP_TAG_TEXT :
2626 case IPP_TAG_NAME :
2627 case IPP_TAG_KEYWORD :
2628 case IPP_TAG_CHARSET :
2629 case IPP_TAG_LANGUAGE :
2630 /*
2631 * Strings can contain characters that need quoting. We need
2632 * at least 2 * len + 2 characters to cover the quotes and
2633 * any backslashes in the string.
2634 */
2635
2636 for (i = 0; i < attr->num_values; i ++)
2637 bytes += 2 * strlen(attr->values[i].string.text) + 2;
2638 break;
2639
2640 default :
2641 break; /* anti-compiler-warning-code */
2642 }
2643 }
2644
2645 return (bytes);
2646 }
2647
2648
2649 /*
2650 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2651 */
2652
2653 static void
2654 set_time(cupsd_job_t *job, /* I - Job to update */
2655 const char *name) /* I - Name of attribute */
2656 {
2657 ipp_attribute_t *attr; /* Time attribute */
2658
2659
2660 if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL)
2661 {
2662 attr->value_tag = IPP_TAG_INTEGER;
2663 attr->values[0].integer = time(NULL);
2664 }
2665 }
2666
2667
2668 /*
2669 * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
2670 */
2671
2672 static void
2673 set_hold_until(cupsd_job_t *job, /* I - Job to update */
2674 time_t holdtime) /* I - Hold until time */
2675 {
2676 ipp_attribute_t *attr; /* job-hold-until attribute */
2677 struct tm *holddate; /* Hold date */
2678 char holdstr[64]; /* Hold time */
2679
2680
2681 /*
2682 * Set the hold_until value and hold the job...
2683 */
2684
2685 cupsdLogMessage(CUPSD_LOG_DEBUG, "set_hold_until: hold_until = %d", (int)holdtime);
2686
2687 job->state->values[0].integer = IPP_JOB_HELD;
2688 job->hold_until = holdtime;
2689
2690 /*
2691 * Update the job-hold-until attribute with a string representing GMT
2692 * time (HH:MM:SS)...
2693 */
2694
2695 holddate = gmtime(&holdtime);
2696 snprintf(holdstr, sizeof(holdstr), "%d:%d:%d", holddate->tm_hour,
2697 holddate->tm_min, holddate->tm_sec);
2698
2699 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
2700 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
2701
2702 /*
2703 * Either add the attribute or update the value of the existing one
2704 */
2705
2706 if (attr == NULL)
2707 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2708 "job-hold-until", NULL, holdstr);
2709 else
2710 cupsdSetString(&attr->values[0].string.text, holdstr);
2711
2712 cupsdSaveJob(job);
2713 }
2714
2715
2716 /*
2717 * End of "$Id: job.c 4906 2006-01-10 20:53:28Z mike $".
2718 */