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