2 * "$Id: job.c,v 1.139 2001/11/09 17:19:43 mike Exp $"
4 * Job management routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2001 by Easy Software Products, all rights reserved.
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
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
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
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.
58 * Include necessary headers...
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
,
78 * 'AddJob()' - Add a new job to the job queue...
81 job_t
* /* O - New job record */
82 AddJob(int priority
, /* I - Job priority */
83 const char *dest
) /* I - Job destination */
85 job_t
*job
, /* New job record */
86 *current
, /* Current job in queue */
87 *prev
; /* Previous job in queue */
90 job
= calloc(sizeof(job_t
), 1);
92 job
->id
= NextJobId
++;
93 job
->priority
= priority
;
94 strncpy(job
->dest
, dest
, sizeof(job
->dest
) - 1);
98 for (current
= Jobs
, prev
= NULL
;
100 prev
= current
, current
= current
->next
)
101 if (job
->priority
> current
->priority
)
115 * 'CancelJob()' - Cancel the specified print job.
119 CancelJob(int id
, /* I - Job to cancel */
120 int purge
) /* I - Purge jobs? */
122 int i
; /* Looping var */
123 job_t
*current
, /* Current job */
124 *prev
; /* Previous job in list */
125 char filename
[1024]; /* Job filename */
128 LogMessage(L_DEBUG
, "CancelJob: id = %d", id
);
130 for (current
= Jobs
, prev
= NULL
; current
!= NULL
; prev
= current
, current
= current
->next
)
131 if (current
->id
== id
)
134 * Stop any processes that are working on the current...
137 DEBUG_puts("CancelJob: found job in list.");
139 if (current
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
140 StopJob(current
->id
);
142 current
->state
->values
[0].integer
= IPP_JOB_CANCELLED
;
144 set_time(current
, "time-at-completed");
147 * Remove the print file for good if we aren't preserving jobs or
151 current
->current_file
= 0;
153 if (!JobHistory
|| !JobFiles
|| purge
)
154 for (i
= 1; i
<= current
->num_files
; i
++)
156 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
161 if (JobHistory
&& !purge
)
164 * Save job state info...
167 SaveJob(current
->id
);
172 * Remove the job info file...
175 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
,
180 * Update pointers if we aren't preserving jobs...
184 Jobs
= current
->next
;
186 prev
->next
= current
->next
;
189 * Free all memory used...
192 if (current
->attrs
!= NULL
)
193 ippDelete(current
->attrs
);
195 free(current
->filetypes
);
208 * 'CancelJobs()' - Cancel all jobs on the given printer or class.
212 CancelJobs(const char *dest
) /* I - Destination to cancel */
214 job_t
*current
; /* Current job */
217 for (current
= Jobs
; current
!= NULL
;)
218 if (strcmp(current
->dest
, dest
) == 0)
221 * Cancel all jobs matching this destination...
224 CancelJob(current
->id
, 1);
229 current
= current
->next
;
236 * 'CheckJobs()' - Check the pending jobs and start any if the destination
243 job_t
*current
, /* Current job in queue */
244 *next
; /* Next job in queue */
245 printer_t
*printer
, /* Printer destination */
246 *pclass
; /* Printer class destination */
249 DEBUG_puts("CheckJobs()");
251 for (current
= Jobs
; current
!= NULL
; current
= next
)
254 * Save next pointer in case the job is cancelled en-route.
257 next
= current
->next
;
260 * Start held jobs if they are ready...
263 if (current
->state
->values
[0].integer
== IPP_JOB_HELD
&&
264 current
->hold_until
&&
265 current
->hold_until
< time(NULL
))
266 current
->state
->values
[0].integer
= IPP_JOB_PENDING
;
269 * Start pending jobs if the destination is available...
272 if (current
->state
->values
[0].integer
== IPP_JOB_PENDING
)
274 if ((pclass
= FindClass(current
->dest
)) != NULL
)
277 * If the class is remote, just pass it to the remote server...
280 if (pclass
->type
& CUPS_PRINTER_REMOTE
)
282 else if (pclass
->state
!= IPP_PRINTER_STOPPED
)
283 printer
= FindAvailablePrinter(current
->dest
);
288 printer
= FindPrinter(current
->dest
);
290 if (printer
!= NULL
&& (printer
->type
& CUPS_PRINTER_IMPLICIT
))
293 * Handle implicit classes...
298 if (pclass
->state
!= IPP_PRINTER_STOPPED
)
299 printer
= FindAvailablePrinter(current
->dest
);
304 if (printer
== NULL
&& pclass
== NULL
)
307 * Whoa, the printer and/or class for this destination went away;
311 LogMessage(L_WARN
, "Printer/class %s has gone away; cancelling job %d!",
312 current
->dest
, current
->id
);
313 CancelJob(current
->id
, 1);
315 else if (printer
!= NULL
)
318 * See if the printer is available or remote and not printing a job;
319 * if so, start the job...
322 if (printer
->state
== IPP_PRINTER_IDLE
|| /* Printer is idle */
323 ((printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is remote */
324 !printer
->job
)) /* and not printing a job */
325 StartJob(current
->id
, printer
);
333 * 'CleanJobs()' - Clean out old jobs.
339 job_t
*job
, /* Current job */
340 *next
; /* Next job */
346 for (job
= Jobs
; job
&& NumJobs
>= MaxJobs
; job
= next
)
350 if (job
->state
->values
[0].integer
>= IPP_JOB_CANCELLED
)
351 CancelJob(job
->id
, 1);
357 * 'FindJob()' - Find the specified job.
360 job_t
* /* O - Job data */
361 FindJob(int id
) /* I - Job ID */
363 job_t
*current
; /* Current job */
366 for (current
= Jobs
; current
!= NULL
; current
= current
->next
)
367 if (current
->id
== id
)
375 * 'GetPrinterJobCount()' - Get the number of pending, processing,
376 * or held jobs in a printer or class.
379 int /* O - Job count */
380 GetPrinterJobCount(const char *dest
) /* I - Printer or class name */
382 int count
; /* Job count */
383 job_t
*job
; /* Current job */
386 for (job
= Jobs
, count
= 0; job
!= NULL
; job
= job
->next
)
387 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
&&
388 strcasecmp(job
->dest
, dest
) == 0)
396 * 'GetUserJobCount()' - Get the number of pending, processing,
397 * or held jobs for a user.
400 int /* O - Job count */
401 GetUserJobCount(const char *username
) /* I - Username */
403 int count
; /* Job count */
404 job_t
*job
; /* Current job */
407 for (job
= Jobs
, count
= 0; job
!= NULL
; job
= job
->next
)
408 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
&&
409 strcmp(job
->username
, username
) == 0)
417 * 'HoldJob()' - Hold the specified job.
421 HoldJob(int id
) /* I - Job ID */
423 job_t
*job
; /* Job data */
426 LogMessage(L_DEBUG
, "HoldJob: id = %d", id
);
428 if ((job
= FindJob(id
)) == NULL
)
431 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
434 DEBUG_puts("HoldJob: setting state to held...");
436 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
445 * 'LoadAllJobs()' - Load all jobs from disk.
451 DIR *dir
; /* Directory */
452 DIRENT
*dent
; /* Directory entry */
453 char filename
[1024]; /* Full filename of job file */
454 job_t
*job
, /* New job */
455 *current
, /* Current job */
456 *prev
; /* Previous job */
457 int jobid
, /* Current job ID */
458 fileid
; /* Current file ID */
459 ipp_attribute_t
*attr
; /* Job attribute */
460 char method
[HTTP_MAX_URI
],
461 /* Method portion of URI */
462 username
[HTTP_MAX_URI
],
463 /* Username portion of URI */
465 /* Host portion of URI */
466 resource
[HTTP_MAX_URI
];
467 /* Resource portion of URI */
468 int port
; /* Port portion of URI */
469 printer_t
*p
; /* Printer or class */
470 const char *dest
; /* Destination */
471 mime_type_t
**filetypes
; /* New filetypes array */
475 * First open the requests directory...
478 if ((dir
= opendir(RequestRoot
)) == NULL
)
482 * Read all the c##### files...
485 while ((dent
= readdir(dir
)) != NULL
)
486 if (NAMLEN(dent
) == 6 && dent
->d_name
[0] == 'c')
489 * Allocate memory for the job...
492 if ((job
= calloc(sizeof(job_t
), 1)) == NULL
)
494 LogMessage(L_ERROR
, "LoadAllJobs: Ran out of memory for jobs!");
499 if ((job
->attrs
= ippNew()) == NULL
)
502 LogMessage(L_ERROR
, "LoadAllJobs: Ran out of memory for job attributes!");
508 * Assign the job ID...
511 job
->id
= atoi(dent
->d_name
+ 1);
513 if (job
->id
>= NextJobId
)
514 NextJobId
= job
->id
+ 1;
517 * Load the job control file...
520 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->d_name
);
521 if (ipp_read_file(filename
, job
->attrs
) != IPP_DATA
)
523 LogMessage(L_ERROR
, "LoadAllJobs: Unable to read job control file \"%s\"!",
525 ippDelete(job
->attrs
);
531 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri", IPP_TAG_URI
)) == NULL
)
533 LogMessage(L_ERROR
, "LoadAllJobs: No job-printer-uri attribute in control file \"%s\"!",
535 ippDelete(job
->attrs
);
541 httpSeparate(attr
->values
[0].string
.text
, method
, username
, host
,
544 if ((dest
= ValidateDest(host
, resource
, &(job
->dtype
))) == NULL
)
547 * Job queued on remote printer or class, so add it...
550 if (strncmp(resource
, "/classes/", 9) == 0)
552 p
= AddClass(resource
+ 9);
553 strcpy(p
->make_model
, "Remote Class on unknown");
557 p
= AddPrinter(resource
+ 10);
558 strcpy(p
->make_model
, "Remote Printer on unknown");
561 p
->state
= IPP_PRINTER_STOPPED
;
562 p
->type
|= CUPS_PRINTER_REMOTE
;
563 p
->browse_time
= 2147483647;
565 strcpy(p
->location
, "Location Unknown");
566 strcpy(p
->info
, "No Information Available");
567 p
->hostname
[0] = '\0';
575 LogMessage(L_ERROR
, "LoadAllJobs: Unable to queue job for destination \"%s\"!",
576 attr
->values
[0].string
.text
);
577 ippDelete(job
->attrs
);
583 strncpy(job
->dest
, dest
, sizeof(job
->dest
) - 1);
585 job
->sheets
= ippFindAttribute(job
->attrs
, "job-media-sheets-completed",
587 job
->state
= ippFindAttribute(job
->attrs
, "job-state", IPP_TAG_ENUM
);
588 job
->job_sheets
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
590 attr
= ippFindAttribute(job
->attrs
, "job-priority", IPP_TAG_INTEGER
);
591 job
->priority
= attr
->values
[0].integer
;
593 attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
);
594 strncpy(job
->title
, attr
->values
[0].string
.text
,
595 sizeof(job
->title
) - 1);
597 attr
= ippFindAttribute(job
->attrs
, "job-originating-user-name", IPP_TAG_NAME
);
598 strncpy(job
->username
, attr
->values
[0].string
.text
,
599 sizeof(job
->username
) - 1);
601 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
603 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
604 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
607 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
609 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
611 else if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
612 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
615 * Insert the job into the array, sorting by job priority and ID...
618 for (current
= Jobs
, prev
= NULL
;
620 prev
= current
, current
= current
->next
)
621 if (job
->priority
> current
->priority
)
623 else if (job
->priority
== current
->priority
&& job
->id
< current
->id
)
636 * Read all the d##### files...
641 while ((dent
= readdir(dir
)) != NULL
)
642 if (NAMLEN(dent
) > 7 && dent
->d_name
[0] == 'd')
648 jobid
= atoi(dent
->d_name
+ 1);
649 fileid
= atoi(dent
->d_name
+ 7);
651 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->d_name
);
653 if ((job
= FindJob(jobid
)) == NULL
)
655 LogMessage(L_ERROR
, "LoadAllJobs: Orphaned print file \"%s\"!",
661 if (fileid
> job
->num_files
)
663 if (job
->num_files
== 0)
664 filetypes
= (mime_type_t
**)calloc(sizeof(mime_type_t
*), fileid
);
666 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
667 sizeof(mime_type_t
*) * fileid
);
669 if (filetypes
== NULL
)
671 LogMessage(L_ERROR
, "LoadAllJobs: Ran out of memory for job file types!");
675 job
->filetypes
= filetypes
;
676 job
->num_files
= fileid
;
679 job
->filetypes
[fileid
- 1] = mimeFileType(MimeDatabase
, filename
);
681 if (job
->filetypes
[fileid
- 1] == NULL
)
682 job
->filetypes
[fileid
- 1] = mimeType(MimeDatabase
, "application",
689 * Clean out old jobs as needed...
695 * Check to see if we need to start any jobs...
703 * 'MoveJob()' - Move the specified job to a different destination.
707 MoveJob(int id
, /* I - Job ID */
708 const char *dest
) /* I - Destination */
710 job_t
*current
;/* Current job */
711 ipp_attribute_t
*attr
; /* job-printer-uri attribute */
712 printer_t
*p
; /* Destination printer or class */
715 if ((p
= FindPrinter(dest
)) == NULL
)
721 for (current
= Jobs
; current
!= NULL
; current
= current
->next
)
722 if (current
->id
== id
)
724 if (current
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
727 strncpy(current
->dest
, dest
, sizeof(current
->dest
) - 1);
729 if ((attr
= ippFindAttribute(current
->attrs
, "job-printer-uri", IPP_TAG_URI
)) != NULL
)
731 free(attr
->values
[0].string
.text
);
732 attr
->values
[0].string
.text
= strdup(p
->uri
);
735 SaveJob(current
->id
);
743 * 'ReleaseJob()' - Release the specified job.
747 ReleaseJob(int id
) /* I - Job ID */
749 job_t
*job
; /* Job data */
752 LogMessage(L_DEBUG
, "ReleaseJob: id = %d", id
);
754 if ((job
= FindJob(id
)) == NULL
)
757 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
759 DEBUG_puts("ReleaseJob: setting state to pending...");
761 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
769 * 'RestartJob()' - Restart the specified job.
773 RestartJob(int id
) /* I - Job ID */
775 job_t
*job
; /* Job data */
778 if ((job
= FindJob(id
)) == NULL
)
781 if (job
->state
->values
[0].integer
== IPP_JOB_STOPPED
|| JobFiles
)
783 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
791 * 'SaveJob()' - Save a job to disk.
795 SaveJob(int id
) /* I - Job ID */
797 job_t
*job
; /* Pointer to job */
798 char filename
[1024]; /* Job control filename */
801 if ((job
= FindJob(id
)) == NULL
)
804 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
, id
);
805 ipp_write_file(filename
, job
->attrs
);
810 * 'SetJobHoldUntil()' - Set the hold time for a job...
814 SetJobHoldUntil(int id
, /* I - Job ID */
815 const char *when
) /* I - When to resume */
817 job_t
*job
; /* Pointer to job */
818 time_t curtime
; /* Current time */
819 struct tm
*curdate
; /* Current date */
820 int hour
; /* Hold hour */
821 int minute
; /* Hold minute */
822 int second
; /* Hold second */
825 LogMessage(L_DEBUG
, "SetJobHoldUntil(%d, \"%s\")", id
, when
);
827 if ((job
= FindJob(id
)) == NULL
)
832 if (strcmp(when
, "indefinite") == 0)
835 * Hold indefinitely...
840 else if (strcmp(when
, "day-time") == 0)
843 * Hold to 6am the next morning unless local time is < 6pm.
846 curtime
= time(NULL
);
847 curdate
= localtime(&curtime
);
849 if (curdate
->tm_hour
< 18)
850 job
->hold_until
= curtime
;
852 job
->hold_until
= curtime
+
853 ((29 - curdate
->tm_hour
) * 60 + 59 -
854 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
856 else if (strcmp(when
, "evening") == 0 || strcmp(when
, "night") == 0)
859 * Hold to 6pm unless local time is > 6pm or < 6am.
862 curtime
= time(NULL
);
863 curdate
= localtime(&curtime
);
865 if (curdate
->tm_hour
< 6 || curdate
->tm_hour
>= 18)
866 job
->hold_until
= curtime
;
868 job
->hold_until
= curtime
+
869 ((17 - curdate
->tm_hour
) * 60 + 59 -
870 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
872 else if (strcmp(when
, "second-shift") == 0)
875 * Hold to 4pm unless local time is > 4pm.
878 curtime
= time(NULL
);
879 curdate
= localtime(&curtime
);
881 if (curdate
->tm_hour
>= 16)
882 job
->hold_until
= curtime
;
884 job
->hold_until
= curtime
+
885 ((15 - curdate
->tm_hour
) * 60 + 59 -
886 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
888 else if (strcmp(when
, "third-shift") == 0)
891 * Hold to 12am unless local time is < 8am.
894 curtime
= time(NULL
);
895 curdate
= localtime(&curtime
);
897 if (curdate
->tm_hour
< 8)
898 job
->hold_until
= curtime
;
900 job
->hold_until
= curtime
+
901 ((23 - curdate
->tm_hour
) * 60 + 59 -
902 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
904 else if (strcmp(when
, "weekend") == 0)
907 * Hold to weekend unless we are in the weekend.
910 curtime
= time(NULL
);
911 curdate
= localtime(&curtime
);
913 if (curdate
->tm_wday
== 0 || curdate
->tm_wday
== 6)
914 job
->hold_until
= curtime
;
916 job
->hold_until
= curtime
+
917 (((5 - curdate
->tm_wday
) * 24 +
918 (17 - curdate
->tm_hour
)) * 60 + 59 -
919 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
921 else if (sscanf(when
, "%d:%d:%d", &hour
, &minute
, &second
) >= 2)
924 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
927 curtime
= time(NULL
);
928 curdate
= gmtime(&curtime
);
930 job
->hold_until
= curtime
+
931 ((hour
- curdate
->tm_hour
) * 60 + minute
-
932 curdate
->tm_min
) * 60 + second
- curdate
->tm_sec
;
935 * Hold until next day as needed...
938 if (job
->hold_until
< curtime
)
939 job
->hold_until
+= 24 * 60 * 60 * 60;
942 LogMessage(L_DEBUG
, "SetJobHoldUntil: hold_until = %d", job
->hold_until
);
947 * 'SetJobPriority()' - Set the priority of a job, moving it up/down in the
952 SetJobPriority(int id
, /* I - Job ID */
953 int priority
) /* I - New priority (0 to 100) */
955 job_t
*job
, /* Job to change */
956 *current
, /* Current job */
957 *prev
; /* Previous job */
958 ipp_attribute_t
*attr
; /* Job attribute */
965 for (current
= Jobs
, prev
= NULL
;
967 prev
= current
, current
= current
->next
)
968 if (current
->id
== id
)
975 * Set the new priority...
979 job
->priority
= priority
;
981 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
982 attr
->values
[0].integer
= priority
;
984 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
990 * See if we need to do any sorting...
993 if ((prev
== NULL
|| job
->priority
< prev
->priority
) &&
994 (job
->next
== NULL
|| job
->next
->priority
< job
->priority
))
998 * Remove the job from the list, and then insert it where it belongs...
1004 prev
->next
= job
->next
;
1006 for (current
= Jobs
, prev
= NULL
;
1008 prev
= current
, current
= current
->next
)
1009 if (job
->priority
> current
->priority
)
1012 job
->next
= current
;
1021 * 'StartJob()' - Start a print job.
1025 StartJob(int id
, /* I - Job ID */
1026 printer_t
*printer
) /* I - Printer to print job */
1028 job_t
*current
; /* Current job */
1029 int i
; /* Looping var */
1030 int slot
; /* Pipe slot */
1031 int num_filters
; /* Number of filters for job */
1032 mime_filter_t
*filters
; /* Filters for job */
1033 char method
[255], /* Method for output */
1034 *optptr
; /* Pointer to options */
1035 ipp_attribute_t
*attr
; /* Current attribute */
1036 int pid
; /* Process ID of new filter process */
1037 int banner_page
; /* 1 if banner page, 0 otherwise */
1038 int statusfds
[2], /* Pipes used between the filters and scheduler */
1039 filterfds
[2][2];/* Pipes used between the filters */
1040 char *argv
[8], /* Filter command-line arguments */
1041 filename
[1024], /* Job filename */
1042 command
[1024], /* Full path to filter/backend command */
1043 jobid
[255], /* Job ID string */
1044 title
[IPP_MAX_NAME
],
1045 /* Job title string */
1046 copies
[255], /* # copies string */
1047 options
[16384], /* Full list of options */
1048 *envp
[20], /* Environment variables */
1049 path
[1024], /* PATH environment variable */
1050 language
[255], /* LANG environment variable */
1051 charset
[255], /* CHARSET environment variable */
1052 classification
[1024], /* CLASSIFICATION environmeent variable */
1053 content_type
[255],/* CONTENT_TYPE environment variable */
1054 device_uri
[1024],/* DEVICE_URI environment variable */
1055 ppd
[1024], /* PPD environment variable */
1056 printer_name
[255],/* PRINTER environment variable */
1057 root
[1024], /* CUPS_SERVERROOT environment variable */
1058 cache
[255], /* RIP_MAX_CACHE environment variable */
1059 tmpdir
[1024], /* TMPDIR environment variable */
1060 ldpath
[1024], /* LD_LIBRARY_PATH environment variable */
1061 datadir
[1024], /* CUPS_DATADIR environment variable */
1062 fontpath
[1050]; /* CUPS_FONTPATH environment variable */
1065 LogMessage(L_DEBUG
, "StartJob(%d, %08x)", id
, printer
);
1067 for (current
= Jobs
; current
!= NULL
; current
= current
->next
)
1068 if (current
->id
== id
)
1071 if (current
== NULL
)
1074 LogMessage(L_DEBUG
, "StartJob() id = %d, file = %d/%d", id
,
1075 current
->current_file
, current
->num_files
);
1077 if (current
->num_files
== 0)
1079 LogMessage(L_ERROR
, "Job ID %d has no files! Cancelling it!", id
);
1085 * Figure out what filters are required to convert from
1086 * the source to the destination type...
1092 if (printer
->type
& CUPS_PRINTER_REMOTE
)
1095 * Remote jobs go directly to the remote job...
1103 * Local jobs get filtered...
1106 filters
= mimeFilter(MimeDatabase
, current
->filetypes
[current
->current_file
],
1107 printer
->filetype
, &num_filters
);
1109 if (num_filters
== 0)
1111 LogMessage(L_ERROR
, "Unable to convert file %d to printable format for job %d!",
1112 current
->current_file
, current
->id
);
1113 current
->current_file
++;
1115 if (current
->current_file
== current
->num_files
)
1116 CancelJob(current
->id
, 0);
1122 * Remove NULL ("-") filters...
1125 for (i
= 0; i
< num_filters
;)
1126 if (strcmp(filters
[i
].filter
, "-") == 0)
1129 if (i
< num_filters
)
1130 memcpy(filters
+ i
, filters
+ i
+ 1,
1131 (num_filters
- i
) * sizeof(mime_filter_t
));
1136 if (num_filters
== 0)
1144 * Compute filter cost...
1147 for (i
= 0; i
< num_filters
; i
++)
1148 current
->cost
+= filters
[i
].cost
;
1153 * See if the filter cost is too high...
1156 if ((FilterLevel
+ current
->cost
) > FilterLimit
&& FilterLevel
> 0 &&
1160 * Don't print this job quite yet...
1163 if (filters
!= NULL
)
1166 LogMessage(L_INFO
, "Holding job %d because filter limit has been reached.",
1168 LogMessage(L_DEBUG
, "StartJob: id = %d, file = %d, "
1169 "cost = %d, level = %d, limit = %d",
1170 id
, current
->current_file
, current
->cost
, FilterLevel
,
1175 FilterLevel
+= current
->cost
;
1178 * Update the printer and job state to "processing"...
1181 current
->state
->values
[0].integer
= IPP_JOB_PROCESSING
;
1182 current
->status
= 0;
1183 current
->printer
= printer
;
1184 printer
->job
= current
;
1185 SetPrinterState(printer
, IPP_PRINTER_PROCESSING
);
1187 if (current
->current_file
== 0)
1188 set_time(current
, "time-at-processing");
1191 * Determine if we are printing a banner page or not...
1194 if (current
->job_sheets
== NULL
)
1195 LogMessage(L_DEBUG
, "No job-sheets attribute.");
1196 else if (current
->job_sheets
->num_values
== 1)
1197 LogMessage(L_DEBUG
, "job-sheets=%s",
1198 current
->job_sheets
->values
[0].string
.text
);
1200 LogMessage(L_DEBUG
, "job-sheets=%s,%s",
1201 current
->job_sheets
->values
[0].string
.text
,
1202 current
->job_sheets
->values
[1].string
.text
);
1204 if (printer
->type
& CUPS_PRINTER_REMOTE
)
1206 else if (current
->job_sheets
== NULL
)
1208 else if (strcasecmp(current
->job_sheets
->values
[0].string
.text
, "none") != 0 &&
1209 current
->current_file
== 0)
1211 else if (current
->job_sheets
->num_values
> 1 &&
1212 strcasecmp(current
->job_sheets
->values
[1].string
.text
, "none") != 0 &&
1213 current
->current_file
== (current
->num_files
- 1))
1218 LogMessage(L_DEBUG
, "banner_page = %d", banner_page
);
1221 * Building the options string is harder than it needs to be, but
1222 * for the moment we need to pass strings for command-line args and
1223 * not IPP attribute pointers... :)
1229 snprintf(title
, sizeof(title
), "%s-%d", printer
->name
, current
->id
);
1230 strcpy(copies
, "1");
1232 for (attr
= current
->attrs
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1234 if (strcmp(attr
->name
, "copies") == 0 &&
1235 attr
->value_tag
== IPP_TAG_INTEGER
)
1238 * Don't use the # copies attribute if we are printing the job sheets...
1242 sprintf(copies
, "%d", attr
->values
[0].integer
);
1244 else if (strcmp(attr
->name
, "job-name") == 0 &&
1245 (attr
->value_tag
== IPP_TAG_NAME
||
1246 attr
->value_tag
== IPP_TAG_NAMELANG
))
1248 strncpy(title
, attr
->values
[0].string
.text
, sizeof(title
) - 1);
1249 title
[sizeof(title
) - 1] = '\0';
1251 else if (attr
->group_tag
== IPP_TAG_JOB
)
1254 * Filter out other unwanted attributes...
1257 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
1258 attr
->value_tag
== IPP_TAG_NAMELANG
||
1259 attr
->value_tag
== IPP_TAG_TEXTLANG
||
1260 attr
->value_tag
== IPP_TAG_URI
||
1261 attr
->value_tag
== IPP_TAG_URISCHEME
)
1264 if (strncmp(attr
->name
, "time-", 5) == 0)
1267 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1268 !(printer
->type
& CUPS_PRINTER_REMOTE
))
1271 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1272 strcmp(attr
->name
, "job-billing") != 0 &&
1273 strcmp(attr
->name
, "job-sheets") != 0 &&
1274 strcmp(attr
->name
, "job-hold-until") != 0 &&
1275 strcmp(attr
->name
, "job-priority") != 0)
1278 if (strcmp(attr
->name
, "page-label") == 0 &&
1283 * Otherwise add them to the list...
1286 if (optptr
> options
)
1287 strncat(optptr
, " ", sizeof(options
) - (optptr
- options
) - 1);
1289 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
1291 strncat(optptr
, attr
->name
, sizeof(options
) - (optptr
- options
) - 1);
1292 strncat(optptr
, "=", sizeof(options
) - (optptr
- options
) - 1);
1295 for (i
= 0; i
< attr
->num_values
; i
++)
1298 strncat(optptr
, ",", sizeof(options
) - (optptr
- options
) - 1);
1300 optptr
+= strlen(optptr
);
1302 switch (attr
->value_tag
)
1304 case IPP_TAG_INTEGER
:
1306 snprintf(optptr
, sizeof(options
) - (optptr
- options
) - 1,
1307 "%d", attr
->values
[i
].integer
);
1310 case IPP_TAG_BOOLEAN
:
1311 if (!attr
->values
[i
].boolean
)
1312 strncat(optptr
, "no", sizeof(options
) - (optptr
- options
) - 1);
1314 case IPP_TAG_NOVALUE
:
1315 strncat(optptr
, attr
->name
,
1316 sizeof(options
) - (optptr
- options
) - 1);
1319 case IPP_TAG_RANGE
:
1320 snprintf(optptr
, sizeof(options
) - (optptr
- options
) - 1,
1321 "%d-%d", attr
->values
[i
].range
.lower
,
1322 attr
->values
[i
].range
.upper
);
1325 case IPP_TAG_RESOLUTION
:
1326 snprintf(optptr
, sizeof(options
) - (optptr
- options
) - 1,
1327 "%dx%d%s", attr
->values
[i
].resolution
.xres
,
1328 attr
->values
[i
].resolution
.yres
,
1329 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
1333 case IPP_TAG_STRING
:
1336 case IPP_TAG_KEYWORD
:
1337 case IPP_TAG_CHARSET
:
1338 case IPP_TAG_LANGUAGE
:
1339 if (strchr(attr
->values
[i
].string
.text
, ' ') != NULL
||
1340 strchr(attr
->values
[i
].string
.text
, '\t') != NULL
||
1341 strchr(attr
->values
[i
].string
.text
, '\n') != NULL
)
1343 strncat(optptr
, "\'", sizeof(options
) - (optptr
- options
) - 1);
1344 strncat(optptr
, attr
->values
[i
].string
.text
,
1345 sizeof(options
) - (optptr
- options
) - 1);
1346 strncat(optptr
, "\'", sizeof(options
) - (optptr
- options
) - 1);
1349 strncat(optptr
, attr
->values
[i
].string
.text
,
1350 sizeof(options
) - (optptr
- options
) - 1);
1354 break; /* anti-compiler-warning-code */
1358 optptr
+= strlen(optptr
);
1363 * Build the command-line arguments for the filters. Each filter
1364 * has 6 or 7 arguments:
1368 * argv[2] = username
1370 * argv[4] = # copies
1372 * argv[6] = filename (optional; normally stdin)
1374 * This allows legacy printer drivers that use the old System V
1375 * printing interface to be used by CUPS.
1378 sprintf(jobid
, "%d", current
->id
);
1379 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
1380 current
->id
, current
->current_file
+ 1);
1382 argv
[0] = printer
->name
;
1384 argv
[2] = current
->username
;
1391 LogMessage(L_DEBUG
, "StartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1392 argv
[0], argv
[1], argv
[2], argv
[3], argv
[4], argv
[5], argv
[6]);
1395 * Create environment variable strings for the filters...
1398 attr
= ippFindAttribute(current
->attrs
, "attributes-natural-language",
1400 snprintf(language
, sizeof(language
), "LANG=%s", attr
->values
[0].string
.text
);
1402 attr
= ippFindAttribute(current
->attrs
, "document-format",
1405 (optptr
= strstr(attr
->values
[0].string
.text
, "charset=")) != NULL
)
1406 snprintf(charset
, sizeof(charset
), "CHARSET=%s", optptr
+ 8);
1409 attr
= ippFindAttribute(current
->attrs
, "attributes-charset",
1411 snprintf(charset
, sizeof(charset
), "CHARSET=%s",
1412 attr
->values
[0].string
.text
);
1415 snprintf(path
, sizeof(path
), "PATH=%s/filter:/bin:/usr/bin", ServerBin
);
1416 snprintf(content_type
, sizeof(content_type
), "CONTENT_TYPE=%s/%s",
1417 current
->filetypes
[current
->current_file
]->super
,
1418 current
->filetypes
[current
->current_file
]->type
);
1419 snprintf(device_uri
, sizeof(device_uri
), "DEVICE_URI=%s", printer
->device_uri
);
1420 snprintf(ppd
, sizeof(ppd
), "PPD=%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
1421 snprintf(printer_name
, sizeof(printer_name
), "PRINTER=%s", printer
->name
);
1422 snprintf(cache
, sizeof(cache
), "RIP_MAX_CACHE=%s", RIPCache
);
1423 snprintf(root
, sizeof(root
), "CUPS_SERVERROOT=%s", ServerRoot
);
1424 snprintf(tmpdir
, sizeof(tmpdir
), "TMPDIR=%s", TempDir
);
1425 snprintf(datadir
, sizeof(datadir
), "CUPS_DATADIR=%s", DataDir
);
1426 snprintf(fontpath
, sizeof(fontpath
), "CUPS_FONTPATH=%s", FontPath
);
1427 if (Classification
[0] && !banner_page
)
1429 if ((attr
= ippFindAttribute(current
->attrs
, "job-sheets",
1430 IPP_TAG_NAME
)) == NULL
)
1431 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1433 else if (attr
->num_values
> 1 &&
1434 strcmp(attr
->values
[1].string
.text
, "none") != 0)
1435 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1436 attr
->values
[1].string
.text
);
1438 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1439 attr
->values
[0].string
.text
);
1442 classification
[0] = '\0';
1443 if (getenv("LD_LIBRARY_PATH") != NULL
)
1444 snprintf(ldpath
, sizeof(ldpath
), "LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
1449 envp
[1] = "SOFTWARE=CUPS/1.1";
1450 envp
[2] = "USER=root";
1458 envp
[10] = content_type
;
1459 envp
[11] = device_uri
;
1460 envp
[12] = printer_name
;
1462 envp
[14] = fontpath
;
1464 envp
[16] = classification
;
1467 LogMessage(L_DEBUG
, "StartJob: envp = \"%s\",\"%s\",\"%s\",\"%s\","
1468 "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\","
1469 "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1470 envp
[0], envp
[1], envp
[2], envp
[3], envp
[4],
1471 envp
[5], envp
[6], envp
[7], envp
[8], envp
[9],
1472 envp
[10], envp
[11], envp
[12], envp
[13], envp
[14],
1473 envp
[15], envp
[16]);
1475 current
->current_file
++;
1478 * Make sure we have a buffer to read status info into...
1481 if (current
->buffer
== NULL
)
1483 LogMessage(L_DEBUG2
, "UpdateJob: Allocating status buffer...");
1485 if ((current
->buffer
= malloc(JOB_BUFFER_SIZE
)) == NULL
)
1487 LogMessage(L_EMERG
, "Unable to allocate memory for job status buffer - %s",
1489 CancelJob(current
->id
, 0);
1493 current
->bufused
= 0;
1497 * Now create processes for all of the filters...
1500 if (pipe(statusfds
))
1502 LogMessage(L_ERROR
, "Unable to create job status pipes - %s.",
1504 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1505 "Unable to create status pipes - %s.", strerror(errno
));
1509 LogMessage(L_DEBUG
, "StartJob: statusfds = %d, %d",
1510 statusfds
[0], statusfds
[1]);
1512 current
->pipe
= statusfds
[0];
1513 current
->status
= 0;
1514 memset(current
->procs
, 0, sizeof(current
->procs
));
1516 filterfds
[1][0] = open("/dev/null", O_RDONLY
);
1517 filterfds
[1][1] = -1;
1519 LogMessage(L_DEBUG
, "StartJob: filterfds[%d] = %d, %d", 1, filterfds
[1][0],
1522 for (i
= 0, slot
= 0; i
< num_filters
; i
++)
1524 if (filters
[i
].filter
[0] != '/')
1525 snprintf(command
, sizeof(command
), "%s/filter/%s", ServerBin
,
1529 strncpy(command
, filters
[i
].filter
, sizeof(command
) - 1);
1530 command
[sizeof(command
) - 1] = '\0';
1533 if (i
< (num_filters
- 1) ||
1534 strncmp(printer
->device_uri
, "file:", 5) != 0)
1535 pipe(filterfds
[slot
]);
1538 filterfds
[slot
][0] = -1;
1539 if (strncmp(printer
->device_uri
, "file:/dev/", 10) == 0)
1540 filterfds
[slot
][1] = open(printer
->device_uri
+ 5,
1543 filterfds
[slot
][1] = open(printer
->device_uri
+ 5,
1544 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
1547 LogMessage(L_DEBUG
, "StartJob: filter = \"%s\"", command
);
1548 LogMessage(L_DEBUG
, "StartJob: filterfds[%d] = %d, %d",
1549 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
1551 pid
= start_process(command
, argv
, envp
, filterfds
[!slot
][0],
1552 filterfds
[slot
][1], statusfds
[1], 0);
1554 close(filterfds
[!slot
][0]);
1555 close(filterfds
[!slot
][1]);
1559 LogMessage(L_ERROR
, "Unable to start filter \"%s\" - %s.",
1560 filters
[i
].filter
, strerror(errno
));
1561 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1562 "Unable to start filter \"%s\" - %s.",
1563 filters
[i
].filter
, strerror(errno
));
1567 current
->procs
[i
] = pid
;
1569 LogMessage(L_INFO
, "Started filter %s (PID %d) for job %d.",
1570 command
, pid
, current
->id
);
1576 if (filters
!= NULL
)
1580 * Finally, pipe the final output into a backend process if needed...
1583 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
1585 sscanf(printer
->device_uri
, "%254[^:]", method
);
1586 snprintf(command
, sizeof(command
), "%s/backend/%s", ServerBin
, method
);
1588 argv
[0] = printer
->device_uri
;
1590 filterfds
[slot
][0] = -1;
1591 filterfds
[slot
][1] = open("/dev/null", O_WRONLY
);
1593 LogMessage(L_DEBUG
, "StartJob: backend = \"%s\"", command
);
1594 LogMessage(L_DEBUG
, "StartJob: filterfds[%d] = %d, %d",
1595 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
1597 pid
= start_process(command
, argv
, envp
, filterfds
[!slot
][0],
1598 filterfds
[slot
][1], statusfds
[1], 1);
1600 close(filterfds
[!slot
][0]);
1601 close(filterfds
[!slot
][1]);
1605 LogMessage(L_ERROR
, "Unable to start backend \"%s\" - %s.",
1606 method
, strerror(errno
));
1607 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1608 "Unable to start backend \"%s\" - %s.", method
, strerror(errno
));
1613 current
->procs
[i
] = pid
;
1615 LogMessage(L_INFO
, "Started backend %s (PID %d) for job %d.", command
, pid
,
1621 filterfds
[slot
][0] = -1;
1622 filterfds
[slot
][1] = -1;
1624 close(filterfds
[!slot
][0]);
1625 close(filterfds
[!slot
][1]);
1628 close(filterfds
[slot
][0]);
1629 close(filterfds
[slot
][1]);
1631 close(statusfds
[1]);
1633 LogMessage(L_DEBUG2
, "StartJob: Adding fd %d to InputSet...", current
->pipe
);
1635 FD_SET(current
->pipe
, &InputSet
);
1640 * 'StopAllJobs()' - Stop all print jobs.
1646 job_t
*current
; /* Current job */
1649 DEBUG_puts("StopAllJobs()");
1651 for (current
= Jobs
; current
!= NULL
; current
= current
->next
)
1652 if (current
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
1654 StopJob(current
->id
);
1655 current
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1661 * 'StopJob()' - Stop a print job.
1665 StopJob(int id
) /* I - Job ID */
1667 int i
; /* Looping var */
1668 job_t
*current
; /* Current job */
1671 LogMessage(L_DEBUG
, "StopJob: id = %d", id
);
1673 for (current
= Jobs
; current
!= NULL
; current
= current
->next
)
1674 if (current
->id
== id
)
1676 DEBUG_puts("StopJob: found job in list.");
1678 if (current
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
1680 DEBUG_puts("StopJob: job state is \'processing\'.");
1682 FilterLevel
-= current
->cost
;
1684 if (current
->status
< 0)
1685 SetPrinterState(current
->printer
, IPP_PRINTER_STOPPED
);
1686 else if (current
->printer
->state
!= IPP_PRINTER_STOPPED
)
1687 SetPrinterState(current
->printer
, IPP_PRINTER_IDLE
);
1689 LogMessage(L_DEBUG
, "StopJob: printer state is %d", current
->printer
->state
);
1691 current
->state
->values
[0].integer
= IPP_JOB_STOPPED
;
1692 current
->printer
->job
= NULL
;
1693 current
->printer
= NULL
;
1695 current
->current_file
--;
1697 for (i
= 0; current
->procs
[i
]; i
++)
1698 if (current
->procs
[i
] > 0)
1700 kill(current
->procs
[i
], SIGTERM
);
1701 current
->procs
[i
] = 0;
1707 * Close the pipe and clear the input bit.
1710 LogMessage(L_DEBUG2
, "StopJob: Removing fd %d from InputSet...",
1713 close(current
->pipe
);
1714 FD_CLR(current
->pipe
, &InputSet
);
1718 if (current
->buffer
)
1721 * Free the status buffer...
1724 LogMessage(L_DEBUG2
, "StopJob: Freeing status buffer...");
1726 free(current
->buffer
);
1727 current
->buffer
= NULL
;
1728 current
->bufused
= 0;
1737 * 'UpdateJob()' - Read a status update from a job's filters.
1741 UpdateJob(job_t
*job
) /* I - Job to check */
1743 int bytes
; /* Number of bytes read */
1744 int copies
; /* Number of copies printed */
1745 char *lineptr
, /* Pointer to end of line in buffer */
1746 *message
; /* Pointer to message text */
1747 int loglevel
; /* Log level for message */
1750 if ((bytes
= read(job
->pipe
, job
->buffer
+ job
->bufused
,
1751 JOB_BUFFER_SIZE
- job
->bufused
- 1)) > 0)
1753 job
->bufused
+= bytes
;
1754 job
->buffer
[job
->bufused
] = '\0';
1755 lineptr
= strchr(job
->buffer
, '\n');
1757 else if (bytes
< 0 && errno
== EINTR
)
1761 lineptr
= job
->buffer
+ job
->bufused
;
1765 while (lineptr
!= NULL
)
1768 * Terminate each line and process it...
1774 * Figure out the logging level...
1777 if (strncmp(job
->buffer
, "EMERG:", 6) == 0)
1780 message
= job
->buffer
+ 6;
1782 else if (strncmp(job
->buffer
, "ALERT:", 6) == 0)
1785 message
= job
->buffer
+ 6;
1787 else if (strncmp(job
->buffer
, "CRIT:", 5) == 0)
1790 message
= job
->buffer
+ 5;
1792 else if (strncmp(job
->buffer
, "ERROR:", 6) == 0)
1795 message
= job
->buffer
+ 6;
1797 else if (strncmp(job
->buffer
, "WARNING:", 8) == 0)
1800 message
= job
->buffer
+ 8;
1802 else if (strncmp(job
->buffer
, "NOTICE:", 6) == 0)
1804 loglevel
= L_NOTICE
;
1805 message
= job
->buffer
+ 6;
1807 else if (strncmp(job
->buffer
, "INFO:", 5) == 0)
1810 message
= job
->buffer
+ 5;
1812 else if (strncmp(job
->buffer
, "DEBUG:", 6) == 0)
1815 message
= job
->buffer
+ 6;
1817 else if (strncmp(job
->buffer
, "DEBUG2:", 7) == 0)
1819 loglevel
= L_DEBUG2
;
1820 message
= job
->buffer
+ 7;
1822 else if (strncmp(job
->buffer
, "PAGE:", 5) == 0)
1825 message
= job
->buffer
+ 5;
1830 message
= job
->buffer
;
1834 * Skip leading whitespace in the message...
1837 while (isspace(*message
))
1841 * Send it to the log file and printer state message as needed...
1844 if (loglevel
== L_PAGE
)
1847 * Page message; send the message to the page_log file and update the
1848 * job sheet count...
1851 if (job
->sheets
!= NULL
)
1853 if (!sscanf(message
, "%*d%d", &copies
))
1855 job
->sheets
->values
[0].integer
++;
1857 if (job
->printer
->page_limit
)
1858 UpdateQuota(job
->printer
, job
->username
, 1, 0);
1862 job
->sheets
->values
[0].integer
+= copies
;
1864 if (job
->printer
->page_limit
)
1865 UpdateQuota(job
->printer
, job
->username
, copies
, 0);
1869 LogPage(job
, message
);
1874 * Other status message; send it to the error_log file...
1877 if (loglevel
!= L_INFO
)
1878 LogMessage(loglevel
, "%s", message
);
1880 if ((loglevel
== L_INFO
&& !job
->status
) ||
1882 strncpy(job
->printer
->state_message
, message
,
1883 sizeof(job
->printer
->state_message
) - 1);
1887 * Copy over the buffer data we've used up...
1890 strcpy(job
->buffer
, lineptr
);
1891 job
->bufused
-= lineptr
- job
->buffer
;
1893 if (job
->bufused
< 0)
1896 lineptr
= strchr(job
->buffer
, '\n');
1901 LogMessage(L_DEBUG
, "UpdateJob: job %d, file %d is complete.",
1902 job
->id
, job
->current_file
- 1);
1907 * Close the pipe and clear the input bit.
1910 LogMessage(L_DEBUG2
, "UpdateJob: Removing fd %d from InputSet...",
1914 FD_CLR(job
->pipe
, &InputSet
);
1918 if (job
->status
< 0)
1921 * Backend had errors; stop it...
1925 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1928 else if (job
->status
> 0)
1931 * Filter had errors; cancel it...
1934 if (job
->current_file
< job
->num_files
)
1935 StartJob(job
->id
, job
->printer
);
1938 CancelJob(job
->id
, 0);
1942 job
->state
->values
[0].integer
= IPP_JOB_ABORTED
;
1952 * Job printed successfully; cancel it...
1955 if (job
->current_file
< job
->num_files
)
1957 FilterLevel
-= job
->cost
;
1958 StartJob(job
->id
, job
->printer
);
1962 CancelJob(job
->id
, 0);
1966 job
->state
->values
[0].integer
= IPP_JOB_COMPLETED
;
1978 * 'ipp_read_file()' - Read an IPP request from a file.
1981 static ipp_state_t
/* O - State */
1982 ipp_read_file(const char *filename
, /* I - File to read from */
1983 ipp_t
*ipp
) /* I - Request to read into */
1985 int fd
; /* File descriptor for file */
1986 int n
; /* Length of data */
1987 unsigned char buffer
[8192], /* Data buffer */
1988 *bufptr
; /* Pointer into buffer */
1989 ipp_attribute_t
*attr
; /* Current attribute */
1990 ipp_tag_t tag
; /* Current tag */
1994 * Open the file if possible...
1997 if (filename
== NULL
|| ipp
== NULL
)
2000 if ((fd
= open(filename
, O_RDONLY
)) == -1)
2004 * Read the IPP request...
2007 ipp
->state
= IPP_IDLE
;
2012 break; /* anti-compiler-warning-code */
2015 ipp
->state
++; /* Avoid common problem... */
2019 * Get the request header...
2022 if ((n
= read(fd
, buffer
, 8)) < 8)
2024 DEBUG_printf(("ipp_read_file: Unable to read header (%d bytes read)!\n", n
));
2026 return (n
== 0 ? IPP_IDLE
: IPP_ERROR
);
2030 * Verify the major version number...
2035 DEBUG_printf(("ipp_read_file: version number (%d.%d) is bad.\n", buffer
[0],
2042 * Then copy the request header over...
2045 ipp
->request
.any
.version
[0] = buffer
[0];
2046 ipp
->request
.any
.version
[1] = buffer
[1];
2047 ipp
->request
.any
.op_status
= (buffer
[2] << 8) | buffer
[3];
2048 ipp
->request
.any
.request_id
= (((((buffer
[4] << 8) | buffer
[5]) << 8) |
2049 buffer
[6]) << 8) | buffer
[7];
2051 ipp
->state
= IPP_ATTRIBUTE
;
2052 ipp
->current
= NULL
;
2053 ipp
->curtag
= IPP_TAG_ZERO
;
2055 case IPP_ATTRIBUTE
:
2056 while (read(fd
, buffer
, 1) > 0)
2059 * Read this attribute...
2062 tag
= (ipp_tag_t
)buffer
[0];
2064 if (tag
== IPP_TAG_END
)
2067 * No more attributes left...
2070 DEBUG_puts("ipp_read_file: IPP_TAG_END!");
2072 ipp
->state
= IPP_DATA
;
2075 else if (tag
< IPP_TAG_UNSUPPORTED_VALUE
)
2078 * Group tag... Set the current group and continue...
2081 if (ipp
->curtag
== tag
)
2082 ippAddSeparator(ipp
);
2085 ipp
->current
= NULL
;
2086 DEBUG_printf(("ipp_read_file: group tag = %x\n", tag
));
2090 DEBUG_printf(("ipp_read_file: value tag = %x\n", tag
));
2096 if (read(fd
, buffer
, 2) < 2)
2098 DEBUG_puts("ipp_read_file: unable to read name length!");
2103 n
= (buffer
[0] << 8) | buffer
[1];
2105 DEBUG_printf(("ipp_read_file: name length = %d\n", n
));
2110 * More values for current attribute...
2113 if (ipp
->current
== NULL
)
2119 attr
= ipp
->current
;
2122 * Finally, reallocate the attribute array as needed...
2125 if ((attr
->num_values
% IPP_MAX_VALUES
) == 0)
2127 ipp_attribute_t
*temp
, /* Pointer to new buffer */
2128 *ptr
; /* Pointer in attribute list */
2132 * Reallocate memory...
2135 if ((temp
= realloc(attr
, sizeof(ipp_attribute_t
) +
2136 (attr
->num_values
+ IPP_MAX_VALUES
- 1) *
2137 sizeof(ipp_value_t
))) == NULL
)
2144 * Reset pointers in the list...
2147 for (ptr
= ipp
->attrs
; ptr
&& ptr
->next
!= attr
; ptr
= ptr
->next
);
2154 attr
= ipp
->current
= ipp
->last
= temp
;
2160 * New attribute; read the name and add it...
2163 if (read(fd
, buffer
, n
) < n
)
2165 DEBUG_puts("ipp_read_file: unable to read name!");
2171 DEBUG_printf(("ipp_read_file: name = \'%s\'\n", buffer
));
2173 attr
= ipp
->current
= _ipp_add_attr(ipp
, IPP_MAX_VALUES
);
2175 attr
->group_tag
= ipp
->curtag
;
2176 attr
->value_tag
= tag
;
2177 attr
->name
= strdup((char *)buffer
);
2178 attr
->num_values
= 0;
2181 if (read(fd
, buffer
, 2) < 2)
2183 DEBUG_puts("ipp_read_file: unable to read value length!");
2188 n
= (buffer
[0] << 8) | buffer
[1];
2189 DEBUG_printf(("ipp_read_file: value length = %d\n", n
));
2193 case IPP_TAG_INTEGER
:
2195 if (read(fd
, buffer
, 4) < 4)
2201 n
= (((((buffer
[0] << 8) | buffer
[1]) << 8) | buffer
[2]) << 8) |
2204 attr
->values
[attr
->num_values
].integer
= n
;
2206 case IPP_TAG_BOOLEAN
:
2207 if (read(fd
, buffer
, 1) < 1)
2213 attr
->values
[attr
->num_values
].boolean
= buffer
[0];
2217 case IPP_TAG_KEYWORD
:
2218 case IPP_TAG_STRING
:
2220 case IPP_TAG_URISCHEME
:
2221 case IPP_TAG_CHARSET
:
2222 case IPP_TAG_LANGUAGE
:
2223 case IPP_TAG_MIMETYPE
:
2224 if (read(fd
, buffer
, n
) < n
)
2231 DEBUG_printf(("ipp_read_file: value = \'%s\'\n", buffer
));
2233 attr
->values
[attr
->num_values
].string
.text
= strdup((char *)buffer
);
2236 if (read(fd
, buffer
, 11) < 11)
2242 memcpy(attr
->values
[attr
->num_values
].date
, buffer
, 11);
2244 case IPP_TAG_RESOLUTION
:
2245 if (read(fd
, buffer
, 9) < 9)
2251 attr
->values
[attr
->num_values
].resolution
.xres
=
2252 (((((buffer
[0] << 8) | buffer
[1]) << 8) | buffer
[2]) << 8) |
2254 attr
->values
[attr
->num_values
].resolution
.yres
=
2255 (((((buffer
[4] << 8) | buffer
[5]) << 8) | buffer
[6]) << 8) |
2257 attr
->values
[attr
->num_values
].resolution
.units
=
2258 (ipp_res_t
)buffer
[8];
2260 case IPP_TAG_RANGE
:
2261 if (read(fd
, buffer
, 8) < 8)
2267 attr
->values
[attr
->num_values
].range
.lower
=
2268 (((((buffer
[0] << 8) | buffer
[1]) << 8) | buffer
[2]) << 8) |
2270 attr
->values
[attr
->num_values
].range
.upper
=
2271 (((((buffer
[4] << 8) | buffer
[5]) << 8) | buffer
[6]) << 8) |
2274 case IPP_TAG_TEXTLANG
:
2275 case IPP_TAG_NAMELANG
:
2276 if (read(fd
, buffer
, n
) < n
)
2282 * text-with-language and name-with-language are composite
2291 n
= (bufptr
[0] << 8) | bufptr
[1];
2293 attr
->values
[attr
->num_values
].string
.charset
= calloc(n
+ 1, 1);
2295 memcpy(attr
->values
[attr
->num_values
].string
.charset
,
2299 n
= (bufptr
[0] << 8) | bufptr
[1];
2301 attr
->values
[attr
->num_values
].string
.text
= calloc(n
+ 1, 1);
2303 memcpy(attr
->values
[attr
->num_values
].string
.text
,
2308 default : /* Other unsupported values */
2309 attr
->values
[attr
->num_values
].unknown
.length
= n
;
2312 attr
->values
[attr
->num_values
].unknown
.data
= malloc(n
);
2313 if (read(fd
, attr
->values
[attr
->num_values
].unknown
.data
, n
) < n
)
2317 attr
->values
[attr
->num_values
].unknown
.data
= NULL
;
2321 attr
->num_values
++;
2330 * Close the file and return...
2335 return (ipp
->state
);
2340 * 'ipp_write_file()' - Write an IPP request to a file.
2343 static ipp_state_t
/* O - State */
2344 ipp_write_file(const char *filename
, /* I - File to write to */
2345 ipp_t
*ipp
) /* I - Request to write */
2347 int fd
; /* File descriptor */
2348 int i
; /* Looping var */
2349 int n
; /* Length of data */
2350 unsigned char buffer
[8192], /* Data buffer */
2351 *bufptr
; /* Pointer into buffer */
2352 ipp_attribute_t
*attr
; /* Current attribute */
2356 * Open the file if possible...
2359 if (filename
== NULL
|| ipp
== NULL
)
2362 if ((fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) == -1)
2366 fchown(fd
, User
, Group
);
2369 * Write the IPP request...
2372 ipp
->state
= IPP_IDLE
;
2377 break; /* anti-compiler-warning-code */
2380 ipp
->state
++; /* Avoid common problem... */
2384 * Send the request header...
2389 *bufptr
++ = ipp
->request
.any
.version
[0];
2390 *bufptr
++ = ipp
->request
.any
.version
[1];
2391 *bufptr
++ = ipp
->request
.any
.op_status
>> 8;
2392 *bufptr
++ = ipp
->request
.any
.op_status
;
2393 *bufptr
++ = ipp
->request
.any
.request_id
>> 24;
2394 *bufptr
++ = ipp
->request
.any
.request_id
>> 16;
2395 *bufptr
++ = ipp
->request
.any
.request_id
>> 8;
2396 *bufptr
++ = ipp
->request
.any
.request_id
;
2398 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2400 DEBUG_puts("ipp_write_file: Could not write IPP header...");
2405 ipp
->state
= IPP_ATTRIBUTE
;
2406 ipp
->current
= ipp
->attrs
;
2407 ipp
->curtag
= IPP_TAG_ZERO
;
2409 case IPP_ATTRIBUTE
:
2410 while (ipp
->current
!= NULL
)
2413 * Write this attribute...
2417 attr
= ipp
->current
;
2419 ipp
->current
= ipp
->current
->next
;
2421 if (ipp
->curtag
!= attr
->group_tag
)
2424 * Send a group operation tag...
2427 ipp
->curtag
= attr
->group_tag
;
2429 if (attr
->group_tag
== IPP_TAG_ZERO
)
2432 DEBUG_printf(("ipp_write_file: wrote group tag = %x\n", attr
->group_tag
));
2433 *bufptr
++ = attr
->group_tag
;
2436 if ((n
= strlen(attr
->name
)) > (sizeof(buffer
) - 3))
2439 DEBUG_printf(("ipp_write_file: writing value tag = %x\n", attr
->value_tag
));
2440 DEBUG_printf(("ipp_write_file: writing name = %d, \'%s\'\n", n
, attr
->name
));
2442 *bufptr
++ = attr
->value_tag
;
2445 memcpy(bufptr
, attr
->name
, n
);
2448 switch (attr
->value_tag
)
2450 case IPP_TAG_INTEGER
:
2452 for (i
= 0; i
< attr
->num_values
; i
++)
2454 if ((sizeof(buffer
) - (bufptr
- buffer
)) < 9)
2456 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2458 DEBUG_puts("ippWrite: Could not write IPP attribute...");
2468 * Arrays and sets are done by sending additional
2469 * values with a zero-length name...
2472 *bufptr
++ = attr
->value_tag
;
2479 *bufptr
++ = attr
->values
[i
].integer
>> 24;
2480 *bufptr
++ = attr
->values
[i
].integer
>> 16;
2481 *bufptr
++ = attr
->values
[i
].integer
>> 8;
2482 *bufptr
++ = attr
->values
[i
].integer
;
2486 case IPP_TAG_BOOLEAN
:
2487 for (i
= 0; i
< attr
->num_values
; i
++)
2489 if ((sizeof(buffer
) - (bufptr
- buffer
)) < 6)
2491 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2493 DEBUG_puts("ippWrite: Could not write IPP attribute...");
2503 * Arrays and sets are done by sending additional
2504 * values with a zero-length name...
2507 *bufptr
++ = attr
->value_tag
;
2514 *bufptr
++ = attr
->values
[i
].boolean
;
2520 case IPP_TAG_KEYWORD
:
2521 case IPP_TAG_STRING
:
2523 case IPP_TAG_URISCHEME
:
2524 case IPP_TAG_CHARSET
:
2525 case IPP_TAG_LANGUAGE
:
2526 case IPP_TAG_MIMETYPE
:
2527 for (i
= 0; i
< attr
->num_values
; i
++)
2532 * Arrays and sets are done by sending additional
2533 * values with a zero-length name...
2536 DEBUG_printf(("ipp_write_file: writing value tag = %x\n",
2538 DEBUG_printf(("ipp_write_file: writing name = 0, \'\'\n"));
2540 if ((sizeof(buffer
) - (bufptr
- buffer
)) < 3)
2542 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2544 DEBUG_puts("ippWrite: Could not write IPP attribute...");
2551 *bufptr
++ = attr
->value_tag
;
2556 n
= strlen(attr
->values
[i
].string
.text
);
2558 if (n
> sizeof(buffer
))
2561 DEBUG_printf(("ipp_write_file: writing string = %d, \'%s\'\n", n
,
2562 attr
->values
[i
].string
.text
));
2564 if ((sizeof(buffer
) - (bufptr
- buffer
)) < (n
+ 2))
2566 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2568 DEBUG_puts("ipp_write_file: Could not write IPP attribute...");
2578 memcpy(bufptr
, attr
->values
[i
].string
.text
, n
);
2584 for (i
= 0; i
< attr
->num_values
; i
++)
2586 if ((sizeof(buffer
) - (bufptr
- buffer
)) < 16)
2588 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2590 DEBUG_puts("ippWrite: Could not write IPP attribute...");
2600 * Arrays and sets are done by sending additional
2601 * values with a zero-length name...
2604 *bufptr
++ = attr
->value_tag
;
2611 memcpy(bufptr
, attr
->values
[i
].date
, 11);
2616 case IPP_TAG_RESOLUTION
:
2617 for (i
= 0; i
< attr
->num_values
; i
++)
2619 if ((sizeof(buffer
) - (bufptr
- buffer
)) < 14)
2621 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2623 DEBUG_puts("ippWrite: Could not write IPP attribute...");
2633 * Arrays and sets are done by sending additional
2634 * values with a zero-length name...
2637 *bufptr
++ = attr
->value_tag
;
2644 *bufptr
++ = attr
->values
[i
].resolution
.xres
>> 24;
2645 *bufptr
++ = attr
->values
[i
].resolution
.xres
>> 16;
2646 *bufptr
++ = attr
->values
[i
].resolution
.xres
>> 8;
2647 *bufptr
++ = attr
->values
[i
].resolution
.xres
;
2648 *bufptr
++ = attr
->values
[i
].resolution
.yres
>> 24;
2649 *bufptr
++ = attr
->values
[i
].resolution
.yres
>> 16;
2650 *bufptr
++ = attr
->values
[i
].resolution
.yres
>> 8;
2651 *bufptr
++ = attr
->values
[i
].resolution
.yres
;
2652 *bufptr
++ = attr
->values
[i
].resolution
.units
;
2656 case IPP_TAG_RANGE
:
2657 for (i
= 0; i
< attr
->num_values
; i
++)
2659 if ((sizeof(buffer
) - (bufptr
- buffer
)) < 13)
2661 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2663 DEBUG_puts("ippWrite: Could not write IPP attribute...");
2673 * Arrays and sets are done by sending additional
2674 * values with a zero-length name...
2677 *bufptr
++ = attr
->value_tag
;
2684 *bufptr
++ = attr
->values
[i
].range
.lower
>> 24;
2685 *bufptr
++ = attr
->values
[i
].range
.lower
>> 16;
2686 *bufptr
++ = attr
->values
[i
].range
.lower
>> 8;
2687 *bufptr
++ = attr
->values
[i
].range
.lower
;
2688 *bufptr
++ = attr
->values
[i
].range
.upper
>> 24;
2689 *bufptr
++ = attr
->values
[i
].range
.upper
>> 16;
2690 *bufptr
++ = attr
->values
[i
].range
.upper
>> 8;
2691 *bufptr
++ = attr
->values
[i
].range
.upper
;
2695 case IPP_TAG_TEXTLANG
:
2696 case IPP_TAG_NAMELANG
:
2697 for (i
= 0; i
< attr
->num_values
; i
++)
2702 * Arrays and sets are done by sending additional
2703 * values with a zero-length name...
2706 if ((sizeof(buffer
) - (bufptr
- buffer
)) < 3)
2708 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2710 DEBUG_puts("ippWrite: Could not write IPP attribute...");
2717 *bufptr
++ = attr
->value_tag
;
2722 n
= strlen(attr
->values
[i
].string
.charset
) +
2723 strlen(attr
->values
[i
].string
.text
) +
2726 if (n
> sizeof(buffer
))
2729 if ((sizeof(buffer
) - (bufptr
- buffer
)) < (n
+ 2))
2731 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2733 DEBUG_puts("ipp_write_file: Could not write IPP attribute...");
2740 /* Length of entire value */
2744 /* Length of charset */
2745 n
= strlen(attr
->values
[i
].string
.charset
);
2750 memcpy(bufptr
, attr
->values
[i
].string
.charset
, n
);
2753 /* Length of text */
2754 n
= strlen(attr
->values
[i
].string
.text
);
2759 memcpy(bufptr
, attr
->values
[i
].string
.text
, n
);
2765 for (i
= 0; i
< attr
->num_values
; i
++)
2770 * Arrays and sets are done by sending additional
2771 * values with a zero-length name...
2774 if ((sizeof(buffer
) - (bufptr
- buffer
)) < 3)
2776 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2778 DEBUG_puts("ippWrite: Could not write IPP attribute...");
2785 *bufptr
++ = attr
->value_tag
;
2790 n
= attr
->values
[i
].unknown
.length
;
2792 if (n
> sizeof(buffer
))
2795 if ((sizeof(buffer
) - (bufptr
- buffer
)) < (n
+ 2))
2797 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2799 DEBUG_puts("ipp_write_file: Could not write IPP attribute...");
2806 /* Length of unknown value */
2813 memcpy(bufptr
, attr
->values
[i
].unknown
.data
, n
);
2821 * Write the data out...
2824 if (write(fd
, (char *)buffer
, bufptr
- buffer
) < 0)
2826 DEBUG_puts("ipp_write_file: Could not write IPP attribute...");
2831 DEBUG_printf(("ipp_write_file: wrote %d bytes\n", bufptr
- buffer
));
2834 if (ipp
->current
== NULL
)
2837 * Done with all of the attributes; add the end-of-attributes tag...
2840 buffer
[0] = IPP_TAG_END
;
2841 if (write(fd
, (char *)buffer
, 1) < 0)
2843 DEBUG_puts("ipp_write_file: Could not write IPP end-tag...");
2848 ipp
->state
= IPP_DATA
;
2857 * Close the file and return...
2862 return (ipp
->state
);
2867 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2871 set_time(job_t
*job
, /* I - Job to update */
2872 const char *name
) /* I - Name of attribute */
2874 ipp_attribute_t
*attr
; /* Time attribute */
2877 if ((attr
= ippFindAttribute(job
->attrs
, name
, IPP_TAG_ZERO
)) != NULL
)
2879 attr
->value_tag
= IPP_TAG_INTEGER
;
2880 attr
->values
[0].integer
= time(NULL
);
2886 * 'start_process()' - Start a background process.
2889 static int /* O - Process ID or 0 */
2890 start_process(const char *command
, /* I - Full path to command */
2891 char *argv
[], /* I - Command-line arguments */
2892 char *envp
[], /* I - Environment */
2893 int infd
, /* I - Standard input file descriptor */
2894 int outfd
, /* I - Standard output file descriptor */
2895 int errfd
, /* I - Standard error file descriptor */
2896 int root
) /* I - Run as root? */
2898 int fd
; /* Looping var */
2899 int pid
; /* Process ID */
2902 LogMessage(L_DEBUG
, "start_process(\"%s\", %08x, %08x, %d, %d, %d)",
2903 command
, argv
, envp
, infd
, outfd
, errfd
);
2905 if ((pid
= fork()) == 0)
2908 * Child process goes here...
2910 * Update stdin/stdout/stderr as needed...
2924 * Close extra file descriptors...
2927 for (fd
= 3; fd
< MaxFDs
; fd
++)
2931 * Change user to something "safe"...
2934 if (!root
&& getuid() == 0)
2937 * Running as root, so change to non-priviledged user...
2948 * Reset group membership to just the main one we belong to.
2954 * Change umask to restrict permissions on created files...
2960 * Execute the command; if for some reason this doesn't work,
2961 * return the error code...
2964 execve(command
, argv
, envp
);
2973 * Error - couldn't fork a new process!
2976 LogMessage(L_ERROR
, "Unable to fork %s - %s.", command
, strerror(errno
));
2986 * End of "$Id: job.c,v 1.139 2001/11/09 17:19:43 mike Exp $".