4 * Job management routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2006 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 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * cupsdAddJob() - Add a new job to the job queue...
27 * cupsdCancelJob() - Cancel the specified print job.
28 * cupsdCancelJobs() - Cancel all jobs for the given
30 * cupsdCheckJobs() - Check the pending jobs and start any if
31 * the destination is available.
32 * cupsdCleanJobs() - Clean out old jobs.
33 * cupsdFinishJob() - Finish a job.
34 * cupsdFreeAllJobs() - Free all jobs from memory.
35 * cupsdFindJob() - Find the specified job.
36 * cupsdGetPrinterJobCount() - Get the number of pending, processing,
37 * cupsdGetUserJobCount() - Get the number of pending, processing,
38 * cupsdHoldJob() - Hold the specified job.
39 * cupsdLoadAllJobs() - Load all jobs from disk.
40 * cupsdLoadJob() - Load a single job...
41 * cupsdMoveJob() - Move the specified job to a different
43 * cupsdReleaseJob() - Release the specified job.
44 * cupsdRestartJob() - Restart the specified job.
45 * cupsdSaveAllJobs() - Save a summary of all jobs to disk.
46 * cupsdSaveJob() - Save a job to disk.
47 * cupsdSetJobHoldUntil() - Set the hold time for a job...
48 * cupsdSetJobPriority() - Set the priority of a job, moving it
49 * up/down in the list as needed.
50 * cupsdStopAllJobs() - Stop all print jobs.
51 * cupsdStopJob() - Stop a print job.
52 * cupsdUnloadCompletedJobs() - Flush completed job history from memory.
53 * cupsdUpdateJob() - Read a status update from a jobs filters.
54 * compare_active_jobs() - Compare the job IDs and priorities of two
56 * compare_jobs() - Compare the job IDs of two jobs.
57 * free_job() - Free all memory used by a job.
58 * ipp_length() - Compute the size of the buffer needed to
59 * hold the textual IPP attributes.
60 * load_job_cache() - Load jobs from the job.cache file.
61 * load_next_job_id() - Load the NextJobId value from the
63 * load_request_root() - Load jobs from the RequestRoot directory.
64 * set_time() - Set one of the "time-at-xyz" attributes...
65 * set_hold_until() - Set the hold time and update job-hold-until
67 * start_job() - Start a print job.
68 * unload_job() - Unload a job from memory.
72 * Include necessary headers...
77 #include <cups/backend.h>
85 static mime_filter_t gziptoany_filter
=
87 NULL
, /* Source type */
88 NULL
, /* Destination type */
90 "gziptoany" /* Filter program to run */
98 static int compare_active_jobs(void *first
, void *second
, void *data
);
99 static int compare_jobs(void *first
, void *second
, void *data
);
100 static void free_job(cupsd_job_t
*job
);
101 static int ipp_length(ipp_t
*ipp
);
102 static void load_job_cache(const char *filename
);
103 static void load_next_job_id(const char *filename
);
104 static void load_request_root(void);
105 static void set_time(cupsd_job_t
*job
, const char *name
);
106 static void set_hold_until(cupsd_job_t
*job
, time_t holdtime
);
107 static void start_job(cupsd_job_t
*job
, cupsd_printer_t
*printer
);
108 static void unload_job(cupsd_job_t
*job
);
112 * 'cupsdAddJob()' - Add a new job to the job queue...
115 cupsd_job_t
* /* O - New job record */
116 cupsdAddJob(int priority
, /* I - Job priority */
117 const char *dest
) /* I - Job destination */
119 cupsd_job_t
*job
; /* New job record */
122 job
= calloc(sizeof(cupsd_job_t
), 1);
124 job
->id
= NextJobId
++;
125 job
->priority
= priority
;
126 job
->back_pipes
[0] = -1;
127 job
->back_pipes
[1] = -1;
128 job
->print_pipes
[0] = -1;
129 job
->print_pipes
[1] = -1;
130 job
->status_pipes
[0] = -1;
131 job
->status_pipes
[1] = -1;
133 cupsdSetString(&job
->dest
, dest
);
136 * Add the new job to the "all jobs" and "active jobs" lists...
139 cupsArrayAdd(Jobs
, job
);
140 cupsArrayAdd(ActiveJobs
, job
);
147 * 'cupsdCancelJob()' - Cancel the specified print job.
151 cupsdCancelJob(cupsd_job_t
*job
, /* I - Job to cancel */
152 int purge
, /* I - Purge jobs? */
153 ipp_jstate_t newstate
) /* I - New job state */
155 int i
; /* Looping var */
156 char filename
[1024]; /* Job filename */
157 cupsd_printer_t
*printer
; /* Printer used by job */
160 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdCancelJob: id = %d", job
->id
);
163 * Stop any processes that are working on the current job...
166 printer
= job
->printer
;
168 if (job
->state_value
== IPP_JOB_PROCESSING
)
169 cupsdStopJob(job
, 0);
174 job
->state
->values
[0].integer
= newstate
;
176 job
->state_value
= newstate
;
178 set_time(job
, "time-at-completed");
181 * Send any pending notifications and then expire them...
189 case IPP_JOB_CANCELED
:
190 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, printer
, job
,
191 purge
? "Job purged." : "Job canceled.");
194 case IPP_JOB_ABORTED
:
195 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, printer
, job
,
196 "Job aborted; please consult the error_log file "
200 case IPP_JOB_COMPLETED
:
202 * Clear the printer's state_message and state_reasons and move on...
205 printer
->state_message
[0] = '\0';
207 cupsdSetPrinterReasons(printer
, "");
208 cupsdSetPrinterState(printer
, IPP_PRINTER_IDLE
, 0);
210 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, printer
, job
,
215 cupsdExpireSubscriptions(NULL
, job
);
218 * Remove the job from the active list...
221 cupsArrayRemove(ActiveJobs
, job
);
224 * Remove any authentication data...
227 snprintf(filename
, sizeof(filename
), "%s/a%05d", RequestRoot
, job
->id
);
231 * Remove the print file for good if we aren't preserving jobs or
235 job
->current_file
= 0;
237 if (!JobHistory
|| !JobFiles
|| purge
|| (job
->dtype
& CUPS_PRINTER_REMOTE
))
239 for (i
= 1; i
<= job
->num_files
; i
++)
241 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
246 if (job
->num_files
> 0)
248 free(job
->filetypes
);
249 free(job
->compressions
);
252 job
->filetypes
= NULL
;
253 job
->compressions
= NULL
;
257 if (JobHistory
&& !purge
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
))
260 * Save job state info...
268 * Remove the job info file...
271 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
,
276 * Remove the job from the "all jobs" list...
279 cupsArrayRemove(Jobs
, job
);
282 * Free all memory used...
291 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user...
295 cupsdCancelJobs(const char *dest
, /* I - Destination to cancel */
296 const char *username
, /* I - Username or NULL */
297 int purge
) /* I - Purge jobs? */
299 cupsd_job_t
*job
; /* Current job */
302 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
304 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
305 if ((dest
== NULL
|| !strcmp(job
->dest
, dest
)) &&
306 (username
== NULL
|| !strcmp(job
->username
, username
)))
309 * Cancel all jobs matching this destination/user...
312 cupsdCancelJob(job
, purge
, IPP_JOB_CANCELED
);
320 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
327 cupsd_job_t
*job
; /* Current job in queue */
328 cupsd_printer_t
*printer
, /* Printer destination */
329 *pclass
; /* Printer class destination */
332 DEBUG_puts("cupsdCheckJobs()");
334 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
335 "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d",
336 cupsArrayCount(ActiveJobs
), Sleeping
, NeedReload
);
338 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
340 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
343 * Start held jobs if they are ready...
346 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
347 "cupsdCheckJobs: Job %d: state_value=%d, loaded=%s",
348 job
->id
, job
->state_value
, job
->attrs
? "yes" : "no");
350 if (job
->state_value
== IPP_JOB_HELD
&&
352 job
->hold_until
< time(NULL
))
354 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
355 job
->state_value
= IPP_JOB_PENDING
;
359 * Start pending jobs if the destination is available...
362 if (job
->state_value
== IPP_JOB_PENDING
&& !NeedReload
&& !Sleeping
)
364 printer
= cupsdFindDest(job
->dest
);
368 (printer
->type
& (CUPS_PRINTER_IMPLICIT
| CUPS_PRINTER_CLASS
)))
371 * If the class is remote, just pass it to the remote server...
376 if (!(pclass
->type
& CUPS_PRINTER_REMOTE
))
378 if (pclass
->state
!= IPP_PRINTER_STOPPED
)
379 printer
= cupsdFindAvailablePrinter(job
->dest
);
385 if (!printer
&& !pclass
)
388 * Whoa, the printer and/or class for this destination went away;
392 cupsdLogMessage(CUPSD_LOG_WARN
,
393 "Printer/class %s has gone away; canceling job %d!",
396 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
397 "Job canceled because the destination printer/class has "
400 cupsdCancelJob(job
, 1, IPP_JOB_ABORTED
);
405 * See if the printer is available or remote and not printing a job;
406 * if so, start the job...
412 * Add/update a job-actual-printer-uri attribute for this job
413 * so that we know which printer actually printed the job...
416 ipp_attribute_t
*attr
; /* job-actual-printer-uri attribute */
419 if ((attr
= ippFindAttribute(job
->attrs
, "job-actual-printer-uri",
420 IPP_TAG_URI
)) != NULL
)
421 cupsdSetString(&attr
->values
[0].string
.text
, printer
->uri
);
423 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
,
424 "job-actual-printer-uri", NULL
, printer
->uri
);
427 if ((!(printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is local */
428 printer
->state
== IPP_PRINTER_IDLE
) || /* and idle */
429 ((printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is remote */
430 !printer
->job
)) /* and not printing */
431 start_job(job
, printer
);
439 * 'cupsdCleanJobs()' - Clean out old jobs.
445 cupsd_job_t
*job
; /* Current job */
451 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
452 job
&& cupsArrayCount(Jobs
) >= MaxJobs
;
453 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
454 if (job
->state_value
>= IPP_JOB_CANCELED
)
455 cupsdCancelJob(job
, 1, IPP_JOB_CANCELED
);
460 * 'cupsdFinishJob()' - Finish a job.
464 cupsdFinishJob(cupsd_job_t
*job
) /* I - Job */
466 int job_history
; /* Did cupsdCancelJob() keep the job? */
467 cupsd_printer_t
*printer
; /* Current printer */
470 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Job %d] File %d is complete.",
471 job
->id
, job
->current_file
- 1);
473 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdFinishJob: job->status is %d",
476 if (job
->status_buffer
&&
477 (job
->status
< 0 || job
->current_file
>= job
->num_files
))
480 * Close the pipe and clear the input bit.
483 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
484 "cupsdFinishJob: Removing fd %d from InputSet...",
485 job
->status_buffer
->fd
);
487 FD_CLR(job
->status_buffer
->fd
, InputSet
);
489 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
490 "cupsdFinishJob: Closing status pipes [ %d %d ]...",
491 job
->status_pipes
[0], job
->status_pipes
[1]);
493 cupsdClosePipe(job
->status_pipes
);
494 cupsdStatBufDelete(job
->status_buffer
);
496 job
->status_buffer
= NULL
;
499 printer
= job
->printer
;
504 * Backend had errors; stop it...
507 int exit_code
; /* Exit code from backend */
511 * Convert the status to an exit code. Due to the way the W* macros are
512 * implemented on MacOS X (bug?), we have to store the exit status in a
513 * variable first and then convert...
516 exit_code
= -job
->status
;
517 if (WIFEXITED(exit_code
))
518 exit_code
= WEXITSTATUS(exit_code
);
520 exit_code
= job
->status
;
522 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Backend returned status %d (%s)",
524 exit_code
== CUPS_BACKEND_FAILED
? "failed" :
525 exit_code
== CUPS_BACKEND_AUTH_REQUIRED
?
526 "authentication required" :
527 exit_code
== CUPS_BACKEND_HOLD
? "hold job" :
528 exit_code
== CUPS_BACKEND_STOP
? "stop printer" :
529 exit_code
== CUPS_BACKEND_CANCEL
? "cancel job" :
530 exit_code
< 0 ? "crashed" : "unknown");
533 * Do what needs to be done...
539 case CUPS_BACKEND_FAILED
:
541 * Backend failure, use the error-policy to determine how to
545 cupsdStopJob(job
, 0);
547 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
550 * Mark the job as pending again - we'll retry on another
554 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
555 job
->state_value
= IPP_JOB_PENDING
;
561 * If the job was queued to a class, try requeuing it... For
562 * faxes and retry-job queues, hold the current job for 5 minutes.
565 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
567 else if ((printer
->type
& CUPS_PRINTER_FAX
) ||
568 !strcmp(printer
->error_policy
, "retry-job"))
571 * See how many times we've tried to send the job; if more than
572 * the limit, cancel the job.
577 if (job
->tries
>= JobRetryLimit
)
583 cupsdLogMessage(CUPSD_LOG_ERROR
,
584 "Canceling job %d since it could not be sent "
586 job
->id
, JobRetryLimit
);
588 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
593 * Try again in N seconds...
596 set_hold_until(job
, time(NULL
) + JobRetryInterval
);
598 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, job
->printer
, job
,
599 "Job held due to fax errors; please consult "
600 "the error_log file for details.");
601 cupsdSetPrinterState(printer
, IPP_PRINTER_IDLE
, 0);
604 else if (!strcmp(printer
->error_policy
, "abort-job"))
605 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
608 cupsdSetPrinterState(printer
, IPP_PRINTER_STOPPED
, 1);
610 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
611 "Job stopped due to backend errors; please consult "
612 "the error_log file for details.");
616 case CUPS_BACKEND_CANCEL
:
621 cupsdCancelJob(job
, 0, IPP_JOB_CANCELED
);
624 case CUPS_BACKEND_HOLD
:
629 cupsdStopJob(job
, 0);
631 cupsdSetJobHoldUntil(job
, "indefinite");
633 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
634 job
->state_value
= IPP_JOB_HELD
;
638 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
639 "Job held due to backend errors; please consult "
640 "the error_log file for details.");
643 case CUPS_BACKEND_STOP
:
645 * Stop the printer...
648 cupsdStopJob(job
, 0);
650 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
651 job
->state_value
= IPP_JOB_PENDING
;
654 cupsdSetPrinterState(printer
, IPP_PRINTER_STOPPED
, 1);
656 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
657 "Job stopped due to backend errors; please consult "
658 "the error_log file for details.");
661 case CUPS_BACKEND_AUTH_REQUIRED
:
662 cupsdStopJob(job
, 0);
664 cupsdSetJobHoldUntil(job
, "authenticated");
666 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
667 job
->state_value
= IPP_JOB_HELD
;
671 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
672 "Authentication is required for job %d.", job
->id
);
677 * Try printing another job...
682 else if (job
->status
> 0)
685 * Filter had errors; stop job...
688 cupsdStopJob(job
, 1);
690 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
691 "Job stopped due to filter errors; please consult the "
692 "error_log file for details.");
698 * Job printed successfully; cancel it...
701 if (job
->current_file
< job
->num_files
)
704 * Start the next file in the job...
707 FilterLevel
-= job
->cost
;
708 start_job(job
, printer
);
713 * Close out this job...
716 job_history
= JobHistory
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
);
718 cupsdCancelJob(job
, 0, IPP_JOB_COMPLETED
);
726 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
730 cupsdFreeAllJobs(void)
732 cupsd_job_t
*job
; /* Current job */
743 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
745 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
747 cupsArrayRemove(Jobs
, job
);
748 cupsArrayRemove(ActiveJobs
, job
);
753 cupsdReleaseSignals();
758 * 'cupsdFindJob()' - Find the specified job.
761 cupsd_job_t
* /* O - Job data */
762 cupsdFindJob(int id
) /* I - Job ID */
764 cupsd_job_t key
; /* Search key */
769 return ((cupsd_job_t
*)cupsArrayFind(Jobs
, &key
));
774 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
775 * or held jobs in a printer or class.
778 int /* O - Job count */
779 cupsdGetPrinterJobCount(
780 const char *dest
) /* I - Printer or class name */
782 int count
; /* Job count */
783 cupsd_job_t
*job
; /* Current job */
786 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
788 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
789 if (job
->dest
&& !strcasecmp(job
->dest
, dest
))
797 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
798 * or held jobs for a user.
801 int /* O - Job count */
802 cupsdGetUserJobCount(
803 const char *username
) /* I - Username */
805 int count
; /* Job count */
806 cupsd_job_t
*job
; /* Current job */
809 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
811 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
812 if (!strcasecmp(job
->username
, username
))
820 * 'cupsdHoldJob()' - Hold the specified job.
824 cupsdHoldJob(cupsd_job_t
*job
) /* I - Job data */
826 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdHoldJob: id = %d", job
->id
);
828 if (job
->state_value
== IPP_JOB_PROCESSING
)
829 cupsdStopJob(job
, 0);
833 DEBUG_puts("cupsdHoldJob: setting state to held...");
835 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
836 job
->state_value
= IPP_JOB_HELD
;
845 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
849 cupsdLoadAllJobs(void)
851 char filename
[1024]; /* Full filename of job.cache file */
852 struct stat fileinfo
, /* Information on job.cache file */
853 dirinfo
; /* Information on RequestRoot dir */
858 * Create the job arrays as needed...
862 Jobs
= cupsArrayNew(compare_jobs
, NULL
);
865 ActiveJobs
= cupsArrayNew(compare_active_jobs
, NULL
);
868 * See whether the job.cache file is older than the RequestRoot directory...
871 snprintf(filename
, sizeof(filename
), "%s/job.cache", CacheDir
);
873 if (stat(filename
, &fileinfo
))
875 fileinfo
.st_mtime
= 0;
878 cupsdLogMessage(CUPSD_LOG_ERROR
,
879 "Unable to get file information for \"%s\" - %s",
880 filename
, strerror(errno
));
883 if (stat(RequestRoot
, &dirinfo
))
885 dirinfo
.st_mtime
= 0;
888 cupsdLogMessage(CUPSD_LOG_ERROR
,
889 "Unable to get directory information for \"%s\" - %s",
890 RequestRoot
, strerror(errno
));
894 * Load the most recent source for job data...
897 if (dirinfo
.st_mtime
> fileinfo
.st_mtime
)
901 load_next_job_id(filename
);
904 load_job_cache(filename
);
907 * Clean out old jobs as needed...
910 if (MaxJobs
> 0 && cupsArrayCount(Jobs
) >= MaxJobs
)
916 * 'cupsdLoadJob()' - Load a single job...
920 cupsdLoadJob(cupsd_job_t
*job
) /* I - Job */
922 char jobfile
[1024]; /* Job filename */
923 cups_file_t
*fp
; /* Job file */
924 int fileid
; /* Current file ID */
925 ipp_attribute_t
*attr
; /* Job attribute */
926 const char *dest
; /* Destination */
927 mime_type_t
**filetypes
; /* New filetypes array */
928 int *compressions
; /* New compressions array */
933 if (job
->state_value
> IPP_JOB_STOPPED
)
934 job
->access_time
= time(NULL
);
939 if ((job
->attrs
= ippNew()) == NULL
)
941 cupsdLogMessage(CUPSD_LOG_ERROR
, "Ran out of memory for job attributes!");
946 * Load job attributes...
949 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Loading attributes for job %d...",
952 snprintf(jobfile
, sizeof(jobfile
), "%s/c%05d", RequestRoot
, job
->id
);
953 if ((fp
= cupsFileOpen(jobfile
, "r")) == NULL
)
955 cupsdLogMessage(CUPSD_LOG_ERROR
,
956 "Unable to open job control file \"%s\" - %s!",
957 jobfile
, strerror(errno
));
958 ippDelete(job
->attrs
);
963 if (ippReadIO(fp
, (ipp_iocb_t
)cupsFileRead
, 1, NULL
, job
->attrs
) != IPP_DATA
)
965 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to read job control file \"%s\"!",
968 ippDelete(job
->attrs
);
977 * Copy attribute data to the job object...
980 if ((job
->state
= ippFindAttribute(job
->attrs
, "job-state",
981 IPP_TAG_ENUM
)) == NULL
)
983 cupsdLogMessage(CUPSD_LOG_ERROR
,
984 "Missing or bad job-state attribute in control "
987 ippDelete(job
->attrs
);
993 job
->state_value
= (ipp_jstate_t
)job
->state
->values
[0].integer
;
997 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri",
998 IPP_TAG_URI
)) == NULL
)
1000 cupsdLogMessage(CUPSD_LOG_ERROR
,
1001 "No job-printer-uri attribute in control file \"%s\"!",
1003 ippDelete(job
->attrs
);
1009 if ((dest
= cupsdValidateDest(attr
->values
[0].string
.text
, &(job
->dtype
),
1012 cupsdLogMessage(CUPSD_LOG_ERROR
,
1013 "Unable to queue job for destination \"%s\"!",
1014 attr
->values
[0].string
.text
);
1015 ippDelete(job
->attrs
);
1021 cupsdSetString(&job
->dest
, dest
);
1024 job
->sheets
= ippFindAttribute(job
->attrs
, "job-media-sheets-completed",
1026 job
->job_sheets
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
1030 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority",
1031 IPP_TAG_INTEGER
)) == NULL
)
1033 cupsdLogMessage(CUPSD_LOG_ERROR
,
1034 "Missing or bad job-priority attribute in control "
1035 "file \"%s\"!", jobfile
);
1036 ippDelete(job
->attrs
);
1042 job
->priority
= attr
->values
[0].integer
;
1047 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-user-name",
1048 IPP_TAG_NAME
)) == NULL
)
1050 cupsdLogMessage(CUPSD_LOG_ERROR
,
1051 "Missing or bad job-originating-user-name attribute "
1052 "in control file \"%s\"!", jobfile
);
1053 ippDelete(job
->attrs
);
1059 cupsdSetString(&job
->username
, attr
->values
[0].string
.text
);
1063 * Set the job hold-until time and state...
1066 if (job
->state_value
== IPP_JOB_HELD
)
1068 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
1069 IPP_TAG_KEYWORD
)) == NULL
)
1070 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
1073 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
);
1076 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1077 job
->state_value
= IPP_JOB_PENDING
;
1080 else if (job
->state_value
== IPP_JOB_PROCESSING
)
1082 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1083 job
->state_value
= IPP_JOB_PENDING
;
1086 if (!job
->num_files
)
1089 * Find all the d##### files...
1092 for (fileid
= 1; fileid
< 10000; fileid
++)
1094 snprintf(jobfile
, sizeof(jobfile
), "%s/d%05d-%03d", RequestRoot
,
1097 if (access(jobfile
, 0))
1100 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Auto-typing document file \"%s\"...",
1103 if (fileid
> job
->num_files
)
1105 if (job
->num_files
== 0)
1107 compressions
= (int *)calloc(fileid
, sizeof(int));
1108 filetypes
= (mime_type_t
**)calloc(fileid
, sizeof(mime_type_t
*));
1112 compressions
= (int *)realloc(job
->compressions
,
1113 sizeof(int) * fileid
);
1114 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
1115 sizeof(mime_type_t
*) *
1119 if (!compressions
|| !filetypes
)
1121 cupsdLogMessage(CUPSD_LOG_ERROR
,
1122 "Ran out of memory for job file types!");
1126 job
->compressions
= compressions
;
1127 job
->filetypes
= filetypes
;
1128 job
->num_files
= fileid
;
1131 job
->filetypes
[fileid
- 1] = mimeFileType(MimeDatabase
, jobfile
, NULL
,
1132 job
->compressions
+ fileid
- 1);
1134 if (!job
->filetypes
[fileid
- 1])
1135 job
->filetypes
[fileid
- 1] = mimeType(MimeDatabase
, "application",
1140 job
->access_time
= time(NULL
);
1145 * 'cupsdMoveJob()' - Move the specified job to a different destination.
1149 cupsdMoveJob(cupsd_job_t
*job
, /* I - Job */
1150 cupsd_printer_t
*p
) /* I - Destination printer or class */
1152 ipp_attribute_t
*attr
; /* job-printer-uri attribute */
1153 const char *olddest
; /* Old destination */
1154 cupsd_printer_t
*oldp
; /* Old pointer */
1158 * Don't move completed jobs...
1161 if (job
->state_value
> IPP_JOB_STOPPED
)
1165 * Get the old destination...
1168 olddest
= job
->dest
;
1171 oldp
= job
->printer
;
1173 oldp
= cupsdFindDest(olddest
);
1176 * Change the destination information...
1181 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, oldp
, job
,
1182 "Job #%d moved from %s to %s.", job
->id
, olddest
,
1185 cupsdSetString(&job
->dest
, p
->name
);
1186 job
->dtype
= p
->type
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
1187 CUPS_PRINTER_IMPLICIT
);
1189 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri",
1190 IPP_TAG_URI
)) != NULL
)
1191 cupsdSetString(&(attr
->values
[0].string
.text
), p
->uri
);
1193 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, p
, job
,
1194 "Job #%d moved from %s to %s.", job
->id
, olddest
,
1202 * 'cupsdReleaseJob()' - Release the specified job.
1206 cupsdReleaseJob(cupsd_job_t
*job
) /* I - Job */
1208 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdReleaseJob: id = %d", job
->id
);
1210 if (job
->state_value
== IPP_JOB_HELD
)
1212 DEBUG_puts("cupsdReleaseJob: setting state to pending...");
1214 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1215 job
->state_value
= IPP_JOB_PENDING
;
1223 * 'cupsdRestartJob()' - Restart the specified job.
1227 cupsdRestartJob(cupsd_job_t
*job
) /* I - Job */
1229 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdRestartJob: id = %d", job
->id
);
1231 if (job
->state_value
== IPP_JOB_STOPPED
|| job
->num_files
)
1233 ipp_jstate_t old_state
; /* Old job state */
1238 old_state
= job
->state_value
;
1241 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1242 job
->state_value
= IPP_JOB_PENDING
;
1246 if (old_state
> IPP_JOB_STOPPED
)
1247 cupsArrayAdd(ActiveJobs
, job
);
1255 * 'cupsdSaveAllJobs()' - Save a summary of all jobs to disk.
1259 cupsdSaveAllJobs(void)
1261 int i
; /* Looping var */
1262 cups_file_t
*fp
; /* Job cache file */
1263 char temp
[1024]; /* Temporary string */
1264 cupsd_job_t
*job
; /* Current job */
1265 time_t curtime
; /* Current time */
1266 struct tm
*curdate
; /* Current date */
1269 snprintf(temp
, sizeof(temp
), "%s/job.cache", CacheDir
);
1270 if ((fp
= cupsFileOpen(temp
, "w")) == NULL
)
1272 cupsdLogMessage(CUPSD_LOG_ERROR
,
1273 "Unable to create job cache file \"%s\" - %s",
1274 temp
, strerror(errno
));
1278 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving job cache file \"%s\"...", temp
);
1281 * Restrict access to the file...
1284 fchown(cupsFileNumber(fp
), getuid(), Group
);
1285 fchmod(cupsFileNumber(fp
), ConfigFilePerm
);
1288 * Write a small header to the file...
1291 curtime
= time(NULL
);
1292 curdate
= localtime(&curtime
);
1293 strftime(temp
, sizeof(temp
) - 1, "%Y-%m-%d %H:%M", curdate
);
1295 cupsFilePuts(fp
, "# Job cache file for " CUPS_SVERSION
"\n");
1296 cupsFilePrintf(fp
, "# Written by cupsd on %s\n", temp
);
1297 cupsFilePrintf(fp
, "NextJobId %d\n", NextJobId
);
1300 * Write each job known to the system...
1303 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
1305 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
1307 cupsFilePrintf(fp
, "<Job %d>\n", job
->id
);
1308 cupsFilePrintf(fp
, "State %d\n", job
->state_value
);
1309 cupsFilePrintf(fp
, "Priority %d\n", job
->priority
);
1310 cupsFilePrintf(fp
, "Username %s\n", job
->username
);
1311 cupsFilePrintf(fp
, "Destination %s\n", job
->dest
);
1312 cupsFilePrintf(fp
, "DestType %d\n", job
->dtype
);
1313 cupsFilePrintf(fp
, "NumFiles %d\n", job
->num_files
);
1314 for (i
= 0; i
< job
->num_files
; i
++)
1315 cupsFilePrintf(fp
, "File %d %s/%s %d\n", i
+ 1, job
->filetypes
[i
]->super
,
1316 job
->filetypes
[i
]->type
, job
->compressions
[i
]);
1317 cupsFilePuts(fp
, "</Job>\n");
1325 * 'cupsdSaveJob()' - Save a job to disk.
1329 cupsdSaveJob(cupsd_job_t
*job
) /* I - Job */
1331 char filename
[1024]; /* Job control filename */
1332 cups_file_t
*fp
; /* Job file */
1335 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSaveJob(job=%p(%d)): job->attrs=%p",
1336 job
, job
->id
, job
->attrs
);
1338 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
, job
->id
);
1340 if ((fp
= cupsFileOpen(filename
, "w")) == NULL
)
1342 cupsdLogMessage(CUPSD_LOG_ERROR
,
1343 "Unable to create job control file \"%s\" - %s.",
1344 filename
, strerror(errno
));
1348 fchmod(cupsFileNumber(fp
), 0600);
1349 fchown(cupsFileNumber(fp
), RunUser
, Group
);
1351 job
->attrs
->state
= IPP_IDLE
;
1353 if (ippWriteIO(fp
, (ipp_iocb_t
)cupsFileWrite
, 1, NULL
,
1354 job
->attrs
) != IPP_DATA
)
1355 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to write job control file \"%s\"!",
1363 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job...
1367 cupsdSetJobHoldUntil(cupsd_job_t
*job
, /* I - Job */
1368 const char *when
) /* I - When to resume */
1370 time_t curtime
; /* Current time */
1371 struct tm
*curdate
; /* Current date */
1372 int hour
; /* Hold hour */
1373 int minute
; /* Hold minute */
1374 int second
; /* Hold second */
1377 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSetJobHoldUntil(%d, \"%s\")",
1382 if (!strcmp(when
, "indefinite") || !strcmp(when
, "authenticated"))
1385 * Hold indefinitely...
1388 job
->hold_until
= 0;
1390 else if (!strcmp(when
, "day-time"))
1393 * Hold to 6am the next morning unless local time is < 6pm.
1396 curtime
= time(NULL
);
1397 curdate
= localtime(&curtime
);
1399 if (curdate
->tm_hour
< 18)
1400 job
->hold_until
= curtime
;
1402 job
->hold_until
= curtime
+
1403 ((29 - curdate
->tm_hour
) * 60 + 59 -
1404 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1406 else if (!strcmp(when
, "evening") || !strcmp(when
, "night"))
1409 * Hold to 6pm unless local time is > 6pm or < 6am.
1412 curtime
= time(NULL
);
1413 curdate
= localtime(&curtime
);
1415 if (curdate
->tm_hour
< 6 || curdate
->tm_hour
>= 18)
1416 job
->hold_until
= curtime
;
1418 job
->hold_until
= curtime
+
1419 ((17 - curdate
->tm_hour
) * 60 + 59 -
1420 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1422 else if (!strcmp(when
, "second-shift"))
1425 * Hold to 4pm unless local time is > 4pm.
1428 curtime
= time(NULL
);
1429 curdate
= localtime(&curtime
);
1431 if (curdate
->tm_hour
>= 16)
1432 job
->hold_until
= curtime
;
1434 job
->hold_until
= curtime
+
1435 ((15 - curdate
->tm_hour
) * 60 + 59 -
1436 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1438 else if (!strcmp(when
, "third-shift"))
1441 * Hold to 12am unless local time is < 8am.
1444 curtime
= time(NULL
);
1445 curdate
= localtime(&curtime
);
1447 if (curdate
->tm_hour
< 8)
1448 job
->hold_until
= curtime
;
1450 job
->hold_until
= curtime
+
1451 ((23 - curdate
->tm_hour
) * 60 + 59 -
1452 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1454 else if (!strcmp(when
, "weekend"))
1457 * Hold to weekend unless we are in the weekend.
1460 curtime
= time(NULL
);
1461 curdate
= localtime(&curtime
);
1463 if (curdate
->tm_wday
|| curdate
->tm_wday
== 6)
1464 job
->hold_until
= curtime
;
1466 job
->hold_until
= curtime
+
1467 (((5 - curdate
->tm_wday
) * 24 +
1468 (17 - curdate
->tm_hour
)) * 60 + 59 -
1469 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1471 else if (sscanf(when
, "%d:%d:%d", &hour
, &minute
, &second
) >= 2)
1474 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1477 curtime
= time(NULL
);
1478 curdate
= gmtime(&curtime
);
1480 job
->hold_until
= curtime
+
1481 ((hour
- curdate
->tm_hour
) * 60 + minute
-
1482 curdate
->tm_min
) * 60 + second
- curdate
->tm_sec
;
1485 * Hold until next day as needed...
1488 if (job
->hold_until
< curtime
)
1489 job
->hold_until
+= 24 * 60 * 60 * 60;
1492 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSetJobHoldUntil: hold_until = %d",
1493 (int)job
->hold_until
);
1498 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
1499 * the list as needed.
1503 cupsdSetJobPriority(
1504 cupsd_job_t
*job
, /* I - Job ID */
1505 int priority
) /* I - New priority (0 to 100) */
1507 ipp_attribute_t
*attr
; /* Job attribute */
1511 * Don't change completed jobs...
1514 if (job
->state_value
>= IPP_JOB_PROCESSING
)
1518 * Set the new priority and re-add the job into the active list...
1521 cupsArrayRemove(ActiveJobs
, job
);
1523 job
->priority
= priority
;
1525 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority",
1526 IPP_TAG_INTEGER
)) != NULL
)
1527 attr
->values
[0].integer
= priority
;
1529 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1532 cupsArrayAdd(ActiveJobs
, job
);
1539 * 'cupsdStopAllJobs()' - Stop all print jobs.
1543 cupsdStopAllJobs(int force
) /* I - 1 = Force all filters to stop */
1545 cupsd_job_t
*job
; /* Current job */
1548 DEBUG_puts("cupsdStopAllJobs()");
1550 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
1552 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
1553 if (job
->state_value
== IPP_JOB_PROCESSING
)
1555 cupsdStopJob(job
, force
);
1556 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1557 job
->state_value
= IPP_JOB_PENDING
;
1563 * 'cupsdStopJob()' - Stop a print job.
1567 cupsdStopJob(cupsd_job_t
*job
, /* I - Job */
1568 int force
) /* I - 1 = Force all filters to stop */
1570 int i
; /* Looping var */
1573 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdStopJob: id = %d, force = %d",
1576 if (job
->state_value
!= IPP_JOB_PROCESSING
)
1579 FilterLevel
-= job
->cost
;
1581 if (job
->printer
->state
== IPP_PRINTER_PROCESSING
)
1582 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_IDLE
, 0);
1584 job
->state
->values
[0].integer
= IPP_JOB_STOPPED
;
1585 job
->state_value
= IPP_JOB_STOPPED
;
1586 job
->printer
->job
= NULL
;
1587 job
->printer
= NULL
;
1589 job
->current_file
--;
1591 for (i
= 0; job
->filters
[i
]; i
++)
1592 if (job
->filters
[i
] > 0)
1594 cupsdEndProcess(job
->filters
[i
], force
);
1595 job
->filters
[i
] = 0;
1598 if (job
->backend
> 0)
1600 cupsdEndProcess(job
->backend
, force
);
1604 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1605 "cupsdStopJob: Closing print pipes [ %d %d ]...",
1606 job
->print_pipes
[0], job
->print_pipes
[1]);
1608 cupsdClosePipe(job
->print_pipes
);
1610 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1611 "cupsdStopJob: Closing back pipes [ %d %d ]...",
1612 job
->back_pipes
[0], job
->back_pipes
[1]);
1614 cupsdClosePipe(job
->back_pipes
);
1616 if (job
->status_buffer
)
1619 * Close the pipe and clear the input bit.
1622 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1623 "cupsdStopJob: Removing fd %d from InputSet...",
1624 job
->status_buffer
->fd
);
1626 FD_CLR(job
->status_buffer
->fd
, InputSet
);
1628 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1629 "cupsdStopJob: Closing status pipes [ %d %d ]...",
1630 job
->status_pipes
[0], job
->status_pipes
[1]);
1632 cupsdClosePipe(job
->status_pipes
);
1633 cupsdStatBufDelete(job
->status_buffer
);
1635 job
->status_buffer
= NULL
;
1641 * 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory.
1645 cupsdUnloadCompletedJobs(void)
1647 cupsd_job_t
*job
; /* Current job */
1648 time_t expire
; /* Expiration time */
1651 expire
= time(NULL
) - 60;
1653 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
1655 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
1656 if (job
->attrs
&& job
->state_value
>= IPP_JOB_STOPPED
&&
1657 job
->access_time
< expire
)
1663 * 'cupsdUpdateJob()' - Read a status update from a job's filters.
1667 cupsdUpdateJob(cupsd_job_t
*job
) /* I - Job to check */
1669 int i
; /* Looping var */
1670 int copies
; /* Number of copies printed */
1671 char message
[1024], /* Message text */
1672 *ptr
; /* Pointer update... */
1673 int loglevel
, /* Log level for message */
1674 event
= 0; /* Events? */
1677 while ((ptr
= cupsdStatBufUpdate(job
->status_buffer
, &loglevel
,
1678 message
, sizeof(message
))) != NULL
)
1681 * Process page and printer state messages as needed...
1684 if (loglevel
== CUPSD_LOG_PAGE
)
1687 * Page message; send the message to the page_log file and update the
1688 * job sheet count...
1691 if (job
->sheets
!= NULL
)
1693 if (!strncasecmp(message
, "total ", 6))
1696 * Got a total count of pages from a backend or filter...
1699 copies
= atoi(message
+ 6);
1700 copies
-= job
->sheets
->values
[0].integer
; /* Just track the delta */
1702 else if (!sscanf(message
, "%*d%d", &copies
))
1705 job
->sheets
->values
[0].integer
+= copies
;
1707 if (job
->printer
->page_limit
)
1708 cupsdUpdateQuota(job
->printer
, job
->username
, copies
, 0);
1711 cupsdLogPage(job
, message
);
1713 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS
, job
->printer
, job
,
1714 "Printed %d page(s).", job
->sheets
->values
[0].integer
);
1716 else if (loglevel
== CUPSD_LOG_STATE
)
1718 cupsdSetPrinterReasons(job
->printer
, message
);
1719 cupsdAddPrinterHistory(job
->printer
);
1720 event
|= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
1722 else if (loglevel
== CUPSD_LOG_ATTR
)
1725 * Set attribute(s)...
1731 else if (!strncmp(message
, "recoverable:", 12))
1733 cupsdSetPrinterReasons(job
->printer
,
1734 "+com.apple.print.recoverable-warning");
1737 while (isspace(*ptr
& 255))
1740 cupsdSetString(&job
->printer
->recoverable
, ptr
);
1741 cupsdAddPrinterHistory(job
->printer
);
1742 event
|= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
1744 else if (!strncmp(message
, "recovered:", 10))
1746 cupsdSetPrinterReasons(job
->printer
,
1747 "-com.apple.print.recoverable-warning");
1750 while (isspace(*ptr
& 255))
1753 cupsdSetString(&job
->printer
->recoverable
, ptr
);
1754 cupsdAddPrinterHistory(job
->printer
);
1755 event
|= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
1757 #endif /* __APPLE__ */
1758 else if (loglevel
<= CUPSD_LOG_INFO
)
1761 * Some message to show in the printer-state-message attribute...
1764 strlcpy(job
->printer
->state_message
, message
,
1765 sizeof(job
->printer
->state_message
));
1766 cupsdAddPrinterHistory(job
->printer
);
1767 event
|= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
1770 if (!strchr(job
->status_buffer
->buffer
, '\n'))
1774 if ((event
& CUPSD_EVENT_PRINTER_STATE_CHANGED
))
1775 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE_CHANGED
, job
->printer
, NULL
,
1776 (job
->printer
->type
& CUPS_PRINTER_CLASS
) ?
1777 "Class \"%s\" state changed." :
1778 "Printer \"%s\" state changed.",
1779 job
->printer
->name
);
1781 if (ptr
== NULL
&& !job
->status_buffer
->bufused
)
1784 * See if all of the filters and the backend have returned their
1788 for (i
= 0; job
->filters
[i
] < 0; i
++);
1790 if (job
->filters
[i
])
1793 if (job
->current_file
>= job
->num_files
&& job
->backend
> 0)
1797 * Handle the end of job stuff...
1800 cupsdFinishJob(job
);
1806 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
1809 static int /* O - Difference */
1810 compare_active_jobs(void *first
, /* I - First job */
1811 void *second
, /* I - Second job */
1812 void *data
) /* I - App data (not used) */
1814 int diff
; /* Difference */
1817 if ((diff
= ((cupsd_job_t
*)second
)->priority
-
1818 ((cupsd_job_t
*)first
)->priority
) != 0)
1821 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
1826 * 'compare_jobs()' - Compare the job IDs of two jobs.
1829 static int /* O - Difference */
1830 compare_jobs(void *first
, /* I - First job */
1831 void *second
, /* I - Second job */
1832 void *data
) /* I - App data (not used) */
1834 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
1839 * 'free_job()' - Free all memory used by a job.
1843 free_job(cupsd_job_t
*job
) /* I - Job */
1845 cupsdClearString(&job
->username
);
1846 cupsdClearString(&job
->dest
);
1848 cupsdClearString(&job
->ccname
);
1849 #endif /* HAVE_GSSAPI */
1851 if (job
->num_files
> 0)
1853 free(job
->compressions
);
1854 free(job
->filetypes
);
1857 ippDelete(job
->attrs
);
1864 * 'ipp_length()' - Compute the size of the buffer needed to hold
1865 * the textual IPP attributes.
1868 static int /* O - Size of attribute buffer */
1869 ipp_length(ipp_t
*ipp
) /* I - IPP request */
1871 int bytes
; /* Number of bytes */
1872 int i
; /* Looping var */
1873 ipp_attribute_t
*attr
; /* Current attribute */
1877 * Loop through all attributes...
1882 for (attr
= ipp
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1885 * Skip attributes that won't be sent to filters...
1888 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
1889 attr
->value_tag
== IPP_TAG_NAMELANG
||
1890 attr
->value_tag
== IPP_TAG_TEXTLANG
||
1891 attr
->value_tag
== IPP_TAG_URI
||
1892 attr
->value_tag
== IPP_TAG_URISCHEME
)
1895 if (strncmp(attr
->name
, "time-", 5) == 0)
1899 * Add space for a leading space and commas between each value.
1900 * For the first attribute, the leading space isn't used, so the
1901 * extra byte can be used as the nul terminator...
1904 bytes
++; /* " " separator */
1905 bytes
+= attr
->num_values
; /* "," separators */
1908 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
1909 * other attributes appear as "foo=value1,value2,...,valueN".
1912 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
1913 bytes
+= strlen(attr
->name
);
1915 bytes
+= attr
->num_values
* strlen(attr
->name
);
1918 * Now add the size required for each value in the attribute...
1921 switch (attr
->value_tag
)
1923 case IPP_TAG_INTEGER
:
1926 * Minimum value of a signed integer is -2147483647, or 11 digits.
1929 bytes
+= attr
->num_values
* 11;
1932 case IPP_TAG_BOOLEAN
:
1934 * Add two bytes for each false ("no") value...
1937 for (i
= 0; i
< attr
->num_values
; i
++)
1938 if (!attr
->values
[i
].boolean
)
1942 case IPP_TAG_RANGE
:
1944 * A range is two signed integers separated by a hyphen, or
1945 * 23 characters max.
1948 bytes
+= attr
->num_values
* 23;
1951 case IPP_TAG_RESOLUTION
:
1953 * A resolution is two signed integers separated by an "x" and
1954 * suffixed by the units, or 26 characters max.
1957 bytes
+= attr
->num_values
* 26;
1960 case IPP_TAG_STRING
:
1963 case IPP_TAG_KEYWORD
:
1964 case IPP_TAG_CHARSET
:
1965 case IPP_TAG_LANGUAGE
:
1968 * Strings can contain characters that need quoting. We need
1969 * at least 2 * len + 2 characters to cover the quotes and
1970 * any backslashes in the string.
1973 for (i
= 0; i
< attr
->num_values
; i
++)
1974 bytes
+= 2 * strlen(attr
->values
[i
].string
.text
) + 2;
1978 break; /* anti-compiler-warning-code */
1987 * 'load_job_cache()' - Load jobs from the job.cache file.
1991 load_job_cache(const char *filename
) /* I - job.cache filename */
1993 cups_file_t
*fp
; /* job.cache file */
1994 char line
[1024], /* Line buffer */
1995 *value
; /* Value on line */
1996 int linenum
; /* Line number in file */
1997 cupsd_job_t
*job
; /* Current job */
1998 int jobid
; /* Job ID */
1999 char jobfile
[1024]; /* Job filename */
2003 * Open the job.cache file...
2006 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
2008 if (errno
!= ENOENT
)
2009 cupsdLogMessage(CUPSD_LOG_ERROR
,
2010 "Unable to open job cache file \"%s\": %s",
2011 filename
, strerror(errno
));
2013 load_request_root();
2019 * Read entries from the job cache file and create jobs as needed.
2022 cupsdLogMessage(CUPSD_LOG_INFO
, "Loading job cache file \"%s\"...",
2028 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
2030 if (!strcasecmp(line
, "NextJobId"))
2033 NextJobId
= atoi(value
);
2035 else if (!strcasecmp(line
, "<Job"))
2039 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing </Job> directive on line %d!",
2046 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing job ID on line %d!", linenum
);
2050 jobid
= atoi(value
);
2054 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad job ID %d on line %d!", jobid
,
2059 snprintf(jobfile
, sizeof(jobfile
), "%s/c%05d", RequestRoot
, jobid
);
2060 if (access(jobfile
, 0))
2062 cupsdLogMessage(CUPSD_LOG_ERROR
, "Job %d files have gone away!", jobid
);
2066 job
= calloc(1, sizeof(cupsd_job_t
));
2069 cupsdLogMessage(CUPSD_LOG_EMERG
,
2070 "Unable to allocate memory for job %d!", jobid
);
2075 job
->back_pipes
[0] = -1;
2076 job
->back_pipes
[1] = -1;
2077 job
->print_pipes
[0] = -1;
2078 job
->print_pipes
[1] = -1;
2079 job
->status_pipes
[0] = -1;
2080 job
->status_pipes
[1] = -1;
2082 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Loading job %d from cache...", job
->id
);
2086 cupsdLogMessage(CUPSD_LOG_ERROR
,
2087 "Missing <Job #> directive on line %d!", linenum
);
2090 else if (!strcasecmp(line
, "</Job>"))
2092 cupsArrayAdd(Jobs
, job
);
2094 if (job
->state_value
<= IPP_JOB_STOPPED
)
2096 cupsArrayAdd(ActiveJobs
, job
);
2104 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing value on line %d!", linenum
);
2107 else if (!strcasecmp(line
, "State"))
2109 job
->state_value
= (ipp_jstate_t
)atoi(value
);
2111 if (job
->state_value
< IPP_JOB_PENDING
)
2112 job
->state_value
= IPP_JOB_PENDING
;
2113 else if (job
->state_value
> IPP_JOB_COMPLETED
)
2114 job
->state_value
= IPP_JOB_COMPLETED
;
2116 else if (!strcasecmp(line
, "Priority"))
2118 job
->priority
= atoi(value
);
2120 else if (!strcasecmp(line
, "Username"))
2122 cupsdSetString(&job
->username
, value
);
2124 else if (!strcasecmp(line
, "Destination"))
2126 cupsdSetString(&job
->dest
, value
);
2128 else if (!strcasecmp(line
, "DestType"))
2130 job
->dtype
= (cups_ptype_t
)atoi(value
);
2132 else if (!strcasecmp(line
, "NumFiles"))
2134 job
->num_files
= atoi(value
);
2136 if (job
->num_files
< 0)
2138 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad NumFiles value %d on line %d!",
2139 job
->num_files
, linenum
);
2144 if (job
->num_files
> 0)
2146 snprintf(jobfile
, sizeof(jobfile
), "%s/d%05d-001", RequestRoot
,
2148 if (access(jobfile
, 0))
2150 cupsdLogMessage(CUPSD_LOG_INFO
,
2151 "Data files for job %d have gone away!", job
->id
);
2156 job
->filetypes
= calloc(job
->num_files
, sizeof(mime_type_t
*));
2157 job
->compressions
= calloc(job
->num_files
, sizeof(int));
2159 if (!job
->filetypes
|| !job
->compressions
)
2161 cupsdLogMessage(CUPSD_LOG_EMERG
,
2162 "Unable to allocate memory for %d files!",
2168 else if (!strcasecmp(line
, "File"))
2170 int number
, /* File number */
2171 compression
; /* Compression value */
2172 char super
[MIME_MAX_SUPER
], /* MIME super type */
2173 type
[MIME_MAX_TYPE
]; /* MIME type */
2176 if (sscanf(value
, "%d%*[ \t]%15[^/]/%255s%d", &number
, super
, type
,
2179 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad File on line %d!", linenum
);
2183 if (number
< 1 || number
> job
->num_files
)
2185 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad File number %d on line %d!",
2192 job
->compressions
[number
] = compression
;
2193 job
->filetypes
[number
] = mimeType(MimeDatabase
, super
, type
);
2195 if (!job
->filetypes
[number
])
2198 * If the original MIME type is unknown, auto-type it!
2201 cupsdLogMessage(CUPSD_LOG_ERROR
,
2202 "Unknown MIME type %s/%s for file %d of job %d!",
2203 super
, type
, number
+ 1, job
->id
);
2205 snprintf(jobfile
, sizeof(jobfile
), "%s/d%05d-%03d", RequestRoot
,
2206 job
->id
, number
+ 1);
2207 job
->filetypes
[number
] = mimeFileType(MimeDatabase
, jobfile
, NULL
,
2208 job
->compressions
+ number
);
2211 * If that didn't work, assume it is raw...
2214 if (!job
->filetypes
[number
])
2215 job
->filetypes
[number
] = mimeType(MimeDatabase
, "application",
2220 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unknown %s directive on line %d!",
2229 * 'load_next_job_id()' - Load the NextJobId value from the job.cache file.
2233 load_next_job_id(const char *filename
) /* I - job.cache filename */
2235 cups_file_t
*fp
; /* job.cache file */
2236 char line
[1024], /* Line buffer */
2237 *value
; /* Value on line */
2238 int linenum
; /* Line number in file */
2239 int next_job_id
; /* NextJobId value from line */
2243 * Read the NextJobId directive from the job.cache file and use
2244 * the value (if any).
2247 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
2249 if (errno
!= ENOENT
)
2250 cupsdLogMessage(CUPSD_LOG_ERROR
,
2251 "Unable to open job cache file \"%s\": %s",
2252 filename
, strerror(errno
));
2257 cupsdLogMessage(CUPSD_LOG_INFO
,
2258 "Loading NextJobId from job cache file \"%s\"...", filename
);
2262 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
2264 if (!strcasecmp(line
, "NextJobId"))
2268 next_job_id
= atoi(value
);
2270 if (next_job_id
> NextJobId
)
2271 NextJobId
= next_job_id
;
2282 * 'load_request_root()' - Load jobs from the RequestRoot directory.
2286 load_request_root(void)
2288 cups_dir_t
*dir
; /* Directory */
2289 cups_dentry_t
*dent
; /* Directory entry */
2290 cupsd_job_t
*job
; /* New job */
2294 * Open the requests directory...
2297 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Scanning %s for jobs...", RequestRoot
);
2299 if ((dir
= cupsDirOpen(RequestRoot
)) == NULL
)
2301 cupsdLogMessage(CUPSD_LOG_ERROR
,
2302 "Unable to open spool directory \"%s\": %s",
2303 RequestRoot
, strerror(errno
));
2308 * Read all the c##### files...
2311 while ((dent
= cupsDirRead(dir
)) != NULL
)
2312 if (strlen(dent
->filename
) >= 6 && dent
->filename
[0] == 'c')
2315 * Allocate memory for the job...
2318 if ((job
= calloc(sizeof(cupsd_job_t
), 1)) == NULL
)
2320 cupsdLogMessage(CUPSD_LOG_ERROR
, "Ran out of memory for jobs!");
2326 * Assign the job ID...
2329 job
->id
= atoi(dent
->filename
+ 1);
2330 job
->back_pipes
[0] = -1;
2331 job
->back_pipes
[1] = -1;
2332 job
->print_pipes
[0] = -1;
2333 job
->print_pipes
[1] = -1;
2334 job
->status_pipes
[0] = -1;
2335 job
->status_pipes
[1] = -1;
2337 if (job
->id
>= NextJobId
)
2338 NextJobId
= job
->id
+ 1;
2347 * Insert the job into the array, sorting by job priority and ID...
2350 cupsArrayAdd(Jobs
, job
);
2352 if (job
->state_value
<= IPP_JOB_STOPPED
)
2353 cupsArrayAdd(ActiveJobs
, job
);
2363 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2367 set_time(cupsd_job_t
*job
, /* I - Job to update */
2368 const char *name
) /* I - Name of attribute */
2370 ipp_attribute_t
*attr
; /* Time attribute */
2373 if ((attr
= ippFindAttribute(job
->attrs
, name
, IPP_TAG_ZERO
)) != NULL
)
2375 attr
->value_tag
= IPP_TAG_INTEGER
;
2376 attr
->values
[0].integer
= time(NULL
);
2382 * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
2386 set_hold_until(cupsd_job_t
*job
, /* I - Job to update */
2387 time_t holdtime
) /* I - Hold until time */
2389 ipp_attribute_t
*attr
; /* job-hold-until attribute */
2390 struct tm
*holddate
; /* Hold date */
2391 char holdstr
[64]; /* Hold time */
2395 * Set the hold_until value and hold the job...
2398 cupsdLogMessage(CUPSD_LOG_DEBUG
, "set_hold_until: hold_until = %d",
2401 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
2402 job
->state_value
= IPP_JOB_HELD
;
2403 job
->hold_until
= holdtime
;
2406 * Update the job-hold-until attribute with a string representing GMT
2407 * time (HH:MM:SS)...
2410 holddate
= gmtime(&holdtime
);
2411 snprintf(holdstr
, sizeof(holdstr
), "%d:%d:%d", holddate
->tm_hour
,
2412 holddate
->tm_min
, holddate
->tm_sec
);
2414 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
2415 IPP_TAG_KEYWORD
)) == NULL
)
2416 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
2419 * Either add the attribute or update the value of the existing one
2423 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
2424 "job-hold-until", NULL
, holdstr
);
2426 cupsdSetString(&attr
->values
[0].string
.text
, holdstr
);
2433 * 'start_job()' - Start a print job.
2437 start_job(cupsd_job_t
*job
, /* I - Job ID */
2438 cupsd_printer_t
*printer
) /* I - Printer to print job */
2440 int i
; /* Looping var */
2441 int slot
; /* Pipe slot */
2442 cups_array_t
*filters
; /* Filters for job */
2443 mime_filter_t
*filter
, /* Current filter */
2444 port_monitor
; /* Port monitor filter */
2445 char method
[255], /* Method for output */
2446 *optptr
, /* Pointer to options */
2447 *valptr
; /* Pointer in value string */
2448 ipp_attribute_t
*attr
; /* Current attribute */
2449 struct stat backinfo
; /* Backend file information */
2450 int backroot
; /* Run backend as root? */
2451 int pid
; /* Process ID of new filter process */
2452 int banner_page
; /* 1 if banner page, 0 otherwise */
2453 int filterfds
[2][2];/* Pipes used between filters */
2454 int envc
; /* Number of environment variables */
2455 char **argv
, /* Filter command-line arguments */
2456 sani_uri
[1024], /* Sanitized DEVICE_URI env var */
2457 filename
[1024], /* Job filename */
2458 command
[1024], /* Full path to command */
2459 jobid
[255], /* Job ID string */
2460 title
[IPP_MAX_NAME
],
2461 /* Job title string */
2462 copies
[255], /* # copies string */
2463 *envp
[MAX_ENV
+ 12],
2464 /* Environment variables */
2465 charset
[255], /* CHARSET env variable */
2466 class_name
[255],/* CLASS env variable */
2467 classification
[1024],
2468 /* CLASSIFICATION env variable */
2470 /* CONTENT_TYPE env variable */
2472 /* DEVICE_URI env variable */
2473 final_content_type
[1024],
2474 /* FINAL_CONTENT_TYPE env variable */
2475 lang
[255], /* LANG env variable */
2476 ppd
[1024], /* PPD env variable */
2478 /* PRINTER env variable */
2480 /* RIP_MAX_CACHE env variable */
2481 static char *options
= NULL
;/* Full list of options */
2482 static int optlength
= 0; /* Length of option buffer */
2485 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: id = %d, file = %d/%d",
2486 job
->id
, job
->current_file
, job
->num_files
);
2488 if (job
->num_files
== 0)
2490 cupsdLogMessage(CUPSD_LOG_ERROR
, "Job ID %d has no files! Canceling it!",
2493 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
2497 if (printer
->raw
&& !strncmp(printer
->device_uri
, "file:", 5))
2499 cupsdLogMessage(CUPSD_LOG_ERROR
,
2500 "Job ID %d cannot be printed to raw queue pointing to "
2504 strlcpy(printer
->state_message
, "Raw printers cannot use file: devices!",
2505 sizeof(printer
->state_message
));
2506 cupsdStopPrinter(printer
, 1);
2507 cupsdAddPrinterHistory(printer
);
2512 * Figure out what filters are required to convert from
2513 * the source to the destination type...
2522 * Remote jobs and raw queues go directly to the printer without
2526 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2527 "[Job %d] Sending job to queue tagged as raw...", job
->id
);
2534 * Local jobs get filtered...
2537 filters
= mimeFilter(MimeDatabase
, job
->filetypes
[job
->current_file
],
2538 printer
->filetype
, &(job
->cost
));
2542 cupsdLogMessage(CUPSD_LOG_ERROR
,
2543 "Unable to convert file %d to printable format for "
2545 job
->current_file
, job
->id
);
2546 cupsdLogMessage(CUPSD_LOG_INFO
,
2547 "Hint: Do you have ESP Ghostscript installed?");
2549 if (LogLevel
< CUPSD_LOG_DEBUG
)
2550 cupsdLogMessage(CUPSD_LOG_INFO
,
2551 "Hint: Try setting the LogLevel to \"debug\".");
2553 job
->current_file
++;
2555 if (job
->current_file
== job
->num_files
)
2556 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
2562 * Remove NULL ("-") filters...
2565 for (filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
2567 filter
= (mime_filter_t
*)cupsArrayNext(filters
))
2568 if (!strcmp(filter
->filter
, "-"))
2569 cupsArrayRemove(filters
, filter
);
2571 if (cupsArrayCount(filters
) == 0)
2573 cupsArrayDelete(filters
);
2579 * Set a minimum cost of 100 for all jobs so that FilterLimit
2580 * works with raw queues and other low-cost paths.
2583 if (job
->cost
< 100)
2587 * See if the filter cost is too high...
2590 if ((FilterLevel
+ job
->cost
) > FilterLimit
&& FilterLevel
> 0 &&
2594 * Don't print this job quite yet...
2597 cupsArrayDelete(filters
);
2599 cupsdLogMessage(CUPSD_LOG_INFO
,
2600 "Holding job %d because filter limit has been reached.",
2602 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2603 "start_job: id=%d, file=%d, cost=%d, level=%d, "
2605 job
->id
, job
->current_file
, job
->cost
, FilterLevel
,
2610 FilterLevel
+= job
->cost
;
2613 * Add decompression filters, if any...
2616 if (!printer
->raw
&& job
->compressions
[job
->current_file
])
2619 * Add gziptoany filter to the front of the list...
2623 filters
= cupsArrayNew(NULL
, NULL
);
2625 if (!cupsArrayInsert(filters
, &gziptoany_filter
))
2627 cupsdLogMessage(CUPSD_LOG_ERROR
,
2628 "Unable to add decompression filter - %s",
2631 cupsArrayDelete(filters
);
2633 job
->current_file
++;
2635 if (job
->current_file
== job
->num_files
)
2636 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
2643 * Add port monitor, if any...
2646 if (printer
->port_monitor
)
2649 * Add port monitor to the end of the list...
2653 filters
= cupsArrayNew(NULL
, NULL
);
2655 if (!cupsArrayAdd(filters
, &port_monitor
))
2657 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add port monitor - %s",
2660 cupsArrayDelete(filters
);
2662 job
->current_file
++;
2664 if (job
->current_file
== job
->num_files
)
2665 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
2670 snprintf(port_monitor
.filter
, sizeof(port_monitor
.filter
),
2671 "%s/monitor/%s", ServerBin
, printer
->port_monitor
);
2675 * Update the printer and job state to "processing"...
2678 job
->state
->values
[0].integer
= IPP_JOB_PROCESSING
;
2679 job
->state_value
= IPP_JOB_PROCESSING
;
2681 job
->printer
= printer
;
2683 cupsdSetPrinterState(printer
, IPP_PRINTER_PROCESSING
, 0);
2685 if (job
->current_file
== 0)
2688 * Set the processing time...
2691 set_time(job
, "time-at-processing");
2694 * Create the backchannel pipes and make them non-blocking...
2697 cupsdOpenPipe(job
->back_pipes
);
2699 fcntl(job
->back_pipes
[0], F_SETFL
,
2700 fcntl(job
->back_pipes
[0], F_GETFL
) | O_NONBLOCK
);
2702 fcntl(job
->back_pipes
[1], F_SETFL
,
2703 fcntl(job
->back_pipes
[1], F_GETFL
) | O_NONBLOCK
);
2707 * Determine if we are printing a banner page or not...
2710 if (job
->job_sheets
== NULL
)
2712 cupsdLogMessage(CUPSD_LOG_DEBUG
, "No job-sheets attribute.");
2713 if ((job
->job_sheets
=
2714 ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
2715 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2716 "... but someone added one without setting job_sheets!");
2718 else if (job
->job_sheets
->num_values
== 1)
2719 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s",
2720 job
->job_sheets
->values
[0].string
.text
);
2722 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s,%s",
2723 job
->job_sheets
->values
[0].string
.text
,
2724 job
->job_sheets
->values
[1].string
.text
);
2726 if (printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
))
2728 else if (job
->job_sheets
== NULL
)
2730 else if (strcasecmp(job
->job_sheets
->values
[0].string
.text
, "none") != 0 &&
2731 job
->current_file
== 0)
2733 else if (job
->job_sheets
->num_values
> 1 &&
2734 strcasecmp(job
->job_sheets
->values
[1].string
.text
, "none") != 0 &&
2735 job
->current_file
== (job
->num_files
- 1))
2740 cupsdLogMessage(CUPSD_LOG_DEBUG
, "banner_page = %d", banner_page
);
2743 * Building the options string is harder than it needs to be, but
2744 * for the moment we need to pass strings for command-line args and
2745 * not IPP attribute pointers... :)
2747 * First allocate/reallocate the option buffer as needed...
2750 i
= ipp_length(job
->attrs
);
2757 optptr
= realloc(options
, i
);
2761 cupsdLogMessage(CUPSD_LOG_CRIT
,
2762 "Unable to allocate %d bytes for option buffer for "
2763 "job %d!", i
, job
->id
);
2765 cupsArrayDelete(filters
);
2767 FilterLevel
-= job
->cost
;
2769 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
2778 * Now loop through the attributes and convert them to the textual
2779 * representation used by the filters...
2785 snprintf(title
, sizeof(title
), "%s-%d", printer
->name
, job
->id
);
2786 strcpy(copies
, "1");
2788 for (attr
= job
->attrs
->attrs
; attr
!= NULL
; attr
= attr
->next
)
2790 if (!strcmp(attr
->name
, "copies") &&
2791 attr
->value_tag
== IPP_TAG_INTEGER
)
2794 * Don't use the # copies attribute if we are printing the job sheets...
2798 sprintf(copies
, "%d", attr
->values
[0].integer
);
2800 else if (!strcmp(attr
->name
, "job-name") &&
2801 (attr
->value_tag
== IPP_TAG_NAME
||
2802 attr
->value_tag
== IPP_TAG_NAMELANG
))
2803 strlcpy(title
, attr
->values
[0].string
.text
, sizeof(title
));
2804 else if (attr
->group_tag
== IPP_TAG_JOB
)
2807 * Filter out other unwanted attributes...
2810 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
2811 attr
->value_tag
== IPP_TAG_NAMELANG
||
2812 attr
->value_tag
== IPP_TAG_TEXTLANG
||
2813 (attr
->value_tag
== IPP_TAG_URI
&& strcmp(attr
->name
, "job-uuid")) ||
2814 attr
->value_tag
== IPP_TAG_URISCHEME
||
2815 attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
) /* Not yet supported */
2818 if (!strncmp(attr
->name
, "time-", 5))
2821 if (!strncmp(attr
->name
, "job-", 4) && strcmp(attr
->name
, "job-uuid") &&
2822 !(printer
->type
& CUPS_PRINTER_REMOTE
))
2825 if (!strncmp(attr
->name
, "job-", 4) &&
2826 strcmp(attr
->name
, "job-uuid") &&
2827 strcmp(attr
->name
, "job-billing") &&
2828 strcmp(attr
->name
, "job-sheets") &&
2829 strcmp(attr
->name
, "job-hold-until") &&
2830 strcmp(attr
->name
, "job-priority"))
2833 if ((!strcmp(attr
->name
, "page-label") ||
2834 !strcmp(attr
->name
, "page-border") ||
2835 !strncmp(attr
->name
, "number-up", 9) ||
2836 !strcmp(attr
->name
, "page-set") ||
2837 !strcasecmp(attr
->name
, "AP_FIRSTPAGE_InputSlot") ||
2838 !strcasecmp(attr
->name
, "AP_FIRSTPAGE_ManualFeed")) &&
2843 * Otherwise add them to the list...
2846 if (optptr
> options
)
2847 strlcat(optptr
, " ", optlength
- (optptr
- options
));
2849 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
2851 strlcat(optptr
, attr
->name
, optlength
- (optptr
- options
));
2852 strlcat(optptr
, "=", optlength
- (optptr
- options
));
2855 for (i
= 0; i
< attr
->num_values
; i
++)
2858 strlcat(optptr
, ",", optlength
- (optptr
- options
));
2860 optptr
+= strlen(optptr
);
2862 switch (attr
->value_tag
)
2864 case IPP_TAG_INTEGER
:
2866 snprintf(optptr
, optlength
- (optptr
- options
),
2867 "%d", attr
->values
[i
].integer
);
2870 case IPP_TAG_BOOLEAN
:
2871 if (!attr
->values
[i
].boolean
)
2872 strlcat(optptr
, "no", optlength
- (optptr
- options
));
2874 case IPP_TAG_NOVALUE
:
2875 strlcat(optptr
, attr
->name
,
2876 optlength
- (optptr
- options
));
2879 case IPP_TAG_RANGE
:
2880 if (attr
->values
[i
].range
.lower
== attr
->values
[i
].range
.upper
)
2881 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
2882 "%d", attr
->values
[i
].range
.lower
);
2884 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
2885 "%d-%d", attr
->values
[i
].range
.lower
,
2886 attr
->values
[i
].range
.upper
);
2889 case IPP_TAG_RESOLUTION
:
2890 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
2891 "%dx%d%s", attr
->values
[i
].resolution
.xres
,
2892 attr
->values
[i
].resolution
.yres
,
2893 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
2897 case IPP_TAG_STRING
:
2900 case IPP_TAG_KEYWORD
:
2901 case IPP_TAG_CHARSET
:
2902 case IPP_TAG_LANGUAGE
:
2904 for (valptr
= attr
->values
[i
].string
.text
; *valptr
;)
2906 if (strchr(" \t\n\\\'\"", *valptr
))
2908 *optptr
++ = *valptr
++;
2915 break; /* anti-compiler-warning-code */
2919 optptr
+= strlen(optptr
);
2924 * Build the command-line arguments for the filters. Each filter
2925 * has 6 or 7 arguments:
2929 * argv[2] = username
2931 * argv[4] = # copies
2933 * argv[6] = filename (optional; normally stdin)
2935 * This allows legacy printer drivers that use the old System V
2936 * printing interface to be used by CUPS.
2938 * For remote jobs, we send all of the files in the argument list.
2941 if (printer
->remote
&& job
->num_files
> 1)
2942 argv
= calloc(7 + job
->num_files
, sizeof(char *));
2944 argv
= calloc(8, sizeof(char *));
2946 sprintf(jobid
, "%d", job
->id
);
2948 argv
[0] = printer
->name
;
2950 argv
[2] = job
->username
;
2955 if (printer
->remote
&& job
->num_files
> 1)
2957 for (i
= 0; i
< job
->num_files
; i
++)
2959 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
2961 argv
[6 + i
] = strdup(filename
);
2966 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
2967 job
->id
, job
->current_file
+ 1);
2971 for (i
= 0; argv
[i
]; i
++)
2972 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2973 "[Job %d] argv[%d]=\"%s\"", job
->id
, i
, argv
[i
]);
2976 * Create environment variable strings for the filters...
2979 attr
= ippFindAttribute(job
->attrs
, "attributes-natural-language",
2982 switch (strlen(attr
->values
[0].string
.text
))
2986 * This is an unknown or badly formatted language code; use
2987 * the POSIX locale...
2990 strcpy(lang
, "LANG=C");
2995 * Just the language code (ll)...
2998 snprintf(lang
, sizeof(lang
), "LANG=%s",
2999 attr
->values
[0].string
.text
);
3004 * Language and country code (ll-cc)...
3007 snprintf(lang
, sizeof(lang
), "LANG=%c%c_%c%c",
3008 attr
->values
[0].string
.text
[0],
3009 attr
->values
[0].string
.text
[1],
3010 toupper(attr
->values
[0].string
.text
[3] & 255),
3011 toupper(attr
->values
[0].string
.text
[4] & 255));
3015 attr
= ippFindAttribute(job
->attrs
, "document-format",
3018 (optptr
= strstr(attr
->values
[0].string
.text
, "charset=")) != NULL
)
3019 snprintf(charset
, sizeof(charset
), "CHARSET=%s", optptr
+ 8);
3022 attr
= ippFindAttribute(job
->attrs
, "attributes-charset",
3024 snprintf(charset
, sizeof(charset
), "CHARSET=%s",
3025 attr
->values
[0].string
.text
);
3028 snprintf(content_type
, sizeof(content_type
), "CONTENT_TYPE=%s/%s",
3029 job
->filetypes
[job
->current_file
]->super
,
3030 job
->filetypes
[job
->current_file
]->type
);
3031 snprintf(device_uri
, sizeof(device_uri
), "DEVICE_URI=%s",
3032 printer
->device_uri
);
3033 cupsdSanitizeURI(printer
->device_uri
, sani_uri
, sizeof(sani_uri
));
3034 snprintf(ppd
, sizeof(ppd
), "PPD=%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
3035 snprintf(printer_name
, sizeof(printer_name
), "PRINTER=%s", printer
->name
);
3036 snprintf(rip_max_cache
, sizeof(rip_max_cache
), "RIP_MAX_CACHE=%s", RIPCache
);
3038 envc
= cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
3040 envp
[envc
++] = charset
;
3041 envp
[envc
++] = lang
;
3042 envp
[envc
++] = ppd
;
3043 envp
[envc
++] = rip_max_cache
;
3044 envp
[envc
++] = content_type
;
3045 envp
[envc
++] = device_uri
;
3046 envp
[envc
++] = printer_name
;
3048 if (!printer
->remote
&&
3049 (filter
= (mime_filter_t
*)cupsArrayLast(filters
)) != NULL
)
3051 snprintf(final_content_type
, sizeof(final_content_type
),
3052 "FINAL_CONTENT_TYPE=%s/%s",
3053 filter
->dst
->super
, filter
->dst
->type
);
3054 envp
[envc
++] = final_content_type
;
3057 if (Classification
&& !banner_page
)
3059 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
3060 IPP_TAG_NAME
)) == NULL
)
3061 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
3063 else if (attr
->num_values
> 1 &&
3064 strcmp(attr
->values
[1].string
.text
, "none") != 0)
3065 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
3066 attr
->values
[1].string
.text
);
3068 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
3069 attr
->values
[0].string
.text
);
3071 envp
[envc
++] = classification
;
3074 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
3076 snprintf(class_name
, sizeof(class_name
), "CLASS=%s", job
->dest
);
3077 envp
[envc
++] = class_name
;
3082 envp
[envc
++] = job
->ccname
;
3083 #endif /* HAVE_GSSAPI */
3087 for (i
= 0; i
< envc
; i
++)
3088 if (strncmp(envp
[i
], "DEVICE_URI=", 11))
3089 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Job %d] envp[%d]=\"%s\"",
3090 job
->id
, i
, envp
[i
]);
3092 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Job %d] envp[%d]=\"DEVICE_URI=%s\"",
3093 job
->id
, i
, sani_uri
);
3095 if (printer
->remote
)
3096 job
->current_file
= job
->num_files
;
3098 job
->current_file
++;
3101 * Now create processes for all of the filters...
3104 filterfds
[0][0] = -1;
3105 filterfds
[0][1] = -1;
3106 filterfds
[1][0] = -1;
3107 filterfds
[1][1] = -1;
3109 if (!job
->status_buffer
)
3111 if (cupsdOpenPipe(job
->status_pipes
))
3113 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to create job status pipes - %s.",
3115 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3116 "Unable to create status pipes - %s.", strerror(errno
));
3118 cupsdAddPrinterHistory(printer
);
3120 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3121 "Job canceled because the server could not create the job "
3127 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: status_pipes = [ %d %d ]",
3128 job
->status_pipes
[0], job
->status_pipes
[1]);
3130 job
->status_buffer
= cupsdStatBufNew(job
->status_pipes
[0], "[Job %d]",
3135 memset(job
->filters
, 0, sizeof(job
->filters
));
3137 for (i
= 0, slot
= 0, filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
3139 i
++, filter
= (mime_filter_t
*)cupsArrayNext(filters
))
3141 if (filter
->filter
[0] != '/')
3142 snprintf(command
, sizeof(command
), "%s/filter/%s", ServerBin
,
3145 strlcpy(command
, filter
->filter
, sizeof(command
));
3147 if (i
< (cupsArrayCount(filters
) - 1))
3149 if (cupsdOpenPipe(filterfds
[slot
]))
3151 cupsdLogMessage(CUPSD_LOG_ERROR
,
3152 "Unable to create job filter pipes - %s.",
3154 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3155 "Unable to create filter pipes - %s.", strerror(errno
));
3156 cupsdAddPrinterHistory(printer
);
3158 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3159 "Job canceled because the server could not create the "
3167 if (job
->current_file
== 1)
3169 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
3171 if (cupsdOpenPipe(job
->print_pipes
))
3173 cupsdLogMessage(CUPSD_LOG_ERROR
,
3174 "Unable to create job backend pipes - %s.",
3176 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3177 "Unable to create backend pipes - %s.", strerror(errno
));
3178 cupsdAddPrinterHistory(printer
);
3180 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3181 "Job canceled because the server could not create "
3182 "the backend pipes.");
3189 job
->print_pipes
[0] = -1;
3190 if (!strcmp(printer
->device_uri
, "file:/dev/null") ||
3191 !strcmp(printer
->device_uri
, "file:///dev/null"))
3192 job
->print_pipes
[1] = -1;
3195 if (!strncmp(printer
->device_uri
, "file:/dev/", 10))
3196 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
3198 else if (!strncmp(printer
->device_uri
, "file:///dev/", 12))
3199 job
->print_pipes
[1] = open(printer
->device_uri
+ 7,
3201 else if (!strncmp(printer
->device_uri
, "file:///", 8))
3202 job
->print_pipes
[1] = open(printer
->device_uri
+ 7,
3203 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3205 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
3206 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3208 if (job
->print_pipes
[1] < 0)
3210 cupsdLogMessage(CUPSD_LOG_ERROR
,
3211 "Unable to open output file \"%s\" - %s.",
3212 printer
->device_uri
, strerror(errno
));
3213 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3214 "Unable to open output file \"%s\" - %s.",
3215 printer
->device_uri
, strerror(errno
));
3217 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3218 "Job canceled because the server could not open the "
3224 fcntl(job
->print_pipes
[1], F_SETFD
,
3225 fcntl(job
->print_pipes
[1], F_GETFD
) | FD_CLOEXEC
);
3229 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3230 "start_job: print_pipes = [ %d %d ]",
3231 job
->print_pipes
[0], job
->print_pipes
[1]);
3234 filterfds
[slot
][0] = job
->print_pipes
[0];
3235 filterfds
[slot
][1] = job
->print_pipes
[1];
3238 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: filter=\"%s\"",
3240 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3241 "start_job: filterfds[%d]=[ %d %d ]",
3242 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3244 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
3245 filterfds
[slot
][1], job
->status_pipes
[1],
3246 job
->back_pipes
[0], 0, job
->filters
+ i
);
3248 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3249 "start_job: Closing filter pipes for slot %d "
3251 !slot
, filterfds
[!slot
][0], filterfds
[!slot
][1]);
3253 cupsdClosePipe(filterfds
[!slot
]);
3257 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start filter \"%s\" - %s.",
3258 filter
->filter
, strerror(errno
));
3259 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3260 "Unable to start filter \"%s\" - %s.",
3261 filter
->filter
, strerror(errno
));
3263 cupsdAddPrinterHistory(printer
);
3265 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3266 "Job canceled because the server could not execute a "
3272 cupsdLogMessage(CUPSD_LOG_INFO
, "Started filter %s (PID %d) for job %d.",
3273 command
, pid
, job
->id
);
3279 cupsArrayDelete(filters
);
3282 * Finally, pipe the final output into a backend process if needed...
3285 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
3287 if (job
->current_file
== 1)
3289 sscanf(printer
->device_uri
, "%254[^:]", method
);
3290 snprintf(command
, sizeof(command
), "%s/backend/%s", ServerBin
, method
);
3293 * See if the backend needs to run as root...
3298 else if (stat(command
, &backinfo
))
3301 backroot
= !(backinfo
.st_mode
& (S_IRWXG
| S_IRWXO
));
3305 filterfds
[slot
][0] = -1;
3306 filterfds
[slot
][1] = -1;
3308 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: backend=\"%s\"",
3310 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3311 "start_job: filterfds[%d] = [ %d %d ]",
3312 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3314 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
3315 filterfds
[slot
][1], job
->status_pipes
[1],
3316 job
->back_pipes
[1], backroot
,
3321 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start backend \"%s\" - %s.",
3322 method
, strerror(errno
));
3323 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3324 "Unable to start backend \"%s\" - %s.", method
,
3327 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3328 "Job canceled because the server could not execute "
3335 cupsdLogMessage(CUPSD_LOG_INFO
,
3336 "Started backend %s (PID %d) for job %d.",
3337 command
, pid
, job
->id
);
3341 if (job
->current_file
== job
->num_files
)
3343 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3344 "start_job: Closing print pipes [ %d %d ]...",
3345 job
->print_pipes
[0], job
->print_pipes
[1]);
3347 cupsdClosePipe(job
->print_pipes
);
3349 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3350 "start_job: Closing back pipes [ %d %d ]...",
3351 job
->back_pipes
[0], job
->back_pipes
[1]);
3353 cupsdClosePipe(job
->back_pipes
);
3355 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3356 "start_job: Closing status output pipe %d...",
3357 job
->status_pipes
[1]);
3359 close(job
->status_pipes
[1]);
3360 job
->status_pipes
[1] = -1;
3365 filterfds
[slot
][0] = -1;
3366 filterfds
[slot
][1] = -1;
3368 if (job
->current_file
== job
->num_files
)
3370 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3371 "start_job: Closing print pipes [ %d %d ]...",
3372 job
->print_pipes
[0], job
->print_pipes
[1]);
3374 cupsdClosePipe(job
->print_pipes
);
3376 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3377 "start_job: Closing status output pipe %d...",
3378 job
->status_pipes
[1]);
3380 close(job
->status_pipes
[1]);
3381 job
->status_pipes
[1] = -1;
3385 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3386 "start_job: Closing filter pipes for slot %d "
3388 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3389 cupsdClosePipe(filterfds
[slot
]);
3391 if (printer
->remote
&& job
->num_files
> 1)
3393 for (i
= 0; i
< job
->num_files
; i
++)
3399 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3400 "start_job: Adding fd %d to InputSet...",
3401 job
->status_buffer
->fd
);
3403 FD_SET(job
->status_buffer
->fd
, InputSet
);
3405 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, job
->printer
, job
, "Job #%d started.",
3412 * If we get here, we need to abort the current job and close out all
3413 * files and pipes...
3418 for (slot
= 0; slot
< 2; slot
++)
3420 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3421 "start_job: Closing filter pipes for slot %d "
3423 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3424 cupsdClosePipe(filterfds
[slot
]);
3427 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3428 "start_job: Closing status pipes [ %d %d ]...",
3429 job
->status_pipes
[0], job
->status_pipes
[1]);
3430 cupsdClosePipe(job
->status_pipes
);
3431 cupsdStatBufDelete(job
->status_buffer
);
3433 cupsArrayDelete(filters
);
3435 if (printer
->remote
&& job
->num_files
> 1)
3437 for (i
= 0; i
< job
->num_files
; i
++)
3443 cupsdStopJob(job
, 0);
3448 * 'unload_job()' - Unload a job from memory.
3452 unload_job(cupsd_job_t
*job
) /* I - Job */
3457 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Unloading job %d...", job
->id
);
3459 ippDelete(job
->attrs
);
3464 job
->job_sheets
= NULL
;