2 * "$Id: job.c 5051 2006-02-02 16:13:16Z 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 destination/user...
29 * cupsdCheckJobs() - Check the pending jobs and start any if the
30 * destination is available.
31 * cupsdCleanJobs() - Clean out old jobs.
32 * cupsdFreeAllJobs() - Free all jobs from memory.
33 * cupsdFindJob() - Find the specified job.
34 * cupsdGetPrinterJobCount() - Get the number of pending, processing,
35 * or held jobs in a printer or class.
36 * cupsdGetUserJobCount() - Get the number of pending, processing,
37 * or held jobs for a user.
38 * cupsdHoldJob() - Hold the specified job.
39 * cupsdLoadAllJobs() - Load all jobs from disk.
40 * cupsdMoveJob() - Move the specified job to a different
42 * cupsdReleaseJob() - Release the specified job.
43 * cupsdRestartJob() - Restart the specified job.
44 * cupsdSaveJob() - Save a job to disk.
45 * cupsdSetJobHoldUntil() - Set the hold time for a job...
46 * cupsdSetJobPriority() - Set the priority of a job, moving it up/down
47 * in the list as needed.
48 * cupsdStartJob() - Start a print job.
49 * cupsdStopAllJobs() - Stop all print jobs.
50 * cupsdStopJob() - Stop a print job.
51 * cupsdUpdateJob() - Read a status update from a job's filters.
52 * compare_active_jobs() - Compare the job IDs and priorities of two jobs.
53 * compare_jobs() - Compare the job IDs of two jobs.
54 * ipp_length() - Compute the size of the buffer needed to hold
55 * the textual IPP attributes.
56 * set_hold_until() - Set the hold time and update job-hold-until attribute.
60 * Include necessary headers...
65 #include <cups/backend.h>
73 static mime_filter_t gziptoany_filter
=
75 NULL
, /* Source type */
76 NULL
, /* Destination type */
78 "gziptoany" /* Filter program to run */
86 static int compare_active_jobs(void *first
, void *second
, void *data
);
87 static int compare_jobs(void *first
, void *second
, void *data
);
88 static int ipp_length(ipp_t
*ipp
);
89 static void set_time(cupsd_job_t
*job
, const char *name
);
90 static void set_hold_until(cupsd_job_t
*job
, time_t holdtime
);
94 * 'cupsdAddJob()' - Add a new job to the job queue...
97 cupsd_job_t
* /* O - New job record */
98 cupsdAddJob(int priority
, /* I - Job priority */
99 const char *dest
) /* I - Job destination */
101 cupsd_job_t
*job
; /* New job record */
104 job
= calloc(sizeof(cupsd_job_t
), 1);
106 job
->id
= NextJobId
++;
107 job
->priority
= priority
;
108 job
->back_pipes
[0] = -1;
109 job
->back_pipes
[1] = -1;
110 job
->print_pipes
[0] = -1;
111 job
->print_pipes
[1] = -1;
113 cupsdSetString(&job
->dest
, dest
);
116 * Add the new job to the "all jobs" and "active jobs" lists...
119 cupsArrayAdd(Jobs
, job
);
120 cupsArrayAdd(ActiveJobs
, job
);
127 * 'cupsdCancelJob()' - Cancel the specified print job.
131 cupsdCancelJob(cupsd_job_t
*job
, /* I - Job to cancel */
132 int purge
) /* I - Purge jobs? */
134 int i
; /* Looping var */
135 char filename
[1024]; /* Job filename */
138 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdCancelJob: id = %d", job
->id
);
141 * Remove the job from the active list...
144 cupsArrayRemove(ActiveJobs
, job
);
147 * Stop any processes that are working on the current job...
150 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
151 cupsdStopJob(job
, 0);
153 cupsArrayRemove(ActiveJobs
, job
);
155 job
->state
->values
[0].integer
= IPP_JOB_CANCELLED
;
157 set_time(job
, "time-at-completed");
159 cupsdExpireSubscriptions(NULL
, job
);
162 * Remove any authentication data...
165 snprintf(filename
, sizeof(filename
), "%s/a%05d", RequestRoot
,
170 * Remove the print file for good if we aren't preserving jobs or
174 job
->current_file
= 0;
176 if (!JobHistory
|| !JobFiles
|| purge
||
177 (job
->dtype
& CUPS_PRINTER_REMOTE
))
178 for (i
= 1; i
<= job
->num_files
; i
++)
180 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
185 if (JobHistory
&& !purge
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
))
188 * Save job state info...
196 * Remove the job info file...
199 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
,
204 * Remove the job from the "all jobs" list...
207 cupsArrayRemove(Jobs
, job
);
210 * Free all memory used...
213 if (job
->attrs
!= NULL
)
214 ippDelete(job
->attrs
);
216 if (job
->num_files
> 0)
218 free(job
->compressions
);
219 free(job
->filetypes
);
222 cupsdClearString(&job
->username
);
223 cupsdClearString(&job
->dest
);
231 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user...
235 cupsdCancelJobs(const char *dest
, /* I - Destination to cancel */
236 const char *username
, /* I - Username or NULL */
237 int purge
) /* I - Purge jobs? */
239 cupsd_job_t
*job
; /* Current job */
242 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
244 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
245 if ((dest
== NULL
|| !strcmp(job
->dest
, dest
)) &&
246 (username
== NULL
|| !strcmp(job
->username
, username
)))
249 * Cancel all jobs matching this destination/user...
252 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
253 purge
? "Job purged." : "Job canceled.");
255 cupsdCancelJob(job
, purge
);
263 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
270 cupsd_job_t
*job
; /* Current job in queue */
271 cupsd_printer_t
*printer
, /* Printer destination */
272 *pclass
; /* Printer class destination */
275 DEBUG_puts("cupsdCheckJobs()");
277 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
279 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
282 * Start held jobs if they are ready...
285 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
&&
287 job
->hold_until
< time(NULL
))
288 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
291 * Start pending jobs if the destination is available...
294 if (job
->state
->values
[0].integer
== IPP_JOB_PENDING
&& !NeedReload
&&
297 printer
= cupsdFindDest(job
->dest
);
301 (printer
->type
& (CUPS_PRINTER_IMPLICIT
| CUPS_PRINTER_CLASS
)))
304 * If the class is remote, just pass it to the remote server...
309 if (!(pclass
->type
& CUPS_PRINTER_REMOTE
))
311 if (pclass
->state
!= IPP_PRINTER_STOPPED
)
312 printer
= cupsdFindAvailablePrinter(job
->dest
);
318 if (!printer
&& !pclass
)
321 * Whoa, the printer and/or class for this destination went away;
325 cupsdLogMessage(CUPSD_LOG_WARN
,
326 "Printer/class %s has gone away; cancelling job %d!",
329 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
330 "Job canceled because the destination printer/class has gone away.");
332 cupsdCancelJob(job
, 1);
337 * See if the printer is available or remote and not printing a job;
338 * if so, start the job...
344 * Add/update a job-actual-printer-uri attribute for this job
345 * so that we know which printer actually printed the job...
348 ipp_attribute_t
*attr
; /* job-actual-printer-uri attribute */
351 if ((attr
= ippFindAttribute(job
->attrs
, "job-actual-printer-uri",
352 IPP_TAG_URI
)) != NULL
)
353 cupsdSetString(&attr
->values
[0].string
.text
, printer
->uri
);
355 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
,
356 "job-actual-printer-uri", NULL
, printer
->uri
);
359 if (printer
->state
== IPP_PRINTER_IDLE
|| /* Printer is idle */
360 ((printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is remote */
361 !printer
->job
)) /* and not printing a job */
362 cupsdStartJob(job
, printer
);
370 * 'cupsdCleanJobs()' - Clean out old jobs.
376 cupsd_job_t
*job
; /* Current job */
382 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
383 job
&& cupsArrayCount(Jobs
) >= MaxJobs
;
384 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
385 if (job
->state
->values
[0].integer
>= IPP_JOB_CANCELLED
)
386 cupsdCancelJob(job
, 1);
391 * 'cupsdFinishJob()' - Finish a job.
395 cupsdFinishJob(cupsd_job_t
*job
) /* I - Job */
397 int job_history
; /* Did cupsdCancelJob() keep the job? */
398 cupsd_printer_t
*printer
; /* Current printer */
401 cupsdLogMessage(CUPSD_LOG_DEBUG
,
402 "cupsdFinishJob: job %d, file %d is complete.",
403 job
->id
, job
->current_file
- 1);
405 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdFinishJob: job->status is %d",
408 if (job
->status_buffer
&& job
->current_file
>= job
->num_files
)
411 * Close the pipe and clear the input bit.
414 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
415 "cupsdFinishJob: Removing fd %d from InputSet...",
416 job
->status_buffer
->fd
);
418 FD_CLR(job
->status_buffer
->fd
, InputSet
);
420 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
421 "cupsdFinishJob: Closing status input pipe %d...",
422 job
->status_buffer
->fd
);
424 cupsdStatBufDelete(job
->status_buffer
);
426 job
->status_buffer
= NULL
;
429 printer
= job
->printer
;
434 * Backend had errors; stop it...
437 switch (-job
->status
)
440 case CUPS_BACKEND_FAILED
:
442 * Backend failure, use the error-policy to determine how to
446 cupsdStopJob(job
, 0);
447 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
451 * If the job was queued to a class, try requeuing it... For
452 * faxes and retry-job queues, hold the current job for 5 minutes.
455 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
457 else if ((printer
->type
& CUPS_PRINTER_FAX
) ||
458 !strcmp(printer
->error_policy
, "retry-job"))
461 * See how many times we've tried to send the job; if more than
462 * the limit, cancel the job.
467 if (job
->tries
>= JobRetryLimit
)
473 cupsdLogMessage(CUPSD_LOG_ERROR
,
474 "Canceling job %d since it could not be sent after %d tries.",
475 job
->id
, JobRetryLimit
);
477 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
478 "Job canceled since it could not be sent after %d tries.",
481 cupsdCancelJob(job
, 0);
486 * Try again in N seconds...
489 set_hold_until(job
, time(NULL
) + JobRetryInterval
);
492 else if (!strcmp(printer
->error_policy
, "abort-job"))
493 cupsdCancelJob(job
, 0);
496 case CUPS_BACKEND_CANCEL
:
501 cupsdCancelJob(job
, 0);
504 case CUPS_BACKEND_HOLD
:
509 cupsdStopJob(job
, 0);
510 cupsdSetJobHoldUntil(job
, "indefinite");
514 case CUPS_BACKEND_STOP
:
516 * Stop the printer...
519 cupsdStopJob(job
, 0);
521 cupsdSetPrinterState(printer
, IPP_PRINTER_STOPPED
, 1);
524 case CUPS_BACKEND_AUTH_REQUIRED
:
525 cupsdStopJob(job
, 0);
526 cupsdSetJobHoldUntil(job
, "authenticated");
529 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
530 "Authentication is required for job %d.", job
->id
);
535 * Try printing another job...
540 else if (job
->status
> 0)
543 * Filter had errors; stop job...
546 cupsdStopJob(job
, 1);
548 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, job
->printer
, job
,
549 "Job stopped due to filter errors; please consult the "
550 "error_log file for details.");
556 * Job printed successfully; cancel it...
559 if (job
->current_file
< job
->num_files
)
562 * Start the next file in the job...
565 FilterLevel
-= job
->cost
;
566 cupsdStartJob(job
, job
->printer
);
571 * Close out this job...
574 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
575 "Job completed successfully.");
577 job_history
= JobHistory
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
);
579 cupsdCancelJob(job
, 0);
583 job
->state
->values
[0].integer
= IPP_JOB_COMPLETED
;
588 * Clear the printer's state_message and move on...
591 printer
->state_message
[0] = '\0';
600 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
604 cupsdFreeAllJobs(void)
606 cupsd_job_t
*job
; /* Current job */
613 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
615 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
617 cupsArrayRemove(Jobs
, job
);
618 cupsArrayRemove(ActiveJobs
, job
);
620 ippDelete(job
->attrs
);
622 if (job
->num_files
> 0)
624 free(job
->compressions
);
625 free(job
->filetypes
);
631 cupsdReleaseSignals();
636 * 'cupsdFindJob()' - Find the specified job.
639 cupsd_job_t
* /* O - Job data */
640 cupsdFindJob(int id
) /* I - Job ID */
642 cupsd_job_t key
; /* Search key */
647 return ((cupsd_job_t
*)cupsArrayFind(Jobs
, &key
));
652 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
653 * or held jobs in a printer or class.
656 int /* O - Job count */
657 cupsdGetPrinterJobCount(
658 const char *dest
) /* I - Printer or class name */
660 int count
; /* Job count */
661 cupsd_job_t
*job
; /* Current job */
664 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
666 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
667 if (!strcasecmp(job
->dest
, dest
))
675 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
676 * or held jobs for a user.
679 int /* O - Job count */
680 cupsdGetUserJobCount(
681 const char *username
) /* I - Username */
683 int count
; /* Job count */
684 cupsd_job_t
*job
; /* Current job */
687 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
689 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
690 if (!strcasecmp(job
->username
, username
))
698 * 'cupsdHoldJob()' - Hold the specified job.
702 cupsdHoldJob(cupsd_job_t
*job
) /* I - Job data */
704 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdHoldJob: id = %d", job
->id
);
706 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
707 cupsdStopJob(job
, 0);
709 DEBUG_puts("cupsdHoldJob: setting state to held...");
711 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
720 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
724 cupsdLoadAllJobs(void)
726 cups_dir_t
*dir
; /* Directory */
727 cups_dentry_t
*dent
; /* Directory entry */
728 char filename
[1024]; /* Full filename of job file */
729 cups_file_t
*fp
; /* Job file */
730 cupsd_job_t
*job
; /* New job */
731 int jobid
, /* Current job ID */
732 fileid
; /* Current file ID */
733 ipp_attribute_t
*attr
; /* Job attribute */
734 char method
[HTTP_MAX_URI
],
735 /* Method portion of URI */
736 username
[HTTP_MAX_URI
],
737 /* Username portion of URI */
739 /* Host portion of URI */
740 resource
[HTTP_MAX_URI
];
741 /* Resource portion of URI */
742 int port
; /* Port portion of URI */
743 const char *dest
; /* Destination */
744 mime_type_t
**filetypes
; /* New filetypes array */
745 int *compressions
; /* New compressions array */
749 * First create the job lists...
753 Jobs
= cupsArrayNew(compare_jobs
, NULL
);
756 ActiveJobs
= cupsArrayNew(compare_active_jobs
, NULL
);
759 * Then open the requests directory...
762 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdLoadAllJobs: Scanning %s...",
765 if ((dir
= cupsDirOpen(RequestRoot
)) == NULL
)
767 cupsdLogMessage(CUPSD_LOG_ERROR
,
768 "cupsdLoadAllJobs: Unable to open spool directory %s: %s",
769 RequestRoot
, strerror(errno
));
774 * Read all the c##### files...
777 while ((dent
= cupsDirRead(dir
)) != NULL
)
778 if (strlen(dent
->filename
) >= 6 && dent
->filename
[0] == 'c')
781 * Allocate memory for the job...
784 if ((job
= calloc(sizeof(cupsd_job_t
), 1)) == NULL
)
786 cupsdLogMessage(CUPSD_LOG_ERROR
,
787 "cupsdLoadAllJobs: Ran out of memory for jobs!");
792 if ((job
->attrs
= ippNew()) == NULL
)
795 cupsdLogMessage(CUPSD_LOG_ERROR
,
796 "cupsdLoadAllJobs: Ran out of memory for job attributes!");
802 * Assign the job ID...
805 job
->id
= atoi(dent
->filename
+ 1);
806 job
->back_pipes
[0] = -1;
807 job
->back_pipes
[1] = -1;
808 job
->print_pipes
[0] = -1;
809 job
->print_pipes
[1] = -1;
811 cupsdLogMessage(CUPSD_LOG_DEBUG
,
812 "cupsdLoadAllJobs: Loading attributes for job %d...",
815 if (job
->id
>= NextJobId
)
816 NextJobId
= job
->id
+ 1;
819 * Load the job control file...
822 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->filename
);
823 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
825 cupsdLogMessage(CUPSD_LOG_ERROR
,
826 "cupsdLoadAllJobs: Unable to open job control file "
828 filename
, strerror(errno
));
829 ippDelete(job
->attrs
);
836 if (ippReadIO(fp
, (ipp_iocb_t
)cupsFileRead
, 1, NULL
,
837 job
->attrs
) != IPP_DATA
)
839 cupsdLogMessage(CUPSD_LOG_ERROR
,
840 "cupsdLoadAllJobs: Unable to read job control file "
844 ippDelete(job
->attrs
);
853 if ((job
->state
= ippFindAttribute(job
->attrs
, "job-state", IPP_TAG_ENUM
)) == NULL
)
855 cupsdLogMessage(CUPSD_LOG_ERROR
,
856 "cupsdLoadAllJobs: Missing or bad job-state attribute "
857 "in control file \"%s\"!",
859 ippDelete(job
->attrs
);
865 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri", IPP_TAG_URI
)) == NULL
)
867 cupsdLogMessage(CUPSD_LOG_ERROR
,
868 "cupsdLoadAllJobs: No job-printer-uri attribute in "
869 "control file \"%s\"!",
871 ippDelete(job
->attrs
);
877 httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[0].string
.text
, method
,
878 sizeof(method
), username
, sizeof(username
), host
,
879 sizeof(host
), &port
, resource
, sizeof(resource
));
881 if ((dest
= cupsdValidateDest(host
, resource
, &(job
->dtype
),
884 cupsdLogMessage(CUPSD_LOG_ERROR
,
885 "cupsdLoadAllJobs: Unable to queue job for destination "
887 attr
->values
[0].string
.text
);
888 ippDelete(job
->attrs
);
894 cupsdSetString(&job
->dest
, dest
);
896 job
->sheets
= ippFindAttribute(job
->attrs
, "job-media-sheets-completed",
898 job
->job_sheets
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
900 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority",
901 IPP_TAG_INTEGER
)) == NULL
)
903 cupsdLogMessage(CUPSD_LOG_ERROR
,
904 "cupsdLoadAllJobs: Missing or bad job-priority "
905 "attribute in control file \"%s\"!",
907 ippDelete(job
->attrs
);
912 job
->priority
= attr
->values
[0].integer
;
914 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-user-name",
915 IPP_TAG_NAME
)) == NULL
)
917 cupsdLogMessage(CUPSD_LOG_ERROR
,
918 "cupsdLoadAllJobs: Missing or bad "
919 "job-originating-user-name attribute in control file "
922 ippDelete(job
->attrs
);
927 cupsdSetString(&job
->username
, attr
->values
[0].string
.text
);
930 * Insert the job into the array, sorting by job priority and ID...
933 cupsArrayAdd(Jobs
, job
);
934 if (job
->state
->values
[0].integer
< IPP_JOB_STOPPED
)
935 cupsArrayAdd(ActiveJobs
,job
);
938 * Set the job hold-until time and state...
941 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
943 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
944 IPP_TAG_KEYWORD
)) == NULL
)
945 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
948 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
950 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
);
952 else if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
953 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
957 * Read all the d##### files...
962 while ((dent
= cupsDirRead(dir
)) != NULL
)
963 if (strlen(dent
->filename
) > 7 && dent
->filename
[0] == 'd' &&
964 strchr(dent
->filename
, '-'))
970 jobid
= atoi(dent
->filename
+ 1);
971 fileid
= atoi(strchr(dent
->filename
, '-') + 1);
973 cupsdLogMessage(CUPSD_LOG_DEBUG
,
974 "cupsdLoadAllJobs: Auto-typing document file %s...",
977 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->filename
);
979 if ((job
= cupsdFindJob(jobid
)) == NULL
)
981 cupsdLogMessage(CUPSD_LOG_ERROR
,
982 "cupsdLoadAllJobs: Orphaned print file \"%s\"!",
988 if (fileid
> job
->num_files
)
990 if (job
->num_files
== 0)
992 compressions
= (int *)calloc(fileid
, sizeof(int));
993 filetypes
= (mime_type_t
**)calloc(fileid
, sizeof(mime_type_t
*));
997 compressions
= (int *)realloc(job
->compressions
,
998 sizeof(int) * fileid
);
999 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
1000 sizeof(mime_type_t
*) * fileid
);
1003 if (compressions
== NULL
|| filetypes
== NULL
)
1005 cupsdLogMessage(CUPSD_LOG_ERROR
,
1006 "cupsdLoadAllJobs: Ran out of memory for job file "
1011 job
->compressions
= compressions
;
1012 job
->filetypes
= filetypes
;
1013 job
->num_files
= fileid
;
1016 job
->filetypes
[fileid
- 1] = mimeFileType(MimeDatabase
, filename
, NULL
,
1017 job
->compressions
+ fileid
- 1);
1019 if (job
->filetypes
[fileid
- 1] == NULL
)
1020 job
->filetypes
[fileid
- 1] = mimeType(MimeDatabase
, "application",
1027 * Clean out old jobs as needed...
1035 * 'cupsdMoveJob()' - Move the specified job to a different destination.
1039 cupsdMoveJob(cupsd_job_t
*job
, /* I - Job */
1040 const char *dest
) /* I - Destination */
1042 ipp_attribute_t
*attr
; /* job-printer-uri attribute */
1043 cupsd_printer_t
*p
; /* Destination printer or class */
1047 * Find the printer...
1050 if ((p
= cupsdFindDest(dest
)) == NULL
)
1054 * Don't move completed jobs...
1057 if (job
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
1061 * Change the destination information...
1064 cupsdSetString(&job
->dest
, dest
);
1065 job
->dtype
= p
->type
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
1066 CUPS_PRINTER_IMPLICIT
);
1068 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri", IPP_TAG_URI
)) != NULL
)
1069 cupsdSetString(&(attr
->values
[0].string
.text
), p
->uri
);
1076 * 'cupsdReleaseJob()' - Release the specified job.
1080 cupsdReleaseJob(cupsd_job_t
*job
) /* I - Job */
1082 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdReleaseJob: id = %d", job
->id
);
1084 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
1086 DEBUG_puts("cupsdReleaseJob: setting state to pending...");
1088 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1096 * 'cupsdRestartJob()' - Restart the specified job.
1100 cupsdRestartJob(cupsd_job_t
*job
) /* I - Job */
1102 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdRestartJob: id = %d", job
->id
);
1104 if (job
->state
->values
[0].integer
== IPP_JOB_STOPPED
|| JobFiles
)
1107 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1115 * 'cupsdSaveJob()' - Save a job to disk.
1119 cupsdSaveJob(cupsd_job_t
*job
) /* I - Job */
1121 char filename
[1024]; /* Job control filename */
1122 cups_file_t
*fp
; /* Job file */
1125 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
, job
->id
);
1127 if ((fp
= cupsFileOpen(filename
, "w")) == NULL
)
1129 cupsdLogMessage(CUPSD_LOG_ERROR
,
1130 "cupsdSaveJob: Unable to create job control file "
1132 filename
, strerror(errno
));
1136 fchmod(cupsFileNumber(fp
), 0600);
1137 fchown(cupsFileNumber(fp
), RunUser
, Group
);
1139 ippWriteIO(fp
, (ipp_iocb_t
)cupsFileWrite
, 1, NULL
, job
->attrs
);
1146 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job...
1150 cupsdSetJobHoldUntil(cupsd_job_t
*job
, /* I - Job */
1151 const char *when
) /* I - When to resume */
1153 time_t curtime
; /* Current time */
1154 struct tm
*curdate
; /* Current date */
1155 int hour
; /* Hold hour */
1156 int minute
; /* Hold minute */
1157 int second
; /* Hold second */
1160 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdSetJobHoldUntil(%d, \"%s\")",
1165 if (!strcmp(when
, "indefinite") || !strcmp(when
, "authenticated"))
1168 * Hold indefinitely...
1171 job
->hold_until
= 0;
1173 else if (!strcmp(when
, "day-time"))
1176 * Hold to 6am the next morning unless local time is < 6pm.
1179 curtime
= time(NULL
);
1180 curdate
= localtime(&curtime
);
1182 if (curdate
->tm_hour
< 18)
1183 job
->hold_until
= curtime
;
1185 job
->hold_until
= curtime
+
1186 ((29 - curdate
->tm_hour
) * 60 + 59 -
1187 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1189 else if (!strcmp(when
, "evening") || strcmp(when
, "night"))
1192 * Hold to 6pm unless local time is > 6pm or < 6am.
1195 curtime
= time(NULL
);
1196 curdate
= localtime(&curtime
);
1198 if (curdate
->tm_hour
< 6 || curdate
->tm_hour
>= 18)
1199 job
->hold_until
= curtime
;
1201 job
->hold_until
= curtime
+
1202 ((17 - curdate
->tm_hour
) * 60 + 59 -
1203 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1205 else if (!strcmp(when
, "second-shift"))
1208 * Hold to 4pm unless local time is > 4pm.
1211 curtime
= time(NULL
);
1212 curdate
= localtime(&curtime
);
1214 if (curdate
->tm_hour
>= 16)
1215 job
->hold_until
= curtime
;
1217 job
->hold_until
= curtime
+
1218 ((15 - curdate
->tm_hour
) * 60 + 59 -
1219 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1221 else if (!strcmp(when
, "third-shift"))
1224 * Hold to 12am unless local time is < 8am.
1227 curtime
= time(NULL
);
1228 curdate
= localtime(&curtime
);
1230 if (curdate
->tm_hour
< 8)
1231 job
->hold_until
= curtime
;
1233 job
->hold_until
= curtime
+
1234 ((23 - curdate
->tm_hour
) * 60 + 59 -
1235 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1237 else if (!strcmp(when
, "weekend"))
1240 * Hold to weekend unless we are in the weekend.
1243 curtime
= time(NULL
);
1244 curdate
= localtime(&curtime
);
1246 if (curdate
->tm_wday
|| curdate
->tm_wday
== 6)
1247 job
->hold_until
= curtime
;
1249 job
->hold_until
= curtime
+
1250 (((5 - curdate
->tm_wday
) * 24 +
1251 (17 - curdate
->tm_hour
)) * 60 + 59 -
1252 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1254 else if (sscanf(when
, "%d:%d:%d", &hour
, &minute
, &second
) >= 2)
1257 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1260 curtime
= time(NULL
);
1261 curdate
= gmtime(&curtime
);
1263 job
->hold_until
= curtime
+
1264 ((hour
- curdate
->tm_hour
) * 60 + minute
-
1265 curdate
->tm_min
) * 60 + second
- curdate
->tm_sec
;
1268 * Hold until next day as needed...
1271 if (job
->hold_until
< curtime
)
1272 job
->hold_until
+= 24 * 60 * 60 * 60;
1275 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdSetJobHoldUntil: hold_until = %d",
1276 (int)job
->hold_until
);
1281 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
1282 * the list as needed.
1286 cupsdSetJobPriority(
1287 cupsd_job_t
*job
, /* I - Job ID */
1288 int priority
) /* I - New priority (0 to 100) */
1290 ipp_attribute_t
*attr
; /* Job attribute */
1294 * Don't change completed jobs...
1297 if (job
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
1301 * Set the new priority and re-add the job into the active list...
1304 cupsArrayRemove(ActiveJobs
, job
);
1306 job
->priority
= priority
;
1308 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
1309 attr
->values
[0].integer
= priority
;
1311 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1314 cupsArrayAdd(ActiveJobs
, job
);
1321 * 'cupsdStartJob()' - Start a print job.
1325 cupsdStartJob(cupsd_job_t
*job
, /* I - Job ID */
1326 cupsd_printer_t
*printer
) /* I - Printer to print job */
1328 int i
; /* Looping var */
1329 int slot
; /* Pipe slot */
1330 cups_array_t
*filters
; /* Filters for job */
1331 mime_filter_t
*filter
, /* Current filter */
1332 port_monitor
; /* Port monitor filter */
1333 char method
[255], /* Method for output */
1334 *optptr
, /* Pointer to options */
1335 *valptr
; /* Pointer in value string */
1336 ipp_attribute_t
*attr
; /* Current attribute */
1337 struct stat backinfo
; /* Backend file information */
1338 int backroot
; /* Run backend as root? */
1339 int pid
; /* Process ID of new filter process */
1340 int banner_page
; /* 1 if banner page, 0 otherwise */
1341 int statusfds
[2], /* Pipes used between the filters and scheduler */
1342 filterfds
[2][2];/* Pipes used between the filters */
1343 int envc
; /* Number of environment variables */
1344 char *argv
[8], /* Filter command-line arguments */
1345 sani_uri
[1024], /* Sanitized DEVICE_URI env var */
1346 filename
[1024], /* Job filename */
1347 command
[1024], /* Full path to filter/backend command */
1348 jobid
[255], /* Job ID string */
1349 title
[IPP_MAX_NAME
],
1350 /* Job title string */
1351 copies
[255], /* # copies string */
1352 *envp
[MAX_ENV
], /* Environment variables */
1353 charset
[255], /* CHARSET environment variable */
1354 class_name
[255],/* CLASS environment variable */
1355 classification
[1024],
1356 /* CLASSIFICATION environment variable */
1358 /* CONTENT_TYPE environment variable */
1360 /* DEVICE_URI environment variable */
1361 lang
[255], /* LANG environment variable */
1362 ppd
[1024], /* PPD environment variable */
1364 /* PRINTER environment variable */
1366 /* RIP_MAX_CACHE environment variable */
1367 static char *options
= NULL
;/* Full list of options */
1368 static int optlength
= 0; /* Length of option buffer */
1371 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob() id = %d, file = %d/%d",
1372 job
->id
, job
->current_file
, job
->num_files
);
1374 if (job
->num_files
== 0)
1376 cupsdLogMessage(CUPSD_LOG_ERROR
, "Job ID %d has no files! Cancelling it!",
1379 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1380 "Job canceled because it has no files.");
1382 cupsdCancelJob(job
, 0);
1387 * Figure out what filters are required to convert from
1388 * the source to the destination type...
1397 * Remote jobs and raw queues go directly to the printer without
1401 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1402 "cupsdStartJob: Sending job to queue tagged as raw...");
1409 * Local jobs get filtered...
1412 filters
= mimeFilter(MimeDatabase
, job
->filetypes
[job
->current_file
],
1413 printer
->filetype
, &(job
->cost
), MAX_FILTERS
- 1);
1417 cupsdLogMessage(CUPSD_LOG_ERROR
,
1418 "Unable to convert file %d to printable format for job %d!",
1419 job
->current_file
, job
->id
);
1420 cupsdLogMessage(CUPSD_LOG_INFO
,
1421 "Hint: Do you have ESP Ghostscript installed?");
1423 if (LogLevel
< CUPSD_LOG_DEBUG
)
1424 cupsdLogMessage(CUPSD_LOG_INFO
,
1425 "Hint: Try setting the LogLevel to \"debug\".");
1427 job
->current_file
++;
1429 if (job
->current_file
== job
->num_files
)
1431 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1432 "Job canceled because it has no files that can be printed.");
1434 cupsdCancelJob(job
, 0);
1441 * Remove NULL ("-") filters...
1444 for (filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
1446 filter
= (mime_filter_t
*)cupsArrayNext(filters
))
1447 if (!strcmp(filter
->filter
, "-"))
1448 cupsArrayRemove(filters
, filter
);
1450 if (cupsArrayCount(filters
) == 0)
1452 cupsArrayDelete(filters
);
1458 * See if the filter cost is too high...
1461 if ((FilterLevel
+ job
->cost
) > FilterLimit
&& FilterLevel
> 0 &&
1465 * Don't print this job quite yet...
1468 cupsArrayDelete(filters
);
1470 cupsdLogMessage(CUPSD_LOG_INFO
,
1471 "Holding job %d because filter limit has been reached.",
1473 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1474 "cupsdStartJob: id=%d, file=%d, cost=%d, level=%d, limit=%d",
1475 job
->id
, job
->current_file
, job
->cost
, FilterLevel
,
1480 FilterLevel
+= job
->cost
;
1483 * Add decompression filters, if any...
1486 if (job
->compressions
[job
->current_file
])
1489 * Add gziptoany filter to the front of the list...
1492 if (!cupsArrayInsert(filters
, &gziptoany_filter
))
1494 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add decompression filter - %s",
1497 if (filters
!= NULL
)
1500 job
->current_file
++;
1502 if (job
->current_file
== job
->num_files
)
1504 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1505 "Job canceled because the print file could not be "
1508 cupsdCancelJob(job
, 0);
1516 * Add port monitor, if any...
1519 if (printer
->port_monitor
)
1522 * Add port monitor to the end of the list...
1525 if (!cupsArrayAdd(filters
, &port_monitor
))
1527 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add port monitor - %s",
1530 if (filters
!= NULL
)
1533 job
->current_file
++;
1535 if (job
->current_file
== job
->num_files
)
1537 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1538 "Job canceled because the port monitor could not be "
1541 cupsdCancelJob(job
, 0);
1547 snprintf(port_monitor
.filter
, sizeof(port_monitor
.filter
),
1548 "%s/monitor/%s", ServerBin
, printer
->port_monitor
);
1552 * Update the printer and job state to "processing"...
1555 job
->state
->values
[0].integer
= IPP_JOB_PROCESSING
;
1557 job
->printer
= printer
;
1559 cupsdSetPrinterState(printer
, IPP_PRINTER_PROCESSING
, 0);
1561 if (job
->current_file
== 0)
1563 set_time(job
, "time-at-processing");
1564 cupsdOpenPipe(job
->back_pipes
);
1568 * Determine if we are printing a banner page or not...
1571 if (job
->job_sheets
== NULL
)
1573 cupsdLogMessage(CUPSD_LOG_DEBUG
, "No job-sheets attribute.");
1574 if ((job
->job_sheets
=
1575 ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
1576 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1577 "... but someone added one without setting job_sheets!");
1579 else if (job
->job_sheets
->num_values
== 1)
1580 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s",
1581 job
->job_sheets
->values
[0].string
.text
);
1583 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s,%s",
1584 job
->job_sheets
->values
[0].string
.text
,
1585 job
->job_sheets
->values
[1].string
.text
);
1587 if (printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
))
1589 else if (job
->job_sheets
== NULL
)
1591 else if (strcasecmp(job
->job_sheets
->values
[0].string
.text
, "none") != 0 &&
1592 job
->current_file
== 0)
1594 else if (job
->job_sheets
->num_values
> 1 &&
1595 strcasecmp(job
->job_sheets
->values
[1].string
.text
, "none") != 0 &&
1596 job
->current_file
== (job
->num_files
- 1))
1601 cupsdLogMessage(CUPSD_LOG_DEBUG
, "banner_page = %d", banner_page
);
1604 * Building the options string is harder than it needs to be, but
1605 * for the moment we need to pass strings for command-line args and
1606 * not IPP attribute pointers... :)
1608 * First allocate/reallocate the option buffer as needed...
1611 i
= ipp_length(job
->attrs
);
1618 optptr
= realloc(options
, i
);
1622 cupsdLogMessage(CUPSD_LOG_CRIT
,
1623 "cupsdStartJob: Unable to allocate %d bytes for option buffer for job %d!",
1626 if (filters
!= NULL
)
1629 FilterLevel
-= job
->cost
;
1631 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1632 "Job canceled because the server ran out of memory.");
1634 cupsdCancelJob(job
, 0);
1643 * Now loop through the attributes and convert them to the textual
1644 * representation used by the filters...
1650 snprintf(title
, sizeof(title
), "%s-%d", printer
->name
, job
->id
);
1651 strcpy(copies
, "1");
1653 for (attr
= job
->attrs
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1655 if (strcmp(attr
->name
, "copies") == 0 &&
1656 attr
->value_tag
== IPP_TAG_INTEGER
)
1659 * Don't use the # copies attribute if we are printing the job sheets...
1663 sprintf(copies
, "%d", attr
->values
[0].integer
);
1665 else if (strcmp(attr
->name
, "job-name") == 0 &&
1666 (attr
->value_tag
== IPP_TAG_NAME
||
1667 attr
->value_tag
== IPP_TAG_NAMELANG
))
1668 strlcpy(title
, attr
->values
[0].string
.text
, sizeof(title
));
1669 else if (attr
->group_tag
== IPP_TAG_JOB
)
1672 * Filter out other unwanted attributes...
1675 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
1676 attr
->value_tag
== IPP_TAG_NAMELANG
||
1677 attr
->value_tag
== IPP_TAG_TEXTLANG
||
1678 attr
->value_tag
== IPP_TAG_URI
||
1679 attr
->value_tag
== IPP_TAG_URISCHEME
||
1680 attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
) /* Not yet supported */
1683 if (strncmp(attr
->name
, "time-", 5) == 0)
1686 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1687 !(printer
->type
& CUPS_PRINTER_REMOTE
))
1690 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1691 strcmp(attr
->name
, "job-billing") != 0 &&
1692 strcmp(attr
->name
, "job-sheets") != 0 &&
1693 strcmp(attr
->name
, "job-hold-until") != 0 &&
1694 strcmp(attr
->name
, "job-priority") != 0)
1697 if ((strcmp(attr
->name
, "page-label") == 0 ||
1698 strcmp(attr
->name
, "page-border") == 0 ||
1699 strncmp(attr
->name
, "number-up", 9) == 0 ||
1700 strcmp(attr
->name
, "page-set") == 0) &&
1705 * Otherwise add them to the list...
1708 if (optptr
> options
)
1709 strlcat(optptr
, " ", optlength
- (optptr
- options
));
1711 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
1713 strlcat(optptr
, attr
->name
, optlength
- (optptr
- options
));
1714 strlcat(optptr
, "=", optlength
- (optptr
- options
));
1717 for (i
= 0; i
< attr
->num_values
; i
++)
1720 strlcat(optptr
, ",", optlength
- (optptr
- options
));
1722 optptr
+= strlen(optptr
);
1724 switch (attr
->value_tag
)
1726 case IPP_TAG_INTEGER
:
1728 snprintf(optptr
, optlength
- (optptr
- options
),
1729 "%d", attr
->values
[i
].integer
);
1732 case IPP_TAG_BOOLEAN
:
1733 if (!attr
->values
[i
].boolean
)
1734 strlcat(optptr
, "no", optlength
- (optptr
- options
));
1736 case IPP_TAG_NOVALUE
:
1737 strlcat(optptr
, attr
->name
,
1738 optlength
- (optptr
- options
));
1741 case IPP_TAG_RANGE
:
1742 if (attr
->values
[i
].range
.lower
== attr
->values
[i
].range
.upper
)
1743 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1744 "%d", attr
->values
[i
].range
.lower
);
1746 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1747 "%d-%d", attr
->values
[i
].range
.lower
,
1748 attr
->values
[i
].range
.upper
);
1751 case IPP_TAG_RESOLUTION
:
1752 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1753 "%dx%d%s", attr
->values
[i
].resolution
.xres
,
1754 attr
->values
[i
].resolution
.yres
,
1755 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
1759 case IPP_TAG_STRING
:
1762 case IPP_TAG_KEYWORD
:
1763 case IPP_TAG_CHARSET
:
1764 case IPP_TAG_LANGUAGE
:
1765 for (valptr
= attr
->values
[i
].string
.text
; *valptr
;)
1767 if (strchr(" \t\n\\\'\"", *valptr
))
1769 *optptr
++ = *valptr
++;
1776 break; /* anti-compiler-warning-code */
1780 optptr
+= strlen(optptr
);
1785 * Build the command-line arguments for the filters. Each filter
1786 * has 6 or 7 arguments:
1790 * argv[2] = username
1792 * argv[4] = # copies
1794 * argv[6] = filename (optional; normally stdin)
1796 * This allows legacy printer drivers that use the old System V
1797 * printing interface to be used by CUPS.
1800 sprintf(jobid
, "%d", job
->id
);
1801 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
1802 job
->id
, job
->current_file
+ 1);
1804 argv
[0] = printer
->name
;
1806 argv
[2] = job
->username
;
1813 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1814 "cupsdStartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1815 argv
[0], argv
[1], argv
[2], argv
[3], argv
[4], argv
[5], argv
[6]);
1818 * Create environment variable strings for the filters...
1821 attr
= ippFindAttribute(job
->attrs
, "attributes-natural-language",
1824 switch (strlen(attr
->values
[0].string
.text
))
1828 * This is an unknown or badly formatted language code; use
1829 * the POSIX locale...
1832 strcpy(lang
, "LANG=C");
1837 * Just the language code (ll)...
1840 snprintf(lang
, sizeof(lang
), "LANG=%s",
1841 attr
->values
[0].string
.text
);
1846 * Language and country code (ll-cc)...
1849 snprintf(lang
, sizeof(lang
), "LANG=%c%c_%c%c",
1850 attr
->values
[0].string
.text
[0],
1851 attr
->values
[0].string
.text
[1],
1852 toupper(attr
->values
[0].string
.text
[3] & 255),
1853 toupper(attr
->values
[0].string
.text
[4] & 255));
1857 attr
= ippFindAttribute(job
->attrs
, "document-format",
1860 (optptr
= strstr(attr
->values
[0].string
.text
, "charset=")) != NULL
)
1861 snprintf(charset
, sizeof(charset
), "CHARSET=%s", optptr
+ 8);
1864 attr
= ippFindAttribute(job
->attrs
, "attributes-charset",
1866 snprintf(charset
, sizeof(charset
), "CHARSET=%s",
1867 attr
->values
[0].string
.text
);
1870 snprintf(content_type
, sizeof(content_type
), "CONTENT_TYPE=%s/%s",
1871 job
->filetypes
[job
->current_file
]->super
,
1872 job
->filetypes
[job
->current_file
]->type
);
1873 snprintf(device_uri
, sizeof(device_uri
), "DEVICE_URI=%s", printer
->device_uri
);
1874 cupsdSanitizeURI(printer
->device_uri
, sani_uri
, sizeof(sani_uri
));
1875 snprintf(ppd
, sizeof(ppd
), "PPD=%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
1876 snprintf(printer_name
, sizeof(printer_name
), "PRINTER=%s", printer
->name
);
1877 snprintf(rip_max_cache
, sizeof(rip_max_cache
), "RIP_MAX_CACHE=%s", RIPCache
);
1879 envc
= cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
1881 envp
[envc
++] = charset
;
1882 envp
[envc
++] = lang
;
1883 envp
[envc
++] = ppd
;
1884 envp
[envc
++] = rip_max_cache
;
1885 envp
[envc
++] = content_type
;
1886 envp
[envc
++] = device_uri
;
1887 envp
[envc
++] = printer_name
;
1889 if (Classification
&& !banner_page
)
1891 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
1892 IPP_TAG_NAME
)) == NULL
)
1893 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1895 else if (attr
->num_values
> 1 &&
1896 strcmp(attr
->values
[1].string
.text
, "none") != 0)
1897 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1898 attr
->values
[1].string
.text
);
1900 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1901 attr
->values
[0].string
.text
);
1903 envp
[envc
++] = classification
;
1906 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
1908 snprintf(class_name
, sizeof(class_name
), "CLASS=%s", job
->dest
);
1909 envp
[envc
++] = class_name
;
1914 for (i
= 0; i
< envc
; i
++)
1915 if (strncmp(envp
[i
], "DEVICE_URI=", 11))
1916 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: envp[%d]=\"%s\"",
1919 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1920 "cupsdStartJob: envp[%d]=\"DEVICE_URI=%s\"", i
, sani_uri
);
1922 job
->current_file
++;
1925 * Now create processes for all of the filters...
1928 if (cupsdOpenPipe(statusfds
))
1930 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to create job status pipes - %s.",
1932 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1933 "Unable to create status pipes - %s.", strerror(errno
));
1935 cupsdAddPrinterHistory(printer
);
1937 if (filters
!= NULL
)
1940 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1941 "Job canceled because the server could not create the job status pipes.");
1943 cupsdCancelJob(job
, 0);
1947 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: statusfds = [ %d %d ]",
1948 statusfds
[0], statusfds
[1]);
1951 fcntl(statusfds
[0], F_SETFD
, FD_CLOEXEC
);
1952 fcntl(statusfds
[1], F_SETFD
, FD_CLOEXEC
);
1953 #endif /* FD_CLOEXEC */
1955 job
->status_buffer
= cupsdStatBufNew(statusfds
[0], "[Job %d]",
1958 memset(job
->filters
, 0, sizeof(job
->filters
));
1960 filterfds
[1][0] = open("/dev/null", O_RDONLY
);
1961 filterfds
[1][1] = -1;
1963 if (filterfds
[1][0] < 0)
1965 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to open \"/dev/null\" - %s.",
1967 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1968 "Unable to open \"/dev/null\" - %s.", strerror(errno
));
1970 cupsdAddPrinterHistory(printer
);
1972 if (filters
!= NULL
)
1975 cupsdClosePipe(statusfds
);
1976 cupsdCancelJob(job
, 0);
1980 fcntl(filterfds
[1][0], F_SETFD
, fcntl(filterfds
[1][0], F_GETFD
) | FD_CLOEXEC
);
1982 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
1983 1, filterfds
[1][0], filterfds
[1][1]);
1985 for (i
= 0, slot
= 0, filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
1987 i
++, filter
= (mime_filter_t
*)cupsArrayNext(filters
))
1989 if (filter
->filter
[0] != '/')
1990 snprintf(command
, sizeof(command
), "%s/filter/%s", ServerBin
,
1993 strlcpy(command
, filter
->filter
, sizeof(command
));
1995 if (i
< (cupsArrayCount(filters
) - 1))
1997 if (cupsdOpenPipe(filterfds
[slot
]))
1999 cupsdLogMessage(CUPSD_LOG_ERROR
,
2000 "Unable to create job filter pipes - %s.",
2002 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2003 "Unable to create filter pipes - %s.", strerror(errno
));
2004 cupsdAddPrinterHistory(printer
);
2006 cupsArrayDelete(filters
);
2008 cupsdClosePipe(statusfds
);
2009 cupsdClosePipe(filterfds
[!slot
]);
2011 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2012 "Job canceled because the server could not create the filter pipes.");
2014 cupsdCancelJob(job
, 0);
2020 if (job
->current_file
== 1)
2022 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
2024 if (cupsdOpenPipe(job
->print_pipes
))
2026 cupsdLogMessage(CUPSD_LOG_ERROR
,
2027 "Unable to create job backend pipes - %s.",
2029 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2030 "Unable to create backend pipes - %s.", strerror(errno
));
2031 cupsdAddPrinterHistory(printer
);
2033 cupsArrayDelete(filters
);
2035 cupsdClosePipe(statusfds
);
2036 cupsdClosePipe(filterfds
[!slot
]);
2038 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2039 "Job canceled because the server could not create the backend pipes.");
2041 cupsdCancelJob(job
, 0);
2047 job
->print_pipes
[0] = -1;
2048 if (!strncmp(printer
->device_uri
, "file:/dev/", 10) &&
2049 strcmp(printer
->device_uri
, "file:/dev/null"))
2050 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
2052 else if (!strncmp(printer
->device_uri
, "file:///dev/", 12) &&
2053 strcmp(printer
->device_uri
, "file:///dev/null"))
2054 job
->print_pipes
[1] = open(printer
->device_uri
+ 7,
2057 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
2058 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
2060 if (job
->print_pipes
[1] < 0)
2062 cupsdLogMessage(CUPSD_LOG_ERROR
,
2063 "Unable to open output file \"%s\" - %s.",
2064 printer
->device_uri
, strerror(errno
));
2065 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2066 "Unable to open output file \"%s\" - %s.",
2067 printer
->device_uri
, strerror(errno
));
2069 cupsdAddPrinterHistory(printer
);
2071 cupsArrayDelete(filters
);
2073 cupsdClosePipe(statusfds
);
2074 cupsdClosePipe(filterfds
[!slot
]);
2076 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2077 "Job canceled because the server could not open the output file.");
2079 cupsdCancelJob(job
, 0);
2083 fcntl(job
->print_pipes
[1], F_SETFD
,
2084 fcntl(job
->print_pipes
[1], F_GETFD
) | FD_CLOEXEC
);
2087 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdStartJob: print_pipes = [ %d %d ]",
2088 job
->print_pipes
[0], job
->print_pipes
[1]);
2091 filterfds
[slot
][0] = job
->print_pipes
[0];
2092 filterfds
[slot
][1] = job
->print_pipes
[1];
2095 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filter = \"%s\"", command
);
2096 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2097 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2099 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
2100 filterfds
[slot
][1], statusfds
[1],
2101 job
->back_pipes
[0], 0, job
->filters
+ i
);
2103 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2104 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2105 !slot
, filterfds
[!slot
][0], filterfds
[!slot
][1]);
2107 cupsdClosePipe(filterfds
[!slot
]);
2111 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start filter \"%s\" - %s.",
2112 filter
->filter
, strerror(errno
));
2113 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2114 "Unable to start filter \"%s\" - %s.",
2115 filter
->filter
, strerror(errno
));
2117 cupsdAddPrinterHistory(printer
);
2119 cupsArrayDelete(filters
);
2121 cupsdAddPrinterHistory(printer
);
2123 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2124 "Job canceled because the server could not execute a filter.");
2126 cupsdCancelJob(job
, 0);
2130 cupsdLogMessage(CUPSD_LOG_INFO
, "Started filter %s (PID %d) for job %d.",
2131 command
, pid
, job
->id
);
2137 cupsArrayDelete(filters
);
2140 * Finally, pipe the final output into a backend process if needed...
2143 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
2145 if (job
->current_file
== 1)
2147 sscanf(printer
->device_uri
, "%254[^:]", method
);
2148 snprintf(command
, sizeof(command
), "%s/backend/%s", ServerBin
, method
);
2151 * See if the backend needs to run as root...
2156 else if (lstat(command
, &backinfo
))
2159 backroot
= !(backinfo
.st_mode
& (S_IRWXG
| S_IRWXO
));
2163 filterfds
[slot
][0] = -1;
2164 filterfds
[slot
][1] = open("/dev/null", O_WRONLY
);
2166 if (filterfds
[slot
][1] < 0)
2168 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to open \"/dev/null\" - %s.",
2170 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2171 "Unable to open \"/dev/null\" - %s.", strerror(errno
));
2173 cupsdAddPrinterHistory(printer
);
2175 cupsdClosePipe(statusfds
);
2177 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2178 "Job canceled because the server could not open a file.");
2180 cupsdCancelJob(job
, 0);
2184 fcntl(filterfds
[slot
][1], F_SETFD
,
2185 fcntl(filterfds
[slot
][1], F_GETFD
) | FD_CLOEXEC
);
2187 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: backend = \"%s\"",
2189 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2190 "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2191 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2193 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
2194 filterfds
[slot
][1], statusfds
[1],
2195 job
->back_pipes
[1], backroot
,
2200 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start backend \"%s\" - %s.",
2201 method
, strerror(errno
));
2202 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2203 "Unable to start backend \"%s\" - %s.", method
, strerror(errno
));
2205 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2206 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2207 job
->print_pipes
[0], job
->print_pipes
[1]);
2209 cupsdClosePipe(job
->print_pipes
);
2211 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2212 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2213 job
->back_pipes
[0], job
->back_pipes
[1]);
2215 cupsdClosePipe(job
->back_pipes
);
2217 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2218 "Job canceled because the server could not execute the backend.");
2220 cupsdCancelJob(job
, 0);
2225 cupsdLogMessage(CUPSD_LOG_INFO
,
2226 "Started backend %s (PID %d) for job %d.",
2227 command
, pid
, job
->id
);
2231 if (job
->current_file
== job
->num_files
)
2233 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2234 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2235 job
->print_pipes
[0], job
->print_pipes
[1]);
2237 cupsdClosePipe(job
->print_pipes
);
2239 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2240 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2241 job
->back_pipes
[0], job
->back_pipes
[1]);
2243 cupsdClosePipe(job
->back_pipes
);
2248 filterfds
[slot
][0] = -1;
2249 filterfds
[slot
][1] = -1;
2251 if (job
->current_file
== job
->num_files
)
2253 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2254 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2255 job
->print_pipes
[0], job
->print_pipes
[1]);
2257 cupsdClosePipe(job
->print_pipes
);
2261 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2262 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2263 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2265 cupsdClosePipe(filterfds
[slot
]);
2267 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2268 "cupsdStartJob: Closing status output pipe %d...",
2271 close(statusfds
[1]);
2273 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2274 "cupsdStartJob: Adding fd %d to InputSet...",
2275 job
->status_buffer
->fd
);
2277 FD_SET(job
->status_buffer
->fd
, InputSet
);
2279 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, job
->printer
, job
, "Job #%d started.",
2285 * 'cupsdStopAllJobs()' - Stop all print jobs.
2289 cupsdStopAllJobs(void)
2291 cupsd_job_t
*job
; /* Current job */
2294 DEBUG_puts("cupsdStopAllJobs()");
2296 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
2298 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
2299 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
2301 cupsdStopJob(job
, 1);
2302 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
2308 * 'cupsdStopJob()' - Stop a print job.
2312 cupsdStopJob(cupsd_job_t
*job
, /* I - Job */
2313 int force
) /* I - 1 = Force all filters to stop */
2315 int i
; /* Looping var */
2318 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStopJob: id = %d, force = %d",
2321 if (job
->state
->values
[0].integer
!= IPP_JOB_PROCESSING
)
2324 FilterLevel
-= job
->cost
;
2326 if (job
->status
< 0 &&
2327 !(job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
)) &&
2328 !(job
->printer
->type
& CUPS_PRINTER_FAX
) &&
2329 !strcmp(job
->printer
->error_policy
, "stop-printer"))
2330 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_STOPPED
, 1);
2331 else if (job
->printer
->state
!= IPP_PRINTER_STOPPED
)
2332 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_IDLE
, 0);
2334 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStopJob: printer state is %d",
2335 job
->printer
->state
);
2337 job
->state
->values
[0].integer
= IPP_JOB_STOPPED
;
2338 job
->printer
->job
= NULL
;
2339 job
->printer
= NULL
;
2341 job
->current_file
--;
2343 for (i
= 0; job
->filters
[i
]; i
++)
2344 if (job
->filters
[i
] > 0)
2346 cupsdEndProcess(job
->filters
[i
], force
);
2347 job
->filters
[i
] = 0;
2350 if (job
->backend
> 0)
2352 cupsdEndProcess(job
->backend
, force
);
2356 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2357 "cupsdStopJob: Closing print pipes [ %d %d ]...",
2358 job
->print_pipes
[0], job
->print_pipes
[1]);
2360 cupsdClosePipe(job
->print_pipes
);
2362 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2363 "cupsdStopJob: Closing back pipes [ %d %d ]...",
2364 job
->back_pipes
[0], job
->back_pipes
[1]);
2366 cupsdClosePipe(job
->back_pipes
);
2368 if (job
->status_buffer
)
2371 * Close the pipe and clear the input bit.
2374 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2375 "cupsdStopJob: Removing fd %d from InputSet...",
2376 job
->status_buffer
->fd
);
2378 FD_CLR(job
->status_buffer
->fd
, InputSet
);
2380 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2381 "cupsdStopJob: Closing status input pipe %d...",
2382 job
->status_buffer
->fd
);
2384 cupsdStatBufDelete(job
->status_buffer
);
2386 job
->status_buffer
= NULL
;
2392 * 'cupsdUpdateJob()' - Read a status update from a job's filters.
2396 cupsdUpdateJob(cupsd_job_t
*job
) /* I - Job to check */
2398 int i
; /* Looping var */
2399 int copies
; /* Number of copies printed */
2400 char message
[1024], /* Message text */
2401 *ptr
; /* Pointer update... */
2402 int loglevel
; /* Log level for message */
2405 while ((ptr
= cupsdStatBufUpdate(job
->status_buffer
, &loglevel
,
2406 message
, sizeof(message
))) != NULL
)
2409 * Process page and printer state messages as needed...
2412 if (loglevel
== CUPSD_LOG_PAGE
)
2415 * Page message; send the message to the page_log file and update the
2416 * job sheet count...
2419 if (job
->sheets
!= NULL
)
2421 if (!strncasecmp(message
, "total ", 6))
2424 * Got a total count of pages from a backend or filter...
2427 copies
= atoi(message
+ 6);
2428 copies
-= job
->sheets
->values
[0].integer
; /* Just track the delta */
2430 else if (!sscanf(message
, "%*d%d", &copies
))
2433 job
->sheets
->values
[0].integer
+= copies
;
2435 if (job
->printer
->page_limit
)
2436 cupsdUpdateQuota(job
->printer
, job
->username
, copies
, 0);
2439 cupsdLogPage(job
, message
);
2441 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS
, job
->printer
, job
,
2442 "Printed %d page(s).", job
->sheets
->values
[0].integer
);
2444 else if (loglevel
== CUPSD_LOG_STATE
)
2446 cupsdSetPrinterReasons(job
->printer
, message
);
2447 cupsdAddPrinterHistory(job
->printer
);
2449 else if (loglevel
== CUPSD_LOG_ATTR
)
2452 * Set attribute(s)...
2458 else if (!strncmp(message
, "recoverable:", 12))
2460 cupsdSetPrinterReasons(job
->printer
,
2461 "+com.apple.print.recoverable-warning");
2464 while (isspace(*ptr
& 255))
2467 cupsdSetString(&job
->printer
->recoverable
, ptr
);
2468 cupsdAddPrinterHistory(job
->printer
);
2470 else if (!strncmp(message
, "recovered:", 10))
2472 cupsdSetPrinterReasons(job
->printer
,
2473 "-com.apple.print.recoverable-warning");
2476 while (isspace(*ptr
& 255))
2479 cupsdSetString(&job
->printer
->recoverable
, ptr
);
2480 cupsdAddPrinterHistory(job
->printer
);
2482 #endif /* __APPLE__ */
2486 * Some message to show in the printer-state-message attribute...
2489 strlcpy(job
->printer
->state_message
, message
,
2490 sizeof(job
->printer
->state_message
));
2491 cupsdAddPrinterHistory(job
->printer
);
2494 if (!strchr(job
->status_buffer
->buffer
, '\n'))
2501 * See if all of the filters and the backend have returned their
2505 for (i
= 0; job
->filters
[i
] < 0; i
++);
2507 if (job
->filters
[i
])
2510 if (job
->current_file
>= job
->num_files
&& job
->backend
> 0)
2514 * Handle the end of job stuff...
2517 cupsdFinishJob(job
);
2523 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
2526 static int /* O - Difference */
2527 compare_active_jobs(void *first
, /* I - First job */
2528 void *second
, /* I - Second job */
2529 void *data
) /* I - App data (not used) */
2531 int diff
; /* Difference */
2534 if ((diff
= ((cupsd_job_t
*)first
)->priority
- ((cupsd_job_t
*)second
)->priority
) != 0)
2537 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
2542 * 'compare_jobs()' - Compare the job IDs of two jobs.
2545 static int /* O - Difference */
2546 compare_jobs(void *first
, /* I - First job */
2547 void *second
, /* I - Second job */
2548 void *data
) /* I - App data (not used) */
2550 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
2555 * 'ipp_length()' - Compute the size of the buffer needed to hold
2556 * the textual IPP attributes.
2559 int /* O - Size of buffer to hold IPP attributes */
2560 ipp_length(ipp_t
*ipp
) /* I - IPP request */
2562 int bytes
; /* Number of bytes */
2563 int i
; /* Looping var */
2564 ipp_attribute_t
*attr
; /* Current attribute */
2568 * Loop through all attributes...
2573 for (attr
= ipp
->attrs
; attr
!= NULL
; attr
= attr
->next
)
2576 * Skip attributes that won't be sent to filters...
2579 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
2580 attr
->value_tag
== IPP_TAG_NAMELANG
||
2581 attr
->value_tag
== IPP_TAG_TEXTLANG
||
2582 attr
->value_tag
== IPP_TAG_URI
||
2583 attr
->value_tag
== IPP_TAG_URISCHEME
)
2586 if (strncmp(attr
->name
, "time-", 5) == 0)
2590 * Add space for a leading space and commas between each value.
2591 * For the first attribute, the leading space isn't used, so the
2592 * extra byte can be used as the nul terminator...
2595 bytes
++; /* " " separator */
2596 bytes
+= attr
->num_values
; /* "," separators */
2599 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
2600 * other attributes appear as "foo=value1,value2,...,valueN".
2603 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
2604 bytes
+= strlen(attr
->name
);
2606 bytes
+= attr
->num_values
* strlen(attr
->name
);
2609 * Now add the size required for each value in the attribute...
2612 switch (attr
->value_tag
)
2614 case IPP_TAG_INTEGER
:
2617 * Minimum value of a signed integer is -2147483647, or 11 digits.
2620 bytes
+= attr
->num_values
* 11;
2623 case IPP_TAG_BOOLEAN
:
2625 * Add two bytes for each false ("no") value...
2628 for (i
= 0; i
< attr
->num_values
; i
++)
2629 if (!attr
->values
[i
].boolean
)
2633 case IPP_TAG_RANGE
:
2635 * A range is two signed integers separated by a hyphen, or
2636 * 23 characters max.
2639 bytes
+= attr
->num_values
* 23;
2642 case IPP_TAG_RESOLUTION
:
2644 * A resolution is two signed integers separated by an "x" and
2645 * suffixed by the units, or 26 characters max.
2648 bytes
+= attr
->num_values
* 26;
2651 case IPP_TAG_STRING
:
2654 case IPP_TAG_KEYWORD
:
2655 case IPP_TAG_CHARSET
:
2656 case IPP_TAG_LANGUAGE
:
2658 * Strings can contain characters that need quoting. We need
2659 * at least 2 * len + 2 characters to cover the quotes and
2660 * any backslashes in the string.
2663 for (i
= 0; i
< attr
->num_values
; i
++)
2664 bytes
+= 2 * strlen(attr
->values
[i
].string
.text
) + 2;
2668 break; /* anti-compiler-warning-code */
2677 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2681 set_time(cupsd_job_t
*job
, /* I - Job to update */
2682 const char *name
) /* I - Name of attribute */
2684 ipp_attribute_t
*attr
; /* Time attribute */
2687 if ((attr
= ippFindAttribute(job
->attrs
, name
, IPP_TAG_ZERO
)) != NULL
)
2689 attr
->value_tag
= IPP_TAG_INTEGER
;
2690 attr
->values
[0].integer
= time(NULL
);
2696 * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
2700 set_hold_until(cupsd_job_t
*job
, /* I - Job to update */
2701 time_t holdtime
) /* I - Hold until time */
2703 ipp_attribute_t
*attr
; /* job-hold-until attribute */
2704 struct tm
*holddate
; /* Hold date */
2705 char holdstr
[64]; /* Hold time */
2709 * Set the hold_until value and hold the job...
2712 cupsdLogMessage(CUPSD_LOG_DEBUG
, "set_hold_until: hold_until = %d", (int)holdtime
);
2714 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
2715 job
->hold_until
= holdtime
;
2718 * Update the job-hold-until attribute with a string representing GMT
2719 * time (HH:MM:SS)...
2722 holddate
= gmtime(&holdtime
);
2723 snprintf(holdstr
, sizeof(holdstr
), "%d:%d:%d", holddate
->tm_hour
,
2724 holddate
->tm_min
, holddate
->tm_sec
);
2726 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
2727 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
2730 * Either add the attribute or update the value of the existing one
2734 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
2735 "job-hold-until", NULL
, holdstr
);
2737 cupsdSetString(&attr
->values
[0].string
.text
, holdstr
);
2744 * End of "$Id: job.c 5051 2006-02-02 16:13:16Z mike $".