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