]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/job.c
2e89397b6ea6cc0061e7295c843585e4dcf6207e
[thirdparty/cups.git] / scheduler / job.c
1 /*
2 * "$Id: job.c,v 1.124.2.14 2002/04/21 12:47:05 mike Exp $"
3 *
4 * Job management routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2002 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-3111 USA
19 *
20 * Voice: (301) 373-9603
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 on the given printer or class.
29 * CheckJobs() - Check the pending jobs and start any if the
30 * destination is available.
31 * CleanJobs() - Clean out old jobs.
32 * FindJob() - Find the specified job.
33 * GetPrinterJobCount() - Get the number of pending, processing,
34 * or held jobs in a printer or class.
35 * GetUserJobCount() - Get the number of pending, processing,
36 * or held jobs for a user.
37 * HoldJob() - Hold the specified job.
38 * LoadAllJobs() - Load all jobs from disk.
39 * LoadJob() - Load a job 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 * start_process() - Start a background process.
53 */
54
55 /*
56 * Include necessary headers...
57 */
58
59 #include "cupsd.h"
60 #include <grp.h>
61
62
63 /*
64 * Local functions...
65 */
66
67 static void set_time(job_t *job, const char *name);
68 static int start_process(const char *command, char *argv[],
69 char *envp[], int infd, int outfd,
70 int errfd, int backfd, int root);
71
72
73 /*
74 * 'AddJob()' - Add a new job to the job queue...
75 */
76
77 job_t * /* O - New job record */
78 AddJob(int priority, /* I - Job priority */
79 const char *dest) /* I - Job destination */
80 {
81 job_t *job, /* New job record */
82 *current, /* Current job in queue */
83 *prev; /* Previous job in queue */
84
85
86 job = calloc(sizeof(job_t), 1);
87
88 job->id = NextJobId ++;
89 job->priority = priority;
90 job->back_pipes[0] = -1;
91 job->back_pipes[1] = -1;
92 job->print_pipes[0] = -1;
93 job->print_pipes[1] = -1;
94 job->status_pipe = -1;
95
96 strncpy(job->dest, dest, sizeof(job->dest) - 1);
97
98 NumJobs ++;
99
100 for (current = Jobs, prev = NULL;
101 current != NULL;
102 prev = current, current = current->next)
103 if (job->priority > current->priority)
104 break;
105
106 job->next = current;
107 if (prev != NULL)
108 prev->next = job;
109 else
110 Jobs = job;
111
112 return (job);
113 }
114
115
116 /*
117 * 'CancelJob()' - Cancel the specified print job.
118 */
119
120 void
121 CancelJob(int id, /* I - Job to cancel */
122 int purge) /* I - Purge jobs? */
123 {
124 int i; /* Looping var */
125 job_t *current, /* Current job */
126 *prev; /* Previous job in list */
127 char filename[1024]; /* Job filename */
128
129
130 LogMessage(L_DEBUG, "CancelJob: id = %d", id);
131
132 for (current = Jobs, prev = NULL; current != NULL; prev = current, current = current->next)
133 if (current->id == id)
134 {
135 /*
136 * Stop any processes that are working on the current...
137 */
138
139 DEBUG_puts("CancelJob: found job in list.");
140
141 if (current->state->values[0].integer == IPP_JOB_PROCESSING)
142 StopJob(current->id, 0);
143
144 current->state->values[0].integer = IPP_JOB_CANCELLED;
145
146 set_time(current, "time-at-completed");
147
148 /*
149 * Remove the print file for good if we aren't preserving jobs or
150 * files...
151 */
152
153 current->current_file = 0;
154
155 if (!JobHistory || !JobFiles || purge ||
156 (current->dtype & CUPS_PRINTER_REMOTE))
157 for (i = 1; i <= current->num_files; i ++)
158 {
159 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
160 current->id, i);
161 unlink(filename);
162 }
163
164 if (JobHistory && !purge && !(current->dtype & CUPS_PRINTER_REMOTE))
165 {
166 /*
167 * Save job state info...
168 */
169
170 SaveJob(current->id);
171 }
172 else
173 {
174 /*
175 * Remove the job info file...
176 */
177
178 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot,
179 current->id);
180 unlink(filename);
181
182 /*
183 * Update pointers if we aren't preserving jobs...
184 */
185
186 if (prev == NULL)
187 Jobs = current->next;
188 else
189 prev->next = current->next;
190
191 /*
192 * Free all memory used...
193 */
194
195 if (current->attrs != NULL)
196 ippDelete(current->attrs);
197
198 free(current->filetypes);
199
200 free(current);
201
202 NumJobs --;
203 }
204
205 return;
206 }
207 }
208
209
210 /*
211 * 'CancelJobs()' - Cancel all jobs on the given printer or class.
212 */
213
214 void
215 CancelJobs(const char *dest) /* I - Destination to cancel */
216 {
217 job_t *current; /* Current job */
218
219
220 for (current = Jobs; current != NULL;)
221 if (strcmp(current->dest, dest) == 0)
222 {
223 /*
224 * Cancel all jobs matching this destination...
225 */
226
227 CancelJob(current->id, 1);
228
229 current = Jobs;
230 }
231 else
232 current = current->next;
233
234 CheckJobs();
235 }
236
237
238 /*
239 * 'CheckJobs()' - Check the pending jobs and start any if the destination
240 * is available.
241 */
242
243 void
244 CheckJobs(void)
245 {
246 job_t *current, /* Current job in queue */
247 *next; /* Next job in queue */
248 printer_t *printer, /* Printer destination */
249 *pclass; /* Printer class destination */
250
251
252 DEBUG_puts("CheckJobs()");
253
254 for (current = Jobs; current != NULL; current = next)
255 {
256 /*
257 * Save next pointer in case the job is cancelled en-route.
258 */
259
260 next = current->next;
261
262 /*
263 * Start held jobs if they are ready...
264 */
265
266 if (current->state->values[0].integer == IPP_JOB_HELD &&
267 current->hold_until &&
268 current->hold_until < time(NULL))
269 current->state->values[0].integer = IPP_JOB_PENDING;
270
271 /*
272 * Start pending jobs if the destination is available...
273 */
274
275 if (current->state->values[0].integer == IPP_JOB_PENDING)
276 {
277 if ((pclass = FindClass(current->dest)) != NULL)
278 {
279 /*
280 * If the class is remote, just pass it to the remote server...
281 */
282
283 if (pclass->type & CUPS_PRINTER_REMOTE)
284 printer = pclass;
285 else if (pclass->state != IPP_PRINTER_STOPPED)
286 printer = FindAvailablePrinter(current->dest);
287 else
288 printer = NULL;
289 }
290 else
291 printer = FindPrinter(current->dest);
292
293 if (printer != NULL && (printer->type & CUPS_PRINTER_IMPLICIT))
294 {
295 /*
296 * Handle implicit classes...
297 */
298
299 pclass = printer;
300
301 if (pclass->state != IPP_PRINTER_STOPPED)
302 printer = FindAvailablePrinter(current->dest);
303 else
304 printer = NULL;
305 }
306
307 if (printer == NULL && pclass == NULL)
308 {
309 /*
310 * Whoa, the printer and/or class for this destination went away;
311 * cancel the job...
312 */
313
314 LogMessage(L_WARN, "Printer/class %s has gone away; cancelling job %d!",
315 current->dest, current->id);
316 CancelJob(current->id, 1);
317 }
318 else if (printer != NULL)
319 {
320 /*
321 * See if the printer is available or remote and not printing a job;
322 * if so, start the job...
323 */
324
325 if (printer->state == IPP_PRINTER_IDLE || /* Printer is idle */
326 ((printer->type & CUPS_PRINTER_REMOTE) && /* Printer is remote */
327 !printer->job)) /* and not printing a job */
328 StartJob(current->id, printer);
329 }
330 }
331 }
332 }
333
334
335 /*
336 * 'CleanJobs()' - Clean out old jobs.
337 */
338
339 void
340 CleanJobs(void)
341 {
342 job_t *job, /* Current job */
343 *next; /* Next job */
344
345
346 if (MaxJobs == 0)
347 return;
348
349 for (job = Jobs; job && NumJobs >= MaxJobs; job = next)
350 {
351 next = job->next;
352
353 if (job->state->values[0].integer >= IPP_JOB_CANCELLED)
354 CancelJob(job->id, 1);
355 }
356 }
357
358
359 /*
360 * 'FindJob()' - Find the specified job.
361 */
362
363 job_t * /* O - Job data */
364 FindJob(int id) /* I - Job ID */
365 {
366 job_t *current; /* Current job */
367
368
369 for (current = Jobs; current != NULL; current = current->next)
370 if (current->id == id)
371 break;
372
373 return (current);
374 }
375
376
377 /*
378 * 'GetPrinterJobCount()' - Get the number of pending, processing,
379 * or held jobs in a printer or class.
380 */
381
382 int /* O - Job count */
383 GetPrinterJobCount(const char *dest) /* I - Printer or class name */
384 {
385 int count; /* Job count */
386 job_t *job; /* Current job */
387
388
389 for (job = Jobs, count = 0; job != NULL; job = job->next)
390 if (job->state->values[0].integer <= IPP_JOB_PROCESSING &&
391 strcasecmp(job->dest, dest) == 0)
392 count ++;
393
394 return (count);
395 }
396
397
398 /*
399 * 'GetUserJobCount()' - Get the number of pending, processing,
400 * or held jobs for a user.
401 */
402
403 int /* O - Job count */
404 GetUserJobCount(const char *username) /* I - Username */
405 {
406 int count; /* Job count */
407 job_t *job; /* Current job */
408
409
410 for (job = Jobs, count = 0; job != NULL; job = job->next)
411 if (job->state->values[0].integer <= IPP_JOB_PROCESSING &&
412 strcmp(job->username, username) == 0)
413 count ++;
414
415 return (count);
416 }
417
418
419 /*
420 * 'HoldJob()' - Hold the specified job.
421 */
422
423 void
424 HoldJob(int id) /* I - Job ID */
425 {
426 job_t *job; /* Job data */
427
428
429 LogMessage(L_DEBUG, "HoldJob: id = %d", id);
430
431 if ((job = FindJob(id)) == NULL)
432 return;
433
434 if (job->state->values[0].integer == IPP_JOB_PROCESSING)
435 StopJob(id, 0);
436
437 DEBUG_puts("HoldJob: setting state to held...");
438
439 job->state->values[0].integer = IPP_JOB_HELD;
440
441 SaveJob(id);
442
443 CheckJobs();
444 }
445
446
447 /*
448 * 'LoadAllJobs()' - Load all jobs from disk.
449 */
450
451 void
452 LoadAllJobs(void)
453 {
454 DIR *dir; /* Directory */
455 DIRENT *dent; /* Directory entry */
456 char filename[1024]; /* Full filename of job file */
457 int fd; /* File descriptor */
458 job_t *job, /* New job */
459 *current, /* Current job */
460 *prev; /* Previous job */
461 int jobid, /* Current job ID */
462 fileid; /* Current file ID */
463 ipp_attribute_t *attr; /* Job attribute */
464 char method[HTTP_MAX_URI],
465 /* Method portion of URI */
466 username[HTTP_MAX_URI],
467 /* Username portion of URI */
468 host[HTTP_MAX_URI],
469 /* Host portion of URI */
470 resource[HTTP_MAX_URI];
471 /* Resource portion of URI */
472 int port; /* Port portion of URI */
473 printer_t *p; /* Printer or class */
474 const char *dest; /* Destination */
475 mime_type_t **filetypes; /* New filetypes array */
476
477
478 /*
479 * First open the requests directory...
480 */
481
482 if ((dir = opendir(RequestRoot)) == NULL)
483 return;
484
485 /*
486 * Read all the c##### files...
487 */
488
489 while ((dent = readdir(dir)) != NULL)
490 if (NAMLEN(dent) == 6 && dent->d_name[0] == 'c')
491 {
492 /*
493 * Allocate memory for the job...
494 */
495
496 if ((job = calloc(sizeof(job_t), 1)) == NULL)
497 {
498 LogMessage(L_ERROR, "LoadAllJobs: Ran out of memory for jobs!");
499 closedir(dir);
500 return;
501 }
502
503 if ((job->attrs = ippNew()) == NULL)
504 {
505 free(job);
506 LogMessage(L_ERROR, "LoadAllJobs: Ran out of memory for job attributes!");
507 closedir(dir);
508 return;
509 }
510
511 /*
512 * Assign the job ID...
513 */
514
515 job->id = atoi(dent->d_name + 1);
516
517 if (job->id >= NextJobId)
518 NextJobId = job->id + 1;
519
520 /*
521 * Load the job control file...
522 */
523
524 snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->d_name);
525 if ((fd = open(filename, O_RDONLY)) < 0)
526 {
527 LogMessage(L_ERROR, "LoadAllJobs: Unable to open job control file \"%s\" - %s!",
528 filename, strerror(errno));
529 ippDelete(job->attrs);
530 free(job);
531 unlink(filename);
532 continue;
533 }
534 else
535 {
536 if (ippReadFile(fd, job->attrs) != IPP_DATA)
537 {
538 LogMessage(L_ERROR, "LoadAllJobs: Unable to read job control file \"%s\"!",
539 filename);
540 close(fd);
541 ippDelete(job->attrs);
542 free(job);
543 unlink(filename);
544 continue;
545 }
546
547 close(fd);
548 }
549
550 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", IPP_TAG_URI)) == NULL)
551 {
552 LogMessage(L_ERROR, "LoadAllJobs: No job-printer-uri attribute in control file \"%s\"!",
553 filename);
554 ippDelete(job->attrs);
555 free(job);
556 unlink(filename);
557 continue;
558 }
559
560 httpSeparate(attr->values[0].string.text, method, username, host,
561 &port, resource);
562
563 if ((dest = ValidateDest(host, resource, &(job->dtype))) == NULL)
564 {
565 /*
566 * Job queued on remote printer or class, so add it...
567 */
568
569 if (strncmp(resource, "/classes/", 9) == 0)
570 {
571 p = AddClass(resource + 9);
572 strcpy(p->make_model, "Remote Class on unknown");
573 }
574 else
575 {
576 p = AddPrinter(resource + 10);
577 strcpy(p->make_model, "Remote Printer on unknown");
578 }
579
580 p->state = IPP_PRINTER_STOPPED;
581 p->type |= CUPS_PRINTER_REMOTE;
582 p->browse_time = 2147483647;
583
584 strcpy(p->location, "Location Unknown");
585 strcpy(p->info, "No Information Available");
586 p->hostname[0] = '\0';
587
588 SetPrinterAttrs(p);
589 dest = p->name;
590 }
591
592 if (dest == NULL)
593 {
594 LogMessage(L_ERROR, "LoadAllJobs: Unable to queue job for destination \"%s\"!",
595 attr->values[0].string.text);
596 ippDelete(job->attrs);
597 free(job);
598 unlink(filename);
599 continue;
600 }
601
602 strncpy(job->dest, dest, sizeof(job->dest) - 1);
603
604 job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed",
605 IPP_TAG_INTEGER);
606 job->state = ippFindAttribute(job->attrs, "job-state", IPP_TAG_ENUM);
607 job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
608
609 attr = ippFindAttribute(job->attrs, "job-priority", IPP_TAG_INTEGER);
610 job->priority = attr->values[0].integer;
611
612 attr = ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME);
613 strncpy(job->title, attr->values[0].string.text,
614 sizeof(job->title) - 1);
615
616 attr = ippFindAttribute(job->attrs, "job-originating-user-name", IPP_TAG_NAME);
617 strncpy(job->username, attr->values[0].string.text,
618 sizeof(job->username) - 1);
619
620 if (job->state->values[0].integer == IPP_JOB_HELD)
621 {
622 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
623 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
624
625 if (attr == NULL)
626 job->state->values[0].integer = IPP_JOB_PENDING;
627 else
628 SetJobHoldUntil(job->id, attr->values[0].string.text);
629 }
630 else if (job->state->values[0].integer == IPP_JOB_PROCESSING)
631 job->state->values[0].integer = IPP_JOB_PENDING;
632
633 /*
634 * Insert the job into the array, sorting by job priority and ID...
635 */
636
637 for (current = Jobs, prev = NULL;
638 current != NULL;
639 prev = current, current = current->next)
640 if (job->priority > current->priority)
641 break;
642 else if (job->priority == current->priority && job->id < current->id)
643 break;
644
645 job->next = current;
646 if (prev != NULL)
647 prev->next = job;
648 else
649 Jobs = job;
650
651 NumJobs ++;
652 }
653
654 /*
655 * Read all the d##### files...
656 */
657
658 rewinddir(dir);
659
660 while ((dent = readdir(dir)) != NULL)
661 if (NAMLEN(dent) > 7 && dent->d_name[0] == 'd')
662 {
663 /*
664 * Find the job...
665 */
666
667 jobid = atoi(dent->d_name + 1);
668 fileid = atoi(dent->d_name + 7);
669
670 snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->d_name);
671
672 if ((job = FindJob(jobid)) == NULL)
673 {
674 LogMessage(L_ERROR, "LoadAllJobs: Orphaned print file \"%s\"!",
675 filename);
676 unlink(filename);
677 continue;
678 }
679
680 if (fileid > job->num_files)
681 {
682 if (job->num_files == 0)
683 filetypes = (mime_type_t **)calloc(sizeof(mime_type_t *), fileid);
684 else
685 filetypes = (mime_type_t **)realloc(job->filetypes,
686 sizeof(mime_type_t *) * fileid);
687
688 if (filetypes == NULL)
689 {
690 LogMessage(L_ERROR, "LoadAllJobs: Ran out of memory for job file types!");
691 continue;
692 }
693
694 job->filetypes = filetypes;
695 job->num_files = fileid;
696 }
697
698 job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, filename);
699
700 if (job->filetypes[fileid - 1] == NULL)
701 job->filetypes[fileid - 1] = mimeType(MimeDatabase, "application",
702 "vnd.cups-raw");
703 }
704
705 closedir(dir);
706
707 /*
708 * Clean out old jobs as needed...
709 */
710
711 CleanJobs();
712
713 /*
714 * Check to see if we need to start any jobs...
715 */
716
717 CheckJobs();
718 }
719
720
721 /*
722 * 'MoveJob()' - Move the specified job to a different destination.
723 */
724
725 void
726 MoveJob(int id, /* I - Job ID */
727 const char *dest) /* I - Destination */
728 {
729 job_t *current;/* Current job */
730 ipp_attribute_t *attr; /* job-printer-uri attribute */
731 printer_t *p; /* Destination printer or class */
732
733
734 if ((p = FindPrinter(dest)) == NULL)
735 p = FindClass(dest);
736
737 if (p == NULL)
738 return;
739
740 for (current = Jobs; current != NULL; current = current->next)
741 if (current->id == id)
742 {
743 if (current->state->values[0].integer >= IPP_JOB_PROCESSING)
744 break;
745
746 strncpy(current->dest, dest, sizeof(current->dest) - 1);
747 current->dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
748
749 if ((attr = ippFindAttribute(current->attrs, "job-printer-uri", IPP_TAG_URI)) != NULL)
750 {
751 free(attr->values[0].string.text);
752 attr->values[0].string.text = strdup(p->uri);
753 }
754
755 SaveJob(current->id);
756
757 return;
758 }
759 }
760
761
762 /*
763 * 'ReleaseJob()' - Release the specified job.
764 */
765
766 void
767 ReleaseJob(int id) /* I - Job ID */
768 {
769 job_t *job; /* Job data */
770
771
772 LogMessage(L_DEBUG, "ReleaseJob: id = %d", id);
773
774 if ((job = FindJob(id)) == NULL)
775 return;
776
777 if (job->state->values[0].integer == IPP_JOB_HELD)
778 {
779 DEBUG_puts("ReleaseJob: setting state to pending...");
780
781 job->state->values[0].integer = IPP_JOB_PENDING;
782 SaveJob(id);
783 CheckJobs();
784 }
785 }
786
787
788 /*
789 * 'RestartJob()' - Restart the specified job.
790 */
791
792 void
793 RestartJob(int id) /* I - Job ID */
794 {
795 job_t *job; /* Job data */
796
797
798 if ((job = FindJob(id)) == NULL)
799 return;
800
801 if (job->state->values[0].integer == IPP_JOB_STOPPED || JobFiles)
802 {
803 job->state->values[0].integer = IPP_JOB_PENDING;
804 SaveJob(id);
805 CheckJobs();
806 }
807 }
808
809
810 /*
811 * 'SaveJob()' - Save a job to disk.
812 */
813
814 void
815 SaveJob(int id) /* I - Job ID */
816 {
817 job_t *job; /* Pointer to job */
818 char filename[1024]; /* Job control filename */
819 int fd; /* File descriptor */
820
821
822 if ((job = FindJob(id)) == NULL)
823 return;
824
825 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, id);
826
827 if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
828 {
829 LogMessage(L_ERROR, "SaveJob: Unable to create job control file \"%s\" - %s.",
830 filename, strerror(errno));
831 return;
832 }
833
834 fchmod(fd, 0600);
835 fchown(fd, User, Group);
836
837 ippWriteFile(fd, job->attrs);
838
839 LogMessage(L_DEBUG2, "SaveJob: Closing file %d...", fd);
840
841 close(fd);
842 }
843
844
845 /*
846 * 'SetJobHoldUntil()' - Set the hold time for a job...
847 */
848
849 void
850 SetJobHoldUntil(int id, /* I - Job ID */
851 const char *when) /* I - When to resume */
852 {
853 job_t *job; /* Pointer to job */
854 time_t curtime; /* Current time */
855 struct tm *curdate; /* Current date */
856 int hour; /* Hold hour */
857 int minute; /* Hold minute */
858 int second; /* Hold second */
859
860
861 LogMessage(L_DEBUG, "SetJobHoldUntil(%d, \"%s\")", id, when);
862
863 if ((job = FindJob(id)) == NULL)
864 return;
865
866 second = 0;
867
868 if (strcmp(when, "indefinite") == 0)
869 {
870 /*
871 * Hold indefinitely...
872 */
873
874 job->hold_until = 0;
875 }
876 else if (strcmp(when, "day-time") == 0)
877 {
878 /*
879 * Hold to 6am the next morning unless local time is < 6pm.
880 */
881
882 curtime = time(NULL);
883 curdate = localtime(&curtime);
884
885 if (curdate->tm_hour < 18)
886 job->hold_until = curtime;
887 else
888 job->hold_until = curtime +
889 ((29 - curdate->tm_hour) * 60 + 59 -
890 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
891 }
892 else if (strcmp(when, "evening") == 0 || strcmp(when, "night") == 0)
893 {
894 /*
895 * Hold to 6pm unless local time is > 6pm or < 6am.
896 */
897
898 curtime = time(NULL);
899 curdate = localtime(&curtime);
900
901 if (curdate->tm_hour < 6 || curdate->tm_hour >= 18)
902 job->hold_until = curtime;
903 else
904 job->hold_until = curtime +
905 ((17 - curdate->tm_hour) * 60 + 59 -
906 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
907 }
908 else if (strcmp(when, "second-shift") == 0)
909 {
910 /*
911 * Hold to 4pm unless local time is > 4pm.
912 */
913
914 curtime = time(NULL);
915 curdate = localtime(&curtime);
916
917 if (curdate->tm_hour >= 16)
918 job->hold_until = curtime;
919 else
920 job->hold_until = curtime +
921 ((15 - curdate->tm_hour) * 60 + 59 -
922 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
923 }
924 else if (strcmp(when, "third-shift") == 0)
925 {
926 /*
927 * Hold to 12am unless local time is < 8am.
928 */
929
930 curtime = time(NULL);
931 curdate = localtime(&curtime);
932
933 if (curdate->tm_hour < 8)
934 job->hold_until = curtime;
935 else
936 job->hold_until = curtime +
937 ((23 - curdate->tm_hour) * 60 + 59 -
938 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
939 }
940 else if (strcmp(when, "weekend") == 0)
941 {
942 /*
943 * Hold to weekend unless we are in the weekend.
944 */
945
946 curtime = time(NULL);
947 curdate = localtime(&curtime);
948
949 if (curdate->tm_wday == 0 || curdate->tm_wday == 6)
950 job->hold_until = curtime;
951 else
952 job->hold_until = curtime +
953 (((5 - curdate->tm_wday) * 24 +
954 (17 - curdate->tm_hour)) * 60 + 59 -
955 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
956 }
957 else if (sscanf(when, "%d:%d:%d", &hour, &minute, &second) >= 2)
958 {
959 /*
960 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
961 */
962
963 curtime = time(NULL);
964 curdate = gmtime(&curtime);
965
966 job->hold_until = curtime +
967 ((hour - curdate->tm_hour) * 60 + minute -
968 curdate->tm_min) * 60 + second - curdate->tm_sec;
969
970 /*
971 * Hold until next day as needed...
972 */
973
974 if (job->hold_until < curtime)
975 job->hold_until += 24 * 60 * 60 * 60;
976 }
977
978 LogMessage(L_DEBUG, "SetJobHoldUntil: hold_until = %d", job->hold_until);
979 }
980
981
982 /*
983 * 'SetJobPriority()' - Set the priority of a job, moving it up/down in the
984 * list as needed.
985 */
986
987 void
988 SetJobPriority(int id, /* I - Job ID */
989 int priority) /* I - New priority (0 to 100) */
990 {
991 job_t *job, /* Job to change */
992 *current, /* Current job */
993 *prev; /* Previous job */
994 ipp_attribute_t *attr; /* Job attribute */
995
996
997 /*
998 * Find the job...
999 */
1000
1001 for (current = Jobs, prev = NULL;
1002 current != NULL;
1003 prev = current, current = current->next)
1004 if (current->id == id)
1005 break;
1006
1007 if (current == NULL)
1008 return;
1009
1010 /*
1011 * Set the new priority...
1012 */
1013
1014 job = current;
1015 job->priority = priority;
1016
1017 if ((attr = ippFindAttribute(job->attrs, "job-priority", IPP_TAG_INTEGER)) != NULL)
1018 attr->values[0].integer = priority;
1019 else
1020 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
1021 priority);
1022
1023 SaveJob(job->id);
1024
1025 /*
1026 * See if we need to do any sorting...
1027 */
1028
1029 if ((prev == NULL || job->priority < prev->priority) &&
1030 (job->next == NULL || job->next->priority < job->priority))
1031 return;
1032
1033 /*
1034 * Remove the job from the list, and then insert it where it belongs...
1035 */
1036
1037 if (prev == NULL)
1038 Jobs = job->next;
1039 else
1040 prev->next = job->next;
1041
1042 for (current = Jobs, prev = NULL;
1043 current != NULL;
1044 prev = current, current = current->next)
1045 if (job->priority > current->priority)
1046 break;
1047
1048 job->next = current;
1049 if (prev != NULL)
1050 prev->next = job;
1051 else
1052 Jobs = job;
1053 }
1054
1055
1056 /*
1057 * 'StartJob()' - Start a print job.
1058 */
1059
1060 void
1061 StartJob(int id, /* I - Job ID */
1062 printer_t *printer) /* I - Printer to print job */
1063 {
1064 job_t *current; /* Current job */
1065 int i; /* Looping var */
1066 int slot; /* Pipe slot */
1067 int num_filters; /* Number of filters for job */
1068 mime_filter_t *filters; /* Filters for job */
1069 char method[255], /* Method for output */
1070 *optptr; /* Pointer to options */
1071 ipp_attribute_t *attr; /* Current attribute */
1072 int pid; /* Process ID of new filter process */
1073 int banner_page; /* 1 if banner page, 0 otherwise */
1074 int statusfds[2], /* Pipes used between the filters and scheduler */
1075 filterfds[2][2];/* Pipes used between the filters */
1076 char *argv[8], /* Filter command-line arguments */
1077 filename[1024], /* Job filename */
1078 command[1024], /* Full path to filter/backend command */
1079 jobid[255], /* Job ID string */
1080 title[IPP_MAX_NAME],
1081 /* Job title string */
1082 copies[255], /* # copies string */
1083 options[16384], /* Full list of options */
1084 *envp[20], /* Environment variables */
1085 path[1024], /* PATH environment variable */
1086 language[255], /* LANG environment variable */
1087 charset[255], /* CHARSET environment variable */
1088 classification[1024], /* CLASSIFICATION environment variable */
1089 content_type[255],/* CONTENT_TYPE environment variable */
1090 device_uri[1024],/* DEVICE_URI environment variable */
1091 ppd[1024], /* PPD environment variable */
1092 printer_name[255],/* PRINTER environment variable */
1093 root[1024], /* CUPS_SERVERROOT environment variable */
1094 cache[255], /* RIP_MAX_CACHE environment variable */
1095 tmpdir[1024], /* TMPDIR environment variable */
1096 ldpath[1024], /* LD_LIBRARY_PATH environment variable */
1097 datadir[1024], /* CUPS_DATADIR environment variable */
1098 fontpath[1050]; /* CUPS_FONTPATH environment variable */
1099
1100
1101 LogMessage(L_DEBUG, "StartJob(%d, %08x)", id, printer);
1102
1103 for (current = Jobs; current != NULL; current = current->next)
1104 if (current->id == id)
1105 break;
1106
1107 if (current == NULL)
1108 return;
1109
1110 LogMessage(L_DEBUG, "StartJob() id = %d, file = %d/%d", id,
1111 current->current_file, current->num_files);
1112
1113 if (current->num_files == 0)
1114 {
1115 LogMessage(L_ERROR, "Job ID %d has no files! Cancelling it!", id);
1116 CancelJob(id, 0);
1117 return;
1118 }
1119
1120 /*
1121 * Figure out what filters are required to convert from
1122 * the source to the destination type...
1123 */
1124
1125 num_filters = 0;
1126 current->cost = 0;
1127
1128 if (printer->type & CUPS_PRINTER_REMOTE)
1129 {
1130 /*
1131 * Remote jobs go directly to the remote job...
1132 */
1133
1134 filters = NULL;
1135 }
1136 else
1137 {
1138 /*
1139 * Local jobs get filtered...
1140 */
1141
1142 filters = mimeFilter(MimeDatabase, current->filetypes[current->current_file],
1143 printer->filetype, &num_filters);
1144
1145 if (num_filters == 0)
1146 {
1147 LogMessage(L_ERROR, "Unable to convert file %d to printable format for job %d!",
1148 current->current_file, current->id);
1149 current->current_file ++;
1150
1151 if (current->current_file == current->num_files)
1152 CancelJob(current->id, 0);
1153
1154 return;
1155 }
1156
1157 /*
1158 * Remove NULL ("-") filters...
1159 */
1160
1161 for (i = 0; i < num_filters;)
1162 if (strcmp(filters[i].filter, "-") == 0)
1163 {
1164 num_filters --;
1165 if (i < num_filters)
1166 memcpy(filters + i, filters + i + 1,
1167 (num_filters - i) * sizeof(mime_filter_t));
1168 }
1169 else
1170 i ++;
1171
1172 if (num_filters == 0)
1173 {
1174 free(filters);
1175 filters = NULL;
1176 }
1177 else
1178 {
1179 /*
1180 * Compute filter cost...
1181 */
1182
1183 for (i = 0; i < num_filters; i ++)
1184 current->cost += filters[i].cost;
1185 }
1186 }
1187
1188 /*
1189 * See if the filter cost is too high...
1190 */
1191
1192 if ((FilterLevel + current->cost) > FilterLimit && FilterLevel > 0 &&
1193 FilterLimit > 0)
1194 {
1195 /*
1196 * Don't print this job quite yet...
1197 */
1198
1199 if (filters != NULL)
1200 free(filters);
1201
1202 LogMessage(L_INFO, "Holding job %d because filter limit has been reached.",
1203 id);
1204 LogMessage(L_DEBUG, "StartJob: id = %d, file = %d, "
1205 "cost = %d, level = %d, limit = %d",
1206 id, current->current_file, current->cost, FilterLevel,
1207 FilterLimit);
1208 return;
1209 }
1210
1211 FilterLevel += current->cost;
1212
1213 /*
1214 * Update the printer and job state to "processing"...
1215 */
1216
1217 current->state->values[0].integer = IPP_JOB_PROCESSING;
1218 current->status = 0;
1219 current->printer = printer;
1220 printer->job = current;
1221 SetPrinterState(printer, IPP_PRINTER_PROCESSING);
1222
1223 if (current->current_file == 0)
1224 {
1225 set_time(current, "time-at-processing");
1226 pipe(current->back_pipes);
1227 }
1228
1229 /*
1230 * Determine if we are printing a banner page or not...
1231 */
1232
1233 if (current->job_sheets == NULL)
1234 {
1235 LogMessage(L_DEBUG, "No job-sheets attribute.");
1236 if ((current->job_sheets =
1237 ippFindAttribute(current->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
1238 LogMessage(L_DEBUG, "... but someone added one without setting job_sheets!");
1239 }
1240 else if (current->job_sheets->num_values == 1)
1241 LogMessage(L_DEBUG, "job-sheets=%s",
1242 current->job_sheets->values[0].string.text);
1243 else
1244 LogMessage(L_DEBUG, "job-sheets=%s,%s",
1245 current->job_sheets->values[0].string.text,
1246 current->job_sheets->values[1].string.text);
1247
1248 if (printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
1249 banner_page = 0;
1250 else if (current->job_sheets == NULL)
1251 banner_page = 0;
1252 else if (strcasecmp(current->job_sheets->values[0].string.text, "none") != 0 &&
1253 current->current_file == 0)
1254 banner_page = 1;
1255 else if (current->job_sheets->num_values > 1 &&
1256 strcasecmp(current->job_sheets->values[1].string.text, "none") != 0 &&
1257 current->current_file == (current->num_files - 1))
1258 banner_page = 1;
1259 else
1260 banner_page = 0;
1261
1262 LogMessage(L_DEBUG, "banner_page = %d", banner_page);
1263
1264 /*
1265 * Building the options string is harder than it needs to be, but
1266 * for the moment we need to pass strings for command-line args and
1267 * not IPP attribute pointers... :)
1268 */
1269
1270 optptr = options;
1271 *optptr = '\0';
1272
1273 snprintf(title, sizeof(title), "%s-%d", printer->name, current->id);
1274 strcpy(copies, "1");
1275
1276 for (attr = current->attrs->attrs; attr != NULL; attr = attr->next)
1277 {
1278 if (strcmp(attr->name, "copies") == 0 &&
1279 attr->value_tag == IPP_TAG_INTEGER)
1280 {
1281 /*
1282 * Don't use the # copies attribute if we are printing the job sheets...
1283 */
1284
1285 if (!banner_page)
1286 sprintf(copies, "%d", attr->values[0].integer);
1287 }
1288 else if (strcmp(attr->name, "job-name") == 0 &&
1289 (attr->value_tag == IPP_TAG_NAME ||
1290 attr->value_tag == IPP_TAG_NAMELANG))
1291 {
1292 strncpy(title, attr->values[0].string.text, sizeof(title) - 1);
1293 title[sizeof(title) - 1] = '\0';
1294 }
1295 else if (attr->group_tag == IPP_TAG_JOB)
1296 {
1297 /*
1298 * Filter out other unwanted attributes...
1299 */
1300
1301 if (attr->value_tag == IPP_TAG_MIMETYPE ||
1302 attr->value_tag == IPP_TAG_NAMELANG ||
1303 attr->value_tag == IPP_TAG_TEXTLANG ||
1304 attr->value_tag == IPP_TAG_URI ||
1305 attr->value_tag == IPP_TAG_URISCHEME)
1306 continue;
1307
1308 if (strncmp(attr->name, "time-", 5) == 0)
1309 continue;
1310
1311 if (strncmp(attr->name, "job-", 4) == 0 &&
1312 !(printer->type & CUPS_PRINTER_REMOTE))
1313 continue;
1314
1315 if (strncmp(attr->name, "job-", 4) == 0 &&
1316 strcmp(attr->name, "job-billing") != 0 &&
1317 strcmp(attr->name, "job-sheets") != 0 &&
1318 strcmp(attr->name, "job-hold-until") != 0 &&
1319 strcmp(attr->name, "job-priority") != 0)
1320 continue;
1321
1322 if (strcmp(attr->name, "page-label") == 0 &&
1323 banner_page)
1324 continue;
1325
1326 /*
1327 * Otherwise add them to the list...
1328 */
1329
1330 if (optptr > options)
1331 strncat(optptr, " ", sizeof(options) - (optptr - options) - 1);
1332
1333 if (attr->value_tag != IPP_TAG_BOOLEAN)
1334 {
1335 strncat(optptr, attr->name, sizeof(options) - (optptr - options) - 1);
1336 strncat(optptr, "=", sizeof(options) - (optptr - options) - 1);
1337 }
1338
1339 for (i = 0; i < attr->num_values; i ++)
1340 {
1341 if (i)
1342 strncat(optptr, ",", sizeof(options) - (optptr - options) - 1);
1343
1344 optptr += strlen(optptr);
1345
1346 switch (attr->value_tag)
1347 {
1348 case IPP_TAG_INTEGER :
1349 case IPP_TAG_ENUM :
1350 snprintf(optptr, sizeof(options) - (optptr - options) - 1,
1351 "%d", attr->values[i].integer);
1352 break;
1353
1354 case IPP_TAG_BOOLEAN :
1355 if (!attr->values[i].boolean)
1356 strncat(optptr, "no", sizeof(options) - (optptr - options) - 1);
1357
1358 case IPP_TAG_NOVALUE :
1359 strncat(optptr, attr->name,
1360 sizeof(options) - (optptr - options) - 1);
1361 break;
1362
1363 case IPP_TAG_RANGE :
1364 if (attr->values[i].range.lower == attr->values[i].range.upper)
1365 snprintf(optptr, sizeof(options) - (optptr - options) - 1,
1366 "%d", attr->values[i].range.lower);
1367 else
1368 snprintf(optptr, sizeof(options) - (optptr - options) - 1,
1369 "%d-%d", attr->values[i].range.lower,
1370 attr->values[i].range.upper);
1371 break;
1372
1373 case IPP_TAG_RESOLUTION :
1374 snprintf(optptr, sizeof(options) - (optptr - options) - 1,
1375 "%dx%d%s", attr->values[i].resolution.xres,
1376 attr->values[i].resolution.yres,
1377 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
1378 "dpi" : "dpc");
1379 break;
1380
1381 case IPP_TAG_STRING :
1382 case IPP_TAG_TEXT :
1383 case IPP_TAG_NAME :
1384 case IPP_TAG_KEYWORD :
1385 case IPP_TAG_CHARSET :
1386 case IPP_TAG_LANGUAGE :
1387 if (strchr(attr->values[i].string.text, ' ') != NULL ||
1388 strchr(attr->values[i].string.text, '\t') != NULL ||
1389 strchr(attr->values[i].string.text, '\n') != NULL)
1390 {
1391 strncat(optptr, "\'", sizeof(options) - (optptr - options) - 1);
1392 strncat(optptr, attr->values[i].string.text,
1393 sizeof(options) - (optptr - options) - 1);
1394 strncat(optptr, "\'", sizeof(options) - (optptr - options) - 1);
1395 }
1396 else
1397 strncat(optptr, attr->values[i].string.text,
1398 sizeof(options) - (optptr - options) - 1);
1399 break;
1400
1401 default :
1402 break; /* anti-compiler-warning-code */
1403 }
1404 }
1405
1406 optptr += strlen(optptr);
1407 }
1408 }
1409
1410 /*
1411 * Build the command-line arguments for the filters. Each filter
1412 * has 6 or 7 arguments:
1413 *
1414 * argv[0] = printer
1415 * argv[1] = job ID
1416 * argv[2] = username
1417 * argv[3] = title
1418 * argv[4] = # copies
1419 * argv[5] = options
1420 * argv[6] = filename (optional; normally stdin)
1421 *
1422 * This allows legacy printer drivers that use the old System V
1423 * printing interface to be used by CUPS.
1424 */
1425
1426 sprintf(jobid, "%d", current->id);
1427 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
1428 current->id, current->current_file + 1);
1429
1430 argv[0] = printer->name;
1431 argv[1] = jobid;
1432 argv[2] = current->username;
1433 argv[3] = title;
1434 argv[4] = copies;
1435 argv[5] = options;
1436 argv[6] = filename;
1437 argv[7] = NULL;
1438
1439 LogMessage(L_DEBUG, "StartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1440 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
1441
1442 /*
1443 * Create environment variable strings for the filters...
1444 */
1445
1446 attr = ippFindAttribute(current->attrs, "attributes-natural-language",
1447 IPP_TAG_LANGUAGE);
1448
1449 switch (strlen(attr->values[0].string.text))
1450 {
1451 default :
1452 /*
1453 * This is an unknown or badly formatted language code; use
1454 * the POSIX locale...
1455 */
1456
1457 strcpy(language, "LANG=C");
1458 break;
1459
1460 case 2 :
1461 /*
1462 * Just the language code (ll)...
1463 */
1464
1465 snprintf(language, sizeof(language), "LANG=%s",
1466 attr->values[0].string.text);
1467 break;
1468
1469 case 5 :
1470 /*
1471 * Language and country code (ll-cc)...
1472 */
1473
1474 snprintf(language, sizeof(language), "LANG=%c%c_%c%c",
1475 attr->values[0].string.text[0],
1476 attr->values[0].string.text[1],
1477 toupper(attr->values[0].string.text[3]),
1478 toupper(attr->values[0].string.text[4]));
1479 break;
1480 }
1481
1482 attr = ippFindAttribute(current->attrs, "document-format",
1483 IPP_TAG_MIMETYPE);
1484 if (attr != NULL &&
1485 (optptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
1486 snprintf(charset, sizeof(charset), "CHARSET=%s", optptr + 8);
1487 else
1488 {
1489 attr = ippFindAttribute(current->attrs, "attributes-charset",
1490 IPP_TAG_CHARSET);
1491 snprintf(charset, sizeof(charset), "CHARSET=%s",
1492 attr->values[0].string.text);
1493 }
1494
1495 snprintf(path, sizeof(path), "PATH=%s/filter:/bin:/usr/bin", ServerBin);
1496 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
1497 current->filetypes[current->current_file]->super,
1498 current->filetypes[current->current_file]->type);
1499 snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s", printer->device_uri);
1500 snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot, printer->name);
1501 snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", printer->name);
1502 snprintf(cache, sizeof(cache), "RIP_MAX_CACHE=%s", RIPCache);
1503 snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot);
1504 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
1505 snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir);
1506 snprintf(fontpath, sizeof(fontpath), "CUPS_FONTPATH=%s", FontPath);
1507 if (Classification[0] && !banner_page)
1508 {
1509 if ((attr = ippFindAttribute(current->attrs, "job-sheets",
1510 IPP_TAG_NAME)) == NULL)
1511 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1512 Classification);
1513 else if (attr->num_values > 1 &&
1514 strcmp(attr->values[1].string.text, "none") != 0)
1515 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1516 attr->values[1].string.text);
1517 else
1518 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1519 attr->values[0].string.text);
1520 }
1521 else
1522 classification[0] = '\0';
1523 if (getenv("LD_LIBRARY_PATH") != NULL)
1524 snprintf(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
1525 else if (getenv("DYLD_LIBRARY_PATH") != NULL)
1526 snprintf(ldpath, sizeof(ldpath), "DYLD_LIBRARY_PATH=%s", getenv("DYLD_LIBRARY_PATH"));
1527 else
1528 ldpath[0] = '\0';
1529
1530 envp[0] = path;
1531 envp[1] = "SOFTWARE=CUPS/1.1";
1532 envp[2] = "USER=root";
1533 envp[3] = charset;
1534 envp[4] = language;
1535 envp[5] = TZ;
1536 envp[6] = ppd;
1537 envp[7] = root;
1538 envp[8] = cache;
1539 envp[9] = tmpdir;
1540 envp[10] = content_type;
1541 envp[11] = device_uri;
1542 envp[12] = printer_name;
1543 envp[13] = datadir;
1544 envp[14] = fontpath;
1545 envp[15] = ldpath;
1546 envp[16] = classification;
1547 envp[17] = NULL;
1548
1549 LogMessage(L_DEBUG, "StartJob: envp = \"%s\",\"%s\",\"%s\",\"%s\","
1550 "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\","
1551 "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1552 envp[0], envp[1], envp[2], envp[3], envp[4],
1553 envp[5], envp[6], envp[7], envp[8], envp[9],
1554 envp[10], envp[11], envp[12], envp[13], envp[14],
1555 envp[15], envp[16]);
1556
1557 current->current_file ++;
1558
1559 /*
1560 * Make sure we have a buffer to read status info into...
1561 */
1562
1563 if (current->buffer == NULL)
1564 {
1565 LogMessage(L_DEBUG2, "UpdateJob: Allocating status buffer...");
1566
1567 if ((current->buffer = malloc(JOB_BUFFER_SIZE)) == NULL)
1568 {
1569 LogMessage(L_EMERG, "Unable to allocate memory for job status buffer - %s",
1570 strerror(errno));
1571 CancelJob(current->id, 0);
1572 return;
1573 }
1574
1575 current->bufused = 0;
1576 }
1577
1578 /*
1579 * Now create processes for all of the filters...
1580 */
1581
1582 if (pipe(statusfds))
1583 {
1584 LogMessage(L_ERROR, "Unable to create job status pipes - %s.",
1585 strerror(errno));
1586 snprintf(printer->state_message, sizeof(printer->state_message),
1587 "Unable to create status pipes - %s.", strerror(errno));
1588 return;
1589 }
1590
1591 LogMessage(L_DEBUG, "StartJob: statusfds = [ %d %d ]",
1592 statusfds[0], statusfds[1]);
1593
1594 current->status_pipe = statusfds[0];
1595 current->status = 0;
1596 memset(current->filters, 0, sizeof(current->filters));
1597
1598 filterfds[1][0] = open("/dev/null", O_RDONLY);
1599 filterfds[1][1] = -1;
1600
1601 LogMessage(L_DEBUG, "StartJob: filterfds[%d] = [ %d %d ]", 1, filterfds[1][0],
1602 filterfds[1][1]);
1603
1604 for (i = 0, slot = 0; i < num_filters; i ++)
1605 {
1606 if (filters[i].filter[0] != '/')
1607 snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
1608 filters[i].filter);
1609 else
1610 {
1611 strncpy(command, filters[i].filter, sizeof(command) - 1);
1612 command[sizeof(command) - 1] = '\0';
1613 }
1614
1615 if (i < (num_filters - 1))
1616 pipe(filterfds[slot]);
1617 else
1618 {
1619 if (current->current_file == 1)
1620 {
1621 if (strncmp(printer->device_uri, "file:", 5) != 0)
1622 pipe(current->print_pipes);
1623 else
1624 {
1625 current->print_pipes[0] = -1;
1626 if (strncmp(printer->device_uri, "file:/dev/", 10) == 0)
1627 current->print_pipes[1] = open(printer->device_uri + 5,
1628 O_WRONLY | O_EXCL);
1629 else
1630 current->print_pipes[1] = open(printer->device_uri + 5,
1631 O_WRONLY | O_CREAT | O_TRUNC, 0600);
1632 }
1633
1634 LogMessage(L_DEBUG2, "StartJob: print_pipes = [ %d %d ]",
1635 current->print_pipes[0], current->print_pipes[1]);
1636 }
1637
1638 filterfds[slot][0] = current->print_pipes[0];
1639 filterfds[slot][1] = current->print_pipes[1];
1640 }
1641
1642 LogMessage(L_DEBUG, "StartJob: filter = \"%s\"", command);
1643 LogMessage(L_DEBUG, "StartJob: filterfds[%d] = [ %d %d ]",
1644 slot, filterfds[slot][0], filterfds[slot][1]);
1645
1646 pid = start_process(command, argv, envp, filterfds[!slot][0],
1647 filterfds[slot][1], statusfds[1],
1648 current->back_pipes[0], 0);
1649
1650 LogMessage(L_DEBUG2, "StartJob: Closing filter pipes for slot %d [ %d %d ]...",
1651 !slot, filterfds[!slot][0], filterfds[!slot][1]);
1652
1653 close(filterfds[!slot][0]);
1654 close(filterfds[!slot][1]);
1655
1656 if (pid == 0)
1657 {
1658 LogMessage(L_ERROR, "Unable to start filter \"%s\" - %s.",
1659 filters[i].filter, strerror(errno));
1660 snprintf(printer->state_message, sizeof(printer->state_message),
1661 "Unable to start filter \"%s\" - %s.",
1662 filters[i].filter, strerror(errno));
1663 return;
1664 }
1665
1666 current->filters[i] = pid;
1667
1668 LogMessage(L_INFO, "Started filter %s (PID %d) for job %d.",
1669 command, pid, current->id);
1670
1671 argv[6] = NULL;
1672 slot = !slot;
1673 }
1674
1675 if (filters != NULL)
1676 free(filters);
1677
1678 /*
1679 * Finally, pipe the final output into a backend process if needed...
1680 */
1681
1682 if (strncmp(printer->device_uri, "file:", 5) != 0)
1683 {
1684 if (current->current_file == 1)
1685 {
1686 sscanf(printer->device_uri, "%254[^:]", method);
1687 snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, method);
1688
1689 argv[0] = printer->device_uri;
1690
1691 filterfds[slot][0] = -1;
1692 filterfds[slot][1] = open("/dev/null", O_WRONLY);
1693
1694 LogMessage(L_DEBUG, "StartJob: backend = \"%s\"", command);
1695 LogMessage(L_DEBUG, "StartJob: filterfds[%d] = [ %d %d ]",
1696 slot, filterfds[slot][0], filterfds[slot][1]);
1697
1698 pid = start_process(command, argv, envp, filterfds[!slot][0],
1699 filterfds[slot][1], statusfds[1],
1700 current->back_pipes[1], 1);
1701
1702 if (pid == 0)
1703 {
1704 LogMessage(L_ERROR, "Unable to start backend \"%s\" - %s.",
1705 method, strerror(errno));
1706 snprintf(printer->state_message, sizeof(printer->state_message),
1707 "Unable to start backend \"%s\" - %s.", method, strerror(errno));
1708
1709 LogMessage(L_DEBUG2, "StartJob: Closing print pipes [ %d %d ]...",
1710 current->print_pipes[0], current->print_pipes[1]);
1711
1712 close(current->print_pipes[0]);
1713 close(current->print_pipes[1]);
1714
1715 current->print_pipes[0] = -1;
1716 current->print_pipes[1] = -1;
1717
1718 LogMessage(L_DEBUG2, "StartJob: Closing back pipes [ %d %d ]...",
1719 current->back_pipes[0], current->back_pipes[1]);
1720
1721 close(current->back_pipes[0]);
1722 close(current->back_pipes[1]);
1723
1724 current->back_pipes[0] = -1;
1725 current->back_pipes[1] = -1;
1726
1727 return;
1728 }
1729 else
1730 {
1731 current->backend = pid;
1732
1733 LogMessage(L_INFO, "Started backend %s (PID %d) for job %d.", command, pid,
1734 current->id);
1735 }
1736 }
1737
1738 if (current->current_file == current->num_files)
1739 {
1740 LogMessage(L_DEBUG2, "StartJob: Closing print pipes [ %d %d ]...",
1741 current->print_pipes[0], current->print_pipes[1]);
1742
1743 close(current->print_pipes[0]);
1744 close(current->print_pipes[1]);
1745
1746 current->print_pipes[0] = -1;
1747 current->print_pipes[1] = -1;
1748
1749 LogMessage(L_DEBUG2, "StartJob: Closing back pipes [ %d %d ]...",
1750 current->back_pipes[0], current->back_pipes[1]);
1751
1752 close(current->back_pipes[0]);
1753 close(current->back_pipes[1]);
1754
1755 current->back_pipes[0] = -1;
1756 current->back_pipes[1] = -1;
1757 }
1758 }
1759 else
1760 {
1761 filterfds[slot][0] = -1;
1762 filterfds[slot][1] = -1;
1763
1764 if (current->current_file == current->num_files)
1765 {
1766 LogMessage(L_DEBUG2, "StartJob: Closing print pipes [ %d %d ]...",
1767 current->print_pipes[0], current->print_pipes[1]);
1768
1769 close(current->print_pipes[0]);
1770 close(current->print_pipes[1]);
1771
1772 current->print_pipes[0] = -1;
1773 current->print_pipes[1] = -1;
1774 }
1775 }
1776
1777 LogMessage(L_DEBUG2, "StartJob: Closing filter pipes for slot %d [ %d %d ]...",
1778 slot, filterfds[slot][0], filterfds[slot][1]);
1779
1780 close(filterfds[slot][0]);
1781 close(filterfds[slot][1]);
1782
1783 LogMessage(L_DEBUG2, "StartJob: Closing status output pipe %d...",
1784 statusfds[1]);
1785
1786 close(statusfds[1]);
1787
1788 LogMessage(L_DEBUG2, "StartJob: Adding fd %d to InputSet...",
1789 current->status_pipe);
1790
1791 FD_SET(current->status_pipe, &InputSet);
1792 }
1793
1794
1795 /*
1796 * 'StopAllJobs()' - Stop all print jobs.
1797 */
1798
1799 void
1800 StopAllJobs(void)
1801 {
1802 job_t *current; /* Current job */
1803
1804
1805 DEBUG_puts("StopAllJobs()");
1806
1807 for (current = Jobs; current != NULL; current = current->next)
1808 if (current->state->values[0].integer == IPP_JOB_PROCESSING)
1809 {
1810 StopJob(current->id, 1);
1811 current->state->values[0].integer = IPP_JOB_PENDING;
1812 }
1813 }
1814
1815
1816 /*
1817 * 'StopJob()' - Stop a print job.
1818 */
1819
1820 void
1821 StopJob(int id, /* I - Job ID */
1822 int force) /* I - 1 = Force all filters to stop */
1823 {
1824 int i; /* Looping var */
1825 job_t *current; /* Current job */
1826
1827
1828 LogMessage(L_DEBUG, "StopJob: id = %d, force = %d", id, force);
1829
1830 for (current = Jobs; current != NULL; current = current->next)
1831 if (current->id == id)
1832 {
1833 DEBUG_puts("StopJob: found job in list.");
1834
1835 if (current->state->values[0].integer == IPP_JOB_PROCESSING)
1836 {
1837 DEBUG_puts("StopJob: job state is \'processing\'.");
1838
1839 FilterLevel -= current->cost;
1840
1841 if (current->status < 0)
1842 SetPrinterState(current->printer, IPP_PRINTER_STOPPED);
1843 else if (current->printer->state != IPP_PRINTER_STOPPED)
1844 SetPrinterState(current->printer, IPP_PRINTER_IDLE);
1845
1846 LogMessage(L_DEBUG, "StopJob: printer state is %d", current->printer->state);
1847
1848 current->state->values[0].integer = IPP_JOB_STOPPED;
1849 current->printer->job = NULL;
1850 current->printer = NULL;
1851
1852 current->current_file --;
1853
1854 for (i = 0; current->filters[i]; i ++)
1855 if (current->filters[i] > 0)
1856 {
1857 kill(current->filters[i], force ? SIGKILL : SIGTERM);
1858 current->filters[i] = 0;
1859 }
1860
1861 if (current->backend > 0)
1862 {
1863 kill(current->backend, force ? SIGKILL : SIGTERM);
1864 current->backend = 0;
1865 }
1866
1867 LogMessage(L_DEBUG2, "StopJob: Closing print pipes [ %d %d ]...",
1868 current->print_pipes[0], current->print_pipes[1]);
1869
1870 close(current->print_pipes[0]);
1871 close(current->print_pipes[1]);
1872
1873 current->print_pipes[0] = -1;
1874 current->print_pipes[1] = -1;
1875
1876 LogMessage(L_DEBUG2, "StopJob: Closing back pipes [ %d %d ]...",
1877 current->back_pipes[0], current->back_pipes[1]);
1878
1879 close(current->back_pipes[0]);
1880 close(current->back_pipes[1]);
1881
1882 current->back_pipes[0] = -1;
1883 current->back_pipes[1] = -1;
1884
1885 if (current->status_pipe >= 0)
1886 {
1887 /*
1888 * Close the pipe and clear the input bit.
1889 */
1890
1891 LogMessage(L_DEBUG2, "StopJob: Closing status input pipe %d...",
1892 current->status_pipe);
1893
1894 close(current->status_pipe);
1895
1896 LogMessage(L_DEBUG2, "StopJob: Removing fd %d from InputSet...",
1897 current->status_pipe);
1898
1899 FD_CLR(current->status_pipe, &InputSet);
1900 current->status_pipe = -1;
1901 }
1902
1903 if (current->buffer)
1904 {
1905 /*
1906 * Free the status buffer...
1907 */
1908
1909 LogMessage(L_DEBUG2, "StopJob: Freeing status buffer...");
1910
1911 free(current->buffer);
1912 current->buffer = NULL;
1913 current->bufused = 0;
1914 }
1915 }
1916 return;
1917 }
1918 }
1919
1920
1921 /*
1922 * 'UpdateJob()' - Read a status update from a job's filters.
1923 */
1924
1925 void
1926 UpdateJob(job_t *job) /* I - Job to check */
1927 {
1928 int bytes; /* Number of bytes read */
1929 int copies; /* Number of copies printed */
1930 char *lineptr, /* Pointer to end of line in buffer */
1931 *message; /* Pointer to message text */
1932 int loglevel; /* Log level for message */
1933
1934
1935 if ((bytes = read(job->status_pipe, job->buffer + job->bufused,
1936 JOB_BUFFER_SIZE - job->bufused - 1)) > 0)
1937 {
1938 job->bufused += bytes;
1939 job->buffer[job->bufused] = '\0';
1940 lineptr = strchr(job->buffer, '\n');
1941 }
1942 else if (bytes < 0 && errno == EINTR)
1943 return;
1944 else
1945 {
1946 lineptr = job->buffer + job->bufused;
1947 lineptr[1] = 0;
1948 }
1949
1950 if (job->bufused == 0 && bytes == 0)
1951 lineptr = NULL;
1952
1953 while (lineptr != NULL)
1954 {
1955 /*
1956 * Terminate each line and process it...
1957 */
1958
1959 *lineptr++ = '\0';
1960
1961 /*
1962 * Figure out the logging level...
1963 */
1964
1965 if (strncmp(job->buffer, "EMERG:", 6) == 0)
1966 {
1967 loglevel = L_EMERG;
1968 message = job->buffer + 6;
1969 }
1970 else if (strncmp(job->buffer, "ALERT:", 6) == 0)
1971 {
1972 loglevel = L_ALERT;
1973 message = job->buffer + 6;
1974 }
1975 else if (strncmp(job->buffer, "CRIT:", 5) == 0)
1976 {
1977 loglevel = L_CRIT;
1978 message = job->buffer + 5;
1979 }
1980 else if (strncmp(job->buffer, "ERROR:", 6) == 0)
1981 {
1982 loglevel = L_ERROR;
1983 message = job->buffer + 6;
1984 }
1985 else if (strncmp(job->buffer, "WARNING:", 8) == 0)
1986 {
1987 loglevel = L_WARN;
1988 message = job->buffer + 8;
1989 }
1990 else if (strncmp(job->buffer, "NOTICE:", 6) == 0)
1991 {
1992 loglevel = L_NOTICE;
1993 message = job->buffer + 6;
1994 }
1995 else if (strncmp(job->buffer, "INFO:", 5) == 0)
1996 {
1997 loglevel = L_INFO;
1998 message = job->buffer + 5;
1999 }
2000 else if (strncmp(job->buffer, "DEBUG:", 6) == 0)
2001 {
2002 loglevel = L_DEBUG;
2003 message = job->buffer + 6;
2004 }
2005 else if (strncmp(job->buffer, "DEBUG2:", 7) == 0)
2006 {
2007 loglevel = L_DEBUG2;
2008 message = job->buffer + 7;
2009 }
2010 else if (strncmp(job->buffer, "PAGE:", 5) == 0)
2011 {
2012 loglevel = L_PAGE;
2013 message = job->buffer + 5;
2014 }
2015 else
2016 {
2017 loglevel = L_DEBUG;
2018 message = job->buffer;
2019 }
2020
2021 /*
2022 * Skip leading whitespace in the message...
2023 */
2024
2025 while (isspace(*message))
2026 message ++;
2027
2028 /*
2029 * Send it to the log file and printer state message as needed...
2030 */
2031
2032 if (loglevel == L_PAGE)
2033 {
2034 /*
2035 * Page message; send the message to the page_log file and update the
2036 * job sheet count...
2037 */
2038
2039 if (job->sheets != NULL)
2040 {
2041 if (!sscanf(message, "%*d%d", &copies))
2042 {
2043 job->sheets->values[0].integer ++;
2044
2045 if (job->printer->page_limit)
2046 UpdateQuota(job->printer, job->username, 1, 0);
2047 }
2048 else
2049 {
2050 job->sheets->values[0].integer += copies;
2051
2052 if (job->printer->page_limit)
2053 UpdateQuota(job->printer, job->username, copies, 0);
2054 }
2055 }
2056
2057 LogPage(job, message);
2058 }
2059 else
2060 {
2061 /*
2062 * Other status message; send it to the error_log file...
2063 */
2064
2065 if (loglevel != L_INFO)
2066 LogMessage(loglevel, "%s", message);
2067
2068 if ((loglevel == L_INFO && !job->status) ||
2069 loglevel < L_INFO)
2070 strncpy(job->printer->state_message, message,
2071 sizeof(job->printer->state_message) - 1);
2072 }
2073
2074 /*
2075 * Copy over the buffer data we've used up...
2076 */
2077
2078 strcpy(job->buffer, lineptr);
2079 job->bufused -= lineptr - job->buffer;
2080
2081 if (job->bufused < 0)
2082 job->bufused = 0;
2083
2084 lineptr = strchr(job->buffer, '\n');
2085 }
2086
2087 if (bytes <= 0)
2088 {
2089 LogMessage(L_DEBUG, "UpdateJob: job %d, file %d is complete.",
2090 job->id, job->current_file - 1);
2091
2092 if (job->status_pipe >= 0)
2093 {
2094 /*
2095 * Close the pipe and clear the input bit.
2096 */
2097
2098 LogMessage(L_DEBUG2, "UpdateJob: Closing status input pipe %d...",
2099 job->status_pipe);
2100
2101 close(job->status_pipe);
2102
2103 LogMessage(L_DEBUG2, "UpdateJob: Removing fd %d from InputSet...",
2104 job->status_pipe);
2105
2106 FD_CLR(job->status_pipe, &InputSet);
2107 job->status_pipe = -1;
2108 }
2109
2110 if (job->status < 0)
2111 {
2112 /*
2113 * Backend had errors; stop it...
2114 */
2115
2116 StopJob(job->id, 0);
2117 job->state->values[0].integer = IPP_JOB_PENDING;
2118 SaveJob(job->id);
2119 }
2120 else if (job->status > 0)
2121 {
2122 /*
2123 * Filter had errors; cancel it...
2124 */
2125
2126 if (job->current_file < job->num_files)
2127 StartJob(job->id, job->printer);
2128 else
2129 {
2130 CancelJob(job->id, 0);
2131
2132 if (JobHistory)
2133 {
2134 job->state->values[0].integer = IPP_JOB_ABORTED;
2135 SaveJob(job->id);
2136 }
2137
2138 CheckJobs();
2139 }
2140 }
2141 else
2142 {
2143 /*
2144 * Job printed successfully; cancel it...
2145 */
2146
2147 if (job->current_file < job->num_files)
2148 {
2149 FilterLevel -= job->cost;
2150 StartJob(job->id, job->printer);
2151 }
2152 else
2153 {
2154 CancelJob(job->id, 0);
2155
2156 if (JobHistory)
2157 {
2158 job->state->values[0].integer = IPP_JOB_COMPLETED;
2159 SaveJob(job->id);
2160 }
2161
2162 CheckJobs();
2163 }
2164 }
2165 }
2166 }
2167
2168
2169 /*
2170 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2171 */
2172
2173 static void
2174 set_time(job_t *job, /* I - Job to update */
2175 const char *name) /* I - Name of attribute */
2176 {
2177 ipp_attribute_t *attr; /* Time attribute */
2178
2179
2180 if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL)
2181 {
2182 attr->value_tag = IPP_TAG_INTEGER;
2183 attr->values[0].integer = time(NULL);
2184 }
2185 }
2186
2187
2188 /*
2189 * 'start_process()' - Start a background process.
2190 */
2191
2192 static int /* O - Process ID or 0 */
2193 start_process(const char *command, /* I - Full path to command */
2194 char *argv[], /* I - Command-line arguments */
2195 char *envp[], /* I - Environment */
2196 int infd, /* I - Standard input file descriptor */
2197 int outfd, /* I - Standard output file descriptor */
2198 int errfd, /* I - Standard error file descriptor */
2199 int backfd, /* I - Backchannel file descriptor */
2200 int root) /* I - Run as root? */
2201 {
2202 int fd; /* Looping var */
2203 int pid; /* Process ID */
2204
2205
2206 LogMessage(L_DEBUG, "start_process(\"%s\", %08x, %08x, %d, %d, %d)",
2207 command, argv, envp, infd, outfd, errfd);
2208
2209 if ((pid = fork()) == 0)
2210 {
2211 /*
2212 * Child process goes here...
2213 *
2214 * Update stdin/stdout/stderr as needed...
2215 */
2216
2217 close(0);
2218 dup(infd);
2219 close(1);
2220 dup(outfd);
2221 if (errfd > 2)
2222 {
2223 close(2);
2224 dup(errfd);
2225 }
2226 if (backfd > 3)
2227 {
2228 close(3);
2229 dup(backfd);
2230 fcntl(3, F_SETFL, O_NDELAY);
2231 }
2232
2233 /*
2234 * Close extra file descriptors...
2235 */
2236
2237 for (fd = 4; fd < MaxFDs; fd ++)
2238 close(fd);
2239
2240 /*
2241 * Change user to something "safe"...
2242 */
2243
2244 if (!root && getuid() == 0)
2245 {
2246 /*
2247 * Running as root, so change to non-priviledged user...
2248 */
2249
2250 if (setgid(Group))
2251 exit(errno);
2252
2253 if (setuid(User))
2254 exit(errno);
2255 }
2256
2257 /*
2258 * Reset group membership to just the main one we belong to.
2259 */
2260
2261 setgroups(0, NULL);
2262
2263 /*
2264 * Change umask to restrict permissions on created files...
2265 */
2266
2267 umask(077);
2268
2269 /*
2270 * Execute the command; if for some reason this doesn't work,
2271 * return the error code...
2272 */
2273
2274 execve(command, argv, envp);
2275
2276 perror(command);
2277
2278 exit(errno);
2279 }
2280 else if (pid < 0)
2281 {
2282 /*
2283 * Error - couldn't fork a new process!
2284 */
2285
2286 LogMessage(L_ERROR, "Unable to fork %s - %s.", command, strerror(errno));
2287
2288 return (0);
2289 }
2290
2291 return (pid);
2292 }
2293
2294
2295 /*
2296 * End of "$Id: job.c,v 1.124.2.14 2002/04/21 12:47:05 mike Exp $".
2297 */