2 * "$Id: job.c 6462 2007-04-23 19:25:13Z mike $"
4 * Job management routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2007 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 * compare_active_jobs() - Compare the job IDs and priorities of two
55 * compare_jobs() - Compare the job IDs of two jobs.
56 * free_job() - Free all memory used by a job.
57 * ipp_length() - Compute the size of the buffer needed to
58 * hold the textual IPP attributes.
59 * load_job_cache() - Load jobs from the job.cache file.
60 * load_next_job_id() - Load the NextJobId value from the
62 * load_request_root() - Load jobs from the RequestRoot directory.
63 * set_time() - Set one of the "time-at-xyz" attributes...
64 * set_hold_until() - Set the hold time and update job-hold-until
66 * start_job() - Start a print job.
67 * unload_job() - Unload a job from memory.
68 * update_job() - Read a status update from a jobs filters.
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
);
109 static void update_job(cupsd_job_t
*job
);
113 * 'cupsdAddJob()' - Add a new job to the job queue...
116 cupsd_job_t
* /* O - New job record */
117 cupsdAddJob(int priority
, /* I - Job priority */
118 const char *dest
) /* I - Job destination */
120 cupsd_job_t
*job
; /* New job record */
123 job
= calloc(sizeof(cupsd_job_t
), 1);
125 job
->id
= NextJobId
++;
126 job
->priority
= priority
;
127 job
->back_pipes
[0] = -1;
128 job
->back_pipes
[1] = -1;
129 job
->print_pipes
[0] = -1;
130 job
->print_pipes
[1] = -1;
131 job
->side_pipes
[0] = -1;
132 job
->side_pipes
[1] = -1;
133 job
->status_pipes
[0] = -1;
134 job
->status_pipes
[1] = -1;
136 cupsdSetString(&job
->dest
, dest
);
139 * Add the new job to the "all jobs" and "active jobs" lists...
142 cupsArrayAdd(Jobs
, job
);
143 cupsArrayAdd(ActiveJobs
, job
);
150 * 'cupsdCancelJob()' - Cancel the specified print job.
154 cupsdCancelJob(cupsd_job_t
*job
, /* I - Job to cancel */
155 int purge
, /* I - Purge jobs? */
156 ipp_jstate_t newstate
) /* I - New job state */
158 int i
; /* Looping var */
159 char filename
[1024]; /* Job filename */
160 cupsd_printer_t
*printer
; /* Printer used by job */
163 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdCancelJob: id = %d", job
->id
);
166 * Stop any processes that are working on the current job...
169 printer
= job
->printer
;
171 if (job
->state_value
== IPP_JOB_PROCESSING
)
172 cupsdStopJob(job
, 0);
177 job
->state
->values
[0].integer
= newstate
;
179 job
->state_value
= newstate
;
181 set_time(job
, "time-at-completed");
184 * Send any pending notifications and then expire them...
192 case IPP_JOB_CANCELED
:
193 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, printer
, job
,
194 purge
? "Job purged." : "Job canceled.");
197 case IPP_JOB_ABORTED
:
198 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, printer
, job
,
199 "Job aborted; please consult the error_log file "
203 case IPP_JOB_COMPLETED
:
205 * Clear the printer's printer-state-message and move on...
208 printer
->state_message
[0] = '\0';
210 cupsdSetPrinterState(printer
, IPP_PRINTER_IDLE
, 0);
212 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, printer
, job
,
217 cupsdExpireSubscriptions(NULL
, job
);
220 * Remove the job from the active list...
223 cupsArrayRemove(ActiveJobs
, job
);
226 * Remove any authentication data...
229 snprintf(filename
, sizeof(filename
), "%s/a%05d", RequestRoot
, job
->id
);
233 * Remove the print file for good if we aren't preserving jobs or
237 job
->current_file
= 0;
239 if (!JobHistory
|| !JobFiles
|| purge
|| (job
->dtype
& CUPS_PRINTER_REMOTE
))
241 for (i
= 1; i
<= job
->num_files
; i
++)
243 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
248 if (job
->num_files
> 0)
250 free(job
->filetypes
);
251 free(job
->compressions
);
254 job
->filetypes
= NULL
;
255 job
->compressions
= NULL
;
259 if (JobHistory
&& !purge
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
))
262 * Save job state info...
270 * Remove the job info file...
273 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
,
278 * Remove the job from the "all jobs" list...
281 cupsArrayRemove(Jobs
, job
);
284 * Free all memory used...
293 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user...
297 cupsdCancelJobs(const char *dest
, /* I - Destination to cancel */
298 const char *username
, /* I - Username or NULL */
299 int purge
) /* I - Purge jobs? */
301 cupsd_job_t
*job
; /* Current job */
304 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
306 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
307 if ((dest
== NULL
|| !strcmp(job
->dest
, dest
)) &&
308 (username
== NULL
|| !strcmp(job
->username
, username
)))
311 * Cancel all jobs matching this destination/user...
314 cupsdCancelJob(job
, purge
, IPP_JOB_CANCELED
);
322 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
329 cupsd_job_t
*job
; /* Current job in queue */
330 cupsd_printer_t
*printer
, /* Printer destination */
331 *pclass
; /* Printer class destination */
334 DEBUG_puts("cupsdCheckJobs()");
336 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
337 "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d",
338 cupsArrayCount(ActiveJobs
), Sleeping
, NeedReload
);
340 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
342 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
345 * Start held jobs if they are ready...
348 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
349 "cupsdCheckJobs: Job %d: state_value=%d, loaded=%s",
350 job
->id
, job
->state_value
, job
->attrs
? "yes" : "no");
352 if (job
->state_value
== IPP_JOB_HELD
&&
354 job
->hold_until
< time(NULL
))
356 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
357 job
->state_value
= IPP_JOB_PENDING
;
361 * Start pending jobs if the destination is available...
364 if (job
->state_value
== IPP_JOB_PENDING
&& !NeedReload
&& !Sleeping
)
366 printer
= cupsdFindDest(job
->dest
);
370 (printer
->type
& (CUPS_PRINTER_IMPLICIT
| CUPS_PRINTER_CLASS
)))
373 * If the class is remote, just pass it to the remote server...
378 if (pclass
->state
== IPP_PRINTER_STOPPED
)
380 else if (pclass
->type
& CUPS_PRINTER_REMOTE
)
383 printer
= cupsdFindAvailablePrinter(printer
->name
);
386 if (!printer
&& !pclass
)
389 * Whoa, the printer and/or class for this destination went away;
393 cupsdLogMessage(CUPSD_LOG_WARN
,
394 "Printer/class %s has gone away; canceling job %d!",
397 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
398 "Job canceled because the destination printer/class has "
401 cupsdCancelJob(job
, 1, IPP_JOB_ABORTED
);
406 * See if the printer is available or remote and not printing a job;
407 * if so, start the job...
413 * Add/update a job-actual-printer-uri attribute for this job
414 * so that we know which printer actually printed the job...
417 ipp_attribute_t
*attr
; /* job-actual-printer-uri attribute */
420 if ((attr
= ippFindAttribute(job
->attrs
, "job-actual-printer-uri",
421 IPP_TAG_URI
)) != NULL
)
422 cupsdSetString(&attr
->values
[0].string
.text
, printer
->uri
);
424 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
,
425 "job-actual-printer-uri", NULL
, printer
->uri
);
428 if ((!(printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is local */
429 printer
->state
== IPP_PRINTER_IDLE
) || /* and idle */
430 ((printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is remote */
431 !printer
->job
)) /* and not printing */
432 start_job(job
, printer
);
440 * 'cupsdCleanJobs()' - Clean out old jobs.
446 cupsd_job_t
*job
; /* Current job */
452 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
453 job
&& cupsArrayCount(Jobs
) >= MaxJobs
;
454 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
455 if (job
->state_value
>= IPP_JOB_CANCELED
)
456 cupsdCancelJob(job
, 1, IPP_JOB_CANCELED
);
461 * 'cupsdFinishJob()' - Finish a job.
465 cupsdFinishJob(cupsd_job_t
*job
) /* I - 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 cupsdRemoveSelect(job
->status_buffer
->fd
);
485 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
486 "cupsdFinishJob: Closing status pipes [ %d %d ]...",
487 job
->status_pipes
[0], job
->status_pipes
[1]);
489 cupsdClosePipe(job
->status_pipes
);
490 cupsdStatBufDelete(job
->status_buffer
);
492 job
->status_buffer
= NULL
;
495 printer
= job
->printer
;
500 * Backend had errors; stop it...
503 int exit_code
; /* Exit code from backend */
507 * Convert the status to an exit code. Due to the way the W* macros are
508 * implemented on MacOS X (bug?), we have to store the exit status in a
509 * variable first and then convert...
512 exit_code
= -job
->status
;
513 if (WIFEXITED(exit_code
))
514 exit_code
= WEXITSTATUS(exit_code
);
516 exit_code
= job
->status
;
518 cupsdLogMessage(CUPSD_LOG_INFO
, "[Job %d] Backend returned status %d (%s)",
520 exit_code
== CUPS_BACKEND_FAILED
? "failed" :
521 exit_code
== CUPS_BACKEND_AUTH_REQUIRED
?
522 "authentication required" :
523 exit_code
== CUPS_BACKEND_HOLD
? "hold job" :
524 exit_code
== CUPS_BACKEND_STOP
? "stop printer" :
525 exit_code
== CUPS_BACKEND_CANCEL
? "cancel job" :
526 exit_code
< 0 ? "crashed" : "unknown");
529 * Do what needs to be done...
535 case CUPS_BACKEND_FAILED
:
537 * Backend failure, use the error-policy to determine how to
541 cupsdStopJob(job
, 0);
543 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
546 * Mark the job as pending again - we'll retry on another
550 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
551 job
->state_value
= IPP_JOB_PENDING
;
557 * If the job was queued to a class, try requeuing it... For
558 * faxes and retry-job queues, hold the current job for 5 minutes.
561 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
563 else if ((printer
->type
& CUPS_PRINTER_FAX
) ||
564 !strcmp(printer
->error_policy
, "retry-job"))
567 * See how many times we've tried to send the job; if more than
568 * the limit, cancel the job.
573 if (job
->tries
>= JobRetryLimit
)
579 cupsdLogMessage(CUPSD_LOG_ERROR
,
580 "Canceling job %d since it could not be sent "
582 job
->id
, JobRetryLimit
);
584 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
589 * Try again in N seconds...
592 set_hold_until(job
, time(NULL
) + JobRetryInterval
);
594 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, job
->printer
, job
,
595 "Job held due to fax errors; please consult "
596 "the error_log file for details.");
597 cupsdSetPrinterState(printer
, IPP_PRINTER_IDLE
, 0);
600 else if (!strcmp(printer
->error_policy
, "abort-job"))
601 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
604 cupsdSetPrinterState(printer
, IPP_PRINTER_STOPPED
, 1);
606 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
607 "Job stopped due to backend errors; please consult "
608 "the error_log file for details.");
612 case CUPS_BACKEND_CANCEL
:
617 cupsdCancelJob(job
, 0, IPP_JOB_CANCELED
);
620 case CUPS_BACKEND_HOLD
:
625 cupsdStopJob(job
, 0);
627 cupsdSetJobHoldUntil(job
, "indefinite");
629 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
630 job
->state_value
= IPP_JOB_HELD
;
634 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
635 "Job held due to backend errors; please consult "
636 "the error_log file for details.");
639 case CUPS_BACKEND_STOP
:
641 * Stop the printer...
644 cupsdStopJob(job
, 0);
646 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
647 job
->state_value
= IPP_JOB_PENDING
;
650 cupsdSetPrinterState(printer
, IPP_PRINTER_STOPPED
, 1);
652 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
653 "Job stopped due to backend errors; please consult "
654 "the error_log file for details.");
657 case CUPS_BACKEND_AUTH_REQUIRED
:
658 cupsdStopJob(job
, 0);
660 cupsdSetJobHoldUntil(job
, "authenticated");
662 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
663 job
->state_value
= IPP_JOB_HELD
;
667 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
668 "Authentication is required for job %d.", job
->id
);
673 * Try printing another job...
678 else if (job
->status
> 0)
681 * Filter had errors; stop job...
684 cupsdStopJob(job
, 1);
686 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
687 "Job stopped due to filter errors; please consult the "
688 "error_log file for details.");
694 * Job printed successfully; cancel it...
697 if (job
->current_file
< job
->num_files
)
700 * Start the next file in the job...
703 FilterLevel
-= job
->cost
;
704 start_job(job
, printer
);
709 * Close out this job...
712 cupsdCancelJob(job
, 0, IPP_JOB_COMPLETED
);
720 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
724 cupsdFreeAllJobs(void)
726 cupsd_job_t
*job
; /* Current job */
737 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
739 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
741 cupsArrayRemove(Jobs
, job
);
742 cupsArrayRemove(ActiveJobs
, job
);
747 cupsdReleaseSignals();
752 * 'cupsdFindJob()' - Find the specified job.
755 cupsd_job_t
* /* O - Job data */
756 cupsdFindJob(int id
) /* I - Job ID */
758 cupsd_job_t key
; /* Search key */
763 return ((cupsd_job_t
*)cupsArrayFind(Jobs
, &key
));
768 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
769 * or held jobs in a printer or class.
772 int /* O - Job count */
773 cupsdGetPrinterJobCount(
774 const char *dest
) /* I - Printer or class name */
776 int count
; /* Job count */
777 cupsd_job_t
*job
; /* Current job */
780 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
782 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
783 if (job
->dest
&& !strcasecmp(job
->dest
, dest
))
791 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
792 * or held jobs for a user.
795 int /* O - Job count */
796 cupsdGetUserJobCount(
797 const char *username
) /* I - Username */
799 int count
; /* Job count */
800 cupsd_job_t
*job
; /* Current job */
803 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
805 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
806 if (!strcasecmp(job
->username
, username
))
814 * 'cupsdHoldJob()' - Hold the specified job.
818 cupsdHoldJob(cupsd_job_t
*job
) /* I - Job data */
820 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdHoldJob: id = %d", job
->id
);
822 if (job
->state_value
== IPP_JOB_PROCESSING
)
823 cupsdStopJob(job
, 0);
827 DEBUG_puts("cupsdHoldJob: setting state to held...");
829 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
830 job
->state_value
= IPP_JOB_HELD
;
839 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
843 cupsdLoadAllJobs(void)
845 char filename
[1024]; /* Full filename of job.cache file */
846 struct stat fileinfo
, /* Information on job.cache file */
847 dirinfo
; /* Information on RequestRoot dir */
852 * Create the job arrays as needed...
856 Jobs
= cupsArrayNew(compare_jobs
, NULL
);
859 ActiveJobs
= cupsArrayNew(compare_active_jobs
, NULL
);
862 * See whether the job.cache file is older than the RequestRoot directory...
865 snprintf(filename
, sizeof(filename
), "%s/job.cache", CacheDir
);
867 if (stat(filename
, &fileinfo
))
869 fileinfo
.st_mtime
= 0;
872 cupsdLogMessage(CUPSD_LOG_ERROR
,
873 "Unable to get file information for \"%s\" - %s",
874 filename
, strerror(errno
));
877 if (stat(RequestRoot
, &dirinfo
))
879 dirinfo
.st_mtime
= 0;
882 cupsdLogMessage(CUPSD_LOG_ERROR
,
883 "Unable to get directory information for \"%s\" - %s",
884 RequestRoot
, strerror(errno
));
888 * Load the most recent source for job data...
891 if (dirinfo
.st_mtime
> fileinfo
.st_mtime
)
895 load_next_job_id(filename
);
898 load_job_cache(filename
);
901 * Clean out old jobs as needed...
904 if (MaxJobs
> 0 && cupsArrayCount(Jobs
) >= MaxJobs
)
910 * 'cupsdLoadJob()' - Load a single job...
914 cupsdLoadJob(cupsd_job_t
*job
) /* I - Job */
916 char jobfile
[1024]; /* Job filename */
917 cups_file_t
*fp
; /* Job file */
918 int fileid
; /* Current file ID */
919 ipp_attribute_t
*attr
; /* Job attribute */
920 const char *dest
; /* Destination */
921 mime_type_t
**filetypes
; /* New filetypes array */
922 int *compressions
; /* New compressions array */
927 if (job
->state_value
> IPP_JOB_STOPPED
)
928 job
->access_time
= time(NULL
);
933 if ((job
->attrs
= ippNew()) == NULL
)
935 cupsdLogMessage(CUPSD_LOG_ERROR
, "Ran out of memory for job attributes!");
940 * Load job attributes...
943 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Loading attributes for job %d...",
946 snprintf(jobfile
, sizeof(jobfile
), "%s/c%05d", RequestRoot
, job
->id
);
947 if ((fp
= cupsFileOpen(jobfile
, "r")) == NULL
)
949 cupsdLogMessage(CUPSD_LOG_ERROR
,
950 "Unable to open job control file \"%s\" - %s!",
951 jobfile
, strerror(errno
));
952 ippDelete(job
->attrs
);
957 if (ippReadIO(fp
, (ipp_iocb_t
)cupsFileRead
, 1, NULL
, job
->attrs
) != IPP_DATA
)
959 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to read job control file \"%s\"!",
962 ippDelete(job
->attrs
);
971 * Copy attribute data to the job object...
974 if ((job
->state
= ippFindAttribute(job
->attrs
, "job-state",
975 IPP_TAG_ENUM
)) == NULL
)
977 cupsdLogMessage(CUPSD_LOG_ERROR
,
978 "Missing or bad job-state attribute in control "
981 ippDelete(job
->attrs
);
987 job
->state_value
= (ipp_jstate_t
)job
->state
->values
[0].integer
;
991 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri",
992 IPP_TAG_URI
)) == NULL
)
994 cupsdLogMessage(CUPSD_LOG_ERROR
,
995 "No job-printer-uri attribute in control file \"%s\"!",
997 ippDelete(job
->attrs
);
1003 if ((dest
= cupsdValidateDest(attr
->values
[0].string
.text
, &(job
->dtype
),
1006 cupsdLogMessage(CUPSD_LOG_ERROR
,
1007 "Unable to queue job for destination \"%s\"!",
1008 attr
->values
[0].string
.text
);
1009 ippDelete(job
->attrs
);
1015 cupsdSetString(&job
->dest
, dest
);
1018 job
->sheets
= ippFindAttribute(job
->attrs
, "job-media-sheets-completed",
1020 job
->job_sheets
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
1024 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority",
1025 IPP_TAG_INTEGER
)) == NULL
)
1027 cupsdLogMessage(CUPSD_LOG_ERROR
,
1028 "Missing or bad job-priority attribute in control "
1029 "file \"%s\"!", jobfile
);
1030 ippDelete(job
->attrs
);
1036 job
->priority
= attr
->values
[0].integer
;
1041 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-user-name",
1042 IPP_TAG_NAME
)) == NULL
)
1044 cupsdLogMessage(CUPSD_LOG_ERROR
,
1045 "Missing or bad job-originating-user-name attribute "
1046 "in control file \"%s\"!", jobfile
);
1047 ippDelete(job
->attrs
);
1053 cupsdSetString(&job
->username
, attr
->values
[0].string
.text
);
1057 * Set the job hold-until time and state...
1060 if (job
->state_value
== IPP_JOB_HELD
)
1062 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
1063 IPP_TAG_KEYWORD
)) == NULL
)
1064 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
1067 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
);
1070 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1071 job
->state_value
= IPP_JOB_PENDING
;
1074 else if (job
->state_value
== IPP_JOB_PROCESSING
)
1076 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1077 job
->state_value
= IPP_JOB_PENDING
;
1080 if (!job
->num_files
)
1083 * Find all the d##### files...
1086 for (fileid
= 1; fileid
< 10000; fileid
++)
1088 snprintf(jobfile
, sizeof(jobfile
), "%s/d%05d-%03d", RequestRoot
,
1091 if (access(jobfile
, 0))
1094 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Auto-typing document file \"%s\"...",
1097 if (fileid
> job
->num_files
)
1099 if (job
->num_files
== 0)
1101 compressions
= (int *)calloc(fileid
, sizeof(int));
1102 filetypes
= (mime_type_t
**)calloc(fileid
, sizeof(mime_type_t
*));
1106 compressions
= (int *)realloc(job
->compressions
,
1107 sizeof(int) * fileid
);
1108 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
1109 sizeof(mime_type_t
*) *
1113 if (!compressions
|| !filetypes
)
1115 cupsdLogMessage(CUPSD_LOG_ERROR
,
1116 "Ran out of memory for job file types!");
1120 job
->compressions
= compressions
;
1121 job
->filetypes
= filetypes
;
1122 job
->num_files
= fileid
;
1125 job
->filetypes
[fileid
- 1] = mimeFileType(MimeDatabase
, jobfile
, NULL
,
1126 job
->compressions
+ fileid
- 1);
1128 if (!job
->filetypes
[fileid
- 1])
1129 job
->filetypes
[fileid
- 1] = mimeType(MimeDatabase
, "application",
1134 job
->access_time
= time(NULL
);
1139 * 'cupsdMoveJob()' - Move the specified job to a different destination.
1143 cupsdMoveJob(cupsd_job_t
*job
, /* I - Job */
1144 cupsd_printer_t
*p
) /* I - Destination printer or class */
1146 ipp_attribute_t
*attr
; /* job-printer-uri attribute */
1147 const char *olddest
; /* Old destination */
1148 cupsd_printer_t
*oldp
; /* Old pointer */
1152 * Don't move completed jobs...
1155 if (job
->state_value
> IPP_JOB_STOPPED
)
1159 * Get the old destination...
1162 olddest
= job
->dest
;
1165 oldp
= job
->printer
;
1167 oldp
= cupsdFindDest(olddest
);
1170 * Change the destination information...
1175 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, oldp
, job
,
1176 "Job #%d moved from %s to %s.", job
->id
, olddest
,
1179 cupsdSetString(&job
->dest
, p
->name
);
1180 job
->dtype
= p
->type
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
1181 CUPS_PRINTER_IMPLICIT
);
1183 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri",
1184 IPP_TAG_URI
)) != NULL
)
1185 cupsdSetString(&(attr
->values
[0].string
.text
), p
->uri
);
1187 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, p
, job
,
1188 "Job #%d moved from %s to %s.", job
->id
, olddest
,
1196 * 'cupsdReleaseJob()' - Release the specified job.
1200 cupsdReleaseJob(cupsd_job_t
*job
) /* I - Job */
1202 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdReleaseJob: id = %d", job
->id
);
1204 if (job
->state_value
== IPP_JOB_HELD
)
1206 DEBUG_puts("cupsdReleaseJob: setting state to pending...");
1208 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1209 job
->state_value
= IPP_JOB_PENDING
;
1217 * 'cupsdRestartJob()' - Restart the specified job.
1221 cupsdRestartJob(cupsd_job_t
*job
) /* I - Job */
1223 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdRestartJob: id = %d", job
->id
);
1225 if (job
->state_value
== IPP_JOB_STOPPED
|| job
->num_files
)
1227 ipp_jstate_t old_state
; /* Old job state */
1232 old_state
= job
->state_value
;
1235 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1236 job
->state_value
= IPP_JOB_PENDING
;
1240 if (old_state
> IPP_JOB_STOPPED
)
1241 cupsArrayAdd(ActiveJobs
, job
);
1249 * 'cupsdSaveAllJobs()' - Save a summary of all jobs to disk.
1253 cupsdSaveAllJobs(void)
1255 int i
; /* Looping var */
1256 cups_file_t
*fp
; /* Job cache file */
1257 char temp
[1024]; /* Temporary string */
1258 cupsd_job_t
*job
; /* Current job */
1259 time_t curtime
; /* Current time */
1260 struct tm
*curdate
; /* Current date */
1263 snprintf(temp
, sizeof(temp
), "%s/job.cache", CacheDir
);
1264 if ((fp
= cupsFileOpen(temp
, "w")) == NULL
)
1266 cupsdLogMessage(CUPSD_LOG_ERROR
,
1267 "Unable to create job cache file \"%s\" - %s",
1268 temp
, strerror(errno
));
1272 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving job cache file \"%s\"...", temp
);
1275 * Restrict access to the file...
1278 fchown(cupsFileNumber(fp
), getuid(), Group
);
1279 fchmod(cupsFileNumber(fp
), ConfigFilePerm
);
1282 * Write a small header to the file...
1285 curtime
= time(NULL
);
1286 curdate
= localtime(&curtime
);
1287 strftime(temp
, sizeof(temp
) - 1, "%Y-%m-%d %H:%M", curdate
);
1289 cupsFilePuts(fp
, "# Job cache file for " CUPS_SVERSION
"\n");
1290 cupsFilePrintf(fp
, "# Written by cupsd on %s\n", temp
);
1291 cupsFilePrintf(fp
, "NextJobId %d\n", NextJobId
);
1294 * Write each job known to the system...
1297 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
1299 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
1301 cupsFilePrintf(fp
, "<Job %d>\n", job
->id
);
1302 cupsFilePrintf(fp
, "State %d\n", job
->state_value
);
1303 cupsFilePrintf(fp
, "Priority %d\n", job
->priority
);
1304 cupsFilePrintf(fp
, "Username %s\n", job
->username
);
1305 cupsFilePrintf(fp
, "Destination %s\n", job
->dest
);
1306 cupsFilePrintf(fp
, "DestType %d\n", job
->dtype
);
1307 cupsFilePrintf(fp
, "NumFiles %d\n", job
->num_files
);
1308 for (i
= 0; i
< job
->num_files
; i
++)
1309 cupsFilePrintf(fp
, "File %d %s/%s %d\n", i
+ 1, job
->filetypes
[i
]->super
,
1310 job
->filetypes
[i
]->type
, job
->compressions
[i
]);
1311 cupsFilePuts(fp
, "</Job>\n");
1319 * 'cupsdSaveJob()' - Save a job to disk.
1323 cupsdSaveJob(cupsd_job_t
*job
) /* I - Job */
1325 char filename
[1024]; /* Job control filename */
1326 cups_file_t
*fp
; /* Job file */
1329 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSaveJob(job=%p(%d)): job->attrs=%p",
1330 job
, job
->id
, job
->attrs
);
1332 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
, job
->id
);
1334 if ((fp
= cupsFileOpen(filename
, "w")) == NULL
)
1336 cupsdLogMessage(CUPSD_LOG_ERROR
,
1337 "Unable to create job control file \"%s\" - %s.",
1338 filename
, strerror(errno
));
1342 fchmod(cupsFileNumber(fp
), 0600);
1343 fchown(cupsFileNumber(fp
), RunUser
, Group
);
1345 job
->attrs
->state
= IPP_IDLE
;
1347 if (ippWriteIO(fp
, (ipp_iocb_t
)cupsFileWrite
, 1, NULL
,
1348 job
->attrs
) != IPP_DATA
)
1349 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to write job control file \"%s\"!",
1357 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job...
1361 cupsdSetJobHoldUntil(cupsd_job_t
*job
, /* I - Job */
1362 const char *when
) /* I - When to resume */
1364 time_t curtime
; /* Current time */
1365 struct tm
*curdate
; /* Current date */
1366 int hour
; /* Hold hour */
1367 int minute
; /* Hold minute */
1368 int second
; /* Hold second */
1371 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSetJobHoldUntil(%d, \"%s\")",
1376 if (!strcmp(when
, "indefinite") || !strcmp(when
, "authenticated"))
1379 * Hold indefinitely...
1382 job
->hold_until
= 0;
1384 else if (!strcmp(when
, "day-time"))
1387 * Hold to 6am the next morning unless local time is < 6pm.
1390 curtime
= time(NULL
);
1391 curdate
= localtime(&curtime
);
1393 if (curdate
->tm_hour
< 18)
1394 job
->hold_until
= curtime
;
1396 job
->hold_until
= curtime
+
1397 ((29 - curdate
->tm_hour
) * 60 + 59 -
1398 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1400 else if (!strcmp(when
, "evening") || !strcmp(when
, "night"))
1403 * Hold to 6pm unless local time is > 6pm or < 6am.
1406 curtime
= time(NULL
);
1407 curdate
= localtime(&curtime
);
1409 if (curdate
->tm_hour
< 6 || curdate
->tm_hour
>= 18)
1410 job
->hold_until
= curtime
;
1412 job
->hold_until
= curtime
+
1413 ((17 - curdate
->tm_hour
) * 60 + 59 -
1414 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1416 else if (!strcmp(when
, "second-shift"))
1419 * Hold to 4pm unless local time is > 4pm.
1422 curtime
= time(NULL
);
1423 curdate
= localtime(&curtime
);
1425 if (curdate
->tm_hour
>= 16)
1426 job
->hold_until
= curtime
;
1428 job
->hold_until
= curtime
+
1429 ((15 - curdate
->tm_hour
) * 60 + 59 -
1430 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1432 else if (!strcmp(when
, "third-shift"))
1435 * Hold to 12am unless local time is < 8am.
1438 curtime
= time(NULL
);
1439 curdate
= localtime(&curtime
);
1441 if (curdate
->tm_hour
< 8)
1442 job
->hold_until
= curtime
;
1444 job
->hold_until
= curtime
+
1445 ((23 - curdate
->tm_hour
) * 60 + 59 -
1446 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1448 else if (!strcmp(when
, "weekend"))
1451 * Hold to weekend unless we are in the weekend.
1454 curtime
= time(NULL
);
1455 curdate
= localtime(&curtime
);
1457 if (curdate
->tm_wday
|| curdate
->tm_wday
== 6)
1458 job
->hold_until
= curtime
;
1460 job
->hold_until
= curtime
+
1461 (((5 - curdate
->tm_wday
) * 24 +
1462 (17 - curdate
->tm_hour
)) * 60 + 59 -
1463 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1465 else if (sscanf(when
, "%d:%d:%d", &hour
, &minute
, &second
) >= 2)
1468 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1471 curtime
= time(NULL
);
1472 curdate
= gmtime(&curtime
);
1474 job
->hold_until
= curtime
+
1475 ((hour
- curdate
->tm_hour
) * 60 + minute
-
1476 curdate
->tm_min
) * 60 + second
- curdate
->tm_sec
;
1479 * Hold until next day as needed...
1482 if (job
->hold_until
< curtime
)
1483 job
->hold_until
+= 24 * 60 * 60;
1486 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSetJobHoldUntil: hold_until = %d",
1487 (int)job
->hold_until
);
1492 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
1493 * the list as needed.
1497 cupsdSetJobPriority(
1498 cupsd_job_t
*job
, /* I - Job ID */
1499 int priority
) /* I - New priority (0 to 100) */
1501 ipp_attribute_t
*attr
; /* Job attribute */
1505 * Don't change completed jobs...
1508 if (job
->state_value
>= IPP_JOB_PROCESSING
)
1512 * Set the new priority and re-add the job into the active list...
1515 cupsArrayRemove(ActiveJobs
, job
);
1517 job
->priority
= priority
;
1519 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority",
1520 IPP_TAG_INTEGER
)) != NULL
)
1521 attr
->values
[0].integer
= priority
;
1523 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1526 cupsArrayAdd(ActiveJobs
, job
);
1533 * 'cupsdStopAllJobs()' - Stop all print jobs.
1537 cupsdStopAllJobs(int force
) /* I - 1 = Force all filters to stop */
1539 cupsd_job_t
*job
; /* Current job */
1542 DEBUG_puts("cupsdStopAllJobs()");
1544 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
1546 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
1547 if (job
->state_value
== IPP_JOB_PROCESSING
)
1549 cupsdStopJob(job
, force
);
1550 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1551 job
->state_value
= IPP_JOB_PENDING
;
1557 * 'cupsdStopJob()' - Stop a print job.
1561 cupsdStopJob(cupsd_job_t
*job
, /* I - Job */
1562 int force
) /* I - 1 = Force all filters to stop */
1564 int i
; /* Looping var */
1567 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdStopJob: id = %d, force = %d",
1570 if (job
->state_value
!= IPP_JOB_PROCESSING
)
1573 FilterLevel
-= job
->cost
;
1575 if (job
->printer
->state
== IPP_PRINTER_PROCESSING
)
1576 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_IDLE
, 0);
1578 job
->state
->values
[0].integer
= IPP_JOB_STOPPED
;
1579 job
->state_value
= IPP_JOB_STOPPED
;
1580 job
->printer
->job
= NULL
;
1581 job
->printer
= NULL
;
1583 job
->current_file
--;
1585 for (i
= 0; job
->filters
[i
]; i
++)
1586 if (job
->filters
[i
] > 0)
1588 cupsdEndProcess(job
->filters
[i
], force
);
1589 job
->filters
[i
] = 0;
1592 if (job
->backend
> 0)
1594 cupsdEndProcess(job
->backend
, force
);
1598 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1599 "cupsdStopJob: Closing print pipes [ %d %d ]...",
1600 job
->print_pipes
[0], job
->print_pipes
[1]);
1602 cupsdClosePipe(job
->print_pipes
);
1604 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1605 "cupsdStopJob: Closing back pipes [ %d %d ]...",
1606 job
->back_pipes
[0], job
->back_pipes
[1]);
1608 cupsdClosePipe(job
->back_pipes
);
1610 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1611 "cupsdStopJob: Closing side pipes [ %d %d ]...",
1612 job
->side_pipes
[0], job
->side_pipes
[1]);
1614 cupsdClosePipe(job
->side_pipes
);
1616 if (job
->status_buffer
)
1619 * Close the pipe and clear the input bit.
1622 cupsdRemoveSelect(job
->status_buffer
->fd
);
1624 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1625 "cupsdStopJob: Closing status pipes [ %d %d ]...",
1626 job
->status_pipes
[0], job
->status_pipes
[1]);
1628 cupsdClosePipe(job
->status_pipes
);
1629 cupsdStatBufDelete(job
->status_buffer
);
1631 job
->status_buffer
= NULL
;
1637 * 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory.
1641 cupsdUnloadCompletedJobs(void)
1643 cupsd_job_t
*job
; /* Current job */
1644 time_t expire
; /* Expiration time */
1647 expire
= time(NULL
) - 60;
1649 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
1651 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
1652 if (job
->attrs
&& job
->state_value
>= IPP_JOB_STOPPED
&&
1653 job
->access_time
< expire
)
1659 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
1662 static int /* O - Difference */
1663 compare_active_jobs(void *first
, /* I - First job */
1664 void *second
, /* I - Second job */
1665 void *data
) /* I - App data (not used) */
1667 int diff
; /* Difference */
1670 if ((diff
= ((cupsd_job_t
*)second
)->priority
-
1671 ((cupsd_job_t
*)first
)->priority
) != 0)
1674 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
1679 * 'compare_jobs()' - Compare the job IDs of two jobs.
1682 static int /* O - Difference */
1683 compare_jobs(void *first
, /* I - First job */
1684 void *second
, /* I - Second job */
1685 void *data
) /* I - App data (not used) */
1687 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
1692 * 'free_job()' - Free all memory used by a job.
1696 free_job(cupsd_job_t
*job
) /* I - Job */
1698 cupsdClearString(&job
->username
);
1699 cupsdClearString(&job
->dest
);
1701 cupsdClearString(&job
->ccname
);
1702 #endif /* HAVE_GSSAPI */
1704 if (job
->num_files
> 0)
1706 free(job
->compressions
);
1707 free(job
->filetypes
);
1710 ippDelete(job
->attrs
);
1717 * 'ipp_length()' - Compute the size of the buffer needed to hold
1718 * the textual IPP attributes.
1721 static int /* O - Size of attribute buffer */
1722 ipp_length(ipp_t
*ipp
) /* I - IPP request */
1724 int bytes
; /* Number of bytes */
1725 int i
; /* Looping var */
1726 ipp_attribute_t
*attr
; /* Current attribute */
1730 * Loop through all attributes...
1735 for (attr
= ipp
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1738 * Skip attributes that won't be sent to filters...
1741 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
1742 attr
->value_tag
== IPP_TAG_NAMELANG
||
1743 attr
->value_tag
== IPP_TAG_TEXTLANG
||
1744 attr
->value_tag
== IPP_TAG_URI
||
1745 attr
->value_tag
== IPP_TAG_URISCHEME
)
1748 if (strncmp(attr
->name
, "time-", 5) == 0)
1752 * Add space for a leading space and commas between each value.
1753 * For the first attribute, the leading space isn't used, so the
1754 * extra byte can be used as the nul terminator...
1757 bytes
++; /* " " separator */
1758 bytes
+= attr
->num_values
; /* "," separators */
1761 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
1762 * other attributes appear as "foo=value1,value2,...,valueN".
1765 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
1766 bytes
+= strlen(attr
->name
);
1768 bytes
+= attr
->num_values
* strlen(attr
->name
);
1771 * Now add the size required for each value in the attribute...
1774 switch (attr
->value_tag
)
1776 case IPP_TAG_INTEGER
:
1779 * Minimum value of a signed integer is -2147483647, or 11 digits.
1782 bytes
+= attr
->num_values
* 11;
1785 case IPP_TAG_BOOLEAN
:
1787 * Add two bytes for each false ("no") value...
1790 for (i
= 0; i
< attr
->num_values
; i
++)
1791 if (!attr
->values
[i
].boolean
)
1795 case IPP_TAG_RANGE
:
1797 * A range is two signed integers separated by a hyphen, or
1798 * 23 characters max.
1801 bytes
+= attr
->num_values
* 23;
1804 case IPP_TAG_RESOLUTION
:
1806 * A resolution is two signed integers separated by an "x" and
1807 * suffixed by the units, or 26 characters max.
1810 bytes
+= attr
->num_values
* 26;
1813 case IPP_TAG_STRING
:
1816 case IPP_TAG_KEYWORD
:
1817 case IPP_TAG_CHARSET
:
1818 case IPP_TAG_LANGUAGE
:
1821 * Strings can contain characters that need quoting. We need
1822 * at least 2 * len + 2 characters to cover the quotes and
1823 * any backslashes in the string.
1826 for (i
= 0; i
< attr
->num_values
; i
++)
1827 bytes
+= 2 * strlen(attr
->values
[i
].string
.text
) + 2;
1831 break; /* anti-compiler-warning-code */
1840 * 'load_job_cache()' - Load jobs from the job.cache file.
1844 load_job_cache(const char *filename
) /* I - job.cache filename */
1846 cups_file_t
*fp
; /* job.cache file */
1847 char line
[1024], /* Line buffer */
1848 *value
; /* Value on line */
1849 int linenum
; /* Line number in file */
1850 cupsd_job_t
*job
; /* Current job */
1851 int jobid
; /* Job ID */
1852 char jobfile
[1024]; /* Job filename */
1856 * Open the job.cache file...
1859 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
1861 if (errno
!= ENOENT
)
1862 cupsdLogMessage(CUPSD_LOG_ERROR
,
1863 "Unable to open job cache file \"%s\": %s",
1864 filename
, strerror(errno
));
1866 load_request_root();
1872 * Read entries from the job cache file and create jobs as needed.
1875 cupsdLogMessage(CUPSD_LOG_INFO
, "Loading job cache file \"%s\"...",
1881 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
1883 if (!strcasecmp(line
, "NextJobId"))
1886 NextJobId
= atoi(value
);
1888 else if (!strcasecmp(line
, "<Job"))
1892 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing </Job> directive on line %d!",
1899 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing job ID on line %d!", linenum
);
1903 jobid
= atoi(value
);
1907 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad job ID %d on line %d!", jobid
,
1912 snprintf(jobfile
, sizeof(jobfile
), "%s/c%05d", RequestRoot
, jobid
);
1913 if (access(jobfile
, 0))
1915 cupsdLogMessage(CUPSD_LOG_ERROR
, "Job %d files have gone away!", jobid
);
1919 job
= calloc(1, sizeof(cupsd_job_t
));
1922 cupsdLogMessage(CUPSD_LOG_EMERG
,
1923 "Unable to allocate memory for job %d!", jobid
);
1928 job
->back_pipes
[0] = -1;
1929 job
->back_pipes
[1] = -1;
1930 job
->print_pipes
[0] = -1;
1931 job
->print_pipes
[1] = -1;
1932 job
->side_pipes
[0] = -1;
1933 job
->side_pipes
[1] = -1;
1934 job
->status_pipes
[0] = -1;
1935 job
->status_pipes
[1] = -1;
1937 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Loading job %d from cache...", job
->id
);
1941 cupsdLogMessage(CUPSD_LOG_ERROR
,
1942 "Missing <Job #> directive on line %d!", linenum
);
1945 else if (!strcasecmp(line
, "</Job>"))
1947 cupsArrayAdd(Jobs
, job
);
1949 if (job
->state_value
<= IPP_JOB_STOPPED
)
1951 cupsArrayAdd(ActiveJobs
, job
);
1959 cupsdLogMessage(CUPSD_LOG_ERROR
, "Missing value on line %d!", linenum
);
1962 else if (!strcasecmp(line
, "State"))
1964 job
->state_value
= (ipp_jstate_t
)atoi(value
);
1966 if (job
->state_value
< IPP_JOB_PENDING
)
1967 job
->state_value
= IPP_JOB_PENDING
;
1968 else if (job
->state_value
> IPP_JOB_COMPLETED
)
1969 job
->state_value
= IPP_JOB_COMPLETED
;
1971 else if (!strcasecmp(line
, "Priority"))
1973 job
->priority
= atoi(value
);
1975 else if (!strcasecmp(line
, "Username"))
1977 cupsdSetString(&job
->username
, value
);
1979 else if (!strcasecmp(line
, "Destination"))
1981 cupsdSetString(&job
->dest
, value
);
1983 else if (!strcasecmp(line
, "DestType"))
1985 job
->dtype
= (cups_ptype_t
)atoi(value
);
1987 else if (!strcasecmp(line
, "NumFiles"))
1989 job
->num_files
= atoi(value
);
1991 if (job
->num_files
< 0)
1993 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad NumFiles value %d on line %d!",
1994 job
->num_files
, linenum
);
1999 if (job
->num_files
> 0)
2001 snprintf(jobfile
, sizeof(jobfile
), "%s/d%05d-001", RequestRoot
,
2003 if (access(jobfile
, 0))
2005 cupsdLogMessage(CUPSD_LOG_INFO
,
2006 "Data files for job %d have gone away!", job
->id
);
2011 job
->filetypes
= calloc(job
->num_files
, sizeof(mime_type_t
*));
2012 job
->compressions
= calloc(job
->num_files
, sizeof(int));
2014 if (!job
->filetypes
|| !job
->compressions
)
2016 cupsdLogMessage(CUPSD_LOG_EMERG
,
2017 "Unable to allocate memory for %d files!",
2023 else if (!strcasecmp(line
, "File"))
2025 int number
, /* File number */
2026 compression
; /* Compression value */
2027 char super
[MIME_MAX_SUPER
], /* MIME super type */
2028 type
[MIME_MAX_TYPE
]; /* MIME type */
2031 if (sscanf(value
, "%d%*[ \t]%15[^/]/%255s%d", &number
, super
, type
,
2034 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad File on line %d!", linenum
);
2038 if (number
< 1 || number
> job
->num_files
)
2040 cupsdLogMessage(CUPSD_LOG_ERROR
, "Bad File number %d on line %d!",
2047 job
->compressions
[number
] = compression
;
2048 job
->filetypes
[number
] = mimeType(MimeDatabase
, super
, type
);
2050 if (!job
->filetypes
[number
])
2053 * If the original MIME type is unknown, auto-type it!
2056 cupsdLogMessage(CUPSD_LOG_ERROR
,
2057 "Unknown MIME type %s/%s for file %d of job %d!",
2058 super
, type
, number
+ 1, job
->id
);
2060 snprintf(jobfile
, sizeof(jobfile
), "%s/d%05d-%03d", RequestRoot
,
2061 job
->id
, number
+ 1);
2062 job
->filetypes
[number
] = mimeFileType(MimeDatabase
, jobfile
, NULL
,
2063 job
->compressions
+ number
);
2066 * If that didn't work, assume it is raw...
2069 if (!job
->filetypes
[number
])
2070 job
->filetypes
[number
] = mimeType(MimeDatabase
, "application",
2075 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unknown %s directive on line %d!",
2084 * 'load_next_job_id()' - Load the NextJobId value from the job.cache file.
2088 load_next_job_id(const char *filename
) /* I - job.cache filename */
2090 cups_file_t
*fp
; /* job.cache file */
2091 char line
[1024], /* Line buffer */
2092 *value
; /* Value on line */
2093 int linenum
; /* Line number in file */
2094 int next_job_id
; /* NextJobId value from line */
2098 * Read the NextJobId directive from the job.cache file and use
2099 * the value (if any).
2102 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
2104 if (errno
!= ENOENT
)
2105 cupsdLogMessage(CUPSD_LOG_ERROR
,
2106 "Unable to open job cache file \"%s\": %s",
2107 filename
, strerror(errno
));
2112 cupsdLogMessage(CUPSD_LOG_INFO
,
2113 "Loading NextJobId from job cache file \"%s\"...", filename
);
2117 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
2119 if (!strcasecmp(line
, "NextJobId"))
2123 next_job_id
= atoi(value
);
2125 if (next_job_id
> NextJobId
)
2126 NextJobId
= next_job_id
;
2137 * 'load_request_root()' - Load jobs from the RequestRoot directory.
2141 load_request_root(void)
2143 cups_dir_t
*dir
; /* Directory */
2144 cups_dentry_t
*dent
; /* Directory entry */
2145 cupsd_job_t
*job
; /* New job */
2149 * Open the requests directory...
2152 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Scanning %s for jobs...", RequestRoot
);
2154 if ((dir
= cupsDirOpen(RequestRoot
)) == NULL
)
2156 cupsdLogMessage(CUPSD_LOG_ERROR
,
2157 "Unable to open spool directory \"%s\": %s",
2158 RequestRoot
, strerror(errno
));
2163 * Read all the c##### files...
2166 while ((dent
= cupsDirRead(dir
)) != NULL
)
2167 if (strlen(dent
->filename
) >= 6 && dent
->filename
[0] == 'c')
2170 * Allocate memory for the job...
2173 if ((job
= calloc(sizeof(cupsd_job_t
), 1)) == NULL
)
2175 cupsdLogMessage(CUPSD_LOG_ERROR
, "Ran out of memory for jobs!");
2181 * Assign the job ID...
2184 job
->id
= atoi(dent
->filename
+ 1);
2185 job
->back_pipes
[0] = -1;
2186 job
->back_pipes
[1] = -1;
2187 job
->print_pipes
[0] = -1;
2188 job
->print_pipes
[1] = -1;
2189 job
->side_pipes
[0] = -1;
2190 job
->side_pipes
[1] = -1;
2191 job
->status_pipes
[0] = -1;
2192 job
->status_pipes
[1] = -1;
2194 if (job
->id
>= NextJobId
)
2195 NextJobId
= job
->id
+ 1;
2204 * Insert the job into the array, sorting by job priority and ID...
2207 cupsArrayAdd(Jobs
, job
);
2209 if (job
->state_value
<= IPP_JOB_STOPPED
)
2210 cupsArrayAdd(ActiveJobs
, job
);
2220 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2224 set_time(cupsd_job_t
*job
, /* I - Job to update */
2225 const char *name
) /* I - Name of attribute */
2227 ipp_attribute_t
*attr
; /* Time attribute */
2230 if ((attr
= ippFindAttribute(job
->attrs
, name
, IPP_TAG_ZERO
)) != NULL
)
2232 attr
->value_tag
= IPP_TAG_INTEGER
;
2233 attr
->values
[0].integer
= time(NULL
);
2239 * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
2243 set_hold_until(cupsd_job_t
*job
, /* I - Job to update */
2244 time_t holdtime
) /* I - Hold until time */
2246 ipp_attribute_t
*attr
; /* job-hold-until attribute */
2247 struct tm
*holddate
; /* Hold date */
2248 char holdstr
[64]; /* Hold time */
2252 * Set the hold_until value and hold the job...
2255 cupsdLogMessage(CUPSD_LOG_DEBUG
, "set_hold_until: hold_until = %d",
2258 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
2259 job
->state_value
= IPP_JOB_HELD
;
2260 job
->hold_until
= holdtime
;
2263 * Update the job-hold-until attribute with a string representing GMT
2264 * time (HH:MM:SS)...
2267 holddate
= gmtime(&holdtime
);
2268 snprintf(holdstr
, sizeof(holdstr
), "%d:%d:%d", holddate
->tm_hour
,
2269 holddate
->tm_min
, holddate
->tm_sec
);
2271 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
2272 IPP_TAG_KEYWORD
)) == NULL
)
2273 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
2276 * Either add the attribute or update the value of the existing one
2280 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
2281 "job-hold-until", NULL
, holdstr
);
2283 cupsdSetString(&attr
->values
[0].string
.text
, holdstr
);
2290 * 'start_job()' - Start a print job.
2294 start_job(cupsd_job_t
*job
, /* I - Job ID */
2295 cupsd_printer_t
*printer
) /* I - Printer to print job */
2297 int i
; /* Looping var */
2298 int slot
; /* Pipe slot */
2299 cups_array_t
*filters
, /* Filters for job */
2300 *prefilters
; /* Filters with prefilters */
2301 mime_filter_t
*filter
, /* Current filter */
2302 *prefilter
, /* Prefilter */
2303 port_monitor
; /* Port monitor filter */
2304 char method
[255], /* Method for output */
2305 *optptr
, /* Pointer to options */
2306 *valptr
; /* Pointer in value string */
2307 ipp_attribute_t
*attr
; /* Current attribute */
2308 struct stat backinfo
; /* Backend file information */
2309 int backroot
; /* Run backend as root? */
2310 int pid
; /* Process ID of new filter process */
2311 int banner_page
; /* 1 if banner page, 0 otherwise */
2312 int filterfds
[2][2];/* Pipes used between filters */
2313 int envc
; /* Number of environment variables */
2314 char **argv
, /* Filter command-line arguments */
2315 sani_uri
[1024], /* Sanitized DEVICE_URI env var */
2316 filename
[1024], /* Job filename */
2317 command
[1024], /* Full path to command */
2318 jobid
[255], /* Job ID string */
2319 title
[IPP_MAX_NAME
],
2320 /* Job title string */
2321 copies
[255], /* # copies string */
2322 *envp
[MAX_ENV
+ 12],
2323 /* Environment variables */
2324 charset
[255], /* CHARSET env variable */
2325 class_name
[255],/* CLASS env variable */
2326 classification
[1024],
2327 /* CLASSIFICATION env variable */
2329 /* CONTENT_TYPE env variable */
2331 /* DEVICE_URI env variable */
2332 final_content_type
[1024],
2333 /* FINAL_CONTENT_TYPE env variable */
2334 lang
[255], /* LANG env variable */
2335 ppd
[1024], /* PPD env variable */
2337 /* PRINTER env variable */
2339 /* RIP_MAX_CACHE env variable */
2340 static char *options
= NULL
;/* Full list of options */
2341 static int optlength
= 0; /* Length of option buffer */
2344 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: id = %d, file = %d/%d",
2345 job
->id
, job
->current_file
, job
->num_files
);
2347 if (job
->num_files
== 0)
2349 cupsdLogMessage(CUPSD_LOG_ERROR
, "Job ID %d has no files! Canceling it!",
2352 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
2357 * Figure out what filters are required to convert from
2358 * the source to the destination type...
2367 * Remote jobs and raw queues go directly to the printer without
2371 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2372 "[Job %d] Sending job to queue tagged as raw...", job
->id
);
2379 * Local jobs get filtered...
2382 filters
= mimeFilter(MimeDatabase
, job
->filetypes
[job
->current_file
],
2383 printer
->filetype
, &(job
->cost
));
2387 cupsdLogMessage(CUPSD_LOG_ERROR
,
2388 "Unable to convert file %d to printable format for "
2390 job
->current_file
, job
->id
);
2391 cupsdLogMessage(CUPSD_LOG_INFO
,
2392 "Hint: Do you have ESP Ghostscript installed?");
2394 if (LogLevel
< CUPSD_LOG_DEBUG
)
2395 cupsdLogMessage(CUPSD_LOG_INFO
,
2396 "Hint: Try setting the LogLevel to \"debug\".");
2398 job
->current_file
++;
2400 if (job
->current_file
== job
->num_files
)
2401 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
2407 * Remove NULL ("-") filters...
2410 for (filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
2412 filter
= (mime_filter_t
*)cupsArrayNext(filters
))
2413 if (!strcmp(filter
->filter
, "-"))
2414 cupsArrayRemove(filters
, filter
);
2416 if (cupsArrayCount(filters
) == 0)
2418 cupsArrayDelete(filters
);
2423 * If this printer has any pre-filters, insert the required pre-filter
2424 * in the filters array...
2427 if (printer
->prefiltertype
&& filters
)
2429 prefilters
= cupsArrayNew(NULL
, NULL
);
2431 for (filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
2433 filter
= (mime_filter_t
*)cupsArrayNext(filters
))
2435 if ((prefilter
= mimeFilterLookup(MimeDatabase
, filter
->src
,
2436 printer
->prefiltertype
)))
2438 cupsArrayAdd(prefilters
, prefilter
);
2439 job
->cost
+= prefilter
->cost
;
2442 cupsArrayAdd(prefilters
, filter
);
2445 cupsArrayDelete(filters
);
2446 filters
= prefilters
;
2451 * Set a minimum cost of 100 for all jobs so that FilterLimit
2452 * works with raw queues and other low-cost paths.
2455 if (job
->cost
< 100)
2459 * See if the filter cost is too high...
2462 if ((FilterLevel
+ job
->cost
) > FilterLimit
&& FilterLevel
> 0 &&
2466 * Don't print this job quite yet...
2469 cupsArrayDelete(filters
);
2471 cupsdLogMessage(CUPSD_LOG_INFO
,
2472 "Holding job %d because filter limit has been reached.",
2474 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2475 "start_job: id=%d, file=%d, cost=%d, level=%d, "
2477 job
->id
, job
->current_file
, job
->cost
, FilterLevel
,
2482 FilterLevel
+= job
->cost
;
2485 * Add decompression/raw filter as needed...
2488 if ((!printer
->raw
&& job
->compressions
[job
->current_file
]) ||
2489 (!filters
&& !printer
->remote
&&
2490 (job
->num_files
> 1 || !strncmp(printer
->device_uri
, "file:", 5))))
2493 * Add gziptoany filter to the front of the list...
2497 filters
= cupsArrayNew(NULL
, NULL
);
2499 if (!cupsArrayInsert(filters
, &gziptoany_filter
))
2501 cupsdLogMessage(CUPSD_LOG_ERROR
,
2502 "Unable to add decompression filter - %s",
2505 cupsArrayDelete(filters
);
2507 job
->current_file
++;
2509 if (job
->current_file
== job
->num_files
)
2510 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
2517 * Add port monitor, if any...
2520 if (printer
->port_monitor
)
2523 * Add port monitor to the end of the list...
2527 filters
= cupsArrayNew(NULL
, NULL
);
2529 if (!cupsArrayAdd(filters
, &port_monitor
))
2531 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add port monitor - %s",
2534 cupsArrayDelete(filters
);
2536 job
->current_file
++;
2538 if (job
->current_file
== job
->num_files
)
2539 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
2544 snprintf(port_monitor
.filter
, sizeof(port_monitor
.filter
),
2545 "%s/monitor/%s", ServerBin
, printer
->port_monitor
);
2549 * Update the printer and job state to "processing"...
2552 job
->state
->values
[0].integer
= IPP_JOB_PROCESSING
;
2553 job
->state_value
= IPP_JOB_PROCESSING
;
2556 job
->printer
= printer
;
2559 cupsdSetPrinterState(printer
, IPP_PRINTER_PROCESSING
, 0);
2561 if (job
->current_file
== 0)
2564 * Set the processing time...
2567 set_time(job
, "time-at-processing");
2570 * Create the backchannel pipes and make them non-blocking...
2573 cupsdOpenPipe(job
->back_pipes
);
2575 fcntl(job
->back_pipes
[0], F_SETFL
,
2576 fcntl(job
->back_pipes
[0], F_GETFL
) | O_NONBLOCK
);
2578 fcntl(job
->back_pipes
[1], F_SETFL
,
2579 fcntl(job
->back_pipes
[1], F_GETFL
) | O_NONBLOCK
);
2582 * Create the side-channel pipes and make them non-blocking...
2585 socketpair(AF_LOCAL
, SOCK_STREAM
, 0, job
->side_pipes
);
2587 fcntl(job
->side_pipes
[0], F_SETFL
,
2588 fcntl(job
->side_pipes
[0], F_GETFL
) | O_NONBLOCK
);
2590 fcntl(job
->side_pipes
[1], F_SETFL
,
2591 fcntl(job
->side_pipes
[1], F_GETFL
) | O_NONBLOCK
);
2595 * Determine if we are printing a banner page or not...
2598 if (job
->job_sheets
== NULL
)
2600 cupsdLogMessage(CUPSD_LOG_DEBUG
, "No job-sheets attribute.");
2601 if ((job
->job_sheets
=
2602 ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
2603 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2604 "... but someone added one without setting job_sheets!");
2606 else if (job
->job_sheets
->num_values
== 1)
2607 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s",
2608 job
->job_sheets
->values
[0].string
.text
);
2610 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s,%s",
2611 job
->job_sheets
->values
[0].string
.text
,
2612 job
->job_sheets
->values
[1].string
.text
);
2614 if (printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
))
2616 else if (job
->job_sheets
== NULL
)
2618 else if (strcasecmp(job
->job_sheets
->values
[0].string
.text
, "none") != 0 &&
2619 job
->current_file
== 0)
2621 else if (job
->job_sheets
->num_values
> 1 &&
2622 strcasecmp(job
->job_sheets
->values
[1].string
.text
, "none") != 0 &&
2623 job
->current_file
== (job
->num_files
- 1))
2628 cupsdLogMessage(CUPSD_LOG_DEBUG
, "banner_page = %d", banner_page
);
2631 * Building the options string is harder than it needs to be, but
2632 * for the moment we need to pass strings for command-line args and
2633 * not IPP attribute pointers... :)
2635 * First allocate/reallocate the option buffer as needed...
2638 i
= ipp_length(job
->attrs
);
2645 optptr
= realloc(options
, i
);
2649 cupsdLogMessage(CUPSD_LOG_CRIT
,
2650 "Unable to allocate %d bytes for option buffer for "
2651 "job %d!", i
, job
->id
);
2653 cupsArrayDelete(filters
);
2655 FilterLevel
-= job
->cost
;
2657 cupsdCancelJob(job
, 0, IPP_JOB_ABORTED
);
2666 * Now loop through the attributes and convert them to the textual
2667 * representation used by the filters...
2673 snprintf(title
, sizeof(title
), "%s-%d", printer
->name
, job
->id
);
2674 strcpy(copies
, "1");
2676 for (attr
= job
->attrs
->attrs
; attr
!= NULL
; attr
= attr
->next
)
2678 if (!strcmp(attr
->name
, "copies") &&
2679 attr
->value_tag
== IPP_TAG_INTEGER
)
2682 * Don't use the # copies attribute if we are printing the job sheets...
2686 sprintf(copies
, "%d", attr
->values
[0].integer
);
2688 else if (!strcmp(attr
->name
, "job-name") &&
2689 (attr
->value_tag
== IPP_TAG_NAME
||
2690 attr
->value_tag
== IPP_TAG_NAMELANG
))
2691 strlcpy(title
, attr
->values
[0].string
.text
, sizeof(title
));
2692 else if (attr
->group_tag
== IPP_TAG_JOB
)
2695 * Filter out other unwanted attributes...
2698 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
2699 attr
->value_tag
== IPP_TAG_NAMELANG
||
2700 attr
->value_tag
== IPP_TAG_TEXTLANG
||
2701 (attr
->value_tag
== IPP_TAG_URI
&& strcmp(attr
->name
, "job-uuid")) ||
2702 attr
->value_tag
== IPP_TAG_URISCHEME
||
2703 attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
) /* Not yet supported */
2706 if (!strncmp(attr
->name
, "time-", 5))
2709 if (!strncmp(attr
->name
, "job-", 4) && strcmp(attr
->name
, "job-uuid") &&
2710 !(printer
->type
& CUPS_PRINTER_REMOTE
))
2713 if (!strncmp(attr
->name
, "job-", 4) &&
2714 strcmp(attr
->name
, "job-uuid") &&
2715 strcmp(attr
->name
, "job-billing") &&
2716 strcmp(attr
->name
, "job-sheets") &&
2717 strcmp(attr
->name
, "job-hold-until") &&
2718 strcmp(attr
->name
, "job-priority"))
2721 if ((!strcmp(attr
->name
, "page-label") ||
2722 !strcmp(attr
->name
, "page-border") ||
2723 !strncmp(attr
->name
, "number-up", 9) ||
2724 !strcmp(attr
->name
, "page-ranges") ||
2725 !strcmp(attr
->name
, "page-set") ||
2726 !strcasecmp(attr
->name
, "AP_FIRSTPAGE_InputSlot") ||
2727 !strcasecmp(attr
->name
, "AP_FIRSTPAGE_ManualFeed")) &&
2732 * Otherwise add them to the list...
2735 if (optptr
> options
)
2736 strlcat(optptr
, " ", optlength
- (optptr
- options
));
2738 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
2740 strlcat(optptr
, attr
->name
, optlength
- (optptr
- options
));
2741 strlcat(optptr
, "=", optlength
- (optptr
- options
));
2744 for (i
= 0; i
< attr
->num_values
; i
++)
2747 strlcat(optptr
, ",", optlength
- (optptr
- options
));
2749 optptr
+= strlen(optptr
);
2751 switch (attr
->value_tag
)
2753 case IPP_TAG_INTEGER
:
2755 snprintf(optptr
, optlength
- (optptr
- options
),
2756 "%d", attr
->values
[i
].integer
);
2759 case IPP_TAG_BOOLEAN
:
2760 if (!attr
->values
[i
].boolean
)
2761 strlcat(optptr
, "no", optlength
- (optptr
- options
));
2763 case IPP_TAG_NOVALUE
:
2764 strlcat(optptr
, attr
->name
,
2765 optlength
- (optptr
- options
));
2768 case IPP_TAG_RANGE
:
2769 if (attr
->values
[i
].range
.lower
== attr
->values
[i
].range
.upper
)
2770 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
2771 "%d", attr
->values
[i
].range
.lower
);
2773 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
2774 "%d-%d", attr
->values
[i
].range
.lower
,
2775 attr
->values
[i
].range
.upper
);
2778 case IPP_TAG_RESOLUTION
:
2779 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
2780 "%dx%d%s", attr
->values
[i
].resolution
.xres
,
2781 attr
->values
[i
].resolution
.yres
,
2782 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
2786 case IPP_TAG_STRING
:
2789 case IPP_TAG_KEYWORD
:
2790 case IPP_TAG_CHARSET
:
2791 case IPP_TAG_LANGUAGE
:
2793 for (valptr
= attr
->values
[i
].string
.text
; *valptr
;)
2795 if (strchr(" \t\n\\\'\"", *valptr
))
2797 *optptr
++ = *valptr
++;
2804 break; /* anti-compiler-warning-code */
2808 optptr
+= strlen(optptr
);
2813 * Build the command-line arguments for the filters. Each filter
2814 * has 6 or 7 arguments:
2818 * argv[2] = username
2820 * argv[4] = # copies
2822 * argv[6] = filename (optional; normally stdin)
2824 * This allows legacy printer drivers that use the old System V
2825 * printing interface to be used by CUPS.
2827 * For remote jobs, we send all of the files in the argument list.
2830 if (printer
->remote
&& job
->num_files
> 1)
2831 argv
= calloc(7 + job
->num_files
, sizeof(char *));
2833 argv
= calloc(8, sizeof(char *));
2835 sprintf(jobid
, "%d", job
->id
);
2837 argv
[0] = printer
->name
;
2839 argv
[2] = job
->username
;
2844 if (printer
->remote
&& job
->num_files
> 1)
2846 for (i
= 0; i
< job
->num_files
; i
++)
2848 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
2850 argv
[6 + i
] = strdup(filename
);
2855 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
2856 job
->id
, job
->current_file
+ 1);
2860 for (i
= 0; argv
[i
]; i
++)
2861 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2862 "[Job %d] argv[%d]=\"%s\"", job
->id
, i
, argv
[i
]);
2865 * Create environment variable strings for the filters...
2868 attr
= ippFindAttribute(job
->attrs
, "attributes-natural-language",
2871 switch (strlen(attr
->values
[0].string
.text
))
2875 * This is an unknown or badly formatted language code; use
2876 * the POSIX locale...
2879 strcpy(lang
, "LANG=C");
2884 * Just the language code (ll)...
2887 snprintf(lang
, sizeof(lang
), "LANG=%s",
2888 attr
->values
[0].string
.text
);
2893 * Language and country code (ll-cc)...
2896 snprintf(lang
, sizeof(lang
), "LANG=%c%c_%c%c",
2897 attr
->values
[0].string
.text
[0],
2898 attr
->values
[0].string
.text
[1],
2899 toupper(attr
->values
[0].string
.text
[3] & 255),
2900 toupper(attr
->values
[0].string
.text
[4] & 255));
2904 attr
= ippFindAttribute(job
->attrs
, "document-format",
2907 (optptr
= strstr(attr
->values
[0].string
.text
, "charset=")) != NULL
)
2908 snprintf(charset
, sizeof(charset
), "CHARSET=%s", optptr
+ 8);
2911 attr
= ippFindAttribute(job
->attrs
, "attributes-charset",
2913 snprintf(charset
, sizeof(charset
), "CHARSET=%s",
2914 attr
->values
[0].string
.text
);
2917 snprintf(content_type
, sizeof(content_type
), "CONTENT_TYPE=%s/%s",
2918 job
->filetypes
[job
->current_file
]->super
,
2919 job
->filetypes
[job
->current_file
]->type
);
2920 snprintf(device_uri
, sizeof(device_uri
), "DEVICE_URI=%s",
2921 printer
->device_uri
);
2922 cupsdSanitizeURI(printer
->device_uri
, sani_uri
, sizeof(sani_uri
));
2923 snprintf(ppd
, sizeof(ppd
), "PPD=%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
2924 snprintf(printer_name
, sizeof(printer_name
), "PRINTER=%s", printer
->name
);
2925 snprintf(rip_max_cache
, sizeof(rip_max_cache
), "RIP_MAX_CACHE=%s", RIPCache
);
2927 envc
= cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
2929 envp
[envc
++] = charset
;
2930 envp
[envc
++] = lang
;
2931 envp
[envc
++] = ppd
;
2932 envp
[envc
++] = rip_max_cache
;
2933 envp
[envc
++] = content_type
;
2934 envp
[envc
++] = device_uri
;
2935 envp
[envc
++] = printer_name
;
2937 if (!printer
->remote
&& !printer
->raw
&&
2938 (filter
= (mime_filter_t
*)cupsArrayLast(filters
)) != NULL
&&
2941 snprintf(final_content_type
, sizeof(final_content_type
),
2942 "FINAL_CONTENT_TYPE=%s/%s",
2943 filter
->dst
->super
, filter
->dst
->type
);
2944 envp
[envc
++] = final_content_type
;
2947 if (Classification
&& !banner_page
)
2949 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
2950 IPP_TAG_NAME
)) == NULL
)
2951 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
2953 else if (attr
->num_values
> 1 &&
2954 strcmp(attr
->values
[1].string
.text
, "none") != 0)
2955 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
2956 attr
->values
[1].string
.text
);
2958 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
2959 attr
->values
[0].string
.text
);
2961 envp
[envc
++] = classification
;
2964 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
2966 snprintf(class_name
, sizeof(class_name
), "CLASS=%s", job
->dest
);
2967 envp
[envc
++] = class_name
;
2972 envp
[envc
++] = job
->ccname
;
2973 #endif /* HAVE_GSSAPI */
2977 for (i
= 0; i
< envc
; i
++)
2978 if (strncmp(envp
[i
], "DEVICE_URI=", 11))
2979 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Job %d] envp[%d]=\"%s\"",
2980 job
->id
, i
, envp
[i
]);
2982 cupsdLogMessage(CUPSD_LOG_DEBUG
, "[Job %d] envp[%d]=\"DEVICE_URI=%s\"",
2983 job
->id
, i
, sani_uri
);
2985 if (printer
->remote
)
2986 job
->current_file
= job
->num_files
;
2988 job
->current_file
++;
2991 * Now create processes for all of the filters...
2994 filterfds
[0][0] = -1;
2995 filterfds
[0][1] = -1;
2996 filterfds
[1][0] = -1;
2997 filterfds
[1][1] = -1;
2999 if (!job
->status_buffer
)
3001 if (cupsdOpenPipe(job
->status_pipes
))
3003 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to create job status pipes - %s.",
3005 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3006 "Unable to create status pipes - %s.", strerror(errno
));
3008 cupsdAddPrinterHistory(printer
);
3010 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3011 "Job canceled because the server could not create the job "
3017 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: status_pipes = [ %d %d ]",
3018 job
->status_pipes
[0], job
->status_pipes
[1]);
3020 job
->status_buffer
= cupsdStatBufNew(job
->status_pipes
[0], "[Job %d]",
3025 memset(job
->filters
, 0, sizeof(job
->filters
));
3027 for (i
= 0, slot
= 0, filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
3029 i
++, filter
= (mime_filter_t
*)cupsArrayNext(filters
))
3031 if (filter
->filter
[0] != '/')
3032 snprintf(command
, sizeof(command
), "%s/filter/%s", ServerBin
,
3035 strlcpy(command
, filter
->filter
, sizeof(command
));
3037 if (i
< (cupsArrayCount(filters
) - 1))
3039 if (cupsdOpenPipe(filterfds
[slot
]))
3041 cupsdLogMessage(CUPSD_LOG_ERROR
,
3042 "Unable to create job filter pipes - %s.",
3044 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3045 "Unable to create filter pipes - %s.", strerror(errno
));
3046 cupsdAddPrinterHistory(printer
);
3048 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3049 "Job canceled because the server could not create the "
3057 if (job
->current_file
== 1)
3059 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
3061 if (cupsdOpenPipe(job
->print_pipes
))
3063 cupsdLogMessage(CUPSD_LOG_ERROR
,
3064 "Unable to create job backend pipes - %s.",
3066 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3067 "Unable to create backend pipes - %s.", strerror(errno
));
3068 cupsdAddPrinterHistory(printer
);
3070 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3071 "Job canceled because the server could not create "
3072 "the backend pipes.");
3079 job
->print_pipes
[0] = -1;
3080 if (!strcmp(printer
->device_uri
, "file:/dev/null") ||
3081 !strcmp(printer
->device_uri
, "file:///dev/null"))
3082 job
->print_pipes
[1] = -1;
3085 if (!strncmp(printer
->device_uri
, "file:/dev/", 10))
3086 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
3088 else if (!strncmp(printer
->device_uri
, "file:///dev/", 12))
3089 job
->print_pipes
[1] = open(printer
->device_uri
+ 7,
3091 else if (!strncmp(printer
->device_uri
, "file:///", 8))
3092 job
->print_pipes
[1] = open(printer
->device_uri
+ 7,
3093 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3095 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
3096 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3098 if (job
->print_pipes
[1] < 0)
3100 cupsdLogMessage(CUPSD_LOG_ERROR
,
3101 "Unable to open output file \"%s\" - %s.",
3102 printer
->device_uri
, strerror(errno
));
3103 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3104 "Unable to open output file \"%s\" - %s.",
3105 printer
->device_uri
, strerror(errno
));
3107 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3108 "Job canceled because the server could not open the "
3114 fcntl(job
->print_pipes
[1], F_SETFD
,
3115 fcntl(job
->print_pipes
[1], F_GETFD
) | FD_CLOEXEC
);
3119 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3120 "start_job: print_pipes = [ %d %d ]",
3121 job
->print_pipes
[0], job
->print_pipes
[1]);
3124 filterfds
[slot
][0] = job
->print_pipes
[0];
3125 filterfds
[slot
][1] = job
->print_pipes
[1];
3128 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: filter=\"%s\"",
3130 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3131 "start_job: filterfds[%d]=[ %d %d ]",
3132 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3134 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
3135 filterfds
[slot
][1], job
->status_pipes
[1],
3136 job
->back_pipes
[0], job
->side_pipes
[0], 0,
3139 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3140 "start_job: Closing filter pipes for slot %d "
3142 !slot
, filterfds
[!slot
][0], filterfds
[!slot
][1]);
3144 cupsdClosePipe(filterfds
[!slot
]);
3148 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start filter \"%s\" - %s.",
3149 filter
->filter
, strerror(errno
));
3150 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3151 "Unable to start filter \"%s\" - %s.",
3152 filter
->filter
, strerror(errno
));
3154 cupsdAddPrinterHistory(printer
);
3156 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3157 "Job canceled because the server could not execute a "
3163 cupsdLogMessage(CUPSD_LOG_INFO
, "Started filter %s (PID %d) for job %d.",
3164 command
, pid
, job
->id
);
3170 cupsArrayDelete(filters
);
3173 * Finally, pipe the final output into a backend process if needed...
3176 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
3178 if (job
->current_file
== 1)
3180 sscanf(printer
->device_uri
, "%254[^:]", method
);
3181 snprintf(command
, sizeof(command
), "%s/backend/%s", ServerBin
, method
);
3184 * See if the backend needs to run as root...
3189 else if (stat(command
, &backinfo
))
3192 backroot
= !(backinfo
.st_mode
& (S_IRWXG
| S_IRWXO
));
3196 filterfds
[slot
][0] = -1;
3197 filterfds
[slot
][1] = -1;
3199 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "start_job: backend=\"%s\"",
3201 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3202 "start_job: filterfds[%d] = [ %d %d ]",
3203 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3205 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
3206 filterfds
[slot
][1], job
->status_pipes
[1],
3207 job
->back_pipes
[1], job
->side_pipes
[1],
3208 backroot
, &(job
->backend
));
3212 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start backend \"%s\" - %s.",
3213 method
, strerror(errno
));
3214 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
3215 "Unable to start backend \"%s\" - %s.", method
,
3218 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
3219 "Job canceled because the server could not execute "
3226 cupsdLogMessage(CUPSD_LOG_INFO
,
3227 "Started backend %s (PID %d) for job %d.",
3228 command
, pid
, job
->id
);
3232 if (job
->current_file
== job
->num_files
)
3234 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3235 "start_job: Closing print pipes [ %d %d ]...",
3236 job
->print_pipes
[0], job
->print_pipes
[1]);
3238 cupsdClosePipe(job
->print_pipes
);
3240 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3241 "start_job: Closing back pipes [ %d %d ]...",
3242 job
->back_pipes
[0], job
->back_pipes
[1]);
3244 cupsdClosePipe(job
->back_pipes
);
3246 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3247 "start_job: Closing side pipes [ %d %d ]...",
3248 job
->side_pipes
[0], job
->side_pipes
[1]);
3250 cupsdClosePipe(job
->side_pipes
);
3252 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3253 "start_job: Closing status output pipe %d...",
3254 job
->status_pipes
[1]);
3256 close(job
->status_pipes
[1]);
3257 job
->status_pipes
[1] = -1;
3262 filterfds
[slot
][0] = -1;
3263 filterfds
[slot
][1] = -1;
3265 if (job
->current_file
== job
->num_files
)
3267 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3268 "start_job: Closing print pipes [ %d %d ]...",
3269 job
->print_pipes
[0], job
->print_pipes
[1]);
3271 cupsdClosePipe(job
->print_pipes
);
3273 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3274 "start_job: Closing status output pipe %d...",
3275 job
->status_pipes
[1]);
3277 close(job
->status_pipes
[1]);
3278 job
->status_pipes
[1] = -1;
3282 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3283 "start_job: Closing filter pipes for slot %d "
3285 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3286 cupsdClosePipe(filterfds
[slot
]);
3288 if (printer
->remote
&& job
->num_files
> 1)
3290 for (i
= 0; i
< job
->num_files
; i
++)
3296 cupsdAddSelect(job
->status_buffer
->fd
, (cupsd_selfunc_t
)update_job
, NULL
,
3299 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, job
->printer
, job
, "Job #%d started.",
3306 * If we get here, we need to abort the current job and close out all
3307 * files and pipes...
3312 for (slot
= 0; slot
< 2; slot
++)
3314 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3315 "start_job: Closing filter pipes for slot %d "
3317 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
3318 cupsdClosePipe(filterfds
[slot
]);
3321 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
3322 "start_job: Closing status pipes [ %d %d ]...",
3323 job
->status_pipes
[0], job
->status_pipes
[1]);
3324 cupsdClosePipe(job
->status_pipes
);
3325 cupsdStatBufDelete(job
->status_buffer
);
3327 job
->status_buffer
= NULL
;
3329 cupsArrayDelete(filters
);
3331 if (printer
->remote
&& job
->num_files
> 1)
3333 for (i
= 0; i
< job
->num_files
; i
++)
3339 cupsdStopJob(job
, 0);
3344 * 'unload_job()' - Unload a job from memory.
3348 unload_job(cupsd_job_t
*job
) /* I - Job */
3353 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Unloading job %d...", job
->id
);
3355 ippDelete(job
->attrs
);
3360 job
->job_sheets
= NULL
;
3365 * 'update_job()' - Read a status update from a job's filters.
3369 update_job(cupsd_job_t
*job
) /* I - Job to check */
3371 int i
; /* Looping var */
3372 int copies
; /* Number of copies printed */
3373 char message
[1024], /* Message text */
3374 *ptr
; /* Pointer update... */
3375 int loglevel
, /* Log level for message */
3376 event
= 0; /* Events? */
3379 while ((ptr
= cupsdStatBufUpdate(job
->status_buffer
, &loglevel
,
3380 message
, sizeof(message
))) != NULL
)
3383 * Process page and printer state messages as needed...
3386 if (loglevel
== CUPSD_LOG_PAGE
)
3389 * Page message; send the message to the page_log file and update the
3390 * job sheet count...
3393 if (job
->sheets
!= NULL
)
3395 if (!strncasecmp(message
, "total ", 6))
3398 * Got a total count of pages from a backend or filter...
3401 copies
= atoi(message
+ 6);
3402 copies
-= job
->sheets
->values
[0].integer
; /* Just track the delta */
3404 else if (!sscanf(message
, "%*d%d", &copies
))
3407 job
->sheets
->values
[0].integer
+= copies
;
3409 if (job
->printer
->page_limit
)
3411 cupsd_quota_t
*q
= cupsdUpdateQuota(job
->printer
, job
->username
,
3415 if (AppleQuotas
&& q
->page_count
== -3)
3418 * Quota limit exceeded, cancel job in progress immediately...
3421 cupsdLogMessage(CUPSD_LOG_INFO
,
3422 "Job %d canceled: pages exceed user %s quota "
3423 "limit on printer %s (%s).",
3424 job
->id
, job
->username
, job
->printer
->name
,
3425 job
->printer
->info
);
3427 cupsdCancelJob(job
, 1, IPP_JOB_CANCELED
);
3432 #endif /* __APPLE__ */
3436 cupsdLogPage(job
, message
);
3438 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS
, job
->printer
, job
,
3439 "Printed %d page(s).", job
->sheets
->values
[0].integer
);
3441 else if (loglevel
== CUPSD_LOG_STATE
)
3443 cupsdSetPrinterReasons(job
->printer
, message
);
3444 cupsdAddPrinterHistory(job
->printer
);
3445 event
|= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
3447 else if (loglevel
== CUPSD_LOG_ATTR
)
3450 * Set attribute(s)...
3453 int num_attrs
; /* Number of attributes */
3454 cups_option_t
*attrs
; /* Attributes */
3455 const char *attr
; /* Attribute */
3458 num_attrs
= cupsParseOptions(message
, 0, &attrs
);
3460 if ((attr
= cupsGetOption("auth-info-required", num_attrs
,
3462 cupsdSetAuthInfoRequired(job
->printer
, attr
, NULL
);
3464 if ((attr
= cupsGetOption("printer-alert", num_attrs
, attrs
)) != NULL
)
3466 cupsdSetString(&job
->printer
->alert
, attr
);
3467 event
|= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
3470 if ((attr
= cupsGetOption("printer-alert-description", num_attrs
,
3473 cupsdSetString(&job
->printer
->alert_description
, attr
);
3474 event
|= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
3477 cupsFreeOptions(num_attrs
, attrs
);
3480 else if (!strncmp(message
, "recoverable:", 12))
3482 cupsdSetPrinterReasons(job
->printer
,
3483 "+com.apple.print.recoverable-warning");
3486 while (isspace(*ptr
& 255))
3489 cupsdSetString(&job
->printer
->recoverable
, ptr
);
3490 cupsdAddPrinterHistory(job
->printer
);
3491 event
|= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
3493 else if (!strncmp(message
, "recovered:", 10))
3495 cupsdSetPrinterReasons(job
->printer
,
3496 "-com.apple.print.recoverable-warning");
3499 while (isspace(*ptr
& 255))
3502 cupsdSetString(&job
->printer
->recoverable
, ptr
);
3503 cupsdAddPrinterHistory(job
->printer
);
3504 event
|= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
3506 #endif /* __APPLE__ */
3507 else if (loglevel
<= CUPSD_LOG_INFO
)
3510 * Some message to show in the printer-state-message attribute...
3513 strlcpy(job
->printer
->state_message
, message
,
3514 sizeof(job
->printer
->state_message
));
3515 cupsdAddPrinterHistory(job
->printer
);
3516 event
|= CUPSD_EVENT_PRINTER_STATE_CHANGED
;
3519 if (!strchr(job
->status_buffer
->buffer
, '\n'))
3523 if ((event
& CUPSD_EVENT_PRINTER_STATE_CHANGED
))
3524 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE_CHANGED
, job
->printer
, NULL
,
3525 (job
->printer
->type
& CUPS_PRINTER_CLASS
) ?
3526 "Class \"%s\" state changed." :
3527 "Printer \"%s\" state changed.",
3528 job
->printer
->name
);
3530 if (ptr
== NULL
&& !job
->status_buffer
->bufused
)
3533 * See if all of the filters and the backend have returned their
3537 for (i
= 0; job
->filters
[i
] < 0; i
++);
3539 if (job
->filters
[i
])
3542 if (job
->current_file
>= job
->num_files
&& job
->backend
> 0)
3546 * Handle the end of job stuff...
3549 cupsdFinishJob(job
);
3555 * End of "$Id: job.c 6462 2007-04-23 19:25:13Z mike $".