2 * "$Id: job.c 4906 2006-01-10 20:53:28Z mike $"
4 * Job management routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2005 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
)
296 printer
= cupsdFindDest(job
->dest
);
300 (printer
->type
& (CUPS_PRINTER_IMPLICIT
| CUPS_PRINTER_CLASS
)))
303 * If the class is remote, just pass it to the remote server...
308 if (!(pclass
->type
& CUPS_PRINTER_REMOTE
))
310 if (pclass
->state
!= IPP_PRINTER_STOPPED
)
311 printer
= cupsdFindAvailablePrinter(job
->dest
);
317 if (!printer
&& !pclass
)
320 * Whoa, the printer and/or class for this destination went away;
324 cupsdLogMessage(CUPSD_LOG_WARN
,
325 "Printer/class %s has gone away; cancelling job %d!",
328 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
329 "Job canceled because the destination printer/class has gone away.");
331 cupsdCancelJob(job
, 1);
336 * See if the printer is available or remote and not printing a job;
337 * if so, start the job...
343 * Add/update a job-actual-printer-uri attribute for this job
344 * so that we know which printer actually printed the job...
347 ipp_attribute_t
*attr
; /* job-actual-printer-uri attribute */
350 if ((attr
= ippFindAttribute(job
->attrs
, "job-actual-printer-uri",
351 IPP_TAG_URI
)) != NULL
)
352 cupsdSetString(&attr
->values
[0].string
.text
, printer
->uri
);
354 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
,
355 "job-actual-printer-uri", NULL
, printer
->uri
);
358 if (printer
->state
== IPP_PRINTER_IDLE
|| /* Printer is idle */
359 ((printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is remote */
360 !printer
->job
)) /* and not printing a job */
361 cupsdStartJob(job
, printer
);
369 * 'cupsdCleanJobs()' - Clean out old jobs.
375 cupsd_job_t
*job
; /* Current job */
381 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
382 job
&& cupsArrayCount(Jobs
) >= MaxJobs
;
383 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
384 if (job
->state
->values
[0].integer
>= IPP_JOB_CANCELLED
)
385 cupsdCancelJob(job
, 1);
390 * 'cupsdFinishJob()' - Finish a job.
394 cupsdFinishJob(cupsd_job_t
*job
) /* I - Job */
396 int job_history
; /* Did cupsdCancelJob() keep the job? */
397 cupsd_printer_t
*printer
; /* Current printer */
400 cupsdLogMessage(CUPSD_LOG_DEBUG
,
401 "cupsdFinishJob: job %d, file %d is complete.",
402 job
->id
, job
->current_file
- 1);
404 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdFinishJob: job->status is %d",
407 if (job
->status_buffer
&& job
->current_file
>= job
->num_files
)
410 * Close the pipe and clear the input bit.
413 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
414 "cupsdFinishJob: Removing fd %d from InputSet...",
415 job
->status_buffer
->fd
);
417 FD_CLR(job
->status_buffer
->fd
, InputSet
);
419 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
420 "cupsdFinishJob: Closing status input pipe %d...",
421 job
->status_buffer
->fd
);
423 cupsdStatBufDelete(job
->status_buffer
);
425 job
->status_buffer
= NULL
;
431 * Backend had errors; stop it...
434 printer
= job
->printer
;
436 switch (-job
->status
)
439 case CUPS_BACKEND_FAILED
:
441 * Backend failure, use the error-policy to determine how to
445 cupsdStopJob(job
, 0);
446 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
450 * If the job was queued to a class, try requeuing it... For
451 * faxes and retry-job queues, hold the current job for 5 minutes.
454 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
456 else if ((printer
->type
& CUPS_PRINTER_FAX
) ||
457 !strcmp(printer
->error_policy
, "retry-job"))
460 * See how many times we've tried to send the job; if more than
461 * the limit, cancel the job.
466 if (job
->tries
>= JobRetryLimit
)
472 cupsdLogMessage(CUPSD_LOG_ERROR
,
473 "Canceling job %d since it could not be sent after %d tries.",
474 job
->id
, JobRetryLimit
);
476 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
477 "Job canceled since it could not be sent after %d tries.",
480 cupsdCancelJob(job
, 0);
485 * Try again in N seconds...
488 set_hold_until(job
, time(NULL
) + JobRetryInterval
);
491 else if (!strcmp(printer
->error_policy
, "abort-job"))
492 cupsdCancelJob(job
, 0);
495 case CUPS_BACKEND_CANCEL
:
500 cupsdCancelJob(job
, 0);
503 case CUPS_BACKEND_HOLD
:
508 cupsdStopJob(job
, 0);
509 cupsdSetJobHoldUntil(job
, "indefinite");
513 case CUPS_BACKEND_STOP
:
515 * Stop the printer...
518 cupsdStopJob(job
, 0);
520 cupsdSetPrinterState(printer
, IPP_PRINTER_STOPPED
, 1);
523 case CUPS_BACKEND_AUTH_REQUIRED
:
524 cupsdStopJob(job
, 0);
525 cupsdSetJobHoldUntil(job
, "authenticated");
528 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
529 "Authentication is required for job %d.", job
->id
);
534 * Try printing another job...
539 else if (job
->status
> 0)
542 * Filter had errors; cancel it...
545 if (job
->current_file
< job
->num_files
)
546 cupsdStartJob(job
, job
->printer
);
549 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
550 "Job aborted due to filter errors; please consult the "
551 "error_log file for details.");
553 job_history
= JobHistory
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
);
555 cupsdCancelJob(job
, 0);
559 job
->state
->values
[0].integer
= IPP_JOB_ABORTED
;
569 * Job printed successfully; cancel it...
572 if (job
->current_file
< job
->num_files
)
574 FilterLevel
-= job
->cost
;
575 cupsdStartJob(job
, job
->printer
);
579 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
580 "Job completed successfully.");
582 job_history
= JobHistory
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
);
584 cupsdCancelJob(job
, 0);
588 job
->state
->values
[0].integer
= IPP_JOB_COMPLETED
;
599 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
603 cupsdFreeAllJobs(void)
605 cupsd_job_t
*job
; /* Current job */
612 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
614 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
616 cupsArrayRemove(Jobs
, job
);
617 cupsArrayRemove(ActiveJobs
, job
);
619 ippDelete(job
->attrs
);
621 if (job
->num_files
> 0)
623 free(job
->compressions
);
624 free(job
->filetypes
);
630 cupsdReleaseSignals();
635 * 'cupsdFindJob()' - Find the specified job.
638 cupsd_job_t
* /* O - Job data */
639 cupsdFindJob(int id
) /* I - Job ID */
641 cupsd_job_t key
; /* Search key */
646 return ((cupsd_job_t
*)cupsArrayFind(Jobs
, &key
));
651 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
652 * or held jobs in a printer or class.
655 int /* O - Job count */
656 cupsdGetPrinterJobCount(
657 const char *dest
) /* I - Printer or class name */
659 int count
; /* Job count */
660 cupsd_job_t
*job
; /* Current job */
663 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
665 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
666 if (!strcasecmp(job
->dest
, dest
))
674 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
675 * or held jobs for a user.
678 int /* O - Job count */
679 cupsdGetUserJobCount(
680 const char *username
) /* I - Username */
682 int count
; /* Job count */
683 cupsd_job_t
*job
; /* Current job */
686 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
688 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
689 if (!strcasecmp(job
->username
, username
))
697 * 'cupsdHoldJob()' - Hold the specified job.
701 cupsdHoldJob(cupsd_job_t
*job
) /* I - Job data */
703 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdHoldJob: id = %d", job
->id
);
705 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
706 cupsdStopJob(job
, 0);
708 DEBUG_puts("cupsdHoldJob: setting state to held...");
710 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
719 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
723 cupsdLoadAllJobs(void)
725 cups_dir_t
*dir
; /* Directory */
726 cups_dentry_t
*dent
; /* Directory entry */
727 char filename
[1024]; /* Full filename of job file */
728 int fd
; /* File descriptor */
729 cupsd_job_t
*job
; /* New job */
730 int jobid
, /* Current job ID */
731 fileid
; /* Current file ID */
732 ipp_attribute_t
*attr
; /* Job attribute */
733 char method
[HTTP_MAX_URI
],
734 /* Method portion of URI */
735 username
[HTTP_MAX_URI
],
736 /* Username portion of URI */
738 /* Host portion of URI */
739 resource
[HTTP_MAX_URI
];
740 /* Resource portion of URI */
741 int port
; /* Port portion of URI */
742 const char *dest
; /* Destination */
743 mime_type_t
**filetypes
; /* New filetypes array */
744 int *compressions
; /* New compressions array */
748 * First create the job lists...
752 Jobs
= cupsArrayNew(compare_jobs
, NULL
);
755 ActiveJobs
= cupsArrayNew(compare_active_jobs
, NULL
);
758 * Then open the requests directory...
761 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdLoadAllJobs: Scanning %s...",
764 if ((dir
= cupsDirOpen(RequestRoot
)) == NULL
)
766 cupsdLogMessage(CUPSD_LOG_ERROR
,
767 "cupsdLoadAllJobs: Unable to open spool directory %s: %s",
768 RequestRoot
, strerror(errno
));
773 * Read all the c##### files...
776 while ((dent
= cupsDirRead(dir
)) != NULL
)
777 if (strlen(dent
->filename
) >= 6 && dent
->filename
[0] == 'c')
780 * Allocate memory for the job...
783 if ((job
= calloc(sizeof(cupsd_job_t
), 1)) == NULL
)
785 cupsdLogMessage(CUPSD_LOG_ERROR
,
786 "cupsdLoadAllJobs: Ran out of memory for jobs!");
791 if ((job
->attrs
= ippNew()) == NULL
)
794 cupsdLogMessage(CUPSD_LOG_ERROR
,
795 "cupsdLoadAllJobs: Ran out of memory for job attributes!");
801 * Assign the job ID...
804 job
->id
= atoi(dent
->filename
+ 1);
805 job
->back_pipes
[0] = -1;
806 job
->back_pipes
[1] = -1;
807 job
->print_pipes
[0] = -1;
808 job
->print_pipes
[1] = -1;
810 cupsdLogMessage(CUPSD_LOG_DEBUG
,
811 "cupsdLoadAllJobs: Loading attributes for job %d...",
814 if (job
->id
>= NextJobId
)
815 NextJobId
= job
->id
+ 1;
818 * Load the job control file...
821 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->filename
);
822 if ((fd
= open(filename
, O_RDONLY
)) < 0)
824 cupsdLogMessage(CUPSD_LOG_ERROR
,
825 "cupsdLoadAllJobs: Unable to open job control file \"%s\" - %s!",
826 filename
, strerror(errno
));
827 ippDelete(job
->attrs
);
834 if (ippReadFile(fd
, job
->attrs
) != IPP_DATA
)
836 cupsdLogMessage(CUPSD_LOG_ERROR
,
837 "cupsdLoadAllJobs: Unable to read job control file \"%s\"!",
840 ippDelete(job
->attrs
);
849 if ((job
->state
= ippFindAttribute(job
->attrs
, "job-state", IPP_TAG_ENUM
)) == NULL
)
851 cupsdLogMessage(CUPSD_LOG_ERROR
,
852 "cupsdLoadAllJobs: Missing or bad job-state attribute in control file \"%s\"!",
854 ippDelete(job
->attrs
);
860 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri", IPP_TAG_URI
)) == NULL
)
862 cupsdLogMessage(CUPSD_LOG_ERROR
,
863 "cupsdLoadAllJobs: No job-printer-uri attribute in control file \"%s\"!",
865 ippDelete(job
->attrs
);
871 httpSeparateURI(attr
->values
[0].string
.text
, method
, sizeof(method
),
872 username
, sizeof(username
), host
, sizeof(host
), &port
,
873 resource
, sizeof(resource
));
875 if ((dest
= cupsdValidateDest(host
, resource
, &(job
->dtype
),
878 cupsdLogMessage(CUPSD_LOG_ERROR
,
879 "cupsdLoadAllJobs: Unable to queue job for destination \"%s\"!",
880 attr
->values
[0].string
.text
);
881 ippDelete(job
->attrs
);
887 cupsdSetString(&job
->dest
, dest
);
889 job
->sheets
= ippFindAttribute(job
->attrs
, "job-media-sheets-completed",
891 job
->job_sheets
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
893 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority", IPP_TAG_INTEGER
)) == NULL
)
895 cupsdLogMessage(CUPSD_LOG_ERROR
,
896 "cupsdLoadAllJobs: Missing or bad job-priority attribute in control file \"%s\"!",
898 ippDelete(job
->attrs
);
903 job
->priority
= attr
->values
[0].integer
;
905 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-user-name", IPP_TAG_NAME
)) == NULL
)
907 cupsdLogMessage(CUPSD_LOG_ERROR
,
908 "cupsdLoadAllJobs: Missing or bad job-originating-user-name attribute in control file \"%s\"!",
910 ippDelete(job
->attrs
);
915 cupsdSetString(&job
->username
, attr
->values
[0].string
.text
);
918 * Insert the job into the array, sorting by job priority and ID...
921 cupsArrayAdd(Jobs
, job
);
922 if (job
->state
->values
[0].integer
< IPP_JOB_STOPPED
)
923 cupsArrayAdd(ActiveJobs
,job
);
926 * Set the job hold-until time and state...
929 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
931 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
932 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
935 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
937 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
);
939 else if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
940 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
944 * Read all the d##### files...
949 while ((dent
= cupsDirRead(dir
)) != NULL
)
950 if (strlen(dent
->filename
) > 7 && dent
->filename
[0] == 'd' &&
951 strchr(dent
->filename
, '-'))
957 jobid
= atoi(dent
->filename
+ 1);
958 fileid
= atoi(strchr(dent
->filename
, '-') + 1);
960 cupsdLogMessage(CUPSD_LOG_DEBUG
,
961 "cupsdLoadAllJobs: Auto-typing document file %s...",
964 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->filename
);
966 if ((job
= cupsdFindJob(jobid
)) == NULL
)
968 cupsdLogMessage(CUPSD_LOG_ERROR
,
969 "cupsdLoadAllJobs: Orphaned print file \"%s\"!",
975 if (fileid
> job
->num_files
)
977 if (job
->num_files
== 0)
979 compressions
= (int *)calloc(fileid
, sizeof(int));
980 filetypes
= (mime_type_t
**)calloc(fileid
, sizeof(mime_type_t
*));
984 compressions
= (int *)realloc(job
->compressions
,
985 sizeof(int) * fileid
);
986 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
987 sizeof(mime_type_t
*) * fileid
);
990 if (compressions
== NULL
|| filetypes
== NULL
)
992 cupsdLogMessage(CUPSD_LOG_ERROR
, "cupsdLoadAllJobs: Ran out of memory for job file types!");
996 job
->compressions
= compressions
;
997 job
->filetypes
= filetypes
;
998 job
->num_files
= fileid
;
1001 job
->filetypes
[fileid
- 1] = mimeFileType(MimeDatabase
, filename
,
1002 job
->compressions
+ fileid
- 1);
1004 if (job
->filetypes
[fileid
- 1] == NULL
)
1005 job
->filetypes
[fileid
- 1] = mimeType(MimeDatabase
, "application",
1012 * Clean out old jobs as needed...
1020 * 'cupsdMoveJob()' - Move the specified job to a different destination.
1024 cupsdMoveJob(cupsd_job_t
*job
, /* I - Job */
1025 const char *dest
) /* I - Destination */
1027 ipp_attribute_t
*attr
; /* job-printer-uri attribute */
1028 cupsd_printer_t
*p
; /* Destination printer or class */
1032 * Find the printer...
1035 if ((p
= cupsdFindDest(dest
)) == NULL
)
1039 * Don't move completed jobs...
1042 if (job
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
1046 * Change the destination information...
1049 cupsdSetString(&job
->dest
, dest
);
1050 job
->dtype
= p
->type
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
1051 CUPS_PRINTER_IMPLICIT
);
1053 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri", IPP_TAG_URI
)) != NULL
)
1054 cupsdSetString(&(attr
->values
[0].string
.text
), p
->uri
);
1061 * 'cupsdReleaseJob()' - Release the specified job.
1065 cupsdReleaseJob(cupsd_job_t
*job
) /* I - Job */
1067 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdReleaseJob: id = %d", job
->id
);
1069 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
1071 DEBUG_puts("cupsdReleaseJob: setting state to pending...");
1073 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1081 * 'cupsdRestartJob()' - Restart the specified job.
1085 cupsdRestartJob(cupsd_job_t
*job
) /* I - Job */
1087 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdRestartJob: id = %d", job
->id
);
1089 if (job
->state
->values
[0].integer
== IPP_JOB_STOPPED
|| JobFiles
)
1092 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1100 * 'cupsdSaveJob()' - Save a job to disk.
1104 cupsdSaveJob(cupsd_job_t
*job
) /* I - Job */
1106 char filename
[1024]; /* Job control filename */
1107 int fd
; /* File descriptor */
1110 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
, job
->id
);
1112 if ((fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
1114 cupsdLogMessage(CUPSD_LOG_ERROR
,
1115 "cupsdSaveJob: Unable to create job control file \"%s\" - %s.",
1116 filename
, strerror(errno
));
1121 fchown(fd
, RunUser
, Group
);
1123 ippWriteFile(fd
, job
->attrs
);
1125 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSaveJob: Closing file %d...", fd
);
1132 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job...
1136 cupsdSetJobHoldUntil(cupsd_job_t
*job
, /* I - Job */
1137 const char *when
) /* I - When to resume */
1139 time_t curtime
; /* Current time */
1140 struct tm
*curdate
; /* Current date */
1141 int hour
; /* Hold hour */
1142 int minute
; /* Hold minute */
1143 int second
; /* Hold second */
1146 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdSetJobHoldUntil(%d, \"%s\")",
1151 if (!strcmp(when
, "indefinite") || !strcmp(when
, "authenticated"))
1154 * Hold indefinitely...
1157 job
->hold_until
= 0;
1159 else if (!strcmp(when
, "day-time"))
1162 * Hold to 6am the next morning unless local time is < 6pm.
1165 curtime
= time(NULL
);
1166 curdate
= localtime(&curtime
);
1168 if (curdate
->tm_hour
< 18)
1169 job
->hold_until
= curtime
;
1171 job
->hold_until
= curtime
+
1172 ((29 - curdate
->tm_hour
) * 60 + 59 -
1173 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1175 else if (!strcmp(when
, "evening") || strcmp(when
, "night"))
1178 * Hold to 6pm unless local time is > 6pm or < 6am.
1181 curtime
= time(NULL
);
1182 curdate
= localtime(&curtime
);
1184 if (curdate
->tm_hour
< 6 || curdate
->tm_hour
>= 18)
1185 job
->hold_until
= curtime
;
1187 job
->hold_until
= curtime
+
1188 ((17 - curdate
->tm_hour
) * 60 + 59 -
1189 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1191 else if (!strcmp(when
, "second-shift"))
1194 * Hold to 4pm unless local time is > 4pm.
1197 curtime
= time(NULL
);
1198 curdate
= localtime(&curtime
);
1200 if (curdate
->tm_hour
>= 16)
1201 job
->hold_until
= curtime
;
1203 job
->hold_until
= curtime
+
1204 ((15 - curdate
->tm_hour
) * 60 + 59 -
1205 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1207 else if (!strcmp(when
, "third-shift"))
1210 * Hold to 12am unless local time is < 8am.
1213 curtime
= time(NULL
);
1214 curdate
= localtime(&curtime
);
1216 if (curdate
->tm_hour
< 8)
1217 job
->hold_until
= curtime
;
1219 job
->hold_until
= curtime
+
1220 ((23 - curdate
->tm_hour
) * 60 + 59 -
1221 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1223 else if (!strcmp(when
, "weekend"))
1226 * Hold to weekend unless we are in the weekend.
1229 curtime
= time(NULL
);
1230 curdate
= localtime(&curtime
);
1232 if (curdate
->tm_wday
|| curdate
->tm_wday
== 6)
1233 job
->hold_until
= curtime
;
1235 job
->hold_until
= curtime
+
1236 (((5 - curdate
->tm_wday
) * 24 +
1237 (17 - curdate
->tm_hour
)) * 60 + 59 -
1238 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1240 else if (sscanf(when
, "%d:%d:%d", &hour
, &minute
, &second
) >= 2)
1243 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1246 curtime
= time(NULL
);
1247 curdate
= gmtime(&curtime
);
1249 job
->hold_until
= curtime
+
1250 ((hour
- curdate
->tm_hour
) * 60 + minute
-
1251 curdate
->tm_min
) * 60 + second
- curdate
->tm_sec
;
1254 * Hold until next day as needed...
1257 if (job
->hold_until
< curtime
)
1258 job
->hold_until
+= 24 * 60 * 60 * 60;
1261 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdSetJobHoldUntil: hold_until = %d",
1262 (int)job
->hold_until
);
1267 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
1268 * the list as needed.
1272 cupsdSetJobPriority(
1273 cupsd_job_t
*job
, /* I - Job ID */
1274 int priority
) /* I - New priority (0 to 100) */
1276 ipp_attribute_t
*attr
; /* Job attribute */
1280 * Don't change completed jobs...
1283 if (job
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
1287 * Set the new priority and re-add the job into the active list...
1290 cupsArrayRemove(ActiveJobs
, job
);
1292 job
->priority
= priority
;
1294 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
1295 attr
->values
[0].integer
= priority
;
1297 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1300 cupsArrayAdd(ActiveJobs
, job
);
1307 * 'cupsdStartJob()' - Start a print job.
1311 cupsdStartJob(cupsd_job_t
*job
, /* I - Job ID */
1312 cupsd_printer_t
*printer
) /* I - Printer to print job */
1314 int i
; /* Looping var */
1315 int slot
; /* Pipe slot */
1316 int num_filters
; /* Number of filters for job */
1317 mime_filter_t
*filters
; /* Filters for job */
1318 char method
[255], /* Method for output */
1319 *optptr
, /* Pointer to options */
1320 *valptr
; /* Pointer in value string */
1321 ipp_attribute_t
*attr
; /* Current attribute */
1322 int pid
; /* Process ID of new filter process */
1323 int banner_page
; /* 1 if banner page, 0 otherwise */
1324 int statusfds
[2], /* Pipes used between the filters and scheduler */
1325 filterfds
[2][2];/* Pipes used between the filters */
1326 int envc
; /* Number of environment variables */
1327 char *argv
[8], /* Filter command-line arguments */
1328 sani_uri
[1024], /* Sanitized DEVICE_URI env var */
1329 filename
[1024], /* Job filename */
1330 command
[1024], /* Full path to filter/backend command */
1331 jobid
[255], /* Job ID string */
1332 title
[IPP_MAX_NAME
],
1333 /* Job title string */
1334 copies
[255], /* # copies string */
1335 *envp
[100], /* Environment variables */
1336 charset
[255], /* CHARSET environment variable */
1337 class_name
[255],/* CLASS environment variable */
1338 classification
[1024],
1339 /* CLASSIFICATION environment variable */
1341 /* CONTENT_TYPE environment variable */
1343 /* DEVICE_URI environment variable */
1344 lang
[255], /* LANG environment variable */
1345 ppd
[1024], /* PPD environment variable */
1347 /* PRINTER environment variable */
1349 /* RIP_MAX_CACHE environment variable */
1350 static char *options
= NULL
;/* Full list of options */
1351 static int optlength
= 0; /* Length of option buffer */
1354 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob() id = %d, file = %d/%d",
1355 job
->id
, job
->current_file
, job
->num_files
);
1357 if (job
->num_files
== 0)
1359 cupsdLogMessage(CUPSD_LOG_ERROR
, "Job ID %d has no files! Cancelling it!",
1362 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1363 "Job canceled because it has no files.");
1365 cupsdCancelJob(job
, 0);
1370 * Figure out what filters are required to convert from
1371 * the source to the destination type...
1380 * Remote jobs and raw queues go directly to the printer without
1384 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1385 "cupsdStartJob: Sending job to queue tagged as raw...");
1392 * Local jobs get filtered...
1395 filters
= mimeFilter(MimeDatabase
, job
->filetypes
[job
->current_file
],
1396 printer
->filetype
, &num_filters
, MAX_FILTERS
- 1);
1398 if (num_filters
== 0)
1400 cupsdLogMessage(CUPSD_LOG_ERROR
,
1401 "Unable to convert file %d to printable format for job %d!",
1402 job
->current_file
, job
->id
);
1403 cupsdLogMessage(CUPSD_LOG_INFO
,
1404 "Hint: Do you have ESP Ghostscript installed?");
1406 if (LogLevel
< CUPSD_LOG_DEBUG
)
1407 cupsdLogMessage(CUPSD_LOG_INFO
,
1408 "Hint: Try setting the LogLevel to \"debug\".");
1410 job
->current_file
++;
1412 if (job
->current_file
== job
->num_files
)
1414 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1415 "Job canceled because it has no files that can be printed.");
1417 cupsdCancelJob(job
, 0);
1424 * Remove NULL ("-") filters...
1427 for (i
= 0; i
< num_filters
;)
1428 if (strcmp(filters
[i
].filter
, "-") == 0)
1431 if (i
< num_filters
)
1432 memcpy(filters
+ i
, filters
+ i
+ 1,
1433 (num_filters
- i
) * sizeof(mime_filter_t
));
1438 if (num_filters
== 0)
1446 * Compute filter cost...
1449 for (i
= 0; i
< num_filters
; i
++)
1450 job
->cost
+= filters
[i
].cost
;
1455 * See if the filter cost is too high...
1458 if ((FilterLevel
+ job
->cost
) > FilterLimit
&& FilterLevel
> 0 &&
1462 * Don't print this job quite yet...
1465 if (filters
!= NULL
)
1468 cupsdLogMessage(CUPSD_LOG_INFO
,
1469 "Holding job %d because filter limit has been reached.",
1471 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1472 "cupsdStartJob: id=%d, file=%d, cost=%d, level=%d, limit=%d",
1473 job
->id
, job
->current_file
, job
->cost
, FilterLevel
,
1478 FilterLevel
+= job
->cost
;
1481 * Add decompression filters, if any...
1484 if (job
->compressions
[job
->current_file
])
1487 * Add gziptoany filter to the front of the list...
1490 mime_filter_t
*temp_filters
;
1492 if (num_filters
== 0)
1493 temp_filters
= malloc(sizeof(mime_filter_t
));
1495 temp_filters
= realloc(filters
,
1496 sizeof(mime_filter_t
) * (num_filters
+ 1));
1498 if (temp_filters
== NULL
)
1500 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add decompression filter - %s",
1503 if (filters
!= NULL
)
1506 job
->current_file
++;
1508 if (job
->current_file
== job
->num_files
)
1510 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1511 "Job canceled because the print file could not be decompressed.");
1513 cupsdCancelJob(job
, 0);
1519 filters
= temp_filters
;
1520 memmove(filters
+ 1, filters
, num_filters
* sizeof(mime_filter_t
));
1521 *filters
= gziptoany_filter
;
1526 * Add port monitor, if any...
1529 if (printer
->port_monitor
)
1532 * Add port monitor to the end of the list...
1535 mime_filter_t
*temp_filters
;
1537 if (num_filters
== 0)
1538 temp_filters
= malloc(sizeof(mime_filter_t
));
1540 temp_filters
= realloc(filters
,
1541 sizeof(mime_filter_t
) * (num_filters
+ 1));
1543 if (temp_filters
== NULL
)
1545 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add port monitor - %s",
1548 if (filters
!= NULL
)
1551 job
->current_file
++;
1553 if (job
->current_file
== job
->num_files
)
1555 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1556 "Job canceled because the port monitor could not be added.");
1558 cupsdCancelJob(job
, 0);
1564 filters
= temp_filters
;
1565 memset(filters
+ num_filters
, 0, sizeof(mime_filter_t
));
1566 snprintf(filters
[num_filters
].filter
, sizeof(filters
[num_filters
].filter
),
1567 "%s/monitor/%s", ServerBin
, printer
->port_monitor
);
1572 * Update the printer and job state to "processing"...
1575 job
->state
->values
[0].integer
= IPP_JOB_PROCESSING
;
1577 job
->printer
= printer
;
1579 cupsdSetPrinterState(printer
, IPP_PRINTER_PROCESSING
, 0);
1581 if (job
->current_file
== 0)
1583 set_time(job
, "time-at-processing");
1584 cupsdOpenPipe(job
->back_pipes
);
1588 * Determine if we are printing a banner page or not...
1591 if (job
->job_sheets
== NULL
)
1593 cupsdLogMessage(CUPSD_LOG_DEBUG
, "No job-sheets attribute.");
1594 if ((job
->job_sheets
=
1595 ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
1596 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1597 "... but someone added one without setting job_sheets!");
1599 else if (job
->job_sheets
->num_values
== 1)
1600 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s",
1601 job
->job_sheets
->values
[0].string
.text
);
1603 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s,%s",
1604 job
->job_sheets
->values
[0].string
.text
,
1605 job
->job_sheets
->values
[1].string
.text
);
1607 if (printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
))
1609 else if (job
->job_sheets
== NULL
)
1611 else if (strcasecmp(job
->job_sheets
->values
[0].string
.text
, "none") != 0 &&
1612 job
->current_file
== 0)
1614 else if (job
->job_sheets
->num_values
> 1 &&
1615 strcasecmp(job
->job_sheets
->values
[1].string
.text
, "none") != 0 &&
1616 job
->current_file
== (job
->num_files
- 1))
1621 cupsdLogMessage(CUPSD_LOG_DEBUG
, "banner_page = %d", banner_page
);
1624 * Building the options string is harder than it needs to be, but
1625 * for the moment we need to pass strings for command-line args and
1626 * not IPP attribute pointers... :)
1628 * First allocate/reallocate the option buffer as needed...
1631 i
= ipp_length(job
->attrs
);
1638 optptr
= realloc(options
, i
);
1642 cupsdLogMessage(CUPSD_LOG_CRIT
,
1643 "cupsdStartJob: Unable to allocate %d bytes for option buffer for job %d!",
1646 if (filters
!= NULL
)
1649 FilterLevel
-= job
->cost
;
1651 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1652 "Job canceled because the server ran out of memory.");
1654 cupsdCancelJob(job
, 0);
1663 * Now loop through the attributes and convert them to the textual
1664 * representation used by the filters...
1670 snprintf(title
, sizeof(title
), "%s-%d", printer
->name
, job
->id
);
1671 strcpy(copies
, "1");
1673 for (attr
= job
->attrs
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1675 if (strcmp(attr
->name
, "copies") == 0 &&
1676 attr
->value_tag
== IPP_TAG_INTEGER
)
1679 * Don't use the # copies attribute if we are printing the job sheets...
1683 sprintf(copies
, "%d", attr
->values
[0].integer
);
1685 else if (strcmp(attr
->name
, "job-name") == 0 &&
1686 (attr
->value_tag
== IPP_TAG_NAME
||
1687 attr
->value_tag
== IPP_TAG_NAMELANG
))
1688 strlcpy(title
, attr
->values
[0].string
.text
, sizeof(title
));
1689 else if (attr
->group_tag
== IPP_TAG_JOB
)
1692 * Filter out other unwanted attributes...
1695 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
1696 attr
->value_tag
== IPP_TAG_NAMELANG
||
1697 attr
->value_tag
== IPP_TAG_TEXTLANG
||
1698 attr
->value_tag
== IPP_TAG_URI
||
1699 attr
->value_tag
== IPP_TAG_URISCHEME
||
1700 attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
) /* Not yet supported */
1703 if (strncmp(attr
->name
, "time-", 5) == 0)
1706 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1707 !(printer
->type
& CUPS_PRINTER_REMOTE
))
1710 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1711 strcmp(attr
->name
, "job-billing") != 0 &&
1712 strcmp(attr
->name
, "job-sheets") != 0 &&
1713 strcmp(attr
->name
, "job-hold-until") != 0 &&
1714 strcmp(attr
->name
, "job-priority") != 0)
1717 if ((strcmp(attr
->name
, "page-label") == 0 ||
1718 strcmp(attr
->name
, "page-border") == 0 ||
1719 strncmp(attr
->name
, "number-up", 9) == 0 ||
1720 strcmp(attr
->name
, "page-set") == 0) &&
1725 * Otherwise add them to the list...
1728 if (optptr
> options
)
1729 strlcat(optptr
, " ", optlength
- (optptr
- options
));
1731 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
1733 strlcat(optptr
, attr
->name
, optlength
- (optptr
- options
));
1734 strlcat(optptr
, "=", optlength
- (optptr
- options
));
1737 for (i
= 0; i
< attr
->num_values
; i
++)
1740 strlcat(optptr
, ",", optlength
- (optptr
- options
));
1742 optptr
+= strlen(optptr
);
1744 switch (attr
->value_tag
)
1746 case IPP_TAG_INTEGER
:
1748 snprintf(optptr
, optlength
- (optptr
- options
),
1749 "%d", attr
->values
[i
].integer
);
1752 case IPP_TAG_BOOLEAN
:
1753 if (!attr
->values
[i
].boolean
)
1754 strlcat(optptr
, "no", optlength
- (optptr
- options
));
1756 case IPP_TAG_NOVALUE
:
1757 strlcat(optptr
, attr
->name
,
1758 optlength
- (optptr
- options
));
1761 case IPP_TAG_RANGE
:
1762 if (attr
->values
[i
].range
.lower
== attr
->values
[i
].range
.upper
)
1763 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1764 "%d", attr
->values
[i
].range
.lower
);
1766 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1767 "%d-%d", attr
->values
[i
].range
.lower
,
1768 attr
->values
[i
].range
.upper
);
1771 case IPP_TAG_RESOLUTION
:
1772 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1773 "%dx%d%s", attr
->values
[i
].resolution
.xres
,
1774 attr
->values
[i
].resolution
.yres
,
1775 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
1779 case IPP_TAG_STRING
:
1782 case IPP_TAG_KEYWORD
:
1783 case IPP_TAG_CHARSET
:
1784 case IPP_TAG_LANGUAGE
:
1785 for (valptr
= attr
->values
[i
].string
.text
; *valptr
;)
1787 if (strchr(" \t\n\\\'\"", *valptr
))
1789 *optptr
++ = *valptr
++;
1796 break; /* anti-compiler-warning-code */
1800 optptr
+= strlen(optptr
);
1805 * Build the command-line arguments for the filters. Each filter
1806 * has 6 or 7 arguments:
1810 * argv[2] = username
1812 * argv[4] = # copies
1814 * argv[6] = filename (optional; normally stdin)
1816 * This allows legacy printer drivers that use the old System V
1817 * printing interface to be used by CUPS.
1820 sprintf(jobid
, "%d", job
->id
);
1821 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
1822 job
->id
, job
->current_file
+ 1);
1824 argv
[0] = printer
->name
;
1826 argv
[2] = job
->username
;
1833 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1834 "cupsdStartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1835 argv
[0], argv
[1], argv
[2], argv
[3], argv
[4], argv
[5], argv
[6]);
1838 * Create environment variable strings for the filters...
1841 attr
= ippFindAttribute(job
->attrs
, "attributes-natural-language",
1844 switch (strlen(attr
->values
[0].string
.text
))
1848 * This is an unknown or badly formatted language code; use
1849 * the POSIX locale...
1852 strcpy(lang
, "LANG=C");
1857 * Just the language code (ll)...
1860 snprintf(lang
, sizeof(lang
), "LANG=%s",
1861 attr
->values
[0].string
.text
);
1866 * Language and country code (ll-cc)...
1869 snprintf(lang
, sizeof(lang
), "LANG=%c%c_%c%c",
1870 attr
->values
[0].string
.text
[0],
1871 attr
->values
[0].string
.text
[1],
1872 toupper(attr
->values
[0].string
.text
[3] & 255),
1873 toupper(attr
->values
[0].string
.text
[4] & 255));
1877 attr
= ippFindAttribute(job
->attrs
, "document-format",
1880 (optptr
= strstr(attr
->values
[0].string
.text
, "charset=")) != NULL
)
1881 snprintf(charset
, sizeof(charset
), "CHARSET=%s", optptr
+ 8);
1884 attr
= ippFindAttribute(job
->attrs
, "attributes-charset",
1886 snprintf(charset
, sizeof(charset
), "CHARSET=%s",
1887 attr
->values
[0].string
.text
);
1890 snprintf(content_type
, sizeof(content_type
), "CONTENT_TYPE=%s/%s",
1891 job
->filetypes
[job
->current_file
]->super
,
1892 job
->filetypes
[job
->current_file
]->type
);
1893 snprintf(device_uri
, sizeof(device_uri
), "DEVICE_URI=%s", printer
->device_uri
);
1894 cupsdSanitizeURI(printer
->device_uri
, sani_uri
, sizeof(sani_uri
));
1895 snprintf(ppd
, sizeof(ppd
), "PPD=%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
1896 snprintf(printer_name
, sizeof(printer_name
), "PRINTER=%s", printer
->name
);
1897 snprintf(rip_max_cache
, sizeof(rip_max_cache
), "RIP_MAX_CACHE=%s", RIPCache
);
1899 envc
= cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
1901 envp
[envc
++] = charset
;
1902 envp
[envc
++] = lang
;
1903 envp
[envc
++] = ppd
;
1904 envp
[envc
++] = rip_max_cache
;
1905 envp
[envc
++] = content_type
;
1906 envp
[envc
++] = device_uri
;
1907 envp
[envc
++] = printer_name
;
1909 if (Classification
&& !banner_page
)
1911 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
1912 IPP_TAG_NAME
)) == NULL
)
1913 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1915 else if (attr
->num_values
> 1 &&
1916 strcmp(attr
->values
[1].string
.text
, "none") != 0)
1917 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1918 attr
->values
[1].string
.text
);
1920 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1921 attr
->values
[0].string
.text
);
1923 envp
[envc
++] = classification
;
1926 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
1928 snprintf(class_name
, sizeof(class_name
), "CLASS=%s", job
->dest
);
1929 envp
[envc
++] = class_name
;
1934 for (i
= 0; i
< envc
; i
++)
1935 if (strncmp(envp
[i
], "DEVICE_URI=", 11))
1936 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: envp[%d]=\"%s\"",
1939 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1940 "cupsdStartJob: envp[%d]=\"DEVICE_URI=%s\"", i
, sani_uri
);
1942 job
->current_file
++;
1945 * Now create processes for all of the filters...
1948 if (cupsdOpenPipe(statusfds
))
1950 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to create job status pipes - %s.",
1952 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1953 "Unable to create status pipes - %s.", strerror(errno
));
1955 cupsdAddPrinterHistory(printer
);
1957 if (filters
!= NULL
)
1960 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1961 "Job canceled because the server could not create the job status pipes.");
1963 cupsdCancelJob(job
, 0);
1967 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: statusfds = [ %d %d ]",
1968 statusfds
[0], statusfds
[1]);
1971 fcntl(statusfds
[0], F_SETFD
, FD_CLOEXEC
);
1972 fcntl(statusfds
[1], F_SETFD
, FD_CLOEXEC
);
1973 #endif /* FD_CLOEXEC */
1975 job
->status_buffer
= cupsdStatBufNew(statusfds
[0], "[Job %d]",
1978 memset(job
->filters
, 0, sizeof(job
->filters
));
1980 filterfds
[1][0] = open("/dev/null", O_RDONLY
);
1981 filterfds
[1][1] = -1;
1983 if (filterfds
[1][0] < 0)
1985 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to open \"/dev/null\" - %s.",
1987 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1988 "Unable to open \"/dev/null\" - %s.", strerror(errno
));
1990 cupsdAddPrinterHistory(printer
);
1992 if (filters
!= NULL
)
1995 cupsdClosePipe(statusfds
);
1996 cupsdCancelJob(job
, 0);
2000 fcntl(filterfds
[1][0], F_SETFD
, fcntl(filterfds
[1][0], F_GETFD
) | FD_CLOEXEC
);
2002 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2003 1, filterfds
[1][0], filterfds
[1][1]);
2005 for (i
= 0, slot
= 0; i
< num_filters
; i
++)
2007 if (filters
[i
].filter
[0] != '/')
2008 snprintf(command
, sizeof(command
), "%s/filter/%s", ServerBin
,
2011 strlcpy(command
, filters
[i
].filter
, sizeof(command
));
2013 if (i
< (num_filters
- 1))
2015 if (cupsdOpenPipe(filterfds
[slot
]))
2017 cupsdLogMessage(CUPSD_LOG_ERROR
,
2018 "Unable to create job filter pipes - %s.",
2020 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2021 "Unable to create filter pipes - %s.", strerror(errno
));
2022 cupsdAddPrinterHistory(printer
);
2024 if (filters
!= NULL
)
2027 cupsdClosePipe(statusfds
);
2028 cupsdClosePipe(filterfds
[!slot
]);
2030 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2031 "Job canceled because the server could not create the filter pipes.");
2033 cupsdCancelJob(job
, 0);
2039 if (job
->current_file
== 1)
2041 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
2043 if (cupsdOpenPipe(job
->print_pipes
))
2045 cupsdLogMessage(CUPSD_LOG_ERROR
,
2046 "Unable to create job backend pipes - %s.",
2048 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2049 "Unable to create backend pipes - %s.", strerror(errno
));
2050 cupsdAddPrinterHistory(printer
);
2052 if (filters
!= NULL
)
2055 cupsdClosePipe(statusfds
);
2056 cupsdClosePipe(filterfds
[!slot
]);
2058 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2059 "Job canceled because the server could not create the backend pipes.");
2061 cupsdCancelJob(job
, 0);
2067 job
->print_pipes
[0] = -1;
2068 if (!strncmp(printer
->device_uri
, "file:/dev/", 10) &&
2069 strcmp(printer
->device_uri
, "file:/dev/null"))
2070 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
2072 else if (!strncmp(printer
->device_uri
, "file:///dev/", 12) &&
2073 strcmp(printer
->device_uri
, "file:///dev/null"))
2074 job
->print_pipes
[1] = open(printer
->device_uri
+ 7,
2077 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
2078 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
2080 if (job
->print_pipes
[1] < 0)
2082 cupsdLogMessage(CUPSD_LOG_ERROR
,
2083 "Unable to open output file \"%s\" - %s.",
2084 printer
->device_uri
, strerror(errno
));
2085 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2086 "Unable to open output file \"%s\" - %s.",
2087 printer
->device_uri
, strerror(errno
));
2089 cupsdAddPrinterHistory(printer
);
2091 if (filters
!= NULL
)
2094 cupsdClosePipe(statusfds
);
2095 cupsdClosePipe(filterfds
[!slot
]);
2097 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2098 "Job canceled because the server could not open the output file.");
2100 cupsdCancelJob(job
, 0);
2104 fcntl(job
->print_pipes
[1], F_SETFD
,
2105 fcntl(job
->print_pipes
[1], F_GETFD
) | FD_CLOEXEC
);
2108 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdStartJob: print_pipes = [ %d %d ]",
2109 job
->print_pipes
[0], job
->print_pipes
[1]);
2112 filterfds
[slot
][0] = job
->print_pipes
[0];
2113 filterfds
[slot
][1] = job
->print_pipes
[1];
2116 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filter = \"%s\"", command
);
2117 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2118 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2120 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
2121 filterfds
[slot
][1], statusfds
[1],
2122 job
->back_pipes
[0], 0, job
->filters
+ i
);
2124 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2125 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2126 !slot
, filterfds
[!slot
][0], filterfds
[!slot
][1]);
2128 cupsdClosePipe(filterfds
[!slot
]);
2132 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start filter \"%s\" - %s.",
2133 filters
[i
].filter
, strerror(errno
));
2134 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2135 "Unable to start filter \"%s\" - %s.",
2136 filters
[i
].filter
, strerror(errno
));
2138 cupsdAddPrinterHistory(printer
);
2140 if (filters
!= NULL
)
2143 cupsdAddPrinterHistory(printer
);
2145 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2146 "Job canceled because the server could not execute a filter.");
2148 cupsdCancelJob(job
, 0);
2152 cupsdLogMessage(CUPSD_LOG_INFO
, "Started filter %s (PID %d) for job %d.",
2153 command
, pid
, job
->id
);
2159 if (filters
!= NULL
)
2163 * Finally, pipe the final output into a backend process if needed...
2166 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
2168 if (job
->current_file
== 1)
2170 sscanf(printer
->device_uri
, "%254[^:]", method
);
2171 snprintf(command
, sizeof(command
), "%s/backend/%s", ServerBin
, method
);
2175 filterfds
[slot
][0] = -1;
2176 filterfds
[slot
][1] = open("/dev/null", O_WRONLY
);
2178 if (filterfds
[slot
][1] < 0)
2180 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to open \"/dev/null\" - %s.",
2182 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2183 "Unable to open \"/dev/null\" - %s.", strerror(errno
));
2185 cupsdAddPrinterHistory(printer
);
2187 if (filters
!= NULL
)
2190 cupsdClosePipe(statusfds
);
2192 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2193 "Job canceled because the server could not open a file.");
2195 cupsdCancelJob(job
, 0);
2199 fcntl(filterfds
[slot
][1], F_SETFD
,
2200 fcntl(filterfds
[slot
][1], F_GETFD
) | FD_CLOEXEC
);
2202 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: backend = \"%s\"",
2204 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2205 "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2206 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2208 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
2209 filterfds
[slot
][1], statusfds
[1],
2210 job
->back_pipes
[1], 1,
2215 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start backend \"%s\" - %s.",
2216 method
, strerror(errno
));
2217 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2218 "Unable to start backend \"%s\" - %s.", method
, strerror(errno
));
2220 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2221 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2222 job
->print_pipes
[0], job
->print_pipes
[1]);
2224 cupsdClosePipe(job
->print_pipes
);
2226 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2227 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2228 job
->back_pipes
[0], job
->back_pipes
[1]);
2230 cupsdClosePipe(job
->back_pipes
);
2232 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2233 "Job canceled because the server could not execute the backend.");
2235 cupsdCancelJob(job
, 0);
2240 cupsdLogMessage(CUPSD_LOG_INFO
,
2241 "Started backend %s (PID %d) for job %d.",
2242 command
, pid
, job
->id
);
2246 if (job
->current_file
== job
->num_files
)
2248 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2249 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2250 job
->print_pipes
[0], job
->print_pipes
[1]);
2252 cupsdClosePipe(job
->print_pipes
);
2254 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2255 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2256 job
->back_pipes
[0], job
->back_pipes
[1]);
2258 cupsdClosePipe(job
->back_pipes
);
2263 filterfds
[slot
][0] = -1;
2264 filterfds
[slot
][1] = -1;
2266 if (job
->current_file
== job
->num_files
)
2268 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2269 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2270 job
->print_pipes
[0], job
->print_pipes
[1]);
2272 cupsdClosePipe(job
->print_pipes
);
2276 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2277 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2278 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2280 cupsdClosePipe(filterfds
[slot
]);
2282 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2283 "cupsdStartJob: Closing status output pipe %d...",
2286 close(statusfds
[1]);
2288 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2289 "cupsdStartJob: Adding fd %d to InputSet...",
2290 job
->status_buffer
->fd
);
2292 FD_SET(job
->status_buffer
->fd
, InputSet
);
2297 * 'cupsdStopAllJobs()' - Stop all print jobs.
2301 cupsdStopAllJobs(void)
2303 cupsd_job_t
*job
; /* Current job */
2306 DEBUG_puts("cupsdStopAllJobs()");
2308 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
2310 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
2311 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
2313 cupsdStopJob(job
, 1);
2314 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
2320 * 'cupsdStopJob()' - Stop a print job.
2324 cupsdStopJob(cupsd_job_t
*job
, /* I - Job */
2325 int force
) /* I - 1 = Force all filters to stop */
2327 int i
; /* Looping var */
2330 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStopJob: id = %d, force = %d",
2333 if (job
->state
->values
[0].integer
!= IPP_JOB_PROCESSING
)
2336 FilterLevel
-= job
->cost
;
2338 if (job
->status
< 0 &&
2339 !(job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
)) &&
2340 !(job
->printer
->type
& CUPS_PRINTER_FAX
) &&
2341 !strcmp(job
->printer
->error_policy
, "stop-printer"))
2342 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_STOPPED
, 1);
2343 else if (job
->printer
->state
!= IPP_PRINTER_STOPPED
)
2344 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_IDLE
, 0);
2346 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStopJob: printer state is %d",
2347 job
->printer
->state
);
2349 job
->state
->values
[0].integer
= IPP_JOB_STOPPED
;
2350 job
->printer
->job
= NULL
;
2351 job
->printer
= NULL
;
2353 job
->current_file
--;
2355 for (i
= 0; job
->filters
[i
]; i
++)
2356 if (job
->filters
[i
] > 0)
2358 cupsdEndProcess(job
->filters
[i
], force
);
2359 job
->filters
[i
] = 0;
2362 if (job
->backend
> 0)
2364 cupsdEndProcess(job
->backend
, force
);
2368 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2369 "cupsdStopJob: Closing print pipes [ %d %d ]...",
2370 job
->print_pipes
[0], job
->print_pipes
[1]);
2372 cupsdClosePipe(job
->print_pipes
);
2374 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2375 "cupsdStopJob: Closing back pipes [ %d %d ]...",
2376 job
->back_pipes
[0], job
->back_pipes
[1]);
2378 cupsdClosePipe(job
->back_pipes
);
2380 if (job
->status_buffer
)
2383 * Close the pipe and clear the input bit.
2386 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2387 "cupsdStopJob: Removing fd %d from InputSet...",
2388 job
->status_buffer
->fd
);
2390 FD_CLR(job
->status_buffer
->fd
, InputSet
);
2392 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2393 "cupsdStopJob: Closing status input pipe %d...",
2394 job
->status_buffer
->fd
);
2396 cupsdStatBufDelete(job
->status_buffer
);
2398 job
->status_buffer
= NULL
;
2404 * 'cupsdUpdateJob()' - Read a status update from a job's filters.
2408 cupsdUpdateJob(cupsd_job_t
*job
) /* I - Job to check */
2410 int i
; /* Looping var */
2411 int copies
; /* Number of copies printed */
2412 char message
[1024], /* Message text */
2413 *ptr
; /* Pointer update... */
2414 int loglevel
; /* Log level for message */
2417 while ((ptr
= cupsdStatBufUpdate(job
->status_buffer
, &loglevel
,
2418 message
, sizeof(message
))) != NULL
)
2421 * Process page and printer state messages as needed...
2424 if (loglevel
== CUPSD_LOG_PAGE
)
2427 * Page message; send the message to the page_log file and update the
2428 * job sheet count...
2431 if (job
->sheets
!= NULL
)
2433 if (!strncasecmp(message
, "total ", 6))
2436 * Got a total count of pages from a backend or filter...
2439 copies
= atoi(message
+ 6);
2440 copies
-= job
->sheets
->values
[0].integer
; /* Just track the delta */
2442 else if (!sscanf(message
, "%*d%d", &copies
))
2445 job
->sheets
->values
[0].integer
+= copies
;
2447 if (job
->printer
->page_limit
)
2448 cupsdUpdateQuota(job
->printer
, job
->username
, copies
, 0);
2451 cupsdLogPage(job
, message
);
2453 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS
, job
->printer
, job
,
2454 "Printed %d page(s).", job
->sheets
->values
[0].integer
);
2456 else if (loglevel
== CUPSD_LOG_STATE
)
2457 cupsdSetPrinterReasons(job
->printer
, message
);
2458 else if (loglevel
== CUPSD_LOG_ATTR
)
2461 * Set attribute(s)...
2467 if (!strchr(job
->status_buffer
->buffer
, '\n'))
2474 * See if all of the filters and the backend have returned their
2478 for (i
= 0; job
->filters
[i
] < 0; i
++);
2480 if (job
->filters
[i
])
2483 if (job
->current_file
>= job
->num_files
&& job
->backend
> 0)
2487 * Handle the end of job stuff...
2490 cupsdFinishJob(job
);
2496 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
2499 static int /* O - Difference */
2500 compare_active_jobs(void *first
, /* I - First job */
2501 void *second
, /* I - Second job */
2502 void *data
) /* I - App data (not used) */
2504 int diff
; /* Difference */
2507 if ((diff
= ((cupsd_job_t
*)first
)->priority
- ((cupsd_job_t
*)second
)->priority
) != 0)
2510 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
2515 * 'compare_jobs()' - Compare the job IDs of two jobs.
2518 static int /* O - Difference */
2519 compare_jobs(void *first
, /* I - First job */
2520 void *second
, /* I - Second job */
2521 void *data
) /* I - App data (not used) */
2523 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
2528 * 'ipp_length()' - Compute the size of the buffer needed to hold
2529 * the textual IPP attributes.
2532 int /* O - Size of buffer to hold IPP attributes */
2533 ipp_length(ipp_t
*ipp
) /* I - IPP request */
2535 int bytes
; /* Number of bytes */
2536 int i
; /* Looping var */
2537 ipp_attribute_t
*attr
; /* Current attribute */
2541 * Loop through all attributes...
2546 for (attr
= ipp
->attrs
; attr
!= NULL
; attr
= attr
->next
)
2549 * Skip attributes that won't be sent to filters...
2552 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
2553 attr
->value_tag
== IPP_TAG_NAMELANG
||
2554 attr
->value_tag
== IPP_TAG_TEXTLANG
||
2555 attr
->value_tag
== IPP_TAG_URI
||
2556 attr
->value_tag
== IPP_TAG_URISCHEME
)
2559 if (strncmp(attr
->name
, "time-", 5) == 0)
2563 * Add space for a leading space and commas between each value.
2564 * For the first attribute, the leading space isn't used, so the
2565 * extra byte can be used as the nul terminator...
2568 bytes
++; /* " " separator */
2569 bytes
+= attr
->num_values
; /* "," separators */
2572 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
2573 * other attributes appear as "foo=value1,value2,...,valueN".
2576 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
2577 bytes
+= strlen(attr
->name
);
2579 bytes
+= attr
->num_values
* strlen(attr
->name
);
2582 * Now add the size required for each value in the attribute...
2585 switch (attr
->value_tag
)
2587 case IPP_TAG_INTEGER
:
2590 * Minimum value of a signed integer is -2147483647, or 11 digits.
2593 bytes
+= attr
->num_values
* 11;
2596 case IPP_TAG_BOOLEAN
:
2598 * Add two bytes for each false ("no") value...
2601 for (i
= 0; i
< attr
->num_values
; i
++)
2602 if (!attr
->values
[i
].boolean
)
2606 case IPP_TAG_RANGE
:
2608 * A range is two signed integers separated by a hyphen, or
2609 * 23 characters max.
2612 bytes
+= attr
->num_values
* 23;
2615 case IPP_TAG_RESOLUTION
:
2617 * A resolution is two signed integers separated by an "x" and
2618 * suffixed by the units, or 26 characters max.
2621 bytes
+= attr
->num_values
* 26;
2624 case IPP_TAG_STRING
:
2627 case IPP_TAG_KEYWORD
:
2628 case IPP_TAG_CHARSET
:
2629 case IPP_TAG_LANGUAGE
:
2631 * Strings can contain characters that need quoting. We need
2632 * at least 2 * len + 2 characters to cover the quotes and
2633 * any backslashes in the string.
2636 for (i
= 0; i
< attr
->num_values
; i
++)
2637 bytes
+= 2 * strlen(attr
->values
[i
].string
.text
) + 2;
2641 break; /* anti-compiler-warning-code */
2650 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2654 set_time(cupsd_job_t
*job
, /* I - Job to update */
2655 const char *name
) /* I - Name of attribute */
2657 ipp_attribute_t
*attr
; /* Time attribute */
2660 if ((attr
= ippFindAttribute(job
->attrs
, name
, IPP_TAG_ZERO
)) != NULL
)
2662 attr
->value_tag
= IPP_TAG_INTEGER
;
2663 attr
->values
[0].integer
= time(NULL
);
2669 * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
2673 set_hold_until(cupsd_job_t
*job
, /* I - Job to update */
2674 time_t holdtime
) /* I - Hold until time */
2676 ipp_attribute_t
*attr
; /* job-hold-until attribute */
2677 struct tm
*holddate
; /* Hold date */
2678 char holdstr
[64]; /* Hold time */
2682 * Set the hold_until value and hold the job...
2685 cupsdLogMessage(CUPSD_LOG_DEBUG
, "set_hold_until: hold_until = %d", (int)holdtime
);
2687 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
2688 job
->hold_until
= holdtime
;
2691 * Update the job-hold-until attribute with a string representing GMT
2692 * time (HH:MM:SS)...
2695 holddate
= gmtime(&holdtime
);
2696 snprintf(holdstr
, sizeof(holdstr
), "%d:%d:%d", holddate
->tm_hour
,
2697 holddate
->tm_min
, holddate
->tm_sec
);
2699 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
2700 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
2703 * Either add the attribute or update the value of the existing one
2707 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
2708 "job-hold-until", NULL
, holdstr
);
2710 cupsdSetString(&attr
->values
[0].string
.text
, holdstr
);
2717 * End of "$Id: job.c 4906 2006-01-10 20:53:28Z mike $".