2 * "$Id: job.c 5305 2006-03-18 03:05:12Z mike $"
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;
131 cupsdSetString(&job
->dest
, dest
);
134 * Add the new job to the "all jobs" and "active jobs" lists...
137 cupsArrayAdd(Jobs
, job
);
138 cupsArrayAdd(ActiveJobs
, job
);
145 * 'cupsdCancelJob()' - Cancel the specified print job.
149 cupsdCancelJob(cupsd_job_t
*job
, /* I - Job to cancel */
150 int purge
) /* I - Purge jobs? */
152 int i
; /* Looping var */
153 char filename
[1024]; /* Job filename */
156 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdCancelJob: id = %d", job
->id
);
159 * Stop any processes that are working on the current job...
162 if (job
->state_value
== IPP_JOB_PROCESSING
)
163 cupsdStopJob(job
, 0);
168 job
->state
->values
[0].integer
= IPP_JOB_CANCELLED
;
170 job
->state_value
= IPP_JOB_CANCELLED
;
172 set_time(job
, "time-at-completed");
174 cupsdExpireSubscriptions(NULL
, job
);
177 * Remove the job from the active list...
180 cupsArrayRemove(ActiveJobs
, job
);
183 * Remove any authentication data...
186 snprintf(filename
, sizeof(filename
), "%s/a%05d", RequestRoot
, job
->id
);
190 * Remove the print file for good if we aren't preserving jobs or
194 job
->current_file
= 0;
196 if (!JobHistory
|| !JobFiles
|| purge
|| (job
->dtype
& CUPS_PRINTER_REMOTE
))
198 for (i
= 1; i
<= job
->num_files
; i
++)
200 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
205 if (job
->num_files
> 0)
207 free(job
->filetypes
);
208 free(job
->compressions
);
211 job
->filetypes
= NULL
;
212 job
->compressions
= NULL
;
216 if (JobHistory
&& !purge
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
))
219 * Save job state info...
227 * Remove the job info file...
230 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
,
235 * Remove the job from the "all jobs" list...
238 cupsArrayRemove(Jobs
, job
);
241 * Free all memory used...
250 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user...
254 cupsdCancelJobs(const char *dest
, /* I - Destination to cancel */
255 const char *username
, /* I - Username or NULL */
256 int purge
) /* I - Purge jobs? */
258 cupsd_job_t
*job
; /* Current job */
261 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
263 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
264 if ((dest
== NULL
|| !strcmp(job
->dest
, dest
)) &&
265 (username
== NULL
|| !strcmp(job
->username
, username
)))
268 * Cancel all jobs matching this destination/user...
271 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
272 purge
? "Job purged." : "Job canceled.");
274 cupsdCancelJob(job
, purge
);
282 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
289 cupsd_job_t
*job
; /* Current job in queue */
290 cupsd_printer_t
*printer
, /* Printer destination */
291 *pclass
; /* Printer class destination */
294 DEBUG_puts("cupsdCheckJobs()");
296 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
297 "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d",
298 cupsArrayCount(ActiveJobs
), Sleeping
, NeedReload
);
300 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
302 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
305 * Start held jobs if they are ready...
308 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
309 "cupsdCheckJobs: Job %d: state_value=%d, loaded=%s",
310 job
->id
, job
->state_value
, job
->attrs
? "yes" : "no");
312 if (job
->state_value
== IPP_JOB_HELD
&&
314 job
->hold_until
< time(NULL
))
316 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
317 job
->state_value
= IPP_JOB_PENDING
;
321 * Start pending jobs if the destination is available...
324 if (job
->state_value
== IPP_JOB_PENDING
&& !NeedReload
&& !Sleeping
)
326 printer
= cupsdFindDest(job
->dest
);
330 (printer
->type
& (CUPS_PRINTER_IMPLICIT
| CUPS_PRINTER_CLASS
)))
333 * If the class is remote, just pass it to the remote server...
338 if (!(pclass
->type
& CUPS_PRINTER_REMOTE
))
340 if (pclass
->state
!= IPP_PRINTER_STOPPED
)
341 printer
= cupsdFindAvailablePrinter(job
->dest
);
347 if (!printer
&& !pclass
)
350 * Whoa, the printer and/or class for this destination went away;
354 cupsdLogMessage(CUPSD_LOG_WARN
,
355 "Printer/class %s has gone away; cancelling job %d!",
358 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
359 "Job canceled because the destination printer/class has "
362 cupsdCancelJob(job
, 1);
367 * See if the printer is available or remote and not printing a job;
368 * if so, start the job...
374 * Add/update a job-actual-printer-uri attribute for this job
375 * so that we know which printer actually printed the job...
378 ipp_attribute_t
*attr
; /* job-actual-printer-uri attribute */
381 if ((attr
= ippFindAttribute(job
->attrs
, "job-actual-printer-uri",
382 IPP_TAG_URI
)) != NULL
)
383 cupsdSetString(&attr
->values
[0].string
.text
, printer
->uri
);
385 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
,
386 "job-actual-printer-uri", NULL
, printer
->uri
);
389 if (printer
->state
== IPP_PRINTER_IDLE
|| /* Printer is idle */
390 ((printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is remote */
391 !printer
->job
)) /* and not printing */
392 start_job(job
, printer
);
400 * 'cupsdCleanJobs()' - Clean out old jobs.
406 cupsd_job_t
*job
; /* Current job */
412 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
413 job
&& cupsArrayCount(Jobs
) >= MaxJobs
;
414 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
415 if (job
->state_value
>= IPP_JOB_CANCELLED
)
416 cupsdCancelJob(job
, 1);
421 * 'cupsdFinishJob()' - Finish a job.
425 cupsdFinishJob(cupsd_job_t
*job
) /* I - Job */
427 int job_history
; /* Did cupsdCancelJob() keep the job? */
428 cupsd_printer_t
*printer
; /* Current printer */
431 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Job %d] File %d is complete.",
432 job
->id
, job
->current_file
- 1);
434 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdFinishJob: job->status is %d",
437 if (job
->status_buffer
&& job
->current_file
>= job
->num_files
)
440 * Close the pipe and clear the input bit.
443 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
444 "cupsdFinishJob: Removing fd %d from InputSet...",
445 job
->status_buffer
->fd
);
447 FD_CLR(job
->status_buffer
->fd
, InputSet
);
449 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
450 "cupsdFinishJob: Closing status input pipe %d...",
451 job
->status_buffer
->fd
);
453 cupsdStatBufDelete(job
->status_buffer
);
455 job
->status_buffer
= NULL
;
458 printer
= job
->printer
;
463 * Backend had errors; stop it...
466 switch (-job
->status
)
469 case CUPS_BACKEND_FAILED
:
471 * Backend failure, use the error-policy to determine how to
475 cupsdStopJob(job
, 0);
476 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
477 job
->state_value
= IPP_JOB_PENDING
;
481 * If the job was queued to a class, try requeuing it... For
482 * faxes and retry-job queues, hold the current job for 5 minutes.
485 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
487 else if ((printer
->type
& CUPS_PRINTER_FAX
) ||
488 !strcmp(printer
->error_policy
, "retry-job"))
491 * See how many times we've tried to send the job; if more than
492 * the limit, cancel the job.
497 if (job
->tries
>= JobRetryLimit
)
503 cupsdLogMessage(CUPSD_LOG_ERROR
,
504 "Canceling job %d since it could not be sent "
506 job
->id
, JobRetryLimit
);
508 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, printer
, job
,
509 "Job canceled since it could not be sent after %d "
513 cupsdCancelJob(job
, 0);
518 * Try again in N seconds...
521 set_hold_until(job
, time(NULL
) + JobRetryInterval
);
524 else if (!strcmp(printer
->error_policy
, "abort-job"))
525 cupsdCancelJob(job
, 0);
528 case CUPS_BACKEND_CANCEL
:
533 cupsdCancelJob(job
, 0);
536 case CUPS_BACKEND_HOLD
:
541 cupsdStopJob(job
, 0);
542 cupsdSetJobHoldUntil(job
, "indefinite");
546 case CUPS_BACKEND_STOP
:
548 * Stop the printer...
551 cupsdStopJob(job
, 0);
553 cupsdSetPrinterState(printer
, IPP_PRINTER_STOPPED
, 1);
556 case CUPS_BACKEND_AUTH_REQUIRED
:
557 cupsdStopJob(job
, 0);
558 cupsdSetJobHoldUntil(job
, "authenticated");
561 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
562 "Authentication is required for job %d.", job
->id
);
567 * Try printing another job...
572 else if (job
->status
> 0)
575 * Filter had errors; stop job...
578 cupsdStopJob(job
, 1);
580 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
581 "Job stopped due to filter errors; please consult the "
582 "error_log file for details.");
588 * Job printed successfully; cancel it...
591 if (job
->current_file
< job
->num_files
)
594 * Start the next file in the job...
597 FilterLevel
-= job
->cost
;
598 start_job(job
, printer
);
603 * Close out this job...
606 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, printer
, job
,
607 "Job completed successfully.");
609 job_history
= JobHistory
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
);
611 cupsdCancelJob(job
, 0);
615 job
->state
->values
[0].integer
= IPP_JOB_COMPLETED
;
616 job
->state_value
= IPP_JOB_COMPLETED
;
621 * Clear the printer's state_message and state_reasons and move on...
624 printer
->state_message
[0] = '\0';
626 cupsdSetPrinterReasons(printer
, "");
635 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
639 cupsdFreeAllJobs(void)
641 cupsd_job_t
*job
; /* Current job */
652 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
654 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
656 cupsArrayRemove(Jobs
, job
);
657 cupsArrayRemove(ActiveJobs
, job
);
662 cupsdReleaseSignals();
667 * 'cupsdFindJob()' - Find the specified job.
670 cupsd_job_t
* /* O - Job data */
671 cupsdFindJob(int id
) /* I - Job ID */
673 cupsd_job_t key
; /* Search key */
678 return ((cupsd_job_t
*)cupsArrayFind(Jobs
, &key
));
683 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
684 * or held jobs in a printer or class.
687 int /* O - Job count */
688 cupsdGetPrinterJobCount(
689 const char *dest
) /* I - Printer or class name */
691 int count
; /* Job count */
692 cupsd_job_t
*job
; /* Current job */
695 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
697 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
698 if (job
->dest
&& !strcasecmp(job
->dest
, dest
))
706 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
707 * or held jobs for a user.
710 int /* O - Job count */
711 cupsdGetUserJobCount(
712 const char *username
) /* I - Username */
714 int count
; /* Job count */
715 cupsd_job_t
*job
; /* Current job */
718 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
720 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
721 if (!strcasecmp(job
->username
, username
))
729 * 'cupsdHoldJob()' - Hold the specified job.
733 cupsdHoldJob(cupsd_job_t
*job
) /* I - Job data */
735 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdHoldJob: id = %d", job
->id
);
737 if (job
->state_value
== IPP_JOB_PROCESSING
)
738 cupsdStopJob(job
, 0);
742 DEBUG_puts("cupsdHoldJob: setting state to held...");
744 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
745 job
->state_value
= IPP_JOB_HELD
;
754 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
758 cupsdLoadAllJobs(void)
760 char filename
[1024]; /* Full filename of job.cache file */
761 struct stat fileinfo
, /* Information on job.cache file */
762 dirinfo
; /* Information on RequestRoot dir */
767 * Create the job arrays as needed...
771 Jobs
= cupsArrayNew(compare_jobs
, NULL
);
774 ActiveJobs
= cupsArrayNew(compare_active_jobs
, NULL
);
777 * See whether the job.cache file is older than the RequestRoot directory...
780 snprintf(filename
, sizeof(filename
), "%s/job.cache", CacheDir
);
782 if (stat(filename
, &fileinfo
))
784 fileinfo
.st_mtime
= 0;
787 cupsdLogMessage(CUPSD_LOG_ERROR
,
788 "Unable to get file information for \"%s\" - %s",
789 filename
, strerror(errno
));
792 if (stat(RequestRoot
, &dirinfo
))
794 dirinfo
.st_mtime
= 0;
797 cupsdLogMessage(CUPSD_LOG_ERROR
,
798 "Unable to get directory information for \"%s\" - %s",
799 RequestRoot
, strerror(errno
));
803 * Load the most recent source for job data...
806 if (dirinfo
.st_mtime
> fileinfo
.st_mtime
)
810 load_next_job_id(filename
);
813 load_job_cache(filename
);
816 * Clean out old jobs as needed...
819 if (MaxJobs
> 0 && cupsArrayCount(Jobs
) >= MaxJobs
)
825 * 'cupsdLoadJob()' - Load a single job...
829 cupsdLoadJob(cupsd_job_t
*job
) /* I - Job */
831 char jobfile
[1024]; /* Job filename */
832 cups_file_t
*fp
; /* Job file */
833 int fileid
; /* Current file ID */
834 ipp_attribute_t
*attr
; /* Job attribute */
835 char scheme
[32], /* Scheme portion of URI */
836 username
[64], /* Username portion of URI */
838 /* Host portion of URI */
839 resource
[HTTP_MAX_URI
];
840 /* Resource portion of URI */
841 int port
; /* Port portion of URI */
842 const char *dest
; /* Destination */
843 mime_type_t
**filetypes
; /* New filetypes array */
844 int *compressions
; /* New compressions array */
849 if (job
->state_value
>= IPP_JOB_STOPPED
)
850 job
->access_time
= time(NULL
);
855 if ((job
->attrs
= ippNew()) == NULL
)
857 cupsdLogMessage(CUPSD_LOG_ERROR
, "Ran out of memory for job attributes!");
862 * Load job attributes...
865 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Loading attributes for job %d...",
868 snprintf(jobfile
, sizeof(jobfile
), "%s/c%05d", RequestRoot
, job
->id
);
869 if ((fp
= cupsFileOpen(jobfile
, "r")) == NULL
)
871 cupsdLogMessage(CUPSD_LOG_ERROR
,
872 "Unable to open job control file \"%s\" - %s!",
873 jobfile
, strerror(errno
));
874 ippDelete(job
->attrs
);
879 if (ippReadIO(fp
, (ipp_iocb_t
)cupsFileRead
, 1, NULL
, job
->attrs
) != IPP_DATA
)
881 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to read job control file \"%s\"!",
884 ippDelete(job
->attrs
);
893 * Copy attribute data to the job object...
896 if ((job
->state
= ippFindAttribute(job
->attrs
, "job-state",
897 IPP_TAG_ENUM
)) == NULL
)
899 cupsdLogMessage(CUPSD_LOG_ERROR
,
900 "Missing or bad job-state attribute in control "
903 ippDelete(job
->attrs
);
909 job
->state_value
= (ipp_jstate_t
)job
->state
->values
[0].integer
;
913 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri",
914 IPP_TAG_URI
)) == NULL
)
916 cupsdLogMessage(CUPSD_LOG_ERROR
,
917 "No job-printer-uri attribute in control file \"%s\"!",
919 ippDelete(job
->attrs
);
925 httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[0].string
.text
, scheme
,
926 sizeof(scheme
), username
, sizeof(username
), host
,
927 sizeof(host
), &port
, resource
, sizeof(resource
));
929 if ((dest
= cupsdValidateDest(host
, resource
, &(job
->dtype
),
932 cupsdLogMessage(CUPSD_LOG_ERROR
,
933 "Unable to queue job for destination \"%s\"!",
934 attr
->values
[0].string
.text
);
935 ippDelete(job
->attrs
);
941 cupsdSetString(&job
->dest
, dest
);
944 job
->sheets
= ippFindAttribute(job
->attrs
, "job-media-sheets-completed",
946 job
->job_sheets
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
950 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority",
951 IPP_TAG_INTEGER
)) == NULL
)
953 cupsdLogMessage(CUPSD_LOG_ERROR
,
954 "Missing or bad job-priority attribute in control "
955 "file \"%s\"!", jobfile
);
956 ippDelete(job
->attrs
);
962 job
->priority
= attr
->values
[0].integer
;
967 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-user-name",
968 IPP_TAG_NAME
)) == NULL
)
970 cupsdLogMessage(CUPSD_LOG_ERROR
,
971 "Missing or bad job-originating-user-name attribute "
972 "in control file \"%s\"!", jobfile
);
973 ippDelete(job
->attrs
);
979 cupsdSetString(&job
->username
, attr
->values
[0].string
.text
);
983 * Set the job hold-until time and state...
986 if (job
->state_value
== IPP_JOB_HELD
)
988 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
989 IPP_TAG_KEYWORD
)) == NULL
)
990 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
993 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
);
996 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
997 job
->state_value
= IPP_JOB_PENDING
;
1000 else if (job
->state_value
== IPP_JOB_PROCESSING
)
1002 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1003 job
->state_value
= IPP_JOB_PENDING
;
1006 if (!job
->num_files
)
1009 * Find all the d##### files...
1012 for (fileid
= 1; fileid
< 10000; fileid
++)
1014 snprintf(jobfile
, sizeof(jobfile
), "%s/d%05d-%03d", RequestRoot
,
1017 if (access(jobfile
, 0))
1020 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Auto-typing document file \"%s\"...",
1023 if (fileid
> job
->num_files
)
1025 if (job
->num_files
== 0)
1027 compressions
= (int *)calloc(fileid
, sizeof(int));
1028 filetypes
= (mime_type_t
**)calloc(fileid
, sizeof(mime_type_t
*));
1032 compressions
= (int *)realloc(job
->compressions
,
1033 sizeof(int) * fileid
);
1034 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
1035 sizeof(mime_type_t
*) *
1039 if (!compressions
|| !filetypes
)
1041 cupsdLogMessage(CUPSD_LOG_ERROR
,
1042 "Ran out of memory for job file types!");
1046 job
->compressions
= compressions
;
1047 job
->filetypes
= filetypes
;
1048 job
->num_files
= fileid
;
1051 job
->filetypes
[fileid
- 1] = mimeFileType(MimeDatabase
, jobfile
, NULL
,
1052 job
->compressions
+ fileid
- 1);
1054 if (!job
->filetypes
[fileid
- 1])
1055 job
->filetypes
[fileid
- 1] = mimeType(MimeDatabase
, "application",
1060 job
->access_time
= time(NULL
);
1065 * 'cupsdMoveJob()' - Move the specified job to a different destination.
1069 cupsdMoveJob(cupsd_job_t
*job
, /* I - Job */
1070 const char *dest
) /* I - Destination */
1072 ipp_attribute_t
*attr
; /* job-printer-uri attribute */
1073 cupsd_printer_t
*p
; /* Destination printer or class */
1077 * Find the printer...
1080 if ((p
= cupsdFindDest(dest
)) == NULL
)
1084 * Don't move completed jobs...
1087 if (job
->state_value
>= IPP_JOB_PROCESSING
)
1091 * Change the destination information...
1096 cupsdSetString(&job
->dest
, dest
);
1097 job
->dtype
= p
->type
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
1098 CUPS_PRINTER_IMPLICIT
);
1100 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri",
1101 IPP_TAG_URI
)) != NULL
)
1102 cupsdSetString(&(attr
->values
[0].string
.text
), p
->uri
);
1109 * 'cupsdReleaseJob()' - Release the specified job.
1113 cupsdReleaseJob(cupsd_job_t
*job
) /* I - Job */
1115 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdReleaseJob: id = %d", job
->id
);
1117 if (job
->state_value
== IPP_JOB_HELD
)
1119 DEBUG_puts("cupsdReleaseJob: setting state to pending...");
1121 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1122 job
->state_value
= IPP_JOB_PENDING
;
1130 * 'cupsdRestartJob()' - Restart the specified job.
1134 cupsdRestartJob(cupsd_job_t
*job
) /* I - Job */
1136 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdRestartJob: id = %d", job
->id
);
1138 if (job
->state_value
== IPP_JOB_STOPPED
|| job
->num_files
)
1143 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1144 job
->state_value
= IPP_JOB_PENDING
;
1152 * 'cupsdSaveAllJobs()' - Save a summary of all jobs to disk.
1156 cupsdSaveAllJobs(void)
1158 int i
; /* Looping var */
1159 cups_file_t
*fp
; /* Job cache file */
1160 char temp
[1024]; /* Temporary string */
1161 cupsd_job_t
*job
; /* Current job */
1162 time_t curtime
; /* Current time */
1163 struct tm
*curdate
; /* Current date */
1166 snprintf(temp
, sizeof(temp
), "%s/job.cache", CacheDir
);
1167 if ((fp
= cupsFileOpen(temp
, "w")) == NULL
)
1169 cupsdLogMessage(CUPSD_LOG_ERROR
,
1170 "Unable to create job cache file \"%s\" - %s",
1171 temp
, strerror(errno
));
1175 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving job cache file \"%s\"...", temp
);
1178 * Restrict access to the file...
1181 fchown(cupsFileNumber(fp
), getuid(), Group
);
1182 fchmod(cupsFileNumber(fp
), ConfigFilePerm
);
1185 * Write a small header to the file...
1188 curtime
= time(NULL
);
1189 curdate
= localtime(&curtime
);
1190 strftime(temp
, sizeof(temp
) - 1, "%Y-%m-%d %H:%M", curdate
);
1192 cupsFilePuts(fp
, "# Job cache file for " CUPS_SVERSION
"\n");
1193 cupsFilePrintf(fp
, "# Written by cupsd on %s\n", temp
);
1194 cupsFilePrintf(fp
, "NextJobId %d\n", NextJobId
);
1197 * Write each job known to the system...
1200 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
1202 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
1204 cupsFilePrintf(fp
, "<Job %d>\n", job
->id
);
1205 cupsFilePrintf(fp
, "State %d\n", job
->state_value
);
1206 cupsFilePrintf(fp
, "Priority %d\n", job
->priority
);
1207 cupsFilePrintf(fp
, "Username %s\n", job
->username
);
1208 cupsFilePrintf(fp
, "Destination %s\n", job
->dest
);
1209 cupsFilePrintf(fp
, "DestType %d\n", job
->dtype
);
1210 cupsFilePrintf(fp
, "NumFiles %d\n", job
->num_files
);
1211 for (i
= 0; i
< job
->num_files
; i
++)
1212 cupsFilePrintf(fp
, "File %d %s/%s %d\n", i
+ 1, job
->filetypes
[i
]->super
,
1213 job
->filetypes
[i
]->type
, job
->compressions
[i
]);
1214 cupsFilePuts(fp
, "</Job>\n");
1222 * 'cupsdSaveJob()' - Save a job to disk.
1226 cupsdSaveJob(cupsd_job_t
*job
) /* I - Job */
1228 char filename
[1024]; /* Job control filename */
1229 cups_file_t
*fp
; /* Job file */
1232 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSaveJob(job=%p(%d)): job->attrs=%p",
1233 job
, job
->id
, job
->attrs
);
1235 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
, job
->id
);
1237 if ((fp
= cupsFileOpen(filename
, "w")) == NULL
)
1239 cupsdLogMessage(CUPSD_LOG_ERROR
,
1240 "Unable to create job control file \"%s\" - %s.",
1241 filename
, strerror(errno
));
1245 fchmod(cupsFileNumber(fp
), 0600);
1246 fchown(cupsFileNumber(fp
), RunUser
, Group
);
1248 job
->attrs
->state
= IPP_IDLE
;
1250 if (ippWriteIO(fp
, (ipp_iocb_t
)cupsFileWrite
, 1, NULL
,
1251 job
->attrs
) != IPP_DATA
)
1252 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to write job control file \"%s\"!",
1260 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job...
1264 cupsdSetJobHoldUntil(cupsd_job_t
*job
, /* I - Job */
1265 const char *when
) /* I - When to resume */
1267 time_t curtime
; /* Current time */
1268 struct tm
*curdate
; /* Current date */
1269 int hour
; /* Hold hour */
1270 int minute
; /* Hold minute */
1271 int second
; /* Hold second */
1274 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSetJobHoldUntil(%d, \"%s\")",
1279 if (!strcmp(when
, "indefinite") || !strcmp(when
, "authenticated"))
1282 * Hold indefinitely...
1285 job
->hold_until
= 0;
1287 else if (!strcmp(when
, "day-time"))
1290 * Hold to 6am the next morning unless local time is < 6pm.
1293 curtime
= time(NULL
);
1294 curdate
= localtime(&curtime
);
1296 if (curdate
->tm_hour
< 18)
1297 job
->hold_until
= curtime
;
1299 job
->hold_until
= curtime
+
1300 ((29 - curdate
->tm_hour
) * 60 + 59 -
1301 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1303 else if (!strcmp(when
, "evening") || strcmp(when
, "night"))
1306 * Hold to 6pm unless local time is > 6pm or < 6am.
1309 curtime
= time(NULL
);
1310 curdate
= localtime(&curtime
);
1312 if (curdate
->tm_hour
< 6 || curdate
->tm_hour
>= 18)
1313 job
->hold_until
= curtime
;
1315 job
->hold_until
= curtime
+
1316 ((17 - curdate
->tm_hour
) * 60 + 59 -
1317 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1319 else if (!strcmp(when
, "second-shift"))
1322 * Hold to 4pm unless local time is > 4pm.
1325 curtime
= time(NULL
);
1326 curdate
= localtime(&curtime
);
1328 if (curdate
->tm_hour
>= 16)
1329 job
->hold_until
= curtime
;
1331 job
->hold_until
= curtime
+
1332 ((15 - curdate
->tm_hour
) * 60 + 59 -
1333 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1335 else if (!strcmp(when
, "third-shift"))
1338 * Hold to 12am unless local time is < 8am.
1341 curtime
= time(NULL
);
1342 curdate
= localtime(&curtime
);
1344 if (curdate
->tm_hour
< 8)
1345 job
->hold_until
= curtime
;
1347 job
->hold_until
= curtime
+
1348 ((23 - curdate
->tm_hour
) * 60 + 59 -
1349 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1351 else if (!strcmp(when
, "weekend"))
1354 * Hold to weekend unless we are in the weekend.
1357 curtime
= time(NULL
);
1358 curdate
= localtime(&curtime
);
1360 if (curdate
->tm_wday
|| curdate
->tm_wday
== 6)
1361 job
->hold_until
= curtime
;
1363 job
->hold_until
= curtime
+
1364 (((5 - curdate
->tm_wday
) * 24 +
1365 (17 - curdate
->tm_hour
)) * 60 + 59 -
1366 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1368 else if (sscanf(when
, "%d:%d:%d", &hour
, &minute
, &second
) >= 2)
1371 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1374 curtime
= time(NULL
);
1375 curdate
= gmtime(&curtime
);
1377 job
->hold_until
= curtime
+
1378 ((hour
- curdate
->tm_hour
) * 60 + minute
-
1379 curdate
->tm_min
) * 60 + second
- curdate
->tm_sec
;
1382 * Hold until next day as needed...
1385 if (job
->hold_until
< curtime
)
1386 job
->hold_until
+= 24 * 60 * 60 * 60;
1389 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSetJobHoldUntil: hold_until = %d",
1390 (int)job
->hold_until
);
1395 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
1396 * the list as needed.
1400 cupsdSetJobPriority(
1401 cupsd_job_t
*job
, /* I - Job ID */
1402 int priority
) /* I - New priority (0 to 100) */
1404 ipp_attribute_t
*attr
; /* Job attribute */
1408 * Don't change completed jobs...
1411 if (job
->state_value
>= IPP_JOB_PROCESSING
)
1415 * Set the new priority and re-add the job into the active list...
1418 cupsArrayRemove(ActiveJobs
, job
);
1420 job
->priority
= priority
;
1422 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority",
1423 IPP_TAG_INTEGER
)) != NULL
)
1424 attr
->values
[0].integer
= priority
;
1426 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1429 cupsArrayAdd(ActiveJobs
, job
);
1436 * 'cupsdStopAllJobs()' - Stop all print jobs.
1440 cupsdStopAllJobs(void)
1442 cupsd_job_t
*job
; /* Current job */
1445 DEBUG_puts("cupsdStopAllJobs()");
1447 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
1449 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
1450 if (job
->state_value
== IPP_JOB_PROCESSING
)
1452 cupsdStopJob(job
, 1);
1453 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1454 job
->state_value
= IPP_JOB_PENDING
;
1460 * 'cupsdStopJob()' - Stop a print job.
1464 cupsdStopJob(cupsd_job_t
*job
, /* I - Job */
1465 int force
) /* I - 1 = Force all filters to stop */
1467 int i
; /* Looping var */
1470 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdStopJob: id = %d, force = %d",
1473 if (job
->state_value
!= IPP_JOB_PROCESSING
)
1476 FilterLevel
-= job
->cost
;
1478 if (job
->status
< 0 &&
1479 !(job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
)) &&
1480 !(job
->printer
->type
& CUPS_PRINTER_FAX
) &&
1481 !strcmp(job
->printer
->error_policy
, "stop-printer"))
1482 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_STOPPED
, 1);
1483 else if (job
->printer
->state
!= IPP_PRINTER_STOPPED
)
1484 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_IDLE
, 0);
1486 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdStopJob: printer state is %d",
1487 job
->printer
->state
);
1489 job
->state
->values
[0].integer
= IPP_JOB_STOPPED
;
1490 job
->state_value
= IPP_JOB_STOPPED
;
1491 job
->printer
->job
= NULL
;
1492 job
->printer
= NULL
;
1494 job
->current_file
--;
1496 for (i
= 0; job
->filters
[i
]; i
++)
1497 if (job
->filters
[i
] > 0)
1499 cupsdEndProcess(job
->filters
[i
], force
);
1500 job
->filters
[i
] = 0;
1503 if (job
->backend
> 0)
1505 cupsdEndProcess(job
->backend
, force
);
1509 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1510 "cupsdStopJob: Closing print pipes [ %d %d ]...",
1511 job
->print_pipes
[0], job
->print_pipes
[1]);
1513 cupsdClosePipe(job
->print_pipes
);
1515 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1516 "cupsdStopJob: Closing back pipes [ %d %d ]...",
1517 job
->back_pipes
[0], job
->back_pipes
[1]);
1519 cupsdClosePipe(job
->back_pipes
);
1521 if (job
->status_buffer
)
1524 * Close the pipe and clear the input bit.
1527 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1528 "cupsdStopJob: Removing fd %d from InputSet...",
1529 job
->status_buffer
->fd
);
1531 FD_CLR(job
->status_buffer
->fd
, InputSet
);
1533 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1534 "cupsdStopJob: Closing status input pipe %d...",
1535 job
->status_buffer
->fd
);
1537 cupsdStatBufDelete(job
->status_buffer
);
1539 job
->status_buffer
= NULL
;
1545 * 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory.
1549 cupsdUnloadCompletedJobs(void)
1551 cupsd_job_t
*job
; /* Current job */
1552 time_t expire
; /* Expiration time */
1555 expire
= time(NULL
) - 60;
1557 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
1559 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
1560 if (job
->attrs
&& job
->state_value
>= IPP_JOB_STOPPED
&&
1561 job
->access_time
< expire
)
1567 * 'cupsdUpdateJob()' - Read a status update from a job's filters.
1571 cupsdUpdateJob(cupsd_job_t
*job
) /* I - Job to check */
1573 int i
; /* Looping var */
1574 int copies
; /* Number of copies printed */
1575 char message
[1024], /* Message text */
1576 *ptr
; /* Pointer update... */
1577 int loglevel
; /* Log level for message */
1580 while ((ptr
= cupsdStatBufUpdate(job
->status_buffer
, &loglevel
,
1581 message
, sizeof(message
))) != NULL
)
1584 * Process page and printer state messages as needed...
1587 if (loglevel
== CUPSD_LOG_PAGE
)
1590 * Page message; send the message to the page_log file and update the
1591 * job sheet count...
1594 if (job
->sheets
!= NULL
)
1596 if (!strncasecmp(message
, "total ", 6))
1599 * Got a total count of pages from a backend or filter...
1602 copies
= atoi(message
+ 6);
1603 copies
-= job
->sheets
->values
[0].integer
; /* Just track the delta */
1605 else if (!sscanf(message
, "%*d%d", &copies
))
1608 job
->sheets
->values
[0].integer
+= copies
;
1610 if (job
->printer
->page_limit
)
1611 cupsdUpdateQuota(job
->printer
, job
->username
, copies
, 0);
1614 cupsdLogPage(job
, message
);
1616 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS
, job
->printer
, job
,
1617 "Printed %d page(s).", job
->sheets
->values
[0].integer
);
1619 else if (loglevel
== CUPSD_LOG_STATE
)
1621 cupsdSetPrinterReasons(job
->printer
, message
);
1622 cupsdAddPrinterHistory(job
->printer
);
1624 else if (loglevel
== CUPSD_LOG_ATTR
)
1627 * Set attribute(s)...
1633 else if (!strncmp(message
, "recoverable:", 12))
1635 cupsdSetPrinterReasons(job
->printer
,
1636 "+com.apple.print.recoverable-warning");
1639 while (isspace(*ptr
& 255))
1642 cupsdSetString(&job
->printer
->recoverable
, ptr
);
1643 cupsdAddPrinterHistory(job
->printer
);
1645 else if (!strncmp(message
, "recovered:", 10))
1647 cupsdSetPrinterReasons(job
->printer
,
1648 "-com.apple.print.recoverable-warning");
1651 while (isspace(*ptr
& 255))
1654 cupsdSetString(&job
->printer
->recoverable
, ptr
);
1655 cupsdAddPrinterHistory(job
->printer
);
1657 #endif /* __APPLE__ */
1658 else if (loglevel
<= CUPSD_LOG_INFO
)
1661 * Some message to show in the printer-state-message attribute...
1664 strlcpy(job
->printer
->state_message
, message
,
1665 sizeof(job
->printer
->state_message
));
1666 cupsdAddPrinterHistory(job
->printer
);
1669 if (!strchr(job
->status_buffer
->buffer
, '\n'))
1676 * See if all of the filters and the backend have returned their
1680 for (i
= 0; job
->filters
[i
] < 0; i
++);
1682 if (job
->filters
[i
])
1685 if (job
->current_file
>= job
->num_files
&& job
->backend
> 0)
1689 * Handle the end of job stuff...
1692 cupsdFinishJob(job
);
1698 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
1701 static int /* O - Difference */
1702 compare_active_jobs(void *first
, /* I - First job */
1703 void *second
, /* I - Second job */
1704 void *data
) /* I - App data (not used) */
1706 int diff
; /* Difference */
1709 if ((diff
= ((cupsd_job_t
*)first
)->priority
-
1710 ((cupsd_job_t
*)second
)->priority
) != 0)
1713 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
1718 * 'compare_jobs()' - Compare the job IDs of two jobs.
1721 static int /* O - Difference */
1722 compare_jobs(void *first
, /* I - First job */
1723 void *second
, /* I - Second job */
1724 void *data
) /* I - App data (not used) */
1726 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
1731 * 'free_job()' - Free all memory used by a job.
1735 free_job(cupsd_job_t
*job
) /* I - Job */
1737 cupsdClearString(&job
->username
);
1738 cupsdClearString(&job
->dest
);
1740 if (job
->num_files
> 0)
1742 free(job
->compressions
);
1743 free(job
->filetypes
);
1746 ippDelete(job
->attrs
);
1753 * 'ipp_length()' - Compute the size of the buffer needed to hold
1754 * the textual IPP attributes.
1757 static int /* O - Size of attribute buffer */
1758 ipp_length(ipp_t
*ipp
) /* I - IPP request */
1760 int bytes
; /* Number of bytes */
1761 int i
; /* Looping var */
1762 ipp_attribute_t
*attr
; /* Current attribute */
1766 * Loop through all attributes...
1771 for (attr
= ipp
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1774 * Skip attributes that won't be sent to filters...
1777 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
1778 attr
->value_tag
== IPP_TAG_NAMELANG
||
1779 attr
->value_tag
== IPP_TAG_TEXTLANG
||
1780 attr
->value_tag
== IPP_TAG_URI
||
1781 attr
->value_tag
== IPP_TAG_URISCHEME
)
1784 if (strncmp(attr
->name
, "time-", 5) == 0)
1788 * Add space for a leading space and commas between each value.
1789 * For the first attribute, the leading space isn't used, so the
1790 * extra byte can be used as the nul terminator...
1793 bytes
++; /* " " separator */
1794 bytes
+= attr
->num_values
; /* "," separators */
1797 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
1798 * other attributes appear as "foo=value1,value2,...,valueN".
1801 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
1802 bytes
+= strlen(attr
->name
);
1804 bytes
+= attr
->num_values
* strlen(attr
->name
);
1807 * Now add the size required for each value in the attribute...
1810 switch (attr
->value_tag
)
1812 case IPP_TAG_INTEGER
:
1815 * Minimum value of a signed integer is -2147483647, or 11 digits.
1818 bytes
+= attr
->num_values
* 11;
1821 case IPP_TAG_BOOLEAN
:
1823 * Add two bytes for each false ("no") value...
1826 for (i
= 0; i
< attr
->num_values
; i
++)
1827 if (!attr
->values
[i
].boolean
)
1831 case IPP_TAG_RANGE
:
1833 * A range is two signed integers separated by a hyphen, or
1834 * 23 characters max.
1837 bytes
+= attr
->num_values
* 23;
1840 case IPP_TAG_RESOLUTION
:
1842 * A resolution is two signed integers separated by an "x" and
1843 * suffixed by the units, or 26 characters max.
1846 bytes
+= attr
->num_values
* 26;
1849 case IPP_TAG_STRING
:
1852 case IPP_TAG_KEYWORD
:
1853 case IPP_TAG_CHARSET
:
1854 case IPP_TAG_LANGUAGE
:
1857 * Strings can contain characters that need quoting. We need
1858 * at least 2 * len + 2 characters to cover the quotes and
1859 * any backslashes in the string.
1862 for (i
= 0; i
< attr
->num_values
; i
++)
1863 bytes
+= 2 * strlen(attr
->values
[i
].string
.text
) + 2;
1867 break; /* anti-compiler-warning-code */
1876 * 'load_job_cache()' - Load jobs from the job.cache file.
1880 load_job_cache(const char *filename
) /* I - job.cache filename */
1882 cups_file_t
*fp
; /* job.cache file */
1883 char line
[1024], /* Line buffer */
1884 *value
; /* Value on line */
1885 int linenum
; /* Line number in file */
1886 cupsd_job_t
*job
; /* Current job */
1887 int jobid
; /* Job ID */
1888 char jobfile
[1024]; /* Job filename */
1892 * Open the job.cache file...
1895 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
1897 if (errno
!= ENOENT
)
1898 cupsdLogMessage(CUPSD_LOG_ERROR
,
1899 "Unable to open job cache file \"%s\": %s",
1900 filename
, strerror(errno
));
1902 load_request_root();
1908 * Read entries from the job cache file and create jobs as needed.
1911 cupsdLogMessage(CUPSD_LOG_INFO
, "Loading job cache file \"%s\"...",
1917 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
1919 if (!strcasecmp(line
, "NextJobId"))
1922 NextJobId
= atoi(value
);
1924 else if (!strcasecmp(line
, "<Job"))
1928 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing </Job> directive on line %d!",
1935 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing job ID on line %d!", linenum
);
1939 jobid
= atoi(value
);
1943 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad job ID %d on line %d!", jobid
,
1948 snprintf(jobfile
, sizeof(jobfile
), "%s/c%05d", RequestRoot
, jobid
);
1949 if (access(jobfile
, 0))
1951 cupsdLogMessage(CUPSD_LOG_ERROR
, "Job %d files have gone away!", jobid
);
1955 job
= calloc(1, sizeof(cupsd_job_t
));
1958 cupsdLogMessage(CUPSD_LOG_EMERG
,
1959 "Unable to allocate memory for job %d!", jobid
);
1964 job
->back_pipes
[0] = -1;
1965 job
->back_pipes
[1] = -1;
1966 job
->print_pipes
[0] = -1;
1967 job
->print_pipes
[1] = -1;
1969 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Loading job %d from cache...", job
->id
);
1973 cupsdLogMessage(CUPSD_LOG_ERROR
,
1974 "Missing <Job #> directive on line %d!", linenum
);
1977 else if (!strcasecmp(line
, "</Job>"))
1979 cupsArrayAdd(Jobs
, job
);
1981 if (job
->state_value
< IPP_JOB_STOPPED
)
1983 cupsArrayAdd(ActiveJobs
, job
);
1991 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing value on line %d!", linenum
);
1994 else if (!strcasecmp(line
, "State"))
1996 job
->state_value
= atoi(value
);
1998 if (job
->state_value
< IPP_JOB_PENDING
)
1999 job
->state_value
= IPP_JOB_PENDING
;
2000 else if (job
->state_value
> IPP_JOB_COMPLETED
)
2001 job
->state_value
= IPP_JOB_COMPLETED
;
2003 else if (!strcasecmp(line
, "Priority"))
2005 job
->priority
= atoi(value
);
2007 else if (!strcasecmp(line
, "Username"))
2009 cupsdSetString(&job
->username
, value
);
2011 else if (!strcasecmp(line
, "Destination"))
2013 cupsdSetString(&job
->dest
, value
);
2015 else if (!strcasecmp(line
, "DestType"))
2017 job
->dtype
= (cups_ptype_t
)atoi(value
);
2019 else if (!strcasecmp(line
, "NumFiles"))
2021 job
->num_files
= atoi(value
);
2023 if (job
->num_files
< 0)
2025 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad NumFiles value %d on line %d!",
2026 job
->num_files
, linenum
);
2031 if (job
->num_files
> 0)
2033 snprintf(jobfile
, sizeof(jobfile
), "%s/d%05d-001", RequestRoot
,
2035 if (access(jobfile
, 0))
2037 cupsdLogMessage(CUPSD_LOG_INFO
,
2038 "Data files for job %d have gone away!", job
->id
);
2043 job
->filetypes
= calloc(job
->num_files
, sizeof(mime_type_t
*));
2044 job
->compressions
= calloc(job
->num_files
, sizeof(int));
2046 if (!job
->filetypes
|| !job
->compressions
)
2048 cupsdLogMessage(CUPSD_LOG_EMERG
,
2049 "Unable to allocate memory for %d files!",
2055 else if (!strcasecmp(line
, "File"))
2057 int number
, /* File number */
2058 compression
; /* Compression value */
2059 char super
[MIME_MAX_SUPER
], /* MIME super type */
2060 type
[MIME_MAX_TYPE
]; /* MIME type */
2063 if (sscanf(value
, "%d%*[ \t]%15[^/]/%255s%d", &number
, super
, type
,
2066 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad File on line %d!", linenum
);
2070 if (number
< 1 || number
> job
->num_files
)
2072 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad File number %d on line %d!",
2079 job
->compressions
[number
] = compression
;
2080 job
->filetypes
[number
] = mimeType(MimeDatabase
, super
, type
);
2082 if (!job
->filetypes
[number
])
2085 * If the original MIME type is unknown, auto-type it!
2088 cupsdLogMessage(CUPSD_LOG_ERROR
,
2089 "Unknown MIME type %s/%s for file %d of job %d!",
2090 super
, type
, number
+ 1, job
->id
);
2092 snprintf(jobfile
, sizeof(jobfile
), "%s/d%05d-%03d", RequestRoot
,
2093 job
->id
, number
+ 1);
2094 job
->filetypes
[number
] = mimeFileType(MimeDatabase
, jobfile
, NULL
,
2095 job
->compressions
+ number
);
2098 * If that didn't work, assume it is raw...
2101 if (!job
->filetypes
[number
])
2102 job
->filetypes
[number
] = mimeType(MimeDatabase
, "application",
2107 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unknown %s directive on line %d!",
2116 * 'load_next_job_id()' - Load the NextJobId value from the job.cache file.
2120 load_next_job_id(const char *filename
) /* I - job.cache filename */
2122 cups_file_t
*fp
; /* job.cache file */
2123 char line
[1024], /* Line buffer */
2124 *value
; /* Value on line */
2125 int linenum
; /* Line number in file */
2129 * Read the NextJobId directive from the job.cache file and use
2130 * the value (if any).
2133 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
2135 if (errno
!= ENOENT
)
2136 cupsdLogMessage(CUPSD_LOG_ERROR
,
2137 "Unable to open job cache file \"%s\": %s",
2138 filename
, strerror(errno
));
2143 cupsdLogMessage(CUPSD_LOG_INFO
,
2144 "Loading NextJobId from job cache file \"%s\"...", filename
);
2148 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
2150 if (!strcasecmp(line
, "NextJobId"))
2153 NextJobId
= atoi(value
);
2164 * 'load_request_root()' - Load jobs from the RequestRoot directory.
2168 load_request_root(void)
2170 cups_dir_t
*dir
; /* Directory */
2171 cups_dentry_t
*dent
; /* Directory entry */
2172 cupsd_job_t
*job
; /* New job */
2176 * Open the requests directory...
2179 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Scanning %s for jobs...", RequestRoot
);
2181 if ((dir
= cupsDirOpen(RequestRoot
)) == NULL
)
2183 cupsdLogMessage(CUPSD_LOG_ERROR
,
2184 "Unable to open spool directory \"%s\": %s",
2185 RequestRoot
, strerror(errno
));
2190 * Read all the c##### files...
2193 while ((dent
= cupsDirRead(dir
)) != NULL
)
2194 if (strlen(dent
->filename
) >= 6 && dent
->filename
[0] == 'c')
2197 * Allocate memory for the job...
2200 if ((job
= calloc(sizeof(cupsd_job_t
), 1)) == NULL
)
2202 cupsdLogMessage(CUPSD_LOG_ERROR
, "Ran out of memory for jobs!");
2208 * Assign the job ID...
2211 job
->id
= atoi(dent
->filename
+ 1);
2212 job
->back_pipes
[0] = -1;
2213 job
->back_pipes
[1] = -1;
2214 job
->print_pipes
[0] = -1;
2215 job
->print_pipes
[1] = -1;
2217 if (job
->id
>= NextJobId
)
2218 NextJobId
= job
->id
+ 1;
2227 * Insert the job into the array, sorting by job priority and ID...
2230 cupsArrayAdd(Jobs
, job
);
2232 if (job
->state_value
< IPP_JOB_STOPPED
)
2233 cupsArrayAdd(ActiveJobs
,job
);
2243 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2247 set_time(cupsd_job_t
*job
, /* I - Job to update */
2248 const char *name
) /* I - Name of attribute */
2250 ipp_attribute_t
*attr
; /* Time attribute */
2253 if ((attr
= ippFindAttribute(job
->attrs
, name
, IPP_TAG_ZERO
)) != NULL
)
2255 attr
->value_tag
= IPP_TAG_INTEGER
;
2256 attr
->values
[0].integer
= time(NULL
);
2262 * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
2266 set_hold_until(cupsd_job_t
*job
, /* I - Job to update */
2267 time_t holdtime
) /* I - Hold until time */
2269 ipp_attribute_t
*attr
; /* job-hold-until attribute */
2270 struct tm
*holddate
; /* Hold date */
2271 char holdstr
[64]; /* Hold time */
2275 * Set the hold_until value and hold the job...
2278 cupsdLogMessage(CUPSD_LOG_DEBUG
, "set_hold_until: hold_until = %d",
2281 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
2282 job
->state_value
= IPP_JOB_HELD
;
2283 job
->hold_until
= holdtime
;
2286 * Update the job-hold-until attribute with a string representing GMT
2287 * time (HH:MM:SS)...
2290 holddate
= gmtime(&holdtime
);
2291 snprintf(holdstr
, sizeof(holdstr
), "%d:%d:%d", holddate
->tm_hour
,
2292 holddate
->tm_min
, holddate
->tm_sec
);
2294 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
2295 IPP_TAG_KEYWORD
)) == NULL
)
2296 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
2299 * Either add the attribute or update the value of the existing one
2303 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
2304 "job-hold-until", NULL
, holdstr
);
2306 cupsdSetString(&attr
->values
[0].string
.text
, holdstr
);
2313 * 'start_job()' - Start a print job.
2317 start_job(cupsd_job_t
*job
, /* I - Job ID */
2318 cupsd_printer_t
*printer
) /* I - Printer to print job */
2320 int i
; /* Looping var */
2321 int slot
; /* Pipe slot */
2322 cups_array_t
*filters
; /* Filters for job */
2323 mime_filter_t
*filter
, /* Current filter */
2324 port_monitor
; /* Port monitor filter */
2325 char method
[255], /* Method for output */
2326 *optptr
, /* Pointer to options */
2327 *valptr
; /* Pointer in value string */
2328 ipp_attribute_t
*attr
; /* Current attribute */
2329 struct stat backinfo
; /* Backend file information */
2330 int backroot
; /* Run backend as root? */
2331 int pid
; /* Process ID of new filter process */
2332 int banner_page
; /* 1 if banner page, 0 otherwise */
2333 int statusfds
[2], /* Pipes used with the scheduler */
2334 filterfds
[2][2];/* Pipes used between filters */
2335 int envc
; /* Number of environment variables */
2336 char **argv
, /* Filter command-line arguments */
2337 sani_uri
[1024], /* Sanitized DEVICE_URI env var */
2338 filename
[1024], /* Job filename */
2339 command
[1024], /* Full path to command */
2340 jobid
[255], /* Job ID string */
2341 title
[IPP_MAX_NAME
],
2342 /* Job title string */
2343 copies
[255], /* # copies string */
2344 *envp
[MAX_ENV
+ 11],
2345 /* Environment variables */
2346 charset
[255], /* CHARSET env variable */
2347 class_name
[255],/* CLASS env variable */
2348 classification
[1024],
2349 /* CLASSIFICATION env variable */
2351 /* CONTENT_TYPE env variable */
2353 /* DEVICE_URI env variable */
2354 final_content_type
[1024],
2355 /* FINAL_CONTENT_TYPE env variable */
2356 lang
[255], /* LANG env variable */
2357 ppd
[1024], /* PPD env variable */
2359 /* PRINTER env variable */
2361 /* RIP_MAX_CACHE env variable */
2362 int remote_job
; /* Remote print job? */
2363 static char *options
= NULL
;/* Full list of options */
2364 static int optlength
= 0; /* Length of option buffer */
2367 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: id = %d, file = %d/%d",
2368 job
->id
, job
->current_file
, job
->num_files
);
2370 if (job
->num_files
== 0)
2372 cupsdLogMessage(CUPSD_LOG_ERROR
, "Job ID %d has no files! Cancelling it!",
2375 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2376 "Job canceled because it has no files.");
2378 cupsdCancelJob(job
, 0);
2383 * Figure out what filters are required to convert from
2384 * the source to the destination type...
2393 * Remote jobs and raw queues go directly to the printer without
2397 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2398 "[Job %d] Sending job to queue tagged as raw...", job
->id
);
2405 * Local jobs get filtered...
2408 filters
= mimeFilter(MimeDatabase
, job
->filetypes
[job
->current_file
],
2409 printer
->filetype
, &(job
->cost
));
2413 cupsdLogMessage(CUPSD_LOG_ERROR
,
2414 "Unable to convert file %d to printable format for "
2416 job
->current_file
, job
->id
);
2417 cupsdLogMessage(CUPSD_LOG_INFO
,
2418 "Hint: Do you have ESP Ghostscript installed?");
2420 if (LogLevel
< CUPSD_LOG_DEBUG
)
2421 cupsdLogMessage(CUPSD_LOG_INFO
,
2422 "Hint: Try setting the LogLevel to \"debug\".");
2424 job
->current_file
++;
2426 if (job
->current_file
== job
->num_files
)
2428 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2429 "Job canceled because it has no files that can be "
2432 cupsdCancelJob(job
, 0);
2439 * Remove NULL ("-") filters...
2442 for (filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
2444 filter
= (mime_filter_t
*)cupsArrayNext(filters
))
2445 if (!strcmp(filter
->filter
, "-"))
2446 cupsArrayRemove(filters
, filter
);
2448 if (cupsArrayCount(filters
) == 0)
2450 cupsArrayDelete(filters
);
2456 * Set a minimum cost of 100 for all jobs so that FilterLimit
2457 * works with raw queues and other low-cost paths.
2460 if (job
->cost
< 100)
2464 * See if the filter cost is too high...
2467 if ((FilterLevel
+ job
->cost
) > FilterLimit
&& FilterLevel
> 0 &&
2471 * Don't print this job quite yet...
2474 cupsArrayDelete(filters
);
2476 cupsdLogMessage(CUPSD_LOG_INFO
,
2477 "Holding job %d because filter limit has been reached.",
2479 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2480 "start_job: id=%d, file=%d, cost=%d, level=%d, "
2482 job
->id
, job
->current_file
, job
->cost
, FilterLevel
,
2487 FilterLevel
+= job
->cost
;
2490 * Determine if we are printing to a remote printer...
2493 remote_job
= printer
->raw
&& job
->num_files
> 1 &&
2494 !strncmp(printer
->device_uri
, "ipp://", 6);
2497 * Add decompression filters, if any...
2500 if (!remote_job
&& job
->compressions
[job
->current_file
])
2503 * Add gziptoany filter to the front of the list...
2506 if (!cupsArrayInsert(filters
, &gziptoany_filter
))
2508 cupsdLogMessage(CUPSD_LOG_ERROR
,
2509 "Unable to add decompression filter - %s",
2512 cupsArrayDelete(filters
);
2514 job
->current_file
++;
2516 if (job
->current_file
== job
->num_files
)
2518 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2519 "Job canceled because the print file could not be "
2522 cupsdCancelJob(job
, 0);
2530 * Add port monitor, if any...
2533 if (printer
->port_monitor
)
2536 * Add port monitor to the end of the list...
2539 if (!cupsArrayAdd(filters
, &port_monitor
))
2541 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add port monitor - %s",
2544 cupsArrayDelete(filters
);
2546 job
->current_file
++;
2548 if (job
->current_file
== job
->num_files
)
2550 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2551 "Job canceled because the port monitor could not be "
2554 cupsdCancelJob(job
, 0);
2560 snprintf(port_monitor
.filter
, sizeof(port_monitor
.filter
),
2561 "%s/monitor/%s", ServerBin
, printer
->port_monitor
);
2565 * Update the printer and job state to "processing"...
2568 job
->state
->values
[0].integer
= IPP_JOB_PROCESSING
;
2569 job
->state_value
= IPP_JOB_PROCESSING
;
2571 job
->printer
= printer
;
2573 cupsdSetPrinterState(printer
, IPP_PRINTER_PROCESSING
, 0);
2575 if (job
->current_file
== 0)
2577 set_time(job
, "time-at-processing");
2578 cupsdOpenPipe(job
->back_pipes
);
2582 * Determine if we are printing a banner page or not...
2585 if (job
->job_sheets
== NULL
)
2587 cupsdLogMessage(CUPSD_LOG_DEBUG
, "No job-sheets attribute.");
2588 if ((job
->job_sheets
=
2589 ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
2590 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2591 "... but someone added one without setting job_sheets!");
2593 else if (job
->job_sheets
->num_values
== 1)
2594 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s",
2595 job
->job_sheets
->values
[0].string
.text
);
2597 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s,%s",
2598 job
->job_sheets
->values
[0].string
.text
,
2599 job
->job_sheets
->values
[1].string
.text
);
2601 if (printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
))
2603 else if (job
->job_sheets
== NULL
)
2605 else if (strcasecmp(job
->job_sheets
->values
[0].string
.text
, "none") != 0 &&
2606 job
->current_file
== 0)
2608 else if (job
->job_sheets
->num_values
> 1 &&
2609 strcasecmp(job
->job_sheets
->values
[1].string
.text
, "none") != 0 &&
2610 job
->current_file
== (job
->num_files
- 1))
2615 cupsdLogMessage(CUPSD_LOG_DEBUG
, "banner_page = %d", banner_page
);
2618 * Building the options string is harder than it needs to be, but
2619 * for the moment we need to pass strings for command-line args and
2620 * not IPP attribute pointers... :)
2622 * First allocate/reallocate the option buffer as needed...
2625 i
= ipp_length(job
->attrs
);
2632 optptr
= realloc(options
, i
);
2636 cupsdLogMessage(CUPSD_LOG_CRIT
,
2637 "Unable to allocate %d bytes for option buffer for "
2638 "job %d!", i
, job
->id
);
2640 cupsArrayDelete(filters
);
2642 FilterLevel
-= job
->cost
;
2644 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2645 "Job canceled because the server ran out of memory.");
2647 cupsdCancelJob(job
, 0);
2656 * Now loop through the attributes and convert them to the textual
2657 * representation used by the filters...
2663 snprintf(title
, sizeof(title
), "%s-%d", printer
->name
, job
->id
);
2664 strcpy(copies
, "1");
2666 for (attr
= job
->attrs
->attrs
; attr
!= NULL
; attr
= attr
->next
)
2668 if (!strcmp(attr
->name
, "copies") &&
2669 attr
->value_tag
== IPP_TAG_INTEGER
)
2672 * Don't use the # copies attribute if we are printing the job sheets...
2676 sprintf(copies
, "%d", attr
->values
[0].integer
);
2678 else if (!strcmp(attr
->name
, "job-name") &&
2679 (attr
->value_tag
== IPP_TAG_NAME
||
2680 attr
->value_tag
== IPP_TAG_NAMELANG
))
2681 strlcpy(title
, attr
->values
[0].string
.text
, sizeof(title
));
2682 else if (attr
->group_tag
== IPP_TAG_JOB
)
2685 * Filter out other unwanted attributes...
2688 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
2689 attr
->value_tag
== IPP_TAG_NAMELANG
||
2690 attr
->value_tag
== IPP_TAG_TEXTLANG
||
2691 (attr
->value_tag
== IPP_TAG_URI
&& strcmp(attr
->name
, "job-uuid")) ||
2692 attr
->value_tag
== IPP_TAG_URISCHEME
||
2693 attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
) /* Not yet supported */
2696 if (!strncmp(attr
->name
, "time-", 5))
2699 if (!strncmp(attr
->name
, "job-", 4) && strcmp(attr
->name
, "job-uuid") &&
2700 !(printer
->type
& CUPS_PRINTER_REMOTE
))
2703 if (!strncmp(attr
->name
, "job-", 4) &&
2704 strcmp(attr
->name
, "job-uuid") &&
2705 strcmp(attr
->name
, "job-billing") &&
2706 strcmp(attr
->name
, "job-sheets") &&
2707 strcmp(attr
->name
, "job-hold-until") &&
2708 strcmp(attr
->name
, "job-priority"))
2711 if ((!strcmp(attr
->name
, "page-label") ||
2712 !strcmp(attr
->name
, "page-border") ||
2713 !strncmp(attr
->name
, "number-up", 9) ||
2714 !strcmp(attr
->name
, "page-set") ||
2715 !strcasecmp(attr
->name
, "AP_FIRSTPAGE_InputSlot") ||
2716 !strcasecmp(attr
->name
, "AP_FIRSTPAGE_ManualFeed")) &&
2721 * Otherwise add them to the list...
2724 if (optptr
> options
)
2725 strlcat(optptr
, " ", optlength
- (optptr
- options
));
2727 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
2729 strlcat(optptr
, attr
->name
, optlength
- (optptr
- options
));
2730 strlcat(optptr
, "=", optlength
- (optptr
- options
));
2733 for (i
= 0; i
< attr
->num_values
; i
++)
2736 strlcat(optptr
, ",", optlength
- (optptr
- options
));
2738 optptr
+= strlen(optptr
);
2740 switch (attr
->value_tag
)
2742 case IPP_TAG_INTEGER
:
2744 snprintf(optptr
, optlength
- (optptr
- options
),
2745 "%d", attr
->values
[i
].integer
);
2748 case IPP_TAG_BOOLEAN
:
2749 if (!attr
->values
[i
].boolean
)
2750 strlcat(optptr
, "no", optlength
- (optptr
- options
));
2752 case IPP_TAG_NOVALUE
:
2753 strlcat(optptr
, attr
->name
,
2754 optlength
- (optptr
- options
));
2757 case IPP_TAG_RANGE
:
2758 if (attr
->values
[i
].range
.lower
== attr
->values
[i
].range
.upper
)
2759 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
2760 "%d", attr
->values
[i
].range
.lower
);
2762 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
2763 "%d-%d", attr
->values
[i
].range
.lower
,
2764 attr
->values
[i
].range
.upper
);
2767 case IPP_TAG_RESOLUTION
:
2768 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
2769 "%dx%d%s", attr
->values
[i
].resolution
.xres
,
2770 attr
->values
[i
].resolution
.yres
,
2771 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
2775 case IPP_TAG_STRING
:
2778 case IPP_TAG_KEYWORD
:
2779 case IPP_TAG_CHARSET
:
2780 case IPP_TAG_LANGUAGE
:
2782 for (valptr
= attr
->values
[i
].string
.text
; *valptr
;)
2784 if (strchr(" \t\n\\\'\"", *valptr
))
2786 *optptr
++ = *valptr
++;
2793 break; /* anti-compiler-warning-code */
2797 optptr
+= strlen(optptr
);
2802 * Build the command-line arguments for the filters. Each filter
2803 * has 6 or 7 arguments:
2807 * argv[2] = username
2809 * argv[4] = # copies
2811 * argv[6] = filename (optional; normally stdin)
2813 * This allows legacy printer drivers that use the old System V
2814 * printing interface to be used by CUPS.
2816 * For remote jobs, we send all of the files in the argument list.
2820 argv
= calloc(7 + job
->num_files
, sizeof(char *));
2822 argv
= calloc(8, sizeof(char *));
2824 sprintf(jobid
, "%d", job
->id
);
2826 argv
[0] = printer
->name
;
2828 argv
[2] = job
->username
;
2835 for (i
= 0; i
< job
->num_files
; i
++)
2837 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
2839 argv
[6 + i
] = strdup(filename
);
2844 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
2845 job
->id
, job
->current_file
+ 1);
2849 for (i
= 0; argv
[i
]; i
++)
2850 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2851 "[Job %d] argv[%d]=\"%s\"", job
->id
, i
, argv
[i
]);
2854 * Create environment variable strings for the filters...
2857 attr
= ippFindAttribute(job
->attrs
, "attributes-natural-language",
2860 switch (strlen(attr
->values
[0].string
.text
))
2864 * This is an unknown or badly formatted language code; use
2865 * the POSIX locale...
2868 strcpy(lang
, "LANG=C");
2873 * Just the language code (ll)...
2876 snprintf(lang
, sizeof(lang
), "LANG=%s",
2877 attr
->values
[0].string
.text
);
2882 * Language and country code (ll-cc)...
2885 snprintf(lang
, sizeof(lang
), "LANG=%c%c_%c%c",
2886 attr
->values
[0].string
.text
[0],
2887 attr
->values
[0].string
.text
[1],
2888 toupper(attr
->values
[0].string
.text
[3] & 255),
2889 toupper(attr
->values
[0].string
.text
[4] & 255));
2893 attr
= ippFindAttribute(job
->attrs
, "document-format",
2896 (optptr
= strstr(attr
->values
[0].string
.text
, "charset=")) != NULL
)
2897 snprintf(charset
, sizeof(charset
), "CHARSET=%s", optptr
+ 8);
2900 attr
= ippFindAttribute(job
->attrs
, "attributes-charset",
2902 snprintf(charset
, sizeof(charset
), "CHARSET=%s",
2903 attr
->values
[0].string
.text
);
2906 snprintf(content_type
, sizeof(content_type
), "CONTENT_TYPE=%s/%s",
2907 job
->filetypes
[job
->current_file
]->super
,
2908 job
->filetypes
[job
->current_file
]->type
);
2909 snprintf(device_uri
, sizeof(device_uri
), "DEVICE_URI=%s",
2910 printer
->device_uri
);
2911 cupsdSanitizeURI(printer
->device_uri
, sani_uri
, sizeof(sani_uri
));
2912 snprintf(ppd
, sizeof(ppd
), "PPD=%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
2913 snprintf(printer_name
, sizeof(printer_name
), "PRINTER=%s", printer
->name
);
2914 snprintf(rip_max_cache
, sizeof(rip_max_cache
), "RIP_MAX_CACHE=%s", RIPCache
);
2916 envc
= cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
2918 envp
[envc
++] = charset
;
2919 envp
[envc
++] = lang
;
2920 envp
[envc
++] = ppd
;
2921 envp
[envc
++] = rip_max_cache
;
2922 envp
[envc
++] = content_type
;
2923 envp
[envc
++] = device_uri
;
2924 envp
[envc
++] = printer_name
;
2926 if ((filter
= (mime_filter_t
*)cupsArrayLast(filters
)) != NULL
)
2928 snprintf(final_content_type
, sizeof(final_content_type
),
2929 "FINAL_CONTENT_TYPE=%s/%s",
2930 filter
->dst
->super
, filter
->dst
->type
);
2931 envp
[envc
++] = final_content_type
;
2934 if (Classification
&& !banner_page
)
2936 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
2937 IPP_TAG_NAME
)) == NULL
)
2938 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
2940 else if (attr
->num_values
> 1 &&
2941 strcmp(attr
->values
[1].string
.text
, "none") != 0)
2942 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
2943 attr
->values
[1].string
.text
);
2945 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
2946 attr
->values
[0].string
.text
);
2948 envp
[envc
++] = classification
;
2951 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
2953 snprintf(class_name
, sizeof(class_name
), "CLASS=%s", job
->dest
);
2954 envp
[envc
++] = class_name
;
2959 for (i
= 0; i
< envc
; i
++)
2960 if (strncmp(envp
[i
], "DEVICE_URI=", 11))
2961 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Job %d] envp[%d]=\"%s\"",
2962 job
->id
, i
, envp
[i
]);
2964 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Job %d] envp[%d]=\"DEVICE_URI=%s\"",
2965 job
->id
, i
, sani_uri
);
2968 job
->current_file
= job
->num_files
;
2970 job
->current_file
++;
2973 * Now create processes for all of the filters...
2976 filterfds
[0][0] = -1;
2977 filterfds
[0][1] = -1;
2978 filterfds
[1][0] = -1;
2979 filterfds
[1][1] = -1;
2981 if (cupsdOpenPipe(statusfds
))
2983 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to create job status pipes - %s.",
2985 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2986 "Unable to create status pipes - %s.", strerror(errno
));
2988 cupsdAddPrinterHistory(printer
);
2990 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2991 "Job canceled because the server could not create the job "
2997 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: statusfds = [ %d %d ]",
2998 statusfds
[0], statusfds
[1]);
3001 fcntl(statusfds
[0], F_SETFD
, FD_CLOEXEC
);
3002 fcntl(statusfds
[1], F_SETFD
, FD_CLOEXEC
);
3003 #endif /* FD_CLOEXEC */
3005 job
->status_buffer
= cupsdStatBufNew(statusfds
[0], "[Job %d]",
3008 memset(job
->filters
, 0, sizeof(job
->filters
));
3010 filterfds
[1][0] = open("/dev/null", O_RDONLY
);
3012 if (filterfds
[1][0] < 0)
3014 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to open \"/dev/null\" - %s.",
3016 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3017 "Unable to open \"/dev/null\" - %s.", strerror(errno
));
3019 cupsdAddPrinterHistory(printer
);
3021 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3022 "Job canceled because the server could not open /dev/null.");
3027 fcntl(filterfds
[1][0], F_SETFD
, fcntl(filterfds
[1][0], F_GETFD
) | FD_CLOEXEC
);
3029 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: filterfds[%d] = [ %d %d ]",
3030 1, filterfds
[1][0], filterfds
[1][1]);
3032 for (i
= 0, slot
= 0, filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
3034 i
++, filter
= (mime_filter_t
*)cupsArrayNext(filters
))
3036 if (filter
->filter
[0] != '/')
3037 snprintf(command
, sizeof(command
), "%s/filter/%s", ServerBin
,
3040 strlcpy(command
, filter
->filter
, sizeof(command
));
3042 if (i
< (cupsArrayCount(filters
) - 1))
3044 if (cupsdOpenPipe(filterfds
[slot
]))
3046 cupsdLogMessage(CUPSD_LOG_ERROR
,
3047 "Unable to create job filter pipes - %s.",
3049 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3050 "Unable to create filter pipes - %s.", strerror(errno
));
3051 cupsdAddPrinterHistory(printer
);
3053 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3054 "Job canceled because the server could not create the "
3062 if (job
->current_file
== 1)
3064 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
3066 if (cupsdOpenPipe(job
->print_pipes
))
3068 cupsdLogMessage(CUPSD_LOG_ERROR
,
3069 "Unable to create job backend pipes - %s.",
3071 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3072 "Unable to create backend pipes - %s.", strerror(errno
));
3073 cupsdAddPrinterHistory(printer
);
3075 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3076 "Job canceled because the server could not create "
3077 "the backend pipes.");
3084 job
->print_pipes
[0] = -1;
3085 if (!strncmp(printer
->device_uri
, "file:/dev/", 10) &&
3086 strcmp(printer
->device_uri
, "file:/dev/null"))
3087 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
3089 else if (!strncmp(printer
->device_uri
, "file:///dev/", 12) &&
3090 strcmp(printer
->device_uri
, "file:///dev/null"))
3091 job
->print_pipes
[1] = open(printer
->device_uri
+ 7,
3094 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
3095 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3097 if (job
->print_pipes
[1] < 0)
3099 cupsdLogMessage(CUPSD_LOG_ERROR
,
3100 "Unable to open output file \"%s\" - %s.",
3101 printer
->device_uri
, strerror(errno
));
3102 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3103 "Unable to open output file \"%s\" - %s.",
3104 printer
->device_uri
, strerror(errno
));
3106 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3107 "Job canceled because the server could not open the "
3113 fcntl(job
->print_pipes
[1], F_SETFD
,
3114 fcntl(job
->print_pipes
[1], F_GETFD
) | FD_CLOEXEC
);
3117 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3118 "start_job: print_pipes = [ %d %d ]",
3119 job
->print_pipes
[0], job
->print_pipes
[1]);
3122 filterfds
[slot
][0] = job
->print_pipes
[0];
3123 filterfds
[slot
][1] = job
->print_pipes
[1];
3126 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: filter=\"%s\"",
3128 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3129 "start_job: filterfds[%d]=[ %d %d ]",
3130 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3132 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
3133 filterfds
[slot
][1], statusfds
[1],
3134 job
->back_pipes
[0], 0, job
->filters
+ i
);
3136 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3137 "start_job: Closing filter pipes for slot %d "
3139 !slot
, filterfds
[!slot
][0], filterfds
[!slot
][1]);
3141 cupsdClosePipe(filterfds
[!slot
]);
3145 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start filter \"%s\" - %s.",
3146 filter
->filter
, strerror(errno
));
3147 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3148 "Unable to start filter \"%s\" - %s.",
3149 filter
->filter
, strerror(errno
));
3151 cupsdAddPrinterHistory(printer
);
3153 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3154 "Job canceled because the server could not execute a "
3160 cupsdLogMessage(CUPSD_LOG_INFO
, "Started filter %s (PID %d) for job %d.",
3161 command
, pid
, job
->id
);
3167 cupsArrayDelete(filters
);
3170 * Finally, pipe the final output into a backend process if needed...
3173 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
3175 if (job
->current_file
== 1)
3177 sscanf(printer
->device_uri
, "%254[^:]", method
);
3178 snprintf(command
, sizeof(command
), "%s/backend/%s", ServerBin
, method
);
3181 * See if the backend needs to run as root...
3186 else if (stat(command
, &backinfo
))
3189 backroot
= !(backinfo
.st_mode
& (S_IRWXG
| S_IRWXO
));
3193 filterfds
[slot
][0] = -1;
3194 filterfds
[slot
][1] = open("/dev/null", O_WRONLY
);
3196 if (filterfds
[slot
][1] < 0)
3198 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to open \"/dev/null\" - %s.",
3200 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3201 "Unable to open \"/dev/null\" - %s.", strerror(errno
));
3203 cupsdAddPrinterHistory(printer
);
3205 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3206 "Job canceled because the server could not open a file.");
3211 fcntl(filterfds
[slot
][1], F_SETFD
,
3212 fcntl(filterfds
[slot
][1], F_GETFD
) | FD_CLOEXEC
);
3214 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: backend=\"%s\"",
3216 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3217 "start_job: filterfds[%d] = [ %d %d ]",
3218 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3220 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
3221 filterfds
[slot
][1], statusfds
[1],
3222 job
->back_pipes
[1], backroot
,
3227 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start backend \"%s\" - %s.",
3228 method
, strerror(errno
));
3229 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3230 "Unable to start backend \"%s\" - %s.", method
,
3233 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3234 "Job canceled because the server could not execute "
3241 cupsdLogMessage(CUPSD_LOG_INFO
,
3242 "Started backend %s (PID %d) for job %d.",
3243 command
, pid
, job
->id
);
3247 if (job
->current_file
== job
->num_files
)
3249 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3250 "start_job: Closing print pipes [ %d %d ]...",
3251 job
->print_pipes
[0], job
->print_pipes
[1]);
3253 cupsdClosePipe(job
->print_pipes
);
3255 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3256 "start_job: Closing back pipes [ %d %d ]...",
3257 job
->back_pipes
[0], job
->back_pipes
[1]);
3259 cupsdClosePipe(job
->back_pipes
);
3264 filterfds
[slot
][0] = -1;
3265 filterfds
[slot
][1] = -1;
3267 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3268 "start_job: Closing filter pipes for slot %d "
3270 !slot
, filterfds
[!slot
][0], filterfds
[!slot
][1]);
3272 cupsdClosePipe(filterfds
[!slot
]);
3274 if (job
->current_file
== job
->num_files
)
3276 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3277 "start_job: Closing print pipes [ %d %d ]...",
3278 job
->print_pipes
[0], job
->print_pipes
[1]);
3280 cupsdClosePipe(job
->print_pipes
);
3284 for (slot
= 0; slot
< 2; slot
++)
3286 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3287 "start_job: Closing filter pipes for slot %d "
3289 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3290 cupsdClosePipe(filterfds
[slot
]);
3295 for (i
= 0; i
< job
->num_files
; i
++)
3301 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3302 "start_job: Closing status output pipe %d...",
3305 close(statusfds
[1]);
3307 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3308 "start_job: Adding fd %d to InputSet...",
3309 job
->status_buffer
->fd
);
3311 FD_SET(job
->status_buffer
->fd
, InputSet
);
3313 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, job
->printer
, job
, "Job #%d started.",
3320 * If we get here, we need to abort the current job and close out all
3321 * files and pipes...
3326 for (slot
= 0; slot
< 2; slot
++)
3328 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3329 "start_job: Closing filter pipes for slot %d "
3331 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3332 cupsdClosePipe(filterfds
[slot
]);
3335 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3336 "start_job: Closing status pipes [ %d %d ]...",
3337 statusfds
[0], statusfds
[1]);
3338 cupsdClosePipe(statusfds
);
3340 cupsArrayDelete(filters
);
3344 for (i
= 0; i
< job
->num_files
; i
++)
3350 cupsdStopJob(job
, 0);
3355 * 'unload_job()' - Unload a job from memory.
3359 unload_job(cupsd_job_t
*job
) /* I - Job */
3364 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Unloading job %d...", job
->id
);
3366 ippDelete(job
->attrs
);
3371 job
->job_sheets
= NULL
;
3376 * End of "$Id: job.c 5305 2006-03-18 03:05:12Z mike $".