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