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