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 cancelled 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...
340 if (printer
->state
== IPP_PRINTER_IDLE
|| /* Printer is idle */
341 ((printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is remote */
342 !printer
->job
)) /* and not printing a job */
343 cupsdStartJob(job
, printer
);
351 * 'cupsdCleanJobs()' - Clean out old jobs.
357 cupsd_job_t
*job
; /* Current job */
363 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
364 job
&& cupsArrayCount(Jobs
) >= MaxJobs
;
365 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
366 if (job
->state
->values
[0].integer
>= IPP_JOB_CANCELLED
)
367 cupsdCancelJob(job
, 1);
372 * 'cupsdFinishJob()' - Finish a job.
376 cupsdFinishJob(cupsd_job_t
*job
) /* I - Job */
378 int job_history
; /* Did cupsdCancelJob() keep the job? */
379 cupsd_printer_t
*printer
; /* Current printer */
382 cupsdLogMessage(CUPSD_LOG_DEBUG
,
383 "cupsdFinishJob: job %d, file %d is complete.",
384 job
->id
, job
->current_file
- 1);
386 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdFinishJob: job->status is %d",
389 if (job
->status_buffer
&& job
->current_file
>= job
->num_files
)
392 * Close the pipe and clear the input bit.
395 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
396 "cupsdFinishJob: Removing fd %d from InputSet...",
397 job
->status_buffer
->fd
);
399 FD_CLR(job
->status_buffer
->fd
, InputSet
);
401 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
402 "cupsdFinishJob: Closing status input pipe %d...",
403 job
->status_buffer
->fd
);
405 cupsdStatBufDelete(job
->status_buffer
);
407 job
->status_buffer
= NULL
;
413 * Backend had errors; stop it...
416 printer
= job
->printer
;
418 switch (-job
->status
)
421 case CUPS_BACKEND_FAILED
:
423 * Backend failure, use the error-policy to determine how to
427 cupsdStopJob(job
, 0);
428 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
432 * If the job was queued to a class, try requeuing it... For
433 * faxes and retry-job queues, hold the current job for 5 minutes.
436 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
438 else if ((printer
->type
& CUPS_PRINTER_FAX
) ||
439 !strcmp(printer
->error_policy
, "retry-job"))
442 * See how many times we've tried to send the job; if more than
443 * the limit, cancel the job.
448 if (job
->tries
>= FaxRetryLimit
)
454 cupsdLogMessage(CUPSD_LOG_ERROR
,
455 "Canceling job %d since it could not be sent after %d tries.",
456 job
->id
, FaxRetryLimit
);
458 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
459 "Job cancelled since it could not be sent after %d tries.",
462 cupsdCancelJob(job
, 0);
467 * Try again in N seconds...
470 set_hold_until(job
, time(NULL
) + FaxRetryInterval
);
473 else if (!strcmp(printer
->error_policy
, "abort-job"))
474 cupsdCancelJob(job
, 0);
477 case CUPS_BACKEND_CANCEL
:
482 cupsdCancelJob(job
, 0);
485 case CUPS_BACKEND_HOLD
:
490 cupsdStopJob(job
, 0);
491 cupsdSetJobHoldUntil(job
, "indefinite");
495 case CUPS_BACKEND_STOP
:
497 * Stop the printer...
500 cupsdStopJob(job
, 0);
502 cupsdSetPrinterState(printer
, IPP_PRINTER_STOPPED
, 1);
505 case CUPS_BACKEND_AUTH_REQUIRED
:
506 cupsdStopJob(job
, 0);
507 cupsdSetJobHoldUntil(job
, "authenticated");
510 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
511 "Authentication is required for job %d.", job
->id
);
516 * Try printing another job...
521 else if (job
->status
> 0)
524 * Filter had errors; cancel it...
527 if (job
->current_file
< job
->num_files
)
528 cupsdStartJob(job
, job
->printer
);
531 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
532 "Job aborted due to filter errors; please consult the "
533 "error_log file for details.");
535 job_history
= JobHistory
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
);
537 cupsdCancelJob(job
, 0);
541 job
->state
->values
[0].integer
= IPP_JOB_ABORTED
;
551 * Job printed successfully; cancel it...
554 if (job
->current_file
< job
->num_files
)
556 FilterLevel
-= job
->cost
;
557 cupsdStartJob(job
, job
->printer
);
561 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
562 "Job completed successfully.");
564 job_history
= JobHistory
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
);
566 cupsdCancelJob(job
, 0);
570 job
->state
->values
[0].integer
= IPP_JOB_COMPLETED
;
581 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
585 cupsdFreeAllJobs(void)
587 cupsd_job_t
*job
; /* Current job */
594 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
596 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
598 cupsArrayRemove(Jobs
, job
);
599 cupsArrayRemove(ActiveJobs
, job
);
601 ippDelete(job
->attrs
);
603 if (job
->num_files
> 0)
605 free(job
->compressions
);
606 free(job
->filetypes
);
612 cupsdReleaseSignals();
617 * 'cupsdFindJob()' - Find the specified job.
620 cupsd_job_t
* /* O - Job data */
621 cupsdFindJob(int id
) /* I - Job ID */
623 cupsd_job_t key
; /* Search key */
628 return ((cupsd_job_t
*)cupsArrayFind(Jobs
, &key
));
633 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
634 * or held jobs in a printer or class.
637 int /* O - Job count */
638 cupsdGetPrinterJobCount(
639 const char *dest
) /* I - Printer or class name */
641 int count
; /* Job count */
642 cupsd_job_t
*job
; /* Current job */
645 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
647 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
648 if (!strcasecmp(job
->dest
, dest
))
656 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
657 * or held jobs for a user.
660 int /* O - Job count */
661 cupsdGetUserJobCount(
662 const char *username
) /* I - Username */
664 int count
; /* Job count */
665 cupsd_job_t
*job
; /* Current job */
668 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
670 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
671 if (!strcasecmp(job
->username
, username
))
679 * 'cupsdHoldJob()' - Hold the specified job.
683 cupsdHoldJob(cupsd_job_t
*job
) /* I - Job data */
685 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdHoldJob: id = %d", job
->id
);
687 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
688 cupsdStopJob(job
, 0);
690 DEBUG_puts("cupsdHoldJob: setting state to held...");
692 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
701 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
705 cupsdLoadAllJobs(void)
707 cups_dir_t
*dir
; /* Directory */
708 cups_dentry_t
*dent
; /* Directory entry */
709 char filename
[1024]; /* Full filename of job file */
710 int fd
; /* File descriptor */
711 cupsd_job_t
*job
; /* New job */
712 int jobid
, /* Current job ID */
713 fileid
; /* Current file ID */
714 ipp_attribute_t
*attr
; /* Job attribute */
715 char method
[HTTP_MAX_URI
],
716 /* Method portion of URI */
717 username
[HTTP_MAX_URI
],
718 /* Username portion of URI */
720 /* Host portion of URI */
721 resource
[HTTP_MAX_URI
];
722 /* Resource portion of URI */
723 int port
; /* Port portion of URI */
724 cupsd_printer_t
*p
; /* Printer or class */
725 const char *dest
; /* Destination */
726 mime_type_t
**filetypes
; /* New filetypes array */
727 int *compressions
; /* New compressions array */
731 * First create the job lists...
735 Jobs
= cupsArrayNew(compare_jobs
, NULL
);
738 ActiveJobs
= cupsArrayNew(compare_active_jobs
, NULL
);
741 * Then open the requests directory...
744 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdLoadAllJobs: Scanning %s...",
747 if ((dir
= cupsDirOpen(RequestRoot
)) == NULL
)
749 cupsdLogMessage(CUPSD_LOG_ERROR
,
750 "cupsdLoadAllJobs: Unable to open spool directory %s: %s",
751 RequestRoot
, strerror(errno
));
756 * Read all the c##### files...
759 while ((dent
= cupsDirRead(dir
)) != NULL
)
760 if (strlen(dent
->filename
) >= 6 && dent
->filename
[0] == 'c')
763 * Allocate memory for the job...
766 if ((job
= calloc(sizeof(cupsd_job_t
), 1)) == NULL
)
768 cupsdLogMessage(CUPSD_LOG_ERROR
,
769 "cupsdLoadAllJobs: Ran out of memory for jobs!");
774 if ((job
->attrs
= ippNew()) == NULL
)
777 cupsdLogMessage(CUPSD_LOG_ERROR
,
778 "cupsdLoadAllJobs: Ran out of memory for job attributes!");
784 * Assign the job ID...
787 job
->id
= atoi(dent
->filename
+ 1);
788 job
->back_pipes
[0] = -1;
789 job
->back_pipes
[1] = -1;
790 job
->print_pipes
[0] = -1;
791 job
->print_pipes
[1] = -1;
793 cupsdLogMessage(CUPSD_LOG_DEBUG
,
794 "cupsdLoadAllJobs: Loading attributes for job %d...",
797 if (job
->id
>= NextJobId
)
798 NextJobId
= job
->id
+ 1;
801 * Load the job control file...
804 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->filename
);
805 if ((fd
= open(filename
, O_RDONLY
)) < 0)
807 cupsdLogMessage(CUPSD_LOG_ERROR
,
808 "cupsdLoadAllJobs: Unable to open job control file \"%s\" - %s!",
809 filename
, strerror(errno
));
810 ippDelete(job
->attrs
);
817 if (ippReadFile(fd
, job
->attrs
) != IPP_DATA
)
819 cupsdLogMessage(CUPSD_LOG_ERROR
,
820 "cupsdLoadAllJobs: Unable to read job control file \"%s\"!",
823 ippDelete(job
->attrs
);
832 if ((job
->state
= ippFindAttribute(job
->attrs
, "job-state", IPP_TAG_ENUM
)) == NULL
)
834 cupsdLogMessage(CUPSD_LOG_ERROR
,
835 "cupsdLoadAllJobs: Missing or bad job-state attribute in control file \"%s\"!",
837 ippDelete(job
->attrs
);
843 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri", IPP_TAG_URI
)) == NULL
)
845 cupsdLogMessage(CUPSD_LOG_ERROR
,
846 "cupsdLoadAllJobs: No job-printer-uri attribute in control file \"%s\"!",
848 ippDelete(job
->attrs
);
854 httpSeparate(attr
->values
[0].string
.text
, method
, username
, host
,
857 if ((dest
= cupsdValidateDest(host
, resource
, &(job
->dtype
), NULL
)) == NULL
&&
858 job
->state
!= NULL
&&
859 job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
)
862 * Job queued on remote printer or class, so add it...
865 if (strncmp(resource
, "/classes/", 9) == 0)
867 p
= cupsdAddClass(resource
+ 9);
868 cupsdSetString(&p
->make_model
, "Remote Class on unknown");
872 p
= cupsdAddPrinter(resource
+ 10);
873 cupsdSetString(&p
->make_model
, "Remote Printer on unknown");
876 p
->state
= IPP_PRINTER_STOPPED
;
877 p
->type
|= CUPS_PRINTER_REMOTE
;
878 p
->browse_time
= 2147483647;
880 cupsdSetString(&p
->location
, "Location Unknown");
881 cupsdSetString(&p
->info
, "No Information Available");
882 p
->hostname
[0] = '\0';
884 cupsdSetPrinterAttrs(p
);
890 cupsdLogMessage(CUPSD_LOG_ERROR
,
891 "cupsdLoadAllJobs: Unable to queue job for destination \"%s\"!",
892 attr
->values
[0].string
.text
);
893 ippDelete(job
->attrs
);
899 cupsdSetString(&job
->dest
, dest
);
901 job
->sheets
= ippFindAttribute(job
->attrs
, "job-media-sheets-completed",
903 job
->job_sheets
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
905 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority", IPP_TAG_INTEGER
)) == NULL
)
907 cupsdLogMessage(CUPSD_LOG_ERROR
,
908 "cupsdLoadAllJobs: Missing or bad job-priority attribute in control file \"%s\"!",
910 ippDelete(job
->attrs
);
915 job
->priority
= attr
->values
[0].integer
;
917 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-user-name", IPP_TAG_NAME
)) == NULL
)
919 cupsdLogMessage(CUPSD_LOG_ERROR
,
920 "cupsdLoadAllJobs: Missing or bad job-originating-user-name attribute in control file \"%s\"!",
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", IPP_TAG_KEYWORD
)) == NULL
)
944 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
947 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
949 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
);
951 else if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
952 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
956 * Read all the d##### files...
961 while ((dent
= cupsDirRead(dir
)) != NULL
)
962 if (strlen(dent
->filename
) > 7 && dent
->filename
[0] == 'd' &&
963 strchr(dent
->filename
, '-'))
969 jobid
= atoi(dent
->filename
+ 1);
970 fileid
= atoi(strchr(dent
->filename
, '-') + 1);
972 cupsdLogMessage(CUPSD_LOG_DEBUG
,
973 "cupsdLoadAllJobs: Auto-typing document file %s...",
976 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->filename
);
978 if ((job
= cupsdFindJob(jobid
)) == NULL
)
980 cupsdLogMessage(CUPSD_LOG_ERROR
,
981 "cupsdLoadAllJobs: Orphaned print file \"%s\"!",
987 if (fileid
> job
->num_files
)
989 if (job
->num_files
== 0)
991 compressions
= (int *)calloc(fileid
, sizeof(int));
992 filetypes
= (mime_type_t
**)calloc(fileid
, sizeof(mime_type_t
*));
996 compressions
= (int *)realloc(job
->compressions
,
997 sizeof(int) * fileid
);
998 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
999 sizeof(mime_type_t
*) * fileid
);
1002 if (compressions
== NULL
|| filetypes
== NULL
)
1004 cupsdLogMessage(CUPSD_LOG_ERROR
, "cupsdLoadAllJobs: Ran out of memory for job file types!");
1008 job
->compressions
= compressions
;
1009 job
->filetypes
= filetypes
;
1010 job
->num_files
= fileid
;
1013 job
->filetypes
[fileid
- 1] = mimeFileType(MimeDatabase
, filename
,
1014 job
->compressions
+ fileid
- 1);
1016 if (job
->filetypes
[fileid
- 1] == NULL
)
1017 job
->filetypes
[fileid
- 1] = mimeType(MimeDatabase
, "application",
1024 * Clean out old jobs as needed...
1032 * 'cupsdMoveJob()' - Move the specified job to a different destination.
1036 cupsdMoveJob(cupsd_job_t
*job
, /* I - Job */
1037 const char *dest
) /* I - Destination */
1039 ipp_attribute_t
*attr
; /* job-printer-uri attribute */
1040 cupsd_printer_t
*p
; /* Destination printer or class */
1044 * Find the printer...
1047 if ((p
= cupsdFindDest(dest
)) == NULL
)
1051 * Don't move completed jobs...
1054 if (job
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
1058 * Change the destination information...
1061 cupsdSetString(&job
->dest
, dest
);
1062 job
->dtype
= p
->type
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
1063 CUPS_PRINTER_IMPLICIT
);
1065 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri", IPP_TAG_URI
)) != NULL
)
1066 cupsdSetString(&(attr
->values
[0].string
.text
), p
->uri
);
1073 * 'cupsdReleaseJob()' - Release the specified job.
1077 cupsdReleaseJob(cupsd_job_t
*job
) /* I - Job */
1079 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdReleaseJob: id = %d", job
->id
);
1081 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
1083 DEBUG_puts("cupsdReleaseJob: setting state to pending...");
1085 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1093 * 'cupsdRestartJob()' - Restart the specified job.
1097 cupsdRestartJob(cupsd_job_t
*job
) /* I - Job */
1099 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdRestartJob: id = %d", job
->id
);
1101 if (job
->state
->values
[0].integer
== IPP_JOB_STOPPED
|| JobFiles
)
1104 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1112 * 'cupsdSaveJob()' - Save a job to disk.
1116 cupsdSaveJob(cupsd_job_t
*job
) /* I - Job */
1118 char filename
[1024]; /* Job control filename */
1119 int fd
; /* File descriptor */
1122 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
, job
->id
);
1124 if ((fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
1126 cupsdLogMessage(CUPSD_LOG_ERROR
,
1127 "cupsdSaveJob: Unable to create job control file \"%s\" - %s.",
1128 filename
, strerror(errno
));
1133 fchown(fd
, RunUser
, Group
);
1135 ippWriteFile(fd
, job
->attrs
);
1137 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdSaveJob: Closing file %d...", fd
);
1144 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job...
1148 cupsdSetJobHoldUntil(cupsd_job_t
*job
, /* I - Job */
1149 const char *when
) /* I - When to resume */
1151 time_t curtime
; /* Current time */
1152 struct tm
*curdate
; /* Current date */
1153 int hour
; /* Hold hour */
1154 int minute
; /* Hold minute */
1155 int second
; /* Hold second */
1158 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdSetJobHoldUntil(%d, \"%s\")",
1163 if (!strcmp(when
, "indefinite") || !strcmp(when
, "authenticated"))
1166 * Hold indefinitely...
1169 job
->hold_until
= 0;
1171 else if (!strcmp(when
, "day-time"))
1174 * Hold to 6am the next morning unless local time is < 6pm.
1177 curtime
= time(NULL
);
1178 curdate
= localtime(&curtime
);
1180 if (curdate
->tm_hour
< 18)
1181 job
->hold_until
= curtime
;
1183 job
->hold_until
= curtime
+
1184 ((29 - curdate
->tm_hour
) * 60 + 59 -
1185 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1187 else if (!strcmp(when
, "evening") || strcmp(when
, "night"))
1190 * Hold to 6pm unless local time is > 6pm or < 6am.
1193 curtime
= time(NULL
);
1194 curdate
= localtime(&curtime
);
1196 if (curdate
->tm_hour
< 6 || curdate
->tm_hour
>= 18)
1197 job
->hold_until
= curtime
;
1199 job
->hold_until
= curtime
+
1200 ((17 - curdate
->tm_hour
) * 60 + 59 -
1201 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1203 else if (!strcmp(when
, "second-shift"))
1206 * Hold to 4pm unless local time is > 4pm.
1209 curtime
= time(NULL
);
1210 curdate
= localtime(&curtime
);
1212 if (curdate
->tm_hour
>= 16)
1213 job
->hold_until
= curtime
;
1215 job
->hold_until
= curtime
+
1216 ((15 - curdate
->tm_hour
) * 60 + 59 -
1217 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1219 else if (!strcmp(when
, "third-shift"))
1222 * Hold to 12am unless local time is < 8am.
1225 curtime
= time(NULL
);
1226 curdate
= localtime(&curtime
);
1228 if (curdate
->tm_hour
< 8)
1229 job
->hold_until
= curtime
;
1231 job
->hold_until
= curtime
+
1232 ((23 - curdate
->tm_hour
) * 60 + 59 -
1233 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1235 else if (!strcmp(when
, "weekend"))
1238 * Hold to weekend unless we are in the weekend.
1241 curtime
= time(NULL
);
1242 curdate
= localtime(&curtime
);
1244 if (curdate
->tm_wday
|| curdate
->tm_wday
== 6)
1245 job
->hold_until
= curtime
;
1247 job
->hold_until
= curtime
+
1248 (((5 - curdate
->tm_wday
) * 24 +
1249 (17 - curdate
->tm_hour
)) * 60 + 59 -
1250 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1252 else if (sscanf(when
, "%d:%d:%d", &hour
, &minute
, &second
) >= 2)
1255 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1258 curtime
= time(NULL
);
1259 curdate
= gmtime(&curtime
);
1261 job
->hold_until
= curtime
+
1262 ((hour
- curdate
->tm_hour
) * 60 + minute
-
1263 curdate
->tm_min
) * 60 + second
- curdate
->tm_sec
;
1266 * Hold until next day as needed...
1269 if (job
->hold_until
< curtime
)
1270 job
->hold_until
+= 24 * 60 * 60 * 60;
1273 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdSetJobHoldUntil: hold_until = %d",
1274 (int)job
->hold_until
);
1279 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
1280 * the list as needed.
1284 cupsdSetJobPriority(
1285 cupsd_job_t
*job
, /* I - Job ID */
1286 int priority
) /* I - New priority (0 to 100) */
1288 ipp_attribute_t
*attr
; /* Job attribute */
1292 * Don't change completed jobs...
1295 if (job
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
1299 * Set the new priority and re-add the job into the active list...
1302 cupsArrayRemove(ActiveJobs
, job
);
1304 job
->priority
= priority
;
1306 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
1307 attr
->values
[0].integer
= priority
;
1309 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1312 cupsArrayAdd(ActiveJobs
, job
);
1319 * 'cupsdStartJob()' - Start a print job.
1323 cupsdStartJob(cupsd_job_t
*job
, /* I - Job ID */
1324 cupsd_printer_t
*printer
) /* I - Printer to print job */
1326 int i
; /* Looping var */
1327 int slot
; /* Pipe slot */
1328 int num_filters
; /* Number of filters for job */
1329 mime_filter_t
*filters
; /* Filters for job */
1330 char method
[255], /* Method for output */
1331 *optptr
, /* Pointer to options */
1332 *valptr
; /* Pointer in value string */
1333 ipp_attribute_t
*attr
; /* Current attribute */
1334 int pid
; /* Process ID of new filter process */
1335 int banner_page
; /* 1 if banner page, 0 otherwise */
1336 int statusfds
[2], /* Pipes used between the filters and scheduler */
1337 filterfds
[2][2];/* Pipes used between the filters */
1338 int envc
; /* Number of environment variables */
1339 char *argv
[8], /* Filter command-line arguments */
1340 sani_uri
[1024], /* Sanitized DEVICE_URI env var */
1341 filename
[1024], /* Job filename */
1342 command
[1024], /* Full path to filter/backend command */
1343 jobid
[255], /* Job ID string */
1344 title
[IPP_MAX_NAME
],
1345 /* Job title string */
1346 copies
[255], /* # copies string */
1347 *envp
[100], /* Environment variables */
1348 charset
[255], /* CHARSET environment variable */
1349 class_name
[255],/* CLASS environment variable */
1350 classification
[1024],
1351 /* CLASSIFICATION environment variable */
1353 /* CONTENT_TYPE environment variable */
1355 /* DEVICE_URI environment variable */
1356 lang
[255], /* LANG environment variable */
1357 ppd
[1024], /* PPD environment variable */
1359 /* PRINTER environment variable */
1361 /* RIP_MAX_CACHE environment variable */
1362 static char *options
= NULL
;/* Full list of options */
1363 static int optlength
= 0; /* Length of option buffer */
1366 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob() id = %d, file = %d/%d",
1367 job
->id
, job
->current_file
, job
->num_files
);
1369 if (job
->num_files
== 0)
1371 cupsdLogMessage(CUPSD_LOG_ERROR
, "Job ID %d has no files! Cancelling it!",
1374 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1375 "Job cancelled because it has no files.");
1377 cupsdCancelJob(job
, 0);
1382 * Figure out what filters are required to convert from
1383 * the source to the destination type...
1392 * Remote jobs and raw queues go directly to the printer without
1396 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1397 "cupsdStartJob: Sending job to queue tagged as raw...");
1404 * Local jobs get filtered...
1407 filters
= mimeFilter(MimeDatabase
, job
->filetypes
[job
->current_file
],
1408 printer
->filetype
, &num_filters
, MAX_FILTERS
- 1);
1410 if (num_filters
== 0)
1412 cupsdLogMessage(CUPSD_LOG_ERROR
,
1413 "Unable to convert file %d to printable format for job %d!",
1414 job
->current_file
, job
->id
);
1415 cupsdLogMessage(CUPSD_LOG_INFO
,
1416 "Hint: Do you have ESP Ghostscript installed?");
1418 if (LogLevel
< CUPSD_LOG_DEBUG
)
1419 cupsdLogMessage(CUPSD_LOG_INFO
,
1420 "Hint: Try setting the LogLevel to \"debug\".");
1422 job
->current_file
++;
1424 if (job
->current_file
== job
->num_files
)
1426 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1427 "Job cancelled because it has no files that can be printed.");
1429 cupsdCancelJob(job
, 0);
1436 * Remove NULL ("-") filters...
1439 for (i
= 0; i
< num_filters
;)
1440 if (strcmp(filters
[i
].filter
, "-") == 0)
1443 if (i
< num_filters
)
1444 memcpy(filters
+ i
, filters
+ i
+ 1,
1445 (num_filters
- i
) * sizeof(mime_filter_t
));
1450 if (num_filters
== 0)
1458 * Compute filter cost...
1461 for (i
= 0; i
< num_filters
; i
++)
1462 job
->cost
+= filters
[i
].cost
;
1467 * See if the filter cost is too high...
1470 if ((FilterLevel
+ job
->cost
) > FilterLimit
&& FilterLevel
> 0 &&
1474 * Don't print this job quite yet...
1477 if (filters
!= NULL
)
1480 cupsdLogMessage(CUPSD_LOG_INFO
,
1481 "Holding job %d because filter limit has been reached.",
1483 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1484 "cupsdStartJob: id=%d, file=%d, cost=%d, level=%d, limit=%d",
1485 job
->id
, job
->current_file
, job
->cost
, FilterLevel
,
1490 FilterLevel
+= job
->cost
;
1493 * Add decompression filters, if any...
1496 if (job
->compressions
[job
->current_file
])
1499 * Add gziptoany filter to the front of the list...
1502 mime_filter_t
*temp_filters
;
1504 if (num_filters
== 0)
1505 temp_filters
= malloc(sizeof(mime_filter_t
));
1507 temp_filters
= realloc(filters
,
1508 sizeof(mime_filter_t
) * (num_filters
+ 1));
1510 if (temp_filters
== NULL
)
1512 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add decompression filter - %s",
1515 if (filters
!= NULL
)
1518 job
->current_file
++;
1520 if (job
->current_file
== job
->num_files
)
1522 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1523 "Job cancelled because the print file could not be decompressed.");
1525 cupsdCancelJob(job
, 0);
1531 filters
= temp_filters
;
1532 memmove(filters
+ 1, filters
, num_filters
* sizeof(mime_filter_t
));
1533 *filters
= gziptoany_filter
;
1538 * Add port monitor, if any...
1541 if (printer
->port_monitor
)
1544 * Add port monitor to the end of the list...
1547 mime_filter_t
*temp_filters
;
1549 if (num_filters
== 0)
1550 temp_filters
= malloc(sizeof(mime_filter_t
));
1552 temp_filters
= realloc(filters
,
1553 sizeof(mime_filter_t
) * (num_filters
+ 1));
1555 if (temp_filters
== NULL
)
1557 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add port monitor - %s",
1560 if (filters
!= NULL
)
1563 job
->current_file
++;
1565 if (job
->current_file
== job
->num_files
)
1567 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1568 "Job cancelled because the port monitor could not be added.");
1570 cupsdCancelJob(job
, 0);
1576 filters
= temp_filters
;
1577 memset(filters
+ num_filters
, 0, sizeof(mime_filter_t
));
1578 snprintf(filters
[num_filters
].filter
, sizeof(filters
[num_filters
].filter
),
1579 "%s/monitor/%s", ServerBin
, printer
->port_monitor
);
1584 * Update the printer and job state to "processing"...
1587 job
->state
->values
[0].integer
= IPP_JOB_PROCESSING
;
1589 job
->printer
= printer
;
1591 cupsdSetPrinterState(printer
, IPP_PRINTER_PROCESSING
, 0);
1593 if (job
->current_file
== 0)
1595 set_time(job
, "time-at-processing");
1596 cupsdOpenPipe(job
->back_pipes
);
1600 * Determine if we are printing a banner page or not...
1603 if (job
->job_sheets
== NULL
)
1605 cupsdLogMessage(CUPSD_LOG_DEBUG
, "No job-sheets attribute.");
1606 if ((job
->job_sheets
=
1607 ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
1608 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1609 "... but someone added one without setting job_sheets!");
1611 else if (job
->job_sheets
->num_values
== 1)
1612 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s",
1613 job
->job_sheets
->values
[0].string
.text
);
1615 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s,%s",
1616 job
->job_sheets
->values
[0].string
.text
,
1617 job
->job_sheets
->values
[1].string
.text
);
1619 if (printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
))
1621 else if (job
->job_sheets
== NULL
)
1623 else if (strcasecmp(job
->job_sheets
->values
[0].string
.text
, "none") != 0 &&
1624 job
->current_file
== 0)
1626 else if (job
->job_sheets
->num_values
> 1 &&
1627 strcasecmp(job
->job_sheets
->values
[1].string
.text
, "none") != 0 &&
1628 job
->current_file
== (job
->num_files
- 1))
1633 cupsdLogMessage(CUPSD_LOG_DEBUG
, "banner_page = %d", banner_page
);
1636 * Building the options string is harder than it needs to be, but
1637 * for the moment we need to pass strings for command-line args and
1638 * not IPP attribute pointers... :)
1640 * First allocate/reallocate the option buffer as needed...
1643 i
= ipp_length(job
->attrs
);
1650 optptr
= realloc(options
, i
);
1654 cupsdLogMessage(CUPSD_LOG_CRIT
,
1655 "cupsdStartJob: Unable to allocate %d bytes for option buffer for job %d!",
1658 if (filters
!= NULL
)
1661 FilterLevel
-= job
->cost
;
1663 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1664 "Job cancelled because the server ran out of memory.");
1666 cupsdCancelJob(job
, 0);
1675 * Now loop through the attributes and convert them to the textual
1676 * representation used by the filters...
1682 snprintf(title
, sizeof(title
), "%s-%d", printer
->name
, job
->id
);
1683 strcpy(copies
, "1");
1685 for (attr
= job
->attrs
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1687 if (strcmp(attr
->name
, "copies") == 0 &&
1688 attr
->value_tag
== IPP_TAG_INTEGER
)
1691 * Don't use the # copies attribute if we are printing the job sheets...
1695 sprintf(copies
, "%d", attr
->values
[0].integer
);
1697 else if (strcmp(attr
->name
, "job-name") == 0 &&
1698 (attr
->value_tag
== IPP_TAG_NAME
||
1699 attr
->value_tag
== IPP_TAG_NAMELANG
))
1700 strlcpy(title
, attr
->values
[0].string
.text
, sizeof(title
));
1701 else if (attr
->group_tag
== IPP_TAG_JOB
)
1704 * Filter out other unwanted attributes...
1707 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
1708 attr
->value_tag
== IPP_TAG_NAMELANG
||
1709 attr
->value_tag
== IPP_TAG_TEXTLANG
||
1710 attr
->value_tag
== IPP_TAG_URI
||
1711 attr
->value_tag
== IPP_TAG_URISCHEME
||
1712 attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
) /* Not yet supported */
1715 if (strncmp(attr
->name
, "time-", 5) == 0)
1718 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1719 !(printer
->type
& CUPS_PRINTER_REMOTE
))
1722 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1723 strcmp(attr
->name
, "job-billing") != 0 &&
1724 strcmp(attr
->name
, "job-sheets") != 0 &&
1725 strcmp(attr
->name
, "job-hold-until") != 0 &&
1726 strcmp(attr
->name
, "job-priority") != 0)
1729 if ((strcmp(attr
->name
, "page-label") == 0 ||
1730 strcmp(attr
->name
, "page-border") == 0 ||
1731 strncmp(attr
->name
, "number-up", 9) == 0 ||
1732 strcmp(attr
->name
, "page-set") == 0) &&
1737 * Otherwise add them to the list...
1740 if (optptr
> options
)
1741 strlcat(optptr
, " ", optlength
- (optptr
- options
));
1743 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
1745 strlcat(optptr
, attr
->name
, optlength
- (optptr
- options
));
1746 strlcat(optptr
, "=", optlength
- (optptr
- options
));
1749 for (i
= 0; i
< attr
->num_values
; i
++)
1752 strlcat(optptr
, ",", optlength
- (optptr
- options
));
1754 optptr
+= strlen(optptr
);
1756 switch (attr
->value_tag
)
1758 case IPP_TAG_INTEGER
:
1760 snprintf(optptr
, optlength
- (optptr
- options
),
1761 "%d", attr
->values
[i
].integer
);
1764 case IPP_TAG_BOOLEAN
:
1765 if (!attr
->values
[i
].boolean
)
1766 strlcat(optptr
, "no", optlength
- (optptr
- options
));
1768 case IPP_TAG_NOVALUE
:
1769 strlcat(optptr
, attr
->name
,
1770 optlength
- (optptr
- options
));
1773 case IPP_TAG_RANGE
:
1774 if (attr
->values
[i
].range
.lower
== attr
->values
[i
].range
.upper
)
1775 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1776 "%d", attr
->values
[i
].range
.lower
);
1778 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1779 "%d-%d", attr
->values
[i
].range
.lower
,
1780 attr
->values
[i
].range
.upper
);
1783 case IPP_TAG_RESOLUTION
:
1784 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1785 "%dx%d%s", attr
->values
[i
].resolution
.xres
,
1786 attr
->values
[i
].resolution
.yres
,
1787 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
1791 case IPP_TAG_STRING
:
1794 case IPP_TAG_KEYWORD
:
1795 case IPP_TAG_CHARSET
:
1796 case IPP_TAG_LANGUAGE
:
1797 for (valptr
= attr
->values
[i
].string
.text
; *valptr
;)
1799 if (strchr(" \t\n\\\'\"", *valptr
))
1801 *optptr
++ = *valptr
++;
1808 break; /* anti-compiler-warning-code */
1812 optptr
+= strlen(optptr
);
1817 * Build the command-line arguments for the filters. Each filter
1818 * has 6 or 7 arguments:
1822 * argv[2] = username
1824 * argv[4] = # copies
1826 * argv[6] = filename (optional; normally stdin)
1828 * This allows legacy printer drivers that use the old System V
1829 * printing interface to be used by CUPS.
1832 sprintf(jobid
, "%d", job
->id
);
1833 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
1834 job
->id
, job
->current_file
+ 1);
1836 argv
[0] = printer
->name
;
1838 argv
[2] = job
->username
;
1845 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1846 "cupsdStartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1847 argv
[0], argv
[1], argv
[2], argv
[3], argv
[4], argv
[5], argv
[6]);
1850 * Create environment variable strings for the filters...
1853 attr
= ippFindAttribute(job
->attrs
, "attributes-natural-language",
1856 switch (strlen(attr
->values
[0].string
.text
))
1860 * This is an unknown or badly formatted language code; use
1861 * the POSIX locale...
1864 strcpy(lang
, "LANG=C");
1869 * Just the language code (ll)...
1872 snprintf(lang
, sizeof(lang
), "LANG=%s",
1873 attr
->values
[0].string
.text
);
1878 * Language and country code (ll-cc)...
1881 snprintf(lang
, sizeof(lang
), "LANG=%c%c_%c%c",
1882 attr
->values
[0].string
.text
[0],
1883 attr
->values
[0].string
.text
[1],
1884 toupper(attr
->values
[0].string
.text
[3] & 255),
1885 toupper(attr
->values
[0].string
.text
[4] & 255));
1889 attr
= ippFindAttribute(job
->attrs
, "document-format",
1892 (optptr
= strstr(attr
->values
[0].string
.text
, "charset=")) != NULL
)
1893 snprintf(charset
, sizeof(charset
), "CHARSET=%s", optptr
+ 8);
1896 attr
= ippFindAttribute(job
->attrs
, "attributes-charset",
1898 snprintf(charset
, sizeof(charset
), "CHARSET=%s",
1899 attr
->values
[0].string
.text
);
1902 snprintf(content_type
, sizeof(content_type
), "CONTENT_TYPE=%s/%s",
1903 job
->filetypes
[job
->current_file
]->super
,
1904 job
->filetypes
[job
->current_file
]->type
);
1905 snprintf(device_uri
, sizeof(device_uri
), "DEVICE_URI=%s", printer
->device_uri
);
1906 cupsdSanitizeURI(printer
->device_uri
, sani_uri
, sizeof(sani_uri
));
1907 snprintf(ppd
, sizeof(ppd
), "PPD=%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
1908 snprintf(printer_name
, sizeof(printer_name
), "PRINTER=%s", printer
->name
);
1909 snprintf(rip_max_cache
, sizeof(rip_max_cache
), "RIP_MAX_CACHE=%s", RIPCache
);
1911 envc
= cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
1913 envp
[envc
++] = charset
;
1914 envp
[envc
++] = lang
;
1915 envp
[envc
++] = ppd
;
1916 envp
[envc
++] = rip_max_cache
;
1917 envp
[envc
++] = content_type
;
1918 envp
[envc
++] = device_uri
;
1919 envp
[envc
++] = printer_name
;
1921 if (Classification
&& !banner_page
)
1923 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
1924 IPP_TAG_NAME
)) == NULL
)
1925 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1927 else if (attr
->num_values
> 1 &&
1928 strcmp(attr
->values
[1].string
.text
, "none") != 0)
1929 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1930 attr
->values
[1].string
.text
);
1932 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1933 attr
->values
[0].string
.text
);
1935 envp
[envc
++] = classification
;
1938 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
1940 snprintf(class_name
, sizeof(class_name
), "CLASS=%s", job
->dest
);
1941 envp
[envc
++] = class_name
;
1946 for (i
= 0; i
< envc
; i
++)
1947 if (strncmp(envp
[i
], "DEVICE_URI=", 11))
1948 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: envp[%d]=\"%s\"",
1951 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1952 "cupsdStartJob: envp[%d]=\"DEVICE_URI=%s\"", i
, sani_uri
);
1954 job
->current_file
++;
1957 * Now create processes for all of the filters...
1960 if (cupsdOpenPipe(statusfds
))
1962 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to create job status pipes - %s.",
1964 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1965 "Unable to create status pipes - %s.", strerror(errno
));
1967 cupsdAddPrinterHistory(printer
);
1969 if (filters
!= NULL
)
1972 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1973 "Job cancelled because the server could not create the job status pipes.");
1975 cupsdCancelJob(job
, 0);
1979 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: statusfds = [ %d %d ]",
1980 statusfds
[0], statusfds
[1]);
1983 fcntl(statusfds
[0], F_SETFD
, FD_CLOEXEC
);
1984 fcntl(statusfds
[1], F_SETFD
, FD_CLOEXEC
);
1985 #endif /* FD_CLOEXEC */
1987 job
->status_buffer
= cupsdStatBufNew(statusfds
[0], "[Job %d]",
1990 memset(job
->filters
, 0, sizeof(job
->filters
));
1992 filterfds
[1][0] = open("/dev/null", O_RDONLY
);
1993 filterfds
[1][1] = -1;
1995 if (filterfds
[1][0] < 0)
1997 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to open \"/dev/null\" - %s.",
1999 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2000 "Unable to open \"/dev/null\" - %s.", strerror(errno
));
2002 cupsdAddPrinterHistory(printer
);
2004 if (filters
!= NULL
)
2007 cupsdClosePipe(statusfds
);
2008 cupsdCancelJob(job
, 0);
2012 fcntl(filterfds
[1][0], F_SETFD
, fcntl(filterfds
[1][0], F_GETFD
) | FD_CLOEXEC
);
2014 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2015 1, filterfds
[1][0], filterfds
[1][1]);
2017 for (i
= 0, slot
= 0; i
< num_filters
; i
++)
2019 if (filters
[i
].filter
[0] != '/')
2020 snprintf(command
, sizeof(command
), "%s/filter/%s", ServerBin
,
2023 strlcpy(command
, filters
[i
].filter
, sizeof(command
));
2025 if (i
< (num_filters
- 1))
2027 if (cupsdOpenPipe(filterfds
[slot
]))
2029 cupsdLogMessage(CUPSD_LOG_ERROR
,
2030 "Unable to create job filter pipes - %s.",
2032 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2033 "Unable to create filter pipes - %s.", strerror(errno
));
2034 cupsdAddPrinterHistory(printer
);
2036 if (filters
!= NULL
)
2039 cupsdClosePipe(statusfds
);
2040 cupsdClosePipe(filterfds
[!slot
]);
2042 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2043 "Job cancelled because the server could not create the filter pipes.");
2045 cupsdCancelJob(job
, 0);
2051 if (job
->current_file
== 1)
2053 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
2055 if (cupsdOpenPipe(job
->print_pipes
))
2057 cupsdLogMessage(CUPSD_LOG_ERROR
,
2058 "Unable to create job backend pipes - %s.",
2060 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2061 "Unable to create backend pipes - %s.", strerror(errno
));
2062 cupsdAddPrinterHistory(printer
);
2064 if (filters
!= NULL
)
2067 cupsdClosePipe(statusfds
);
2068 cupsdClosePipe(filterfds
[!slot
]);
2070 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2071 "Job cancelled because the server could not create the backend pipes.");
2073 cupsdCancelJob(job
, 0);
2079 job
->print_pipes
[0] = -1;
2080 if (!strncmp(printer
->device_uri
, "file:/dev/", 10) &&
2081 strcmp(printer
->device_uri
, "file:/dev/null"))
2082 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
2084 else if (!strncmp(printer
->device_uri
, "file:///dev/", 12) &&
2085 strcmp(printer
->device_uri
, "file:///dev/null"))
2086 job
->print_pipes
[1] = open(printer
->device_uri
+ 7,
2089 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
2090 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
2092 if (job
->print_pipes
[1] < 0)
2094 cupsdLogMessage(CUPSD_LOG_ERROR
,
2095 "Unable to open output file \"%s\" - %s.",
2096 printer
->device_uri
, strerror(errno
));
2097 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2098 "Unable to open output file \"%s\" - %s.",
2099 printer
->device_uri
, strerror(errno
));
2101 cupsdAddPrinterHistory(printer
);
2103 if (filters
!= NULL
)
2106 cupsdClosePipe(statusfds
);
2107 cupsdClosePipe(filterfds
[!slot
]);
2109 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2110 "Job cancelled because the server could not open the output file.");
2112 cupsdCancelJob(job
, 0);
2116 fcntl(job
->print_pipes
[1], F_SETFD
,
2117 fcntl(job
->print_pipes
[1], F_GETFD
) | FD_CLOEXEC
);
2120 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdStartJob: print_pipes = [ %d %d ]",
2121 job
->print_pipes
[0], job
->print_pipes
[1]);
2124 filterfds
[slot
][0] = job
->print_pipes
[0];
2125 filterfds
[slot
][1] = job
->print_pipes
[1];
2128 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filter = \"%s\"", command
);
2129 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2130 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2132 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
2133 filterfds
[slot
][1], statusfds
[1],
2134 job
->back_pipes
[0], 0, job
->filters
+ i
);
2136 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2137 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2138 !slot
, filterfds
[!slot
][0], filterfds
[!slot
][1]);
2140 cupsdClosePipe(filterfds
[!slot
]);
2144 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start filter \"%s\" - %s.",
2145 filters
[i
].filter
, strerror(errno
));
2146 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2147 "Unable to start filter \"%s\" - %s.",
2148 filters
[i
].filter
, strerror(errno
));
2150 cupsdAddPrinterHistory(printer
);
2152 if (filters
!= NULL
)
2155 cupsdAddPrinterHistory(printer
);
2157 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2158 "Job cancelled because the server could not execute a filter.");
2160 cupsdCancelJob(job
, 0);
2164 cupsdLogMessage(CUPSD_LOG_INFO
, "Started filter %s (PID %d) for job %d.",
2165 command
, pid
, job
->id
);
2171 if (filters
!= NULL
)
2175 * Finally, pipe the final output into a backend process if needed...
2178 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
2180 if (job
->current_file
== 1)
2182 sscanf(printer
->device_uri
, "%254[^:]", method
);
2183 snprintf(command
, sizeof(command
), "%s/backend/%s", ServerBin
, method
);
2187 filterfds
[slot
][0] = -1;
2188 filterfds
[slot
][1] = open("/dev/null", O_WRONLY
);
2190 if (filterfds
[slot
][1] < 0)
2192 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to open \"/dev/null\" - %s.",
2194 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2195 "Unable to open \"/dev/null\" - %s.", strerror(errno
));
2197 cupsdAddPrinterHistory(printer
);
2199 if (filters
!= NULL
)
2202 cupsdClosePipe(statusfds
);
2204 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2205 "Job cancelled because the server could not open a file.");
2207 cupsdCancelJob(job
, 0);
2211 fcntl(filterfds
[slot
][1], F_SETFD
,
2212 fcntl(filterfds
[slot
][1], F_GETFD
) | FD_CLOEXEC
);
2214 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: backend = \"%s\"",
2216 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2217 "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2218 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2220 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
2221 filterfds
[slot
][1], statusfds
[1],
2222 job
->back_pipes
[1], 1,
2227 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start backend \"%s\" - %s.",
2228 method
, strerror(errno
));
2229 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2230 "Unable to start backend \"%s\" - %s.", method
, strerror(errno
));
2232 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2233 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2234 job
->print_pipes
[0], job
->print_pipes
[1]);
2236 cupsdClosePipe(job
->print_pipes
);
2238 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2239 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2240 job
->back_pipes
[0], job
->back_pipes
[1]);
2242 cupsdClosePipe(job
->back_pipes
);
2244 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2245 "Job cancelled because the server could not execute the backend.");
2247 cupsdCancelJob(job
, 0);
2252 cupsdLogMessage(CUPSD_LOG_INFO
,
2253 "Started backend %s (PID %d) for job %d.",
2254 command
, pid
, job
->id
);
2258 if (job
->current_file
== job
->num_files
)
2260 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2261 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2262 job
->print_pipes
[0], job
->print_pipes
[1]);
2264 cupsdClosePipe(job
->print_pipes
);
2266 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2267 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2268 job
->back_pipes
[0], job
->back_pipes
[1]);
2270 cupsdClosePipe(job
->back_pipes
);
2275 filterfds
[slot
][0] = -1;
2276 filterfds
[slot
][1] = -1;
2278 if (job
->current_file
== job
->num_files
)
2280 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2281 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2282 job
->print_pipes
[0], job
->print_pipes
[1]);
2284 cupsdClosePipe(job
->print_pipes
);
2288 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2289 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2290 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2292 cupsdClosePipe(filterfds
[slot
]);
2294 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2295 "cupsdStartJob: Closing status output pipe %d...",
2298 close(statusfds
[1]);
2300 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2301 "cupsdStartJob: Adding fd %d to InputSet...",
2302 job
->status_buffer
->fd
);
2304 FD_SET(job
->status_buffer
->fd
, InputSet
);
2309 * 'cupsdStopAllJobs()' - Stop all print jobs.
2313 cupsdStopAllJobs(void)
2315 cupsd_job_t
*job
; /* Current job */
2318 DEBUG_puts("cupsdStopAllJobs()");
2320 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
2322 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
2323 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
2325 cupsdStopJob(job
, 1);
2326 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
2332 * 'cupsdStopJob()' - Stop a print job.
2336 cupsdStopJob(cupsd_job_t
*job
, /* I - Job */
2337 int force
) /* I - 1 = Force all filters to stop */
2339 int i
; /* Looping var */
2342 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStopJob: id = %d, force = %d",
2345 if (job
->state
->values
[0].integer
!= IPP_JOB_PROCESSING
)
2348 FilterLevel
-= job
->cost
;
2350 if (job
->status
< 0 &&
2351 !(job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
)) &&
2352 !(job
->printer
->type
& CUPS_PRINTER_FAX
) &&
2353 !strcmp(job
->printer
->error_policy
, "stop-printer"))
2354 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_STOPPED
, 1);
2355 else if (job
->printer
->state
!= IPP_PRINTER_STOPPED
)
2356 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_IDLE
, 0);
2358 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStopJob: printer state is %d",
2359 job
->printer
->state
);
2361 job
->state
->values
[0].integer
= IPP_JOB_STOPPED
;
2362 job
->printer
->job
= NULL
;
2363 job
->printer
= NULL
;
2365 job
->current_file
--;
2367 for (i
= 0; job
->filters
[i
]; i
++)
2368 if (job
->filters
[i
] > 0)
2370 cupsdEndProcess(job
->filters
[i
], force
);
2371 job
->filters
[i
] = 0;
2374 if (job
->backend
> 0)
2376 cupsdEndProcess(job
->backend
, force
);
2380 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2381 "cupsdStopJob: Closing print pipes [ %d %d ]...",
2382 job
->print_pipes
[0], job
->print_pipes
[1]);
2384 cupsdClosePipe(job
->print_pipes
);
2386 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2387 "cupsdStopJob: Closing back pipes [ %d %d ]...",
2388 job
->back_pipes
[0], job
->back_pipes
[1]);
2390 cupsdClosePipe(job
->back_pipes
);
2392 if (job
->status_buffer
)
2395 * Close the pipe and clear the input bit.
2398 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2399 "cupsdStopJob: Removing fd %d from InputSet...",
2400 job
->status_buffer
->fd
);
2402 FD_CLR(job
->status_buffer
->fd
, InputSet
);
2404 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2405 "cupsdStopJob: Closing status input pipe %d...",
2406 job
->status_buffer
->fd
);
2408 cupsdStatBufDelete(job
->status_buffer
);
2410 job
->status_buffer
= NULL
;
2416 * 'cupsdUpdateJob()' - Read a status update from a job's filters.
2420 cupsdUpdateJob(cupsd_job_t
*job
) /* I - Job to check */
2422 int i
; /* Looping var */
2423 int copies
; /* Number of copies printed */
2424 char message
[1024], /* Message text */
2425 *ptr
; /* Pointer update... */
2426 int loglevel
; /* Log level for message */
2429 while ((ptr
= cupsdStatBufUpdate(job
->status_buffer
, &loglevel
,
2430 message
, sizeof(message
))) != NULL
)
2433 * Process page and printer state messages as needed...
2436 if (loglevel
== CUPSD_LOG_PAGE
)
2439 * Page message; send the message to the page_log file and update the
2440 * job sheet count...
2443 if (job
->sheets
!= NULL
)
2445 if (!strncasecmp(message
, "total ", 6))
2448 * Got a total count of pages from a backend or filter...
2451 copies
= atoi(message
+ 6);
2452 copies
-= job
->sheets
->values
[0].integer
; /* Just track the delta */
2454 else if (!sscanf(message
, "%*d%d", &copies
))
2457 job
->sheets
->values
[0].integer
+= copies
;
2459 if (job
->printer
->page_limit
)
2460 cupsdUpdateQuota(job
->printer
, job
->username
, copies
, 0);
2463 cupsdLogPage(job
, message
);
2465 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS
, job
->printer
, job
,
2466 "Printed %d page(s).", job
->sheets
->values
[0].integer
);
2468 else if (loglevel
== CUPSD_LOG_STATE
)
2469 cupsdSetPrinterReasons(job
->printer
, message
);
2470 else if (loglevel
== CUPSD_LOG_ATTR
)
2473 * Set attribute(s)...
2479 if (!strchr(job
->status_buffer
->buffer
, '\n'))
2486 * See if all of the filters and the backend have returned their
2490 for (i
= 0; job
->filters
[i
] < 0; i
++);
2492 if (job
->filters
[i
])
2495 if (job
->current_file
>= job
->num_files
&& job
->backend
> 0)
2499 * Handle the end of job stuff...
2502 cupsdFinishJob(job
);
2508 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
2511 static int /* O - Difference */
2512 compare_active_jobs(void *first
, /* I - First job */
2513 void *second
, /* I - Second job */
2514 void *data
) /* I - App data (not used) */
2516 int diff
; /* Difference */
2519 if ((diff
= ((cupsd_job_t
*)first
)->priority
- ((cupsd_job_t
*)second
)->priority
) != 0)
2522 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
2527 * 'compare_jobs()' - Compare the job IDs of two jobs.
2530 static int /* O - Difference */
2531 compare_jobs(void *first
, /* I - First job */
2532 void *second
, /* I - Second job */
2533 void *data
) /* I - App data (not used) */
2535 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
2540 * 'ipp_length()' - Compute the size of the buffer needed to hold
2541 * the textual IPP attributes.
2544 int /* O - Size of buffer to hold IPP attributes */
2545 ipp_length(ipp_t
*ipp
) /* I - IPP request */
2547 int bytes
; /* Number of bytes */
2548 int i
; /* Looping var */
2549 ipp_attribute_t
*attr
; /* Current attribute */
2553 * Loop through all attributes...
2558 for (attr
= ipp
->attrs
; attr
!= NULL
; attr
= attr
->next
)
2561 * Skip attributes that won't be sent to filters...
2564 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
2565 attr
->value_tag
== IPP_TAG_NAMELANG
||
2566 attr
->value_tag
== IPP_TAG_TEXTLANG
||
2567 attr
->value_tag
== IPP_TAG_URI
||
2568 attr
->value_tag
== IPP_TAG_URISCHEME
)
2571 if (strncmp(attr
->name
, "time-", 5) == 0)
2575 * Add space for a leading space and commas between each value.
2576 * For the first attribute, the leading space isn't used, so the
2577 * extra byte can be used as the nul terminator...
2580 bytes
++; /* " " separator */
2581 bytes
+= attr
->num_values
; /* "," separators */
2584 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
2585 * other attributes appear as "foo=value1,value2,...,valueN".
2588 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
2589 bytes
+= strlen(attr
->name
);
2591 bytes
+= attr
->num_values
* strlen(attr
->name
);
2594 * Now add the size required for each value in the attribute...
2597 switch (attr
->value_tag
)
2599 case IPP_TAG_INTEGER
:
2602 * Minimum value of a signed integer is -2147483647, or 11 digits.
2605 bytes
+= attr
->num_values
* 11;
2608 case IPP_TAG_BOOLEAN
:
2610 * Add two bytes for each false ("no") value...
2613 for (i
= 0; i
< attr
->num_values
; i
++)
2614 if (!attr
->values
[i
].boolean
)
2618 case IPP_TAG_RANGE
:
2620 * A range is two signed integers separated by a hyphen, or
2621 * 23 characters max.
2624 bytes
+= attr
->num_values
* 23;
2627 case IPP_TAG_RESOLUTION
:
2629 * A resolution is two signed integers separated by an "x" and
2630 * suffixed by the units, or 26 characters max.
2633 bytes
+= attr
->num_values
* 26;
2636 case IPP_TAG_STRING
:
2639 case IPP_TAG_KEYWORD
:
2640 case IPP_TAG_CHARSET
:
2641 case IPP_TAG_LANGUAGE
:
2643 * Strings can contain characters that need quoting. We need
2644 * at least 2 * len + 2 characters to cover the quotes and
2645 * any backslashes in the string.
2648 for (i
= 0; i
< attr
->num_values
; i
++)
2649 bytes
+= 2 * strlen(attr
->values
[i
].string
.text
) + 2;
2653 break; /* anti-compiler-warning-code */
2662 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2666 set_time(cupsd_job_t
*job
, /* I - Job to update */
2667 const char *name
) /* I - Name of attribute */
2669 ipp_attribute_t
*attr
; /* Time attribute */
2672 if ((attr
= ippFindAttribute(job
->attrs
, name
, IPP_TAG_ZERO
)) != NULL
)
2674 attr
->value_tag
= IPP_TAG_INTEGER
;
2675 attr
->values
[0].integer
= time(NULL
);
2681 * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
2685 set_hold_until(cupsd_job_t
*job
, /* I - Job to update */
2686 time_t holdtime
) /* I - Hold until time */
2688 ipp_attribute_t
*attr
; /* job-hold-until attribute */
2689 struct tm
*holddate
; /* Hold date */
2690 char holdstr
[64]; /* Hold time */
2694 * Set the hold_until value and hold the job...
2697 cupsdLogMessage(CUPSD_LOG_DEBUG
, "set_hold_until: hold_until = %d", (int)holdtime
);
2699 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
2700 job
->hold_until
= holdtime
;
2703 * Update the job-hold-until attribute with a string representing GMT
2704 * time (HH:MM:SS)...
2707 holddate
= gmtime(&holdtime
);
2708 snprintf(holdstr
, sizeof(holdstr
), "%d:%d:%d", holddate
->tm_hour
,
2709 holddate
->tm_min
, holddate
->tm_sec
);
2711 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
2712 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
2715 * Either add the attribute or update the value of the existing one
2719 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
2720 "job-hold-until", NULL
, holdstr
);
2722 cupsdSetString(&attr
->values
[0].string
.text
, holdstr
);