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