2 * "$Id: job.c,v 1.124.2.64 2003/04/10 20:15:53 mike Exp $"
4 * Job management routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2003 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 for the given destination/user...
29 * CheckJobs() - Check the pending jobs and start any if the
30 * destination is available.
31 * CleanJobs() - Clean out old jobs.
32 * FreeAllJobs() - Free all jobs from memory.
33 * FindJob() - Find the specified job.
34 * GetPrinterJobCount() - Get the number of pending, processing,
35 * or held jobs in a printer or class.
36 * GetUserJobCount() - Get the number of pending, processing,
37 * or held jobs for a user.
38 * HoldJob() - Hold the specified job.
39 * LoadAllJobs() - Load all jobs from disk.
40 * LoadJob() - Load a job from disk.
41 * MoveJob() - Move the specified job to a different
43 * ReleaseJob() - Release the specified job.
44 * RestartJob() - Restart the specified job.
45 * SaveJob() - Save a job to disk.
46 * SetJobHoldUntil() - Set the hold time for a job...
47 * SetJobPriority() - Set the priority of a job, moving it up/down
48 * in the list as needed.
49 * StartJob() - Start a print job.
50 * StopAllJobs() - Stop all print jobs.
51 * StopJob() - Stop a print job.
52 * UpdateJob() - Read a status update from a job's filters.
53 * ipp_length() - Compute the size of the buffer needed to hold
54 * the textual IPP attributes.
55 * start_process() - Start a background process.
59 * Include necessary headers...
70 static mime_filter_t gziptoany_filter
=
72 NULL
, /* Source type */
73 NULL
, /* Destination type */
75 "gziptoany" /* Filter program to run */
83 static int ipp_length(ipp_t
*ipp
);
84 static void set_time(job_t
*job
, const char *name
);
85 static int start_process(const char *command
, char *argv
[],
86 char *envp
[], int infd
, int outfd
,
87 int errfd
, int backfd
, int root
,
92 * 'AddJob()' - Add a new job to the job queue...
95 job_t
* /* O - New job record */
96 AddJob(int priority
, /* I - Job priority */
97 const char *dest
) /* I - Job destination */
99 job_t
*job
, /* New job record */
100 *current
, /* Current job in queue */
101 *prev
; /* Previous job in queue */
104 job
= calloc(sizeof(job_t
), 1);
106 job
->id
= NextJobId
++;
107 job
->priority
= priority
;
108 job
->back_pipes
[0] = -1;
109 job
->back_pipes
[1] = -1;
110 job
->print_pipes
[0] = -1;
111 job
->print_pipes
[1] = -1;
112 job
->status_pipe
= -1;
114 SetString(&job
->dest
, dest
);
118 for (current
= Jobs
, prev
= NULL
;
120 prev
= current
, current
= current
->next
)
121 if (job
->priority
> current
->priority
)
135 * 'CancelJob()' - Cancel the specified print job.
139 CancelJob(int id
, /* I - Job to cancel */
140 int purge
) /* I - Purge jobs? */
142 int i
; /* Looping var */
143 job_t
*current
, /* Current job */
144 *prev
; /* Previous job in list */
145 char filename
[1024]; /* Job filename */
148 LogMessage(L_DEBUG
, "CancelJob: id = %d", id
);
150 for (current
= Jobs
, prev
= NULL
; current
!= NULL
; prev
= current
, current
= current
->next
)
151 if (current
->id
== id
)
154 * Stop any processes that are working on the current...
157 DEBUG_puts("CancelJob: found job in list.");
159 if (current
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
160 StopJob(current
->id
, 0);
162 current
->state
->values
[0].integer
= IPP_JOB_CANCELLED
;
164 set_time(current
, "time-at-completed");
167 * Remove the print file for good if we aren't preserving jobs or
171 current
->current_file
= 0;
173 if (!JobHistory
|| !JobFiles
|| purge
||
174 (current
->dtype
& CUPS_PRINTER_REMOTE
))
175 for (i
= 1; i
<= current
->num_files
; i
++)
177 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
182 if (JobHistory
&& !purge
&& !(current
->dtype
& CUPS_PRINTER_REMOTE
))
185 * Save job state info...
188 SaveJob(current
->id
);
193 * Remove the job info file...
196 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
,
201 * Update pointers if we aren't preserving jobs...
205 Jobs
= current
->next
;
207 prev
->next
= current
->next
;
210 * Free all memory used...
213 if (current
->attrs
!= NULL
)
214 ippDelete(current
->attrs
);
216 if (current
->num_files
> 0)
218 free(current
->compressions
);
219 free(current
->filetypes
);
222 ClearString(¤t
->username
);
223 ClearString(¤t
->dest
);
236 * 'CancelJobs()' - Cancel all jobs for the given destination/user...
240 CancelJobs(const char *dest
, /* I - Destination to cancel */
241 const char *username
,/* I - Username or NULL */
242 int purge
) /* I - Purge jobs? */
244 job_t
*current
; /* Current job */
247 for (current
= Jobs
; current
!= NULL
;)
248 if ((dest
== NULL
|| !strcmp(current
->dest
, dest
)) &&
249 (username
== NULL
|| !strcmp(current
->username
, username
)))
252 * Cancel all jobs matching this destination/user...
255 CancelJob(current
->id
, purge
);
260 current
= current
->next
;
267 * 'CheckJobs()' - Check the pending jobs and start any if the destination
274 job_t
*current
, /* Current job in queue */
275 *next
; /* Next job in queue */
276 printer_t
*printer
, /* Printer destination */
277 *pclass
; /* Printer class destination */
280 DEBUG_puts("CheckJobs()");
282 for (current
= Jobs
; current
!= NULL
; current
= next
)
285 * Save next pointer in case the job is cancelled en-route.
288 next
= current
->next
;
291 * Start held jobs if they are ready...
294 if (current
->state
->values
[0].integer
== IPP_JOB_HELD
&&
295 current
->hold_until
&&
296 current
->hold_until
< time(NULL
))
297 current
->state
->values
[0].integer
= IPP_JOB_PENDING
;
300 * Start pending jobs if the destination is available...
303 if (current
->state
->values
[0].integer
== IPP_JOB_PENDING
)
305 if ((pclass
= FindClass(current
->dest
)) != NULL
)
308 * If the class is remote, just pass it to the remote server...
311 if (pclass
->type
& CUPS_PRINTER_REMOTE
)
313 else if (pclass
->state
!= IPP_PRINTER_STOPPED
)
314 printer
= FindAvailablePrinter(current
->dest
);
319 printer
= FindPrinter(current
->dest
);
321 if (printer
!= NULL
&& (printer
->type
& CUPS_PRINTER_IMPLICIT
))
324 * Handle implicit classes...
329 if (pclass
->state
!= IPP_PRINTER_STOPPED
)
330 printer
= FindAvailablePrinter(current
->dest
);
335 if (printer
== NULL
&& pclass
== NULL
)
338 * Whoa, the printer and/or class for this destination went away;
342 LogMessage(L_WARN
, "Printer/class %s has gone away; cancelling job %d!",
343 current
->dest
, current
->id
);
344 CancelJob(current
->id
, 1);
346 else if (printer
!= NULL
)
349 * See if the printer is available or remote and not printing a job;
350 * if so, start the job...
353 if (printer
->state
== IPP_PRINTER_IDLE
|| /* Printer is idle */
354 ((printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is remote */
355 !printer
->job
)) /* and not printing a job */
356 StartJob(current
->id
, printer
);
364 * 'CleanJobs()' - Clean out old jobs.
370 job_t
*job
, /* Current job */
371 *next
; /* Next job */
377 for (job
= Jobs
; job
&& NumJobs
>= MaxJobs
; job
= next
)
381 if (job
->state
->values
[0].integer
>= IPP_JOB_CANCELLED
)
382 CancelJob(job
->id
, 1);
388 * 'FreeAllJobs()' - Free all jobs from memory.
394 job_t
*job
, /* Current job */
395 *next
; /* Next job */
402 for (job
= Jobs
; job
; job
= next
)
406 ippDelete(job
->attrs
);
408 if (job
->num_files
> 0)
410 free(job
->compressions
);
411 free(job
->filetypes
);
424 * 'FindJob()' - Find the specified job.
427 job_t
* /* O - Job data */
428 FindJob(int id
) /* I - Job ID */
430 job_t
*current
; /* Current job */
433 for (current
= Jobs
; current
!= NULL
; current
= current
->next
)
434 if (current
->id
== id
)
442 * 'GetPrinterJobCount()' - Get the number of pending, processing,
443 * or held jobs in a printer or class.
446 int /* O - Job count */
447 GetPrinterJobCount(const char *dest
) /* I - Printer or class name */
449 int count
; /* Job count */
450 job_t
*job
; /* Current job */
453 for (job
= Jobs
, count
= 0; job
!= NULL
; job
= job
->next
)
454 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
&&
455 strcasecmp(job
->dest
, dest
) == 0)
463 * 'GetUserJobCount()' - Get the number of pending, processing,
464 * or held jobs for a user.
467 int /* O - Job count */
468 GetUserJobCount(const char *username
) /* I - Username */
470 int count
; /* Job count */
471 job_t
*job
; /* Current job */
474 for (job
= Jobs
, count
= 0; job
!= NULL
; job
= job
->next
)
475 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
&&
476 strcmp(job
->username
, username
) == 0)
484 * 'HoldJob()' - Hold the specified job.
488 HoldJob(int id
) /* I - Job ID */
490 job_t
*job
; /* Job data */
493 LogMessage(L_DEBUG
, "HoldJob: id = %d", id
);
495 if ((job
= FindJob(id
)) == NULL
)
498 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
501 DEBUG_puts("HoldJob: setting state to held...");
503 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
512 * 'LoadAllJobs()' - Load all jobs from disk.
518 DIR *dir
; /* Directory */
519 DIRENT
*dent
; /* Directory entry */
520 char filename
[1024]; /* Full filename of job file */
521 int fd
; /* File descriptor */
522 job_t
*job
, /* New job */
523 *current
, /* Current job */
524 *prev
; /* Previous job */
525 int jobid
, /* Current job ID */
526 fileid
; /* Current file ID */
527 ipp_attribute_t
*attr
; /* Job attribute */
528 char method
[HTTP_MAX_URI
],
529 /* Method portion of URI */
530 username
[HTTP_MAX_URI
],
531 /* Username portion of URI */
533 /* Host portion of URI */
534 resource
[HTTP_MAX_URI
];
535 /* Resource portion of URI */
536 int port
; /* Port portion of URI */
537 printer_t
*p
; /* Printer or class */
538 const char *dest
; /* Destination */
539 mime_type_t
**filetypes
; /* New filetypes array */
540 int *compressions
; /* New compressions array */
544 * First open the requests directory...
547 LogMessage(L_DEBUG
, "LoadAllJobs: Scanning %s...", RequestRoot
);
551 if ((dir
= opendir(RequestRoot
)) == NULL
)
553 LogMessage(L_ERROR
, "LoadAllJobs: Unable to open spool directory %s: %s",
554 RequestRoot
, strerror(errno
));
559 * Read all the c##### files...
562 while ((dent
= readdir(dir
)) != NULL
)
563 if (NAMLEN(dent
) == 6 && dent
->d_name
[0] == 'c')
566 * Allocate memory for the job...
569 if ((job
= calloc(sizeof(job_t
), 1)) == NULL
)
571 LogMessage(L_ERROR
, "LoadAllJobs: Ran out of memory for jobs!");
576 if ((job
->attrs
= ippNew()) == NULL
)
579 LogMessage(L_ERROR
, "LoadAllJobs: Ran out of memory for job attributes!");
585 * Assign the job ID...
588 job
->id
= atoi(dent
->d_name
+ 1);
590 LogMessage(L_DEBUG
, "LoadAllJobs: Loading attributes for job %d...\n",
593 if (job
->id
>= NextJobId
)
594 NextJobId
= job
->id
+ 1;
597 * Load the job control file...
600 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->d_name
);
601 if ((fd
= open(filename
, O_RDONLY
)) < 0)
603 LogMessage(L_ERROR
, "LoadAllJobs: Unable to open job control file \"%s\" - %s!",
604 filename
, strerror(errno
));
605 ippDelete(job
->attrs
);
612 if (ippReadFile(fd
, job
->attrs
) != IPP_DATA
)
614 LogMessage(L_ERROR
, "LoadAllJobs: Unable to read job control file \"%s\"!",
617 ippDelete(job
->attrs
);
626 job
->state
= ippFindAttribute(job
->attrs
, "job-state", IPP_TAG_ENUM
);
628 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri", IPP_TAG_URI
)) == NULL
)
630 LogMessage(L_ERROR
, "LoadAllJobs: No job-printer-uri attribute in control file \"%s\"!",
632 ippDelete(job
->attrs
);
638 httpSeparate(attr
->values
[0].string
.text
, method
, username
, host
,
641 if ((dest
= ValidateDest(host
, resource
, &(job
->dtype
))) == NULL
&&
642 job
->state
!= NULL
&&
643 job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
)
646 * Job queued on remote printer or class, so add it...
649 if (strncmp(resource
, "/classes/", 9) == 0)
651 p
= AddClass(resource
+ 9);
652 SetString(&p
->make_model
, "Remote Class on unknown");
656 p
= AddPrinter(resource
+ 10);
657 SetString(&p
->make_model
, "Remote Printer on unknown");
660 p
->state
= IPP_PRINTER_STOPPED
;
661 p
->type
|= CUPS_PRINTER_REMOTE
;
662 p
->browse_time
= 2147483647;
664 SetString(&p
->location
, "Location Unknown");
665 SetString(&p
->info
, "No Information Available");
666 p
->hostname
[0] = '\0';
674 LogMessage(L_ERROR
, "LoadAllJobs: Unable to queue job for destination \"%s\"!",
675 attr
->values
[0].string
.text
);
676 ippDelete(job
->attrs
);
682 SetString(&job
->dest
, dest
);
684 job
->sheets
= ippFindAttribute(job
->attrs
, "job-media-sheets-completed",
686 job
->job_sheets
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
688 attr
= ippFindAttribute(job
->attrs
, "job-priority", IPP_TAG_INTEGER
);
689 job
->priority
= attr
->values
[0].integer
;
691 attr
= ippFindAttribute(job
->attrs
, "job-originating-user-name", IPP_TAG_NAME
);
692 SetString(&job
->username
, attr
->values
[0].string
.text
);
695 * Insert the job into the array, sorting by job priority and ID...
698 for (current
= Jobs
, prev
= NULL
;
700 prev
= current
, current
= current
->next
)
701 if (job
->priority
> current
->priority
)
703 else if (job
->priority
== current
->priority
&& job
->id
< current
->id
)
715 * Set the job hold-until time and state...
718 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
720 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
721 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
724 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
726 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
728 else if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
729 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
733 * Read all the d##### files...
738 while ((dent
= readdir(dir
)) != NULL
)
739 if (NAMLEN(dent
) > 7 && dent
->d_name
[0] == 'd')
745 jobid
= atoi(dent
->d_name
+ 1);
746 fileid
= atoi(dent
->d_name
+ 7);
748 LogMessage(L_DEBUG
, "LoadAllJobs: Auto-typing document file %s...",
751 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->d_name
);
753 if ((job
= FindJob(jobid
)) == NULL
)
755 LogMessage(L_ERROR
, "LoadAllJobs: Orphaned print file \"%s\"!",
761 if (fileid
> job
->num_files
)
763 if (job
->num_files
== 0)
765 compressions
= (int *)calloc(fileid
, sizeof(int));
766 filetypes
= (mime_type_t
**)calloc(fileid
, sizeof(mime_type_t
*));
770 compressions
= (int *)realloc(job
->compressions
,
771 sizeof(int) * fileid
);
772 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
773 sizeof(mime_type_t
*) * fileid
);
776 if (compressions
== NULL
|| filetypes
== NULL
)
778 LogMessage(L_ERROR
, "LoadAllJobs: Ran out of memory for job file types!");
782 job
->compressions
= compressions
;
783 job
->filetypes
= filetypes
;
784 job
->num_files
= fileid
;
787 job
->filetypes
[fileid
- 1] = mimeFileType(MimeDatabase
, filename
,
788 job
->compressions
+ fileid
- 1);
790 if (job
->filetypes
[fileid
- 1] == NULL
)
791 job
->filetypes
[fileid
- 1] = mimeType(MimeDatabase
, "application",
798 * Clean out old jobs as needed...
804 * Check to see if we need to start any jobs...
812 * 'MoveJob()' - Move the specified job to a different destination.
816 MoveJob(int id
, /* I - Job ID */
817 const char *dest
) /* I - Destination */
819 job_t
*current
;/* Current job */
820 ipp_attribute_t
*attr
; /* job-printer-uri attribute */
821 printer_t
*p
; /* Destination printer or class */
824 if ((p
= FindPrinter(dest
)) == NULL
)
830 for (current
= Jobs
; current
!= NULL
; current
= current
->next
)
831 if (current
->id
== id
)
833 if (current
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
836 SetString(¤t
->dest
, dest
);
837 current
->dtype
= p
->type
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
838 CUPS_PRINTER_IMPLICIT
| CUPS_PRINTER_FAX
);
840 if ((attr
= ippFindAttribute(current
->attrs
, "job-printer-uri", IPP_TAG_URI
)) != NULL
)
842 free(attr
->values
[0].string
.text
);
843 attr
->values
[0].string
.text
= strdup(p
->uri
);
846 SaveJob(current
->id
);
854 * 'ReleaseJob()' - Release the specified job.
858 ReleaseJob(int id
) /* I - Job ID */
860 job_t
*job
; /* Job data */
863 LogMessage(L_DEBUG
, "ReleaseJob: id = %d", id
);
865 if ((job
= FindJob(id
)) == NULL
)
868 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
870 DEBUG_puts("ReleaseJob: setting state to pending...");
872 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
880 * 'RestartJob()' - Restart the specified job.
884 RestartJob(int id
) /* I - Job ID */
886 job_t
*job
; /* Job data */
889 if ((job
= FindJob(id
)) == NULL
)
892 if (job
->state
->values
[0].integer
== IPP_JOB_STOPPED
|| JobFiles
)
895 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
903 * 'SaveJob()' - Save a job to disk.
907 SaveJob(int id
) /* I - Job ID */
909 job_t
*job
; /* Pointer to job */
910 char filename
[1024]; /* Job control filename */
911 int fd
; /* File descriptor */
914 if ((job
= FindJob(id
)) == NULL
)
917 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
, id
);
919 if ((fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
921 LogMessage(L_ERROR
, "SaveJob: Unable to create job control file \"%s\" - %s.",
922 filename
, strerror(errno
));
927 fchown(fd
, User
, Group
);
929 ippWriteFile(fd
, job
->attrs
);
931 LogMessage(L_DEBUG2
, "SaveJob: Closing file %d...", fd
);
938 * 'SetJobHoldUntil()' - Set the hold time for a job...
942 SetJobHoldUntil(int id
, /* I - Job ID */
943 const char *when
) /* I - When to resume */
945 job_t
*job
; /* Pointer to job */
946 time_t curtime
; /* Current time */
947 struct tm
*curdate
; /* Current date */
948 int hour
; /* Hold hour */
949 int minute
; /* Hold minute */
950 int second
; /* Hold second */
953 LogMessage(L_DEBUG
, "SetJobHoldUntil(%d, \"%s\")", id
, when
);
955 if ((job
= FindJob(id
)) == NULL
)
960 if (strcmp(when
, "indefinite") == 0)
963 * Hold indefinitely...
968 else if (strcmp(when
, "day-time") == 0)
971 * Hold to 6am the next morning unless local time is < 6pm.
974 curtime
= time(NULL
);
975 curdate
= localtime(&curtime
);
977 if (curdate
->tm_hour
< 18)
978 job
->hold_until
= curtime
;
980 job
->hold_until
= curtime
+
981 ((29 - curdate
->tm_hour
) * 60 + 59 -
982 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
984 else if (strcmp(when
, "evening") == 0 || strcmp(when
, "night") == 0)
987 * Hold to 6pm unless local time is > 6pm or < 6am.
990 curtime
= time(NULL
);
991 curdate
= localtime(&curtime
);
993 if (curdate
->tm_hour
< 6 || curdate
->tm_hour
>= 18)
994 job
->hold_until
= curtime
;
996 job
->hold_until
= curtime
+
997 ((17 - curdate
->tm_hour
) * 60 + 59 -
998 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1000 else if (strcmp(when
, "second-shift") == 0)
1003 * Hold to 4pm unless local time is > 4pm.
1006 curtime
= time(NULL
);
1007 curdate
= localtime(&curtime
);
1009 if (curdate
->tm_hour
>= 16)
1010 job
->hold_until
= curtime
;
1012 job
->hold_until
= curtime
+
1013 ((15 - curdate
->tm_hour
) * 60 + 59 -
1014 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1016 else if (strcmp(when
, "third-shift") == 0)
1019 * Hold to 12am unless local time is < 8am.
1022 curtime
= time(NULL
);
1023 curdate
= localtime(&curtime
);
1025 if (curdate
->tm_hour
< 8)
1026 job
->hold_until
= curtime
;
1028 job
->hold_until
= curtime
+
1029 ((23 - curdate
->tm_hour
) * 60 + 59 -
1030 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1032 else if (strcmp(when
, "weekend") == 0)
1035 * Hold to weekend unless we are in the weekend.
1038 curtime
= time(NULL
);
1039 curdate
= localtime(&curtime
);
1041 if (curdate
->tm_wday
== 0 || curdate
->tm_wday
== 6)
1042 job
->hold_until
= curtime
;
1044 job
->hold_until
= curtime
+
1045 (((5 - curdate
->tm_wday
) * 24 +
1046 (17 - curdate
->tm_hour
)) * 60 + 59 -
1047 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1049 else if (sscanf(when
, "%d:%d:%d", &hour
, &minute
, &second
) >= 2)
1052 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1055 curtime
= time(NULL
);
1056 curdate
= gmtime(&curtime
);
1058 job
->hold_until
= curtime
+
1059 ((hour
- curdate
->tm_hour
) * 60 + minute
-
1060 curdate
->tm_min
) * 60 + second
- curdate
->tm_sec
;
1063 * Hold until next day as needed...
1066 if (job
->hold_until
< curtime
)
1067 job
->hold_until
+= 24 * 60 * 60 * 60;
1070 LogMessage(L_DEBUG
, "SetJobHoldUntil: hold_until = %d", (int)job
->hold_until
);
1075 * 'SetJobPriority()' - Set the priority of a job, moving it up/down in the
1080 SetJobPriority(int id
, /* I - Job ID */
1081 int priority
) /* I - New priority (0 to 100) */
1083 job_t
*job
, /* Job to change */
1084 *current
, /* Current job */
1085 *prev
; /* Previous job */
1086 ipp_attribute_t
*attr
; /* Job attribute */
1093 for (current
= Jobs
, prev
= NULL
;
1095 prev
= current
, current
= current
->next
)
1096 if (current
->id
== id
)
1099 if (current
== NULL
)
1103 * Set the new priority...
1107 job
->priority
= priority
;
1109 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
1110 attr
->values
[0].integer
= priority
;
1112 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1118 * See if we need to do any sorting...
1121 if ((prev
== NULL
|| job
->priority
< prev
->priority
) &&
1122 (job
->next
== NULL
|| job
->next
->priority
< job
->priority
))
1126 * Remove the job from the list, and then insert it where it belongs...
1132 prev
->next
= job
->next
;
1134 for (current
= Jobs
, prev
= NULL
;
1136 prev
= current
, current
= current
->next
)
1137 if (job
->priority
> current
->priority
)
1140 job
->next
= current
;
1149 * 'StartJob()' - Start a print job.
1153 StartJob(int id
, /* I - Job ID */
1154 printer_t
*printer
) /* I - Printer to print job */
1156 job_t
*current
; /* Current job */
1157 int i
; /* Looping var */
1158 int slot
; /* Pipe slot */
1159 int num_filters
; /* Number of filters for job */
1160 mime_filter_t
*filters
; /* Filters for job */
1161 char method
[255], /* Method for output */
1162 *optptr
; /* Pointer to options */
1163 ipp_attribute_t
*attr
; /* Current attribute */
1164 int pid
; /* Process ID of new filter process */
1165 int banner_page
; /* 1 if banner page, 0 otherwise */
1166 int statusfds
[2], /* Pipes used between the filters and scheduler */
1167 filterfds
[2][2]; /* Pipes used between the filters */
1168 int envc
; /* Number of environment variables */
1169 char *argv
[8], /* Filter command-line arguments */
1170 filename
[1024], /* Job filename */
1171 command
[1024], /* Full path to filter/backend command */
1172 jobid
[255], /* Job ID string */
1173 title
[IPP_MAX_NAME
], /* Job title string */
1174 copies
[255], /* # copies string */
1175 *envp
[100], /* Environment variables */
1177 processPath
[1050], /* CFProcessPath environment variable */
1178 #endif /* __APPLE__ */
1179 path
[1024], /* PATH environment variable */
1180 language
[255], /* LANG environment variable */
1181 charset
[255], /* CHARSET environment variable */
1182 classification
[1024], /* CLASSIFICATION environment variable */
1183 content_type
[1024], /* CONTENT_TYPE environment variable */
1184 device_uri
[1024], /* DEVICE_URI environment variable */
1185 ppd
[1024], /* PPD environment variable */
1186 class_name
[255], /* CLASS environment variable */
1187 printer_name
[255], /* PRINTER environment variable */
1188 root
[1024], /* CUPS_SERVERROOT environment variable */
1189 cache
[255], /* RIP_MAX_CACHE environment variable */
1190 tmpdir
[1024], /* TMPDIR environment variable */
1191 ld_library_path
[1024], /* LD_LIBRARY_PATH environment variable */
1192 ld_preload
[1024], /* LD_PRELOAD environment variable */
1193 dyld_library_path
[1024],/* DYLD_LIBRARY_PATH environment variable */
1194 shlib_path
[1024], /* SHLIB_PATH environment variable */
1195 nlspath
[1024], /* NLSPATH environment variable */
1196 datadir
[1024], /* CUPS_DATADIR environment variable */
1197 fontpath
[1050]; /* CUPS_FONTPATH environment variable */
1198 static char *options
= NULL
; /* Full list of options */
1199 static int optlength
= 0; /* Length of option buffer */
1202 LogMessage(L_DEBUG
, "StartJob(%d, %p)", id
, printer
);
1204 for (current
= Jobs
; current
!= NULL
; current
= current
->next
)
1205 if (current
->id
== id
)
1208 if (current
== NULL
)
1211 LogMessage(L_DEBUG
, "StartJob() id = %d, file = %d/%d", id
,
1212 current
->current_file
, current
->num_files
);
1214 if (current
->num_files
== 0)
1216 LogMessage(L_ERROR
, "Job ID %d has no files! Cancelling it!", id
);
1222 * Figure out what filters are required to convert from
1223 * the source to the destination type...
1232 * Remote jobs and raw queues go directly to the printer without
1236 LogMessage(L_DEBUG
, "StartJob: Sending job to queue tagged as raw...");
1243 * Local jobs get filtered...
1246 filters
= mimeFilter(MimeDatabase
, current
->filetypes
[current
->current_file
],
1247 printer
->filetype
, &num_filters
, MAX_FILTERS
- 1);
1249 if (num_filters
== 0)
1251 LogMessage(L_ERROR
, "Unable to convert file %d to printable format for job %d!",
1252 current
->current_file
, current
->id
);
1253 LogMessage(L_INFO
, "Hint: Do you have ESP Ghostscript installed?");
1255 if (LogLevel
< L_DEBUG
)
1256 LogMessage(L_INFO
, "Hint: Try setting the LogLevel to \"debug\".");
1258 current
->current_file
++;
1260 if (current
->current_file
== current
->num_files
)
1261 CancelJob(current
->id
, 0);
1267 * Remove NULL ("-") filters...
1270 for (i
= 0; i
< num_filters
;)
1271 if (strcmp(filters
[i
].filter
, "-") == 0)
1274 if (i
< num_filters
)
1275 memcpy(filters
+ i
, filters
+ i
+ 1,
1276 (num_filters
- i
) * sizeof(mime_filter_t
));
1281 if (num_filters
== 0)
1289 * Compute filter cost...
1292 for (i
= 0; i
< num_filters
; i
++)
1293 current
->cost
+= filters
[i
].cost
;
1298 * See if the filter cost is too high...
1301 if ((FilterLevel
+ current
->cost
) > FilterLimit
&& FilterLevel
> 0 &&
1305 * Don't print this job quite yet...
1308 if (filters
!= NULL
)
1311 LogMessage(L_INFO
, "Holding job %d because filter limit has been reached.",
1313 LogMessage(L_DEBUG
, "StartJob: id = %d, file = %d, "
1314 "cost = %d, level = %d, limit = %d",
1315 id
, current
->current_file
, current
->cost
, FilterLevel
,
1320 FilterLevel
+= current
->cost
;
1323 * Add decompression filters, if any...
1326 if (current
->compressions
[current
->current_file
])
1329 * Add gziptoany filter to the front of the list...
1332 mime_filter_t
*temp_filters
;
1334 if (num_filters
== 0)
1335 temp_filters
= malloc(sizeof(mime_filter_t
));
1337 temp_filters
= realloc(filters
,
1338 sizeof(mime_filter_t
) * (num_filters
+ 1));
1340 if (temp_filters
== NULL
)
1342 LogMessage(L_ERROR
, "Unable to add decompression filter - %s",
1347 current
->current_file
++;
1349 if (current
->current_file
== current
->num_files
)
1350 CancelJob(current
->id
, 0);
1355 filters
= temp_filters
;
1356 memmove(filters
+ 1, filters
, num_filters
* sizeof(mime_filter_t
));
1357 *filters
= gziptoany_filter
;
1362 * Update the printer and job state to "processing"...
1365 current
->state
->values
[0].integer
= IPP_JOB_PROCESSING
;
1366 current
->status
= 0;
1367 current
->printer
= printer
;
1368 printer
->job
= current
;
1369 SetPrinterState(printer
, IPP_PRINTER_PROCESSING
);
1371 if (current
->current_file
== 0)
1373 set_time(current
, "time-at-processing");
1374 pipe(current
->back_pipes
);
1378 * Determine if we are printing a banner page or not...
1381 if (current
->job_sheets
== NULL
)
1383 LogMessage(L_DEBUG
, "No job-sheets attribute.");
1384 if ((current
->job_sheets
=
1385 ippFindAttribute(current
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
1386 LogMessage(L_DEBUG
, "... but someone added one without setting job_sheets!");
1388 else if (current
->job_sheets
->num_values
== 1)
1389 LogMessage(L_DEBUG
, "job-sheets=%s",
1390 current
->job_sheets
->values
[0].string
.text
);
1392 LogMessage(L_DEBUG
, "job-sheets=%s,%s",
1393 current
->job_sheets
->values
[0].string
.text
,
1394 current
->job_sheets
->values
[1].string
.text
);
1396 if (printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
))
1398 else if (current
->job_sheets
== NULL
)
1400 else if (strcasecmp(current
->job_sheets
->values
[0].string
.text
, "none") != 0 &&
1401 current
->current_file
== 0)
1403 else if (current
->job_sheets
->num_values
> 1 &&
1404 strcasecmp(current
->job_sheets
->values
[1].string
.text
, "none") != 0 &&
1405 current
->current_file
== (current
->num_files
- 1))
1410 LogMessage(L_DEBUG
, "banner_page = %d", banner_page
);
1413 * Building the options string is harder than it needs to be, but
1414 * for the moment we need to pass strings for command-line args and
1415 * not IPP attribute pointers... :)
1417 * First allocate/reallocate the option buffer as needed...
1420 i
= ipp_length(current
->attrs
);
1427 optptr
= realloc(options
, i
);
1431 LogMessage(L_CRIT
, "StartJob: Unable to allocate %d bytes for option buffer for job %d!",
1434 if (filters
!= NULL
)
1437 FilterLevel
-= current
->cost
;
1448 * Now loop through the attributes and convert them to the textual
1449 * representation used by the filters...
1455 snprintf(title
, sizeof(title
), "%s-%d", printer
->name
, current
->id
);
1456 strcpy(copies
, "1");
1458 for (attr
= current
->attrs
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1460 if (strcmp(attr
->name
, "copies") == 0 &&
1461 attr
->value_tag
== IPP_TAG_INTEGER
)
1464 * Don't use the # copies attribute if we are printing the job sheets...
1468 sprintf(copies
, "%d", attr
->values
[0].integer
);
1470 else if (strcmp(attr
->name
, "job-name") == 0 &&
1471 (attr
->value_tag
== IPP_TAG_NAME
||
1472 attr
->value_tag
== IPP_TAG_NAMELANG
))
1473 strlcpy(title
, attr
->values
[0].string
.text
, sizeof(title
));
1474 else if (attr
->group_tag
== IPP_TAG_JOB
)
1477 * Filter out other unwanted attributes...
1480 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
1481 attr
->value_tag
== IPP_TAG_NAMELANG
||
1482 attr
->value_tag
== IPP_TAG_TEXTLANG
||
1483 attr
->value_tag
== IPP_TAG_URI
||
1484 attr
->value_tag
== IPP_TAG_URISCHEME
||
1485 attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
) /* Not yet supported */
1488 if (strncmp(attr
->name
, "time-", 5) == 0)
1491 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1492 !(printer
->type
& CUPS_PRINTER_REMOTE
))
1495 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1496 strcmp(attr
->name
, "job-billing") != 0 &&
1497 strcmp(attr
->name
, "job-sheets") != 0 &&
1498 strcmp(attr
->name
, "job-hold-until") != 0 &&
1499 strcmp(attr
->name
, "job-priority") != 0)
1502 if ((strcmp(attr
->name
, "page-label") == 0 ||
1503 strcmp(attr
->name
, "page-border") == 0 ||
1504 strncmp(attr
->name
, "number-up", 9) == 0) &&
1509 * Otherwise add them to the list...
1512 if (optptr
> options
)
1513 strlcat(optptr
, " ", optlength
- (optptr
- options
));
1515 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
1517 strlcat(optptr
, attr
->name
, optlength
- (optptr
- options
));
1518 strlcat(optptr
, "=", optlength
- (optptr
- options
));
1521 for (i
= 0; i
< attr
->num_values
; i
++)
1524 strlcat(optptr
, ",", optlength
- (optptr
- options
));
1526 optptr
+= strlen(optptr
);
1528 switch (attr
->value_tag
)
1530 case IPP_TAG_INTEGER
:
1532 snprintf(optptr
, optlength
- (optptr
- options
),
1533 "%d", attr
->values
[i
].integer
);
1536 case IPP_TAG_BOOLEAN
:
1537 if (!attr
->values
[i
].boolean
)
1538 strlcat(optptr
, "no", optlength
- (optptr
- options
));
1540 case IPP_TAG_NOVALUE
:
1541 strlcat(optptr
, attr
->name
,
1542 optlength
- (optptr
- options
));
1545 case IPP_TAG_RANGE
:
1546 if (attr
->values
[i
].range
.lower
== attr
->values
[i
].range
.upper
)
1547 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1548 "%d", attr
->values
[i
].range
.lower
);
1550 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1551 "%d-%d", attr
->values
[i
].range
.lower
,
1552 attr
->values
[i
].range
.upper
);
1555 case IPP_TAG_RESOLUTION
:
1556 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1557 "%dx%d%s", attr
->values
[i
].resolution
.xres
,
1558 attr
->values
[i
].resolution
.yres
,
1559 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
1563 case IPP_TAG_STRING
:
1566 case IPP_TAG_KEYWORD
:
1567 case IPP_TAG_CHARSET
:
1568 case IPP_TAG_LANGUAGE
:
1569 if (strchr(attr
->values
[i
].string
.text
, ' ') != NULL
||
1570 strchr(attr
->values
[i
].string
.text
, '\t') != NULL
||
1571 strchr(attr
->values
[i
].string
.text
, '\n') != NULL
)
1573 strlcat(optptr
, "\'", optlength
- (optptr
- options
));
1574 strlcat(optptr
, attr
->values
[i
].string
.text
,
1575 optlength
- (optptr
- options
));
1576 strlcat(optptr
, "\'", optlength
- (optptr
- options
));
1579 strlcat(optptr
, attr
->values
[i
].string
.text
,
1580 optlength
- (optptr
- options
));
1584 break; /* anti-compiler-warning-code */
1588 optptr
+= strlen(optptr
);
1593 * Build the command-line arguments for the filters. Each filter
1594 * has 6 or 7 arguments:
1598 * argv[2] = username
1600 * argv[4] = # copies
1602 * argv[6] = filename (optional; normally stdin)
1604 * This allows legacy printer drivers that use the old System V
1605 * printing interface to be used by CUPS.
1608 sprintf(jobid
, "%d", current
->id
);
1609 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
1610 current
->id
, current
->current_file
+ 1);
1612 argv
[0] = printer
->name
;
1614 argv
[2] = current
->username
;
1621 LogMessage(L_DEBUG
, "StartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1622 argv
[0], argv
[1], argv
[2], argv
[3], argv
[4], argv
[5], argv
[6]);
1625 * Create environment variable strings for the filters...
1628 attr
= ippFindAttribute(current
->attrs
, "attributes-natural-language",
1631 switch (strlen(attr
->values
[0].string
.text
))
1635 * This is an unknown or badly formatted language code; use
1636 * the POSIX locale...
1639 strcpy(language
, "LANG=C");
1644 * Just the language code (ll)...
1647 snprintf(language
, sizeof(language
), "LANG=%s",
1648 attr
->values
[0].string
.text
);
1653 * Language and country code (ll-cc)...
1656 snprintf(language
, sizeof(language
), "LANG=%c%c_%c%c",
1657 attr
->values
[0].string
.text
[0],
1658 attr
->values
[0].string
.text
[1],
1659 toupper(attr
->values
[0].string
.text
[3]),
1660 toupper(attr
->values
[0].string
.text
[4]));
1664 attr
= ippFindAttribute(current
->attrs
, "document-format",
1667 (optptr
= strstr(attr
->values
[0].string
.text
, "charset=")) != NULL
)
1668 snprintf(charset
, sizeof(charset
), "CHARSET=%s", optptr
+ 8);
1671 attr
= ippFindAttribute(current
->attrs
, "attributes-charset",
1673 snprintf(charset
, sizeof(charset
), "CHARSET=%s",
1674 attr
->values
[0].string
.text
);
1677 snprintf(path
, sizeof(path
), "PATH=%s/filter:/bin:/usr/bin", ServerBin
);
1678 snprintf(content_type
, sizeof(content_type
), "CONTENT_TYPE=%s/%s",
1679 current
->filetypes
[current
->current_file
]->super
,
1680 current
->filetypes
[current
->current_file
]->type
);
1681 snprintf(device_uri
, sizeof(device_uri
), "DEVICE_URI=%s", printer
->device_uri
);
1682 snprintf(ppd
, sizeof(ppd
), "PPD=%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
1683 snprintf(printer_name
, sizeof(printer_name
), "PRINTER=%s", printer
->name
);
1684 snprintf(cache
, sizeof(cache
), "RIP_MAX_CACHE=%s", RIPCache
);
1685 snprintf(root
, sizeof(root
), "CUPS_SERVERROOT=%s", ServerRoot
);
1686 snprintf(tmpdir
, sizeof(tmpdir
), "TMPDIR=%s", TempDir
);
1687 snprintf(datadir
, sizeof(datadir
), "CUPS_DATADIR=%s", DataDir
);
1688 snprintf(fontpath
, sizeof(fontpath
), "CUPS_FONTPATH=%s", FontPath
);
1692 envp
[envc
++] = path
;
1693 envp
[envc
++] = "SOFTWARE=CUPS/1.1";
1694 envp
[envc
++] = "USER=root";
1695 envp
[envc
++] = charset
;
1696 envp
[envc
++] = language
;
1698 envp
[envc
++] = ppd
;
1699 envp
[envc
++] = root
;
1700 envp
[envc
++] = cache
;
1701 envp
[envc
++] = tmpdir
;
1702 envp
[envc
++] = content_type
;
1703 envp
[envc
++] = device_uri
;
1704 envp
[envc
++] = printer_name
;
1705 envp
[envc
++] = datadir
;
1706 envp
[envc
++] = fontpath
;
1708 if (getenv("LD_LIBRARY_PATH") != NULL
)
1710 snprintf(ld_library_path
, sizeof(ld_library_path
), "LD_LIBRARY_PATH=%s",
1711 getenv("LD_LIBRARY_PATH"));
1712 envp
[envc
++] = ld_library_path
;
1715 if (getenv("LD_PRELOAD") != NULL
)
1717 snprintf(ld_preload
, sizeof(ld_preload
), "LD_PRELOAD=%s",
1718 getenv("LD_PRELOAD"));
1719 envp
[envc
++] = ld_preload
;
1722 if (getenv("DYLD_LIBRARY_PATH") != NULL
)
1724 snprintf(dyld_library_path
, sizeof(dyld_library_path
), "DYLD_LIBRARY_PATH=%s",
1725 getenv("DYLD_LIBRARY_PATH"));
1726 envp
[envc
++] = dyld_library_path
;
1729 if (getenv("SHLIB_PATH") != NULL
)
1731 snprintf(shlib_path
, sizeof(shlib_path
), "SHLIB_PATH=%s",
1732 getenv("SHLIB_PATH"));
1733 envp
[envc
++] = shlib_path
;
1736 if (getenv("NLSPATH") != NULL
)
1738 snprintf(nlspath
, sizeof(nlspath
), "NLSPATH=%s", getenv("NLSPATH"));
1739 envp
[envc
++] = nlspath
;
1742 if (Classification
&& !banner_page
)
1744 if ((attr
= ippFindAttribute(current
->attrs
, "job-sheets",
1745 IPP_TAG_NAME
)) == NULL
)
1746 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1748 else if (attr
->num_values
> 1 &&
1749 strcmp(attr
->values
[1].string
.text
, "none") != 0)
1750 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1751 attr
->values
[1].string
.text
);
1753 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1754 attr
->values
[0].string
.text
);
1756 envp
[envc
++] = classification
;
1759 if (current
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
1761 snprintf(class_name
, sizeof(class_name
), "CLASS=%s", current
->dest
);
1762 envp
[envc
++] = class_name
;
1766 envp
[envc
++] = processPath
;
1767 #endif /* __APPLE__ */
1769 envp
[envc
++] = NULL
;
1771 for (i
= 0; i
< envc
; i
++)
1772 LogMessage(L_DEBUG
, "StartJob: envp[%d]=\"%s\"", i
, envp
[i
]);
1774 current
->current_file
++;
1777 * Make sure we have a buffer to read status info into...
1780 if (current
->buffer
== NULL
)
1782 LogMessage(L_DEBUG2
, "StartJob: Allocating status buffer...");
1784 if ((current
->buffer
= malloc(JOB_BUFFER_SIZE
)) == NULL
)
1786 LogMessage(L_EMERG
, "Unable to allocate memory for job status buffer - %s",
1788 CancelJob(current
->id
, 0);
1792 current
->bufused
= 0;
1796 * Now create processes for all of the filters...
1799 if (pipe(statusfds
))
1801 LogMessage(L_ERROR
, "Unable to create job status pipes - %s.",
1803 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1804 "Unable to create status pipes - %s.", strerror(errno
));
1808 LogMessage(L_DEBUG
, "StartJob: statusfds = [ %d %d ]",
1809 statusfds
[0], statusfds
[1]);
1811 current
->status_pipe
= statusfds
[0];
1812 current
->status
= 0;
1813 memset(current
->filters
, 0, sizeof(current
->filters
));
1815 filterfds
[1][0] = open("/dev/null", O_RDONLY
);
1816 filterfds
[1][1] = -1;
1818 LogMessage(L_DEBUG
, "StartJob: filterfds[%d] = [ %d %d ]", 1, filterfds
[1][0],
1821 for (i
= 0, slot
= 0; i
< num_filters
; i
++)
1823 if (filters
[i
].filter
[0] != '/')
1824 snprintf(command
, sizeof(command
), "%s/filter/%s", ServerBin
,
1827 strlcpy(command
, filters
[i
].filter
, sizeof(command
));
1831 * Setting CFProcessPath lets OS X's Core Foundation code find
1832 * the bundle that may be associated with a filter or backend.
1835 snprintf(processPath
, sizeof(processPath
), "CFProcessPath=%s", command
);
1836 LogMessage(L_DEBUG
, "StartJob: %s\n", processPath
);
1837 #endif /* __APPLE__ */
1839 if (i
< (num_filters
- 1))
1840 pipe(filterfds
[slot
]);
1843 if (current
->current_file
== 1)
1845 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
1846 pipe(current
->print_pipes
);
1849 current
->print_pipes
[0] = -1;
1850 if (strncmp(printer
->device_uri
, "file:/dev/", 10) == 0)
1851 current
->print_pipes
[1] = open(printer
->device_uri
+ 5,
1854 current
->print_pipes
[1] = open(printer
->device_uri
+ 5,
1855 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
1858 LogMessage(L_DEBUG2
, "StartJob: print_pipes = [ %d %d ]",
1859 current
->print_pipes
[0], current
->print_pipes
[1]);
1862 filterfds
[slot
][0] = current
->print_pipes
[0];
1863 filterfds
[slot
][1] = current
->print_pipes
[1];
1866 LogMessage(L_DEBUG
, "StartJob: filter = \"%s\"", command
);
1867 LogMessage(L_DEBUG
, "StartJob: filterfds[%d] = [ %d %d ]",
1868 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
1870 pid
= start_process(command
, argv
, envp
, filterfds
[!slot
][0],
1871 filterfds
[slot
][1], statusfds
[1],
1872 current
->back_pipes
[0], 0, current
->filters
+ i
);
1874 LogMessage(L_DEBUG2
, "StartJob: Closing filter pipes for slot %d [ %d %d ]...",
1875 !slot
, filterfds
[!slot
][0], filterfds
[!slot
][1]);
1877 close(filterfds
[!slot
][0]);
1878 close(filterfds
[!slot
][1]);
1882 LogMessage(L_ERROR
, "Unable to start filter \"%s\" - %s.",
1883 filters
[i
].filter
, strerror(errno
));
1884 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1885 "Unable to start filter \"%s\" - %s.",
1886 filters
[i
].filter
, strerror(errno
));
1888 if (filters
!= NULL
)
1891 CancelJob(current
->id
, 0);
1895 LogMessage(L_INFO
, "Started filter %s (PID %d) for job %d.",
1896 command
, pid
, current
->id
);
1902 if (filters
!= NULL
)
1906 * Finally, pipe the final output into a backend process if needed...
1909 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
1911 if (current
->current_file
== 1)
1913 sscanf(printer
->device_uri
, "%254[^:]", method
);
1914 snprintf(command
, sizeof(command
), "%s/backend/%s", ServerBin
, method
);
1918 * Setting CFProcessPath lets OS X's Core Foundation code find
1919 * the bundle that may be associated with a filter or backend.
1922 snprintf(processPath
, sizeof(processPath
), "CFProcessPath=%s", command
);
1923 LogMessage(L_DEBUG
, "StartJob: %s\n", processPath
);
1924 #endif /* __APPLE__ */
1926 argv
[0] = printer
->device_uri
;
1928 filterfds
[slot
][0] = -1;
1929 filterfds
[slot
][1] = open("/dev/null", O_WRONLY
);
1931 LogMessage(L_DEBUG
, "StartJob: backend = \"%s\"", command
);
1932 LogMessage(L_DEBUG
, "StartJob: filterfds[%d] = [ %d %d ]",
1933 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
1935 pid
= start_process(command
, argv
, envp
, filterfds
[!slot
][0],
1936 filterfds
[slot
][1], statusfds
[1],
1937 current
->back_pipes
[1], 1,
1938 &(current
->backend
));
1942 LogMessage(L_ERROR
, "Unable to start backend \"%s\" - %s.",
1943 method
, strerror(errno
));
1944 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1945 "Unable to start backend \"%s\" - %s.", method
, strerror(errno
));
1947 LogMessage(L_DEBUG2
, "StartJob: Closing print pipes [ %d %d ]...",
1948 current
->print_pipes
[0], current
->print_pipes
[1]);
1950 close(current
->print_pipes
[0]);
1951 close(current
->print_pipes
[1]);
1953 current
->print_pipes
[0] = -1;
1954 current
->print_pipes
[1] = -1;
1956 LogMessage(L_DEBUG2
, "StartJob: Closing back pipes [ %d %d ]...",
1957 current
->back_pipes
[0], current
->back_pipes
[1]);
1959 close(current
->back_pipes
[0]);
1960 close(current
->back_pipes
[1]);
1962 current
->back_pipes
[0] = -1;
1963 current
->back_pipes
[1] = -1;
1965 CancelJob(current
->id
, 0);
1970 LogMessage(L_INFO
, "Started backend %s (PID %d) for job %d.",
1971 command
, pid
, current
->id
);
1975 if (current
->current_file
== current
->num_files
)
1977 LogMessage(L_DEBUG2
, "StartJob: Closing print pipes [ %d %d ]...",
1978 current
->print_pipes
[0], current
->print_pipes
[1]);
1980 close(current
->print_pipes
[0]);
1981 close(current
->print_pipes
[1]);
1983 current
->print_pipes
[0] = -1;
1984 current
->print_pipes
[1] = -1;
1986 LogMessage(L_DEBUG2
, "StartJob: Closing back pipes [ %d %d ]...",
1987 current
->back_pipes
[0], current
->back_pipes
[1]);
1989 close(current
->back_pipes
[0]);
1990 close(current
->back_pipes
[1]);
1992 current
->back_pipes
[0] = -1;
1993 current
->back_pipes
[1] = -1;
1998 filterfds
[slot
][0] = -1;
1999 filterfds
[slot
][1] = -1;
2001 if (current
->current_file
== current
->num_files
)
2003 LogMessage(L_DEBUG2
, "StartJob: Closing print pipes [ %d %d ]...",
2004 current
->print_pipes
[0], current
->print_pipes
[1]);
2006 close(current
->print_pipes
[0]);
2007 close(current
->print_pipes
[1]);
2009 current
->print_pipes
[0] = -1;
2010 current
->print_pipes
[1] = -1;
2014 LogMessage(L_DEBUG2
, "StartJob: Closing filter pipes for slot %d [ %d %d ]...",
2015 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2017 close(filterfds
[slot
][0]);
2018 close(filterfds
[slot
][1]);
2020 LogMessage(L_DEBUG2
, "StartJob: Closing status output pipe %d...",
2023 close(statusfds
[1]);
2025 LogMessage(L_DEBUG2
, "StartJob: Adding fd %d to InputSet...",
2026 current
->status_pipe
);
2028 FD_SET(current
->status_pipe
, InputSet
);
2033 * 'StopAllJobs()' - Stop all print jobs.
2039 job_t
*current
; /* Current job */
2042 DEBUG_puts("StopAllJobs()");
2044 for (current
= Jobs
; current
!= NULL
; current
= current
->next
)
2045 if (current
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
2047 StopJob(current
->id
, 1);
2048 current
->state
->values
[0].integer
= IPP_JOB_PENDING
;
2054 * 'StopJob()' - Stop a print job.
2058 StopJob(int id
, /* I - Job ID */
2059 int force
) /* I - 1 = Force all filters to stop */
2061 int i
; /* Looping var */
2062 job_t
*current
; /* Current job */
2065 LogMessage(L_DEBUG
, "StopJob: id = %d, force = %d", id
, force
);
2067 for (current
= Jobs
; current
!= NULL
; current
= current
->next
)
2068 if (current
->id
== id
)
2070 DEBUG_puts("StopJob: found job in list.");
2072 if (current
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
2074 DEBUG_puts("StopJob: job state is \'processing\'.");
2076 FilterLevel
-= current
->cost
;
2078 if (current
->status
< 0 &&
2079 !(current
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
)))
2080 SetPrinterState(current
->printer
, IPP_PRINTER_STOPPED
);
2081 else if (current
->printer
->state
!= IPP_PRINTER_STOPPED
)
2082 SetPrinterState(current
->printer
, IPP_PRINTER_IDLE
);
2084 LogMessage(L_DEBUG
, "StopJob: printer state is %d", current
->printer
->state
);
2086 current
->state
->values
[0].integer
= IPP_JOB_STOPPED
;
2087 current
->printer
->job
= NULL
;
2088 current
->printer
= NULL
;
2090 current
->current_file
--;
2092 for (i
= 0; current
->filters
[i
]; i
++)
2093 if (current
->filters
[i
] > 0)
2095 kill(current
->filters
[i
], force
? SIGKILL
: SIGTERM
);
2096 current
->filters
[i
] = 0;
2099 if (current
->backend
> 0)
2101 kill(current
->backend
, force
? SIGKILL
: SIGTERM
);
2102 current
->backend
= 0;
2105 LogMessage(L_DEBUG2
, "StopJob: Closing print pipes [ %d %d ]...",
2106 current
->print_pipes
[0], current
->print_pipes
[1]);
2108 close(current
->print_pipes
[0]);
2109 close(current
->print_pipes
[1]);
2111 current
->print_pipes
[0] = -1;
2112 current
->print_pipes
[1] = -1;
2114 LogMessage(L_DEBUG2
, "StopJob: Closing back pipes [ %d %d ]...",
2115 current
->back_pipes
[0], current
->back_pipes
[1]);
2117 close(current
->back_pipes
[0]);
2118 close(current
->back_pipes
[1]);
2120 current
->back_pipes
[0] = -1;
2121 current
->back_pipes
[1] = -1;
2123 if (current
->status_pipe
>= 0)
2126 * Close the pipe and clear the input bit.
2129 LogMessage(L_DEBUG2
, "StopJob: Closing status input pipe %d...",
2130 current
->status_pipe
);
2132 close(current
->status_pipe
);
2134 LogMessage(L_DEBUG2
, "StopJob: Removing fd %d from InputSet...",
2135 current
->status_pipe
);
2137 FD_CLR(current
->status_pipe
, InputSet
);
2138 current
->status_pipe
= -1;
2141 if (current
->buffer
)
2144 * Free the status buffer...
2147 LogMessage(L_DEBUG2
, "StopJob: Freeing status buffer...");
2149 free(current
->buffer
);
2150 current
->buffer
= NULL
;
2151 current
->bufused
= 0;
2160 * 'UpdateJob()' - Read a status update from a job's filters.
2164 UpdateJob(job_t
*job
) /* I - Job to check */
2166 int bytes
; /* Number of bytes read */
2167 int copies
; /* Number of copies printed */
2168 char *lineptr
, /* Pointer to end of line in buffer */
2169 *message
; /* Pointer to message text */
2170 int loglevel
; /* Log level for message */
2171 int job_history
; /* Did CancelJob() keep the job? */
2174 if ((bytes
= read(job
->status_pipe
, job
->buffer
+ job
->bufused
,
2175 JOB_BUFFER_SIZE
- job
->bufused
- 1)) > 0)
2177 job
->bufused
+= bytes
;
2178 job
->buffer
[job
->bufused
] = '\0';
2180 if ((lineptr
= strchr(job
->buffer
, '\n')) == NULL
&&
2181 job
->bufused
== (JOB_BUFFER_SIZE
- 1))
2182 lineptr
= job
->buffer
+ job
->bufused
;
2184 else if (bytes
< 0 && errno
== EINTR
)
2188 lineptr
= job
->buffer
+ job
->bufused
;
2192 if (job
->bufused
== 0 && bytes
== 0)
2195 while (lineptr
!= NULL
)
2198 * Terminate each line and process it...
2204 * Figure out the logging level...
2207 if (strncmp(job
->buffer
, "EMERG:", 6) == 0)
2210 message
= job
->buffer
+ 6;
2212 else if (strncmp(job
->buffer
, "ALERT:", 6) == 0)
2215 message
= job
->buffer
+ 6;
2217 else if (strncmp(job
->buffer
, "CRIT:", 5) == 0)
2220 message
= job
->buffer
+ 5;
2222 else if (strncmp(job
->buffer
, "ERROR:", 6) == 0)
2225 message
= job
->buffer
+ 6;
2227 else if (strncmp(job
->buffer
, "WARNING:", 8) == 0)
2230 message
= job
->buffer
+ 8;
2232 else if (strncmp(job
->buffer
, "NOTICE:", 6) == 0)
2234 loglevel
= L_NOTICE
;
2235 message
= job
->buffer
+ 6;
2237 else if (strncmp(job
->buffer
, "INFO:", 5) == 0)
2240 message
= job
->buffer
+ 5;
2242 else if (strncmp(job
->buffer
, "DEBUG:", 6) == 0)
2245 message
= job
->buffer
+ 6;
2247 else if (strncmp(job
->buffer
, "DEBUG2:", 7) == 0)
2249 loglevel
= L_DEBUG2
;
2250 message
= job
->buffer
+ 7;
2252 else if (strncmp(job
->buffer
, "PAGE:", 5) == 0)
2255 message
= job
->buffer
+ 5;
2257 else if (strncmp(job
->buffer
, "STATE:", 6) == 0)
2260 message
= job
->buffer
+ 6;
2265 message
= job
->buffer
;
2269 * Skip leading whitespace in the message...
2272 while (isspace(*message
))
2276 * Send it to the log file and printer state message as needed...
2279 if (loglevel
== L_PAGE
)
2282 * Page message; send the message to the page_log file and update the
2283 * job sheet count...
2286 if (job
->sheets
!= NULL
)
2288 if (!strncasecmp(message
, "total ", 6))
2291 * Got a total count of pages from a backend or filter...
2294 copies
= atoi(message
+ 6);
2295 copies
-= job
->sheets
->values
[0].integer
; /* Just track the delta */
2297 else if (!sscanf(message
, "%*d%d", &copies
))
2300 job
->sheets
->values
[0].integer
+= copies
;
2302 if (job
->printer
->page_limit
)
2303 UpdateQuota(job
->printer
, job
->username
, copies
, 0);
2306 LogPage(job
, message
);
2308 else if (loglevel
== L_STATE
)
2309 SetPrinterReasons(job
->printer
, message
);
2313 * Other status message; send it to the error_log file...
2316 if (loglevel
!= L_INFO
|| LogLevel
== L_DEBUG2
)
2317 LogMessage(loglevel
, "[Job %d] %s", job
->id
, message
);
2319 if ((loglevel
== L_INFO
&& !job
->status
) ||
2321 strlcpy(job
->printer
->state_message
, message
,
2322 sizeof(job
->printer
->state_message
));
2326 * Copy over the buffer data we've used up...
2329 strcpy(job
->buffer
, lineptr
);
2330 job
->bufused
-= lineptr
- job
->buffer
;
2332 if (job
->bufused
< 0)
2335 lineptr
= strchr(job
->buffer
, '\n');
2340 LogMessage(L_DEBUG
, "UpdateJob: job %d, file %d is complete.",
2341 job
->id
, job
->current_file
- 1);
2343 if (job
->status_pipe
>= 0)
2346 * Close the pipe and clear the input bit.
2349 LogMessage(L_DEBUG2
, "UpdateJob: Closing status input pipe %d...",
2352 close(job
->status_pipe
);
2354 LogMessage(L_DEBUG2
, "UpdateJob: Removing fd %d from InputSet...",
2357 FD_CLR(job
->status_pipe
, InputSet
);
2358 job
->status_pipe
= -1;
2361 if (job
->status
< 0)
2364 * Backend had errors; stop it...
2367 StopJob(job
->id
, 0);
2368 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
2372 * If the job was queued to a class, try requeuing it... For
2373 * faxes, hold the current job for 5 minutes.
2376 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
2378 else if (job
->dtype
& CUPS_PRINTER_FAX
)
2381 * See how many times we've tried to send the job; if more than
2382 * the limit, cancel the job.
2387 if (job
->tries
>= FaxRetryLimit
)
2393 LogMessage(L_ERROR
, "Canceling fax job %d since it could not be sent after %d tries.",
2394 job
->id
, FaxRetryLimit
);
2395 CancelJob(job
->id
, 0);
2400 * Try again in N seconds...
2403 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
2404 job
->hold_until
= time(NULL
) + FaxRetryInterval
;
2410 else if (job
->status
> 0)
2413 * Filter had errors; cancel it...
2416 if (job
->current_file
< job
->num_files
)
2417 StartJob(job
->id
, job
->printer
);
2420 job_history
= JobHistory
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
);
2422 CancelJob(job
->id
, 0);
2426 job
->state
->values
[0].integer
= IPP_JOB_ABORTED
;
2436 * Job printed successfully; cancel it...
2439 if (job
->current_file
< job
->num_files
)
2441 FilterLevel
-= job
->cost
;
2442 StartJob(job
->id
, job
->printer
);
2446 job_history
= JobHistory
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
);
2448 CancelJob(job
->id
, 0);
2452 job
->state
->values
[0].integer
= IPP_JOB_COMPLETED
;
2464 * 'ipp_length()' - Compute the size of the buffer needed to hold
2465 * the textual IPP attributes.
2468 int /* O - Size of buffer to hold IPP attributes */
2469 ipp_length(ipp_t
*ipp
) /* I - IPP request */
2471 int bytes
; /* Number of bytes */
2472 int i
; /* Looping var */
2473 ipp_attribute_t
*attr
; /* Current attribute */
2477 * Loop through all attributes...
2482 for (attr
= ipp
->attrs
; attr
!= NULL
; attr
= attr
->next
)
2485 * Skip attributes that won't be sent to filters...
2488 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
2489 attr
->value_tag
== IPP_TAG_NAMELANG
||
2490 attr
->value_tag
== IPP_TAG_TEXTLANG
||
2491 attr
->value_tag
== IPP_TAG_URI
||
2492 attr
->value_tag
== IPP_TAG_URISCHEME
)
2495 if (strncmp(attr
->name
, "time-", 5) == 0)
2499 * Add space for a leading space and commas between each value.
2500 * For the first attribute, the leading space isn't used, so the
2501 * extra byte can be used as the nul terminator...
2504 bytes
++; /* " " separator */
2505 bytes
+= attr
->num_values
; /* "," separators */
2508 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
2509 * other attributes appear as "foo=value1,value2,...,valueN".
2512 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
2513 bytes
+= strlen(attr
->name
);
2515 bytes
+= attr
->num_values
* strlen(attr
->name
);
2518 * Now add the size required for each value in the attribute...
2521 switch (attr
->value_tag
)
2523 case IPP_TAG_INTEGER
:
2526 * Minimum value of a signed integer is -2147483647, or 11 digits.
2529 bytes
+= attr
->num_values
* 11;
2532 case IPP_TAG_BOOLEAN
:
2534 * Add two bytes for each false ("no") value...
2537 for (i
= 0; i
< attr
->num_values
; i
++)
2538 if (!attr
->values
[i
].boolean
)
2542 case IPP_TAG_RANGE
:
2544 * A range is two signed integers separated by a hyphen, or
2545 * 23 characters max.
2548 bytes
+= attr
->num_values
* 23;
2551 case IPP_TAG_RESOLUTION
:
2553 * A resolution is two signed integers separated by an "x" and
2554 * suffixed by the units, or 26 characters max.
2557 bytes
+= attr
->num_values
* 26;
2560 case IPP_TAG_STRING
:
2563 case IPP_TAG_KEYWORD
:
2564 case IPP_TAG_CHARSET
:
2565 case IPP_TAG_LANGUAGE
:
2567 * Strings can contain characters that need quoting. We need
2568 * at least 2 * len + 2 characters to cover the quotes and
2569 * any backslashes in the string.
2572 for (i
= 0; i
< attr
->num_values
; i
++)
2573 bytes
+= 2 * strlen(attr
->values
[i
].string
.text
) + 2;
2577 break; /* anti-compiler-warning-code */
2586 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2590 set_time(job_t
*job
, /* I - Job to update */
2591 const char *name
) /* I - Name of attribute */
2593 ipp_attribute_t
*attr
; /* Time attribute */
2596 if ((attr
= ippFindAttribute(job
->attrs
, name
, IPP_TAG_ZERO
)) != NULL
)
2598 attr
->value_tag
= IPP_TAG_INTEGER
;
2599 attr
->values
[0].integer
= time(NULL
);
2605 * 'start_process()' - Start a background process.
2608 static int /* O - Process ID or 0 */
2609 start_process(const char *command
, /* I - Full path to command */
2610 char *argv
[], /* I - Command-line arguments */
2611 char *envp
[], /* I - Environment */
2612 int infd
, /* I - Standard input file descriptor */
2613 int outfd
, /* I - Standard output file descriptor */
2614 int errfd
, /* I - Standard error file descriptor */
2615 int backfd
, /* I - Backchannel file descriptor */
2616 int root
, /* I - Run as root? */
2617 int *pid
) /* O - Process ID */
2619 int fd
; /* Looping var */
2620 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
2621 struct sigaction action
; /* POSIX signal handler */
2622 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
2625 LogMessage(L_DEBUG
, "start_process(\"%s\", %p, %p, %d, %d, %d)",
2626 command
, argv
, envp
, infd
, outfd
, errfd
);
2629 * Block signals before forking...
2634 if ((*pid
= fork()) == 0)
2637 * Child process goes here...
2639 * Update stdin/stdout/stderr as needed...
2655 fcntl(3, F_SETFL
, O_NDELAY
);
2659 * Close extra file descriptors...
2662 for (fd
= 4; fd
< MaxFDs
; fd
++)
2666 * Change the priority of the process based on the FilterNice setting.
2667 * (this is not done for backends...)
2674 * Change user to something "safe"...
2677 if (!root
&& getuid() == 0)
2680 * Running as root, so change to non-priviledged user...
2691 * Reset group membership to just the main one we belong to.
2697 * Change umask to restrict permissions on created files...
2703 * Unblock signals before doing the exec...
2707 sigset(SIGTERM
, SIG_DFL
);
2708 sigset(SIGCHLD
, SIG_DFL
);
2709 #elif defined(HAVE_SIGACTION)
2710 memset(&action
, 0, sizeof(action
));
2712 sigemptyset(&action
.sa_mask
);
2713 action
.sa_handler
= SIG_DFL
;
2715 sigaction(SIGTERM
, &action
, NULL
);
2716 sigaction(SIGCHLD
, &action
, NULL
);
2718 signal(SIGTERM
, SIG_DFL
);
2719 signal(SIGCHLD
, SIG_DFL
);
2720 #endif /* HAVE_SIGSET */
2725 * Execute the command; if for some reason this doesn't work,
2726 * return the error code...
2729 execve(command
, argv
, envp
);
2738 * Error - couldn't fork a new process!
2741 LogMessage(L_ERROR
, "Unable to fork %s - %s.", command
, strerror(errno
));
2753 * End of "$Id: job.c,v 1.124.2.64 2003/04/10 20:15:53 mike Exp $".