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