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