]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/job.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / job.c
CommitLineData
ef416fc2 1/*
e00b005a 2 * "$Id: job.c 5046 2006-02-01 22:11:58Z mike $"
ef416fc2 3 *
4 * Job management routines for the Common UNIX Printing System (CUPS).
5 *
e00b005a 6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
ef416fc2 7 *
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
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
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
41 * destination.
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.
57 */
58
59/*
60 * Include necessary headers...
61 */
62
63#include "cupsd.h"
64#include <grp.h>
65#include <cups/backend.h>
66#include <cups/dir.h>
67
68
69/*
70 * Local globals...
71 */
72
73static mime_filter_t gziptoany_filter =
74 {
75 NULL, /* Source type */
76 NULL, /* Destination type */
77 0, /* Cost */
78 "gziptoany" /* Filter program to run */
79 };
80
81
82/*
83 * Local functions...
84 */
85
86static int compare_active_jobs(void *first, void *second, void *data);
87static int compare_jobs(void *first, void *second, void *data);
88static int ipp_length(ipp_t *ipp);
89static void set_time(cupsd_job_t *job, const char *name);
90static void set_hold_until(cupsd_job_t *job, time_t holdtime);
91
92
93/*
94 * 'cupsdAddJob()' - Add a new job to the job queue...
95 */
96
97cupsd_job_t * /* O - New job record */
98cupsdAddJob(int priority, /* I - Job priority */
99 const char *dest) /* I - Job destination */
100{
101 cupsd_job_t *job; /* New job record */
102
103
104 job = calloc(sizeof(cupsd_job_t), 1);
105
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;
112
113 cupsdSetString(&job->dest, dest);
114
115 /*
116 * Add the new job to the "all jobs" and "active jobs" lists...
117 */
118
119 cupsArrayAdd(Jobs, job);
120 cupsArrayAdd(ActiveJobs, job);
121
122 return (job);
123}
124
125
126/*
127 * 'cupsdCancelJob()' - Cancel the specified print job.
128 */
129
130void
131cupsdCancelJob(cupsd_job_t *job, /* I - Job to cancel */
132 int purge) /* I - Purge jobs? */
133{
134 int i; /* Looping var */
135 char filename[1024]; /* Job filename */
136
137
138 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCancelJob: id = %d", job->id);
139
140 /*
141 * Remove the job from the active list...
142 */
143
144 cupsArrayRemove(ActiveJobs, job);
145
146 /*
147 * Stop any processes that are working on the current job...
148 */
149
150 if (job->state->values[0].integer == IPP_JOB_PROCESSING)
151 cupsdStopJob(job, 0);
152
153 cupsArrayRemove(ActiveJobs, job);
154
155 job->state->values[0].integer = IPP_JOB_CANCELLED;
156
157 set_time(job, "time-at-completed");
158
159 cupsdExpireSubscriptions(NULL, job);
160
161 /*
162 * Remove any authentication data...
163 */
164
165 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot,
166 job->id);
167 unlink(filename);
168
169 /*
170 * Remove the print file for good if we aren't preserving jobs or
171 * files...
172 */
173
174 job->current_file = 0;
175
176 if (!JobHistory || !JobFiles || purge ||
177 (job->dtype & CUPS_PRINTER_REMOTE))
178 for (i = 1; i <= job->num_files; i ++)
179 {
180 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
181 job->id, i);
182 unlink(filename);
183 }
184
185 if (JobHistory && !purge && !(job->dtype & CUPS_PRINTER_REMOTE))
186 {
187 /*
188 * Save job state info...
189 */
190
191 cupsdSaveJob(job);
192 }
193 else
194 {
195 /*
196 * Remove the job info file...
197 */
198
199 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot,
200 job->id);
201 unlink(filename);
202
203 /*
204 * Remove the job from the "all jobs" list...
205 */
206
207 cupsArrayRemove(Jobs, job);
208
209 /*
210 * Free all memory used...
211 */
212
213 if (job->attrs != NULL)
214 ippDelete(job->attrs);
215
216 if (job->num_files > 0)
217 {
218 free(job->compressions);
219 free(job->filetypes);
220 }
221
222 cupsdClearString(&job->username);
223 cupsdClearString(&job->dest);
224
225 free(job);
226 }
227}
228
229
230/*
231 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user...
232 */
233
234void
235cupsdCancelJobs(const char *dest, /* I - Destination to cancel */
236 const char *username, /* I - Username or NULL */
237 int purge) /* I - Purge jobs? */
238{
239 cupsd_job_t *job; /* Current job */
240
241
242 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
243 job;
244 job = (cupsd_job_t *)cupsArrayNext(Jobs))
245 if ((dest == NULL || !strcmp(job->dest, dest)) &&
246 (username == NULL || !strcmp(job->username, username)))
247 {
248 /*
249 * Cancel all jobs matching this destination/user...
250 */
251
252 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
253 purge ? "Job purged." : "Job canceled.");
254
255 cupsdCancelJob(job, purge);
256 }
257
258 cupsdCheckJobs();
259}
260
261
262/*
263 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
264 * is available.
265 */
266
267void
268cupsdCheckJobs(void)
269{
270 cupsd_job_t *job; /* Current job in queue */
271 cupsd_printer_t *printer, /* Printer destination */
272 *pclass; /* Printer class destination */
273
274
275 DEBUG_puts("cupsdCheckJobs()");
276
277 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
278 job;
279 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
280 {
281 /*
282 * Start held jobs if they are ready...
283 */
284
285 if (job->state->values[0].integer == IPP_JOB_HELD &&
286 job->hold_until &&
287 job->hold_until < time(NULL))
288 job->state->values[0].integer = IPP_JOB_PENDING;
289
290 /*
291 * Start pending jobs if the destination is available...
292 */
293
09ec0018 294 if (job->state->values[0].integer == IPP_JOB_PENDING && !NeedReload &&
295 !Sleeping)
ef416fc2 296 {
297 printer = cupsdFindDest(job->dest);
298 pclass = NULL;
299
300 while (printer &&
301 (printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS)))
302 {
303 /*
304 * If the class is remote, just pass it to the remote server...
305 */
306
307 pclass = printer;
308
309 if (!(pclass->type & CUPS_PRINTER_REMOTE))
310 {
311 if (pclass->state != IPP_PRINTER_STOPPED)
312 printer = cupsdFindAvailablePrinter(job->dest);
313 else
314 printer = NULL;
315 }
316 }
317
318 if (!printer && !pclass)
319 {
320 /*
321 * Whoa, the printer and/or class for this destination went away;
322 * cancel the job...
323 */
324
325 cupsdLogMessage(CUPSD_LOG_WARN,
326 "Printer/class %s has gone away; cancelling job %d!",
327 job->dest, job->id);
328
329 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
330 "Job canceled because the destination printer/class has gone away.");
331
332 cupsdCancelJob(job, 1);
333 }
334 else if (printer)
335 {
336 /*
337 * See if the printer is available or remote and not printing a job;
338 * if so, start the job...
339 */
340
341 if (pclass)
342 {
343 /*
344 * Add/update a job-actual-printer-uri attribute for this job
345 * so that we know which printer actually printed the job...
346 */
347
348 ipp_attribute_t *attr; /* job-actual-printer-uri attribute */
349
350
351 if ((attr = ippFindAttribute(job->attrs, "job-actual-printer-uri",
352 IPP_TAG_URI)) != NULL)
353 cupsdSetString(&attr->values[0].string.text, printer->uri);
354 else
355 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI,
356 "job-actual-printer-uri", NULL, printer->uri);
357 }
358
359 if (printer->state == IPP_PRINTER_IDLE || /* Printer is idle */
360 ((printer->type & CUPS_PRINTER_REMOTE) && /* Printer is remote */
361 !printer->job)) /* and not printing a job */
362 cupsdStartJob(job, printer);
363 }
364 }
365 }
366}
367
368
369/*
370 * 'cupsdCleanJobs()' - Clean out old jobs.
371 */
372
373void
374cupsdCleanJobs(void)
375{
376 cupsd_job_t *job; /* Current job */
377
378
379 if (!MaxJobs)
380 return;
381
382 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
383 job && cupsArrayCount(Jobs) >= MaxJobs;
384 job = (cupsd_job_t *)cupsArrayNext(Jobs))
385 if (job->state->values[0].integer >= IPP_JOB_CANCELLED)
386 cupsdCancelJob(job, 1);
387}
388
389
390/*
391 * 'cupsdFinishJob()' - Finish a job.
392 */
393
394void
395cupsdFinishJob(cupsd_job_t *job) /* I - Job */
396{
397 int job_history; /* Did cupsdCancelJob() keep the job? */
398 cupsd_printer_t *printer; /* Current printer */
399
400
401 cupsdLogMessage(CUPSD_LOG_DEBUG,
402 "cupsdFinishJob: job %d, file %d is complete.",
403 job->id, job->current_file - 1);
404
405 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFinishJob: job->status is %d",
406 job->status);
407
408 if (job->status_buffer && job->current_file >= job->num_files)
409 {
410 /*
411 * Close the pipe and clear the input bit.
412 */
413
414 cupsdLogMessage(CUPSD_LOG_DEBUG2,
415 "cupsdFinishJob: Removing fd %d from InputSet...",
416 job->status_buffer->fd);
417
418 FD_CLR(job->status_buffer->fd, InputSet);
419
420 cupsdLogMessage(CUPSD_LOG_DEBUG2,
421 "cupsdFinishJob: Closing status input pipe %d...",
422 job->status_buffer->fd);
423
424 cupsdStatBufDelete(job->status_buffer);
425
426 job->status_buffer = NULL;
427 }
428
e00b005a 429 printer = job->printer;
430
ef416fc2 431 if (job->status < 0)
432 {
433 /*
434 * Backend had errors; stop it...
435 */
436
ef416fc2 437 switch (-job->status)
438 {
439 default :
440 case CUPS_BACKEND_FAILED :
441 /*
442 * Backend failure, use the error-policy to determine how to
443 * act...
444 */
445
446 cupsdStopJob(job, 0);
447 job->state->values[0].integer = IPP_JOB_PENDING;
448 cupsdSaveJob(job);
449
450 /*
451 * If the job was queued to a class, try requeuing it... For
452 * faxes and retry-job queues, hold the current job for 5 minutes.
453 */
454
455 if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
456 cupsdCheckJobs();
457 else if ((printer->type & CUPS_PRINTER_FAX) ||
458 !strcmp(printer->error_policy, "retry-job"))
459 {
460 /*
461 * See how many times we've tried to send the job; if more than
462 * the limit, cancel the job.
463 */
464
465 job->tries ++;
466
467 if (job->tries >= JobRetryLimit)
468 {
469 /*
470 * Too many tries...
471 */
472
473 cupsdLogMessage(CUPSD_LOG_ERROR,
474 "Canceling job %d since it could not be sent after %d tries.",
475 job->id, JobRetryLimit);
476
477 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
478 "Job canceled since it could not be sent after %d tries.",
479 JobRetryLimit);
480
481 cupsdCancelJob(job, 0);
482 }
483 else
484 {
485 /*
486 * Try again in N seconds...
487 */
488
489 set_hold_until(job, time(NULL) + JobRetryInterval);
490 }
491 }
492 else if (!strcmp(printer->error_policy, "abort-job"))
493 cupsdCancelJob(job, 0);
494 break;
495
496 case CUPS_BACKEND_CANCEL :
497 /*
498 * Cancel the job...
499 */
500
501 cupsdCancelJob(job, 0);
502 break;
503
504 case CUPS_BACKEND_HOLD :
505 /*
506 * Hold the job...
507 */
508
509 cupsdStopJob(job, 0);
510 cupsdSetJobHoldUntil(job, "indefinite");
511 cupsdSaveJob(job);
512 break;
513
514 case CUPS_BACKEND_STOP :
515 /*
516 * Stop the printer...
517 */
518
519 cupsdStopJob(job, 0);
520 cupsdSaveJob(job);
521 cupsdSetPrinterState(printer, IPP_PRINTER_STOPPED, 1);
522 break;
523
524 case CUPS_BACKEND_AUTH_REQUIRED :
525 cupsdStopJob(job, 0);
526 cupsdSetJobHoldUntil(job, "authenticated");
527 cupsdSaveJob(job);
528
529 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, printer, job,
530 "Authentication is required for job %d.", job->id);
531 break;
532 }
533
534 /*
535 * Try printing another job...
536 */
537
538 cupsdCheckJobs();
539 }
540 else if (job->status > 0)
541 {
542 /*
e00b005a 543 * Filter had errors; stop job...
ef416fc2 544 */
545
e00b005a 546 cupsdStopJob(job, 1);
547 cupsdSaveJob(job);
548 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, job->printer, job,
549 "Job stopped due to filter errors; please consult the "
550 "error_log file for details.");
551 cupsdCheckJobs();
ef416fc2 552 }
553 else
554 {
555 /*
556 * Job printed successfully; cancel it...
557 */
558
559 if (job->current_file < job->num_files)
560 {
e00b005a 561 /*
562 * Start the next file in the job...
563 */
564
ef416fc2 565 FilterLevel -= job->cost;
566 cupsdStartJob(job, job->printer);
567 }
568 else
569 {
e00b005a 570 /*
571 * Close out this job...
572 */
573
ef416fc2 574 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
575 "Job completed successfully.");
576
577 job_history = JobHistory && !(job->dtype & CUPS_PRINTER_REMOTE);
578
579 cupsdCancelJob(job, 0);
580
581 if (job_history)
582 {
583 job->state->values[0].integer = IPP_JOB_COMPLETED;
584 cupsdSaveJob(job);
585 }
586
e00b005a 587 /*
588 * Clear the printer's state_message and move on...
589 */
590
591 printer->state_message[0] = '\0';
592
ef416fc2 593 cupsdCheckJobs();
594 }
595 }
596}
597
598
599/*
600 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
601 */
602
603void
604cupsdFreeAllJobs(void)
605{
606 cupsd_job_t *job; /* Current job */
607
608
609 cupsdHoldSignals();
610
611 cupsdStopAllJobs();
612
613 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
614 job;
615 job = (cupsd_job_t *)cupsArrayNext(Jobs))
616 {
617 cupsArrayRemove(Jobs, job);
618 cupsArrayRemove(ActiveJobs, job);
619
620 ippDelete(job->attrs);
621
622 if (job->num_files > 0)
623 {
624 free(job->compressions);
625 free(job->filetypes);
626 }
627
628 free(job);
629 }
630
631 cupsdReleaseSignals();
632}
633
634
635/*
636 * 'cupsdFindJob()' - Find the specified job.
637 */
638
639cupsd_job_t * /* O - Job data */
640cupsdFindJob(int id) /* I - Job ID */
641{
642 cupsd_job_t key; /* Search key */
643
644
645 key.id = id;
646
647 return ((cupsd_job_t *)cupsArrayFind(Jobs, &key));
648}
649
650
651/*
652 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
653 * or held jobs in a printer or class.
654 */
655
656int /* O - Job count */
657cupsdGetPrinterJobCount(
658 const char *dest) /* I - Printer or class name */
659{
660 int count; /* Job count */
661 cupsd_job_t *job; /* Current job */
662
663
664 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
665 job;
666 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
667 if (!strcasecmp(job->dest, dest))
668 count ++;
669
670 return (count);
671}
672
673
674/*
675 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
676 * or held jobs for a user.
677 */
678
679int /* O - Job count */
680cupsdGetUserJobCount(
681 const char *username) /* I - Username */
682{
683 int count; /* Job count */
684 cupsd_job_t *job; /* Current job */
685
686
687 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
688 job;
689 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
690 if (!strcasecmp(job->username, username))
691 count ++;
692
693 return (count);
694}
695
696
697/*
698 * 'cupsdHoldJob()' - Hold the specified job.
699 */
700
701void
702cupsdHoldJob(cupsd_job_t *job) /* I - Job data */
703{
704 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdHoldJob: id = %d", job->id);
705
706 if (job->state->values[0].integer == IPP_JOB_PROCESSING)
707 cupsdStopJob(job, 0);
708
709 DEBUG_puts("cupsdHoldJob: setting state to held...");
710
711 job->state->values[0].integer = IPP_JOB_HELD;
712
713 cupsdSaveJob(job);
714
715 cupsdCheckJobs();
716}
717
718
719/*
720 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
721 */
722
723void
724cupsdLoadAllJobs(void)
725{
726 cups_dir_t *dir; /* Directory */
727 cups_dentry_t *dent; /* Directory entry */
728 char filename[1024]; /* Full filename of job file */
fa73b229 729 cups_file_t *fp; /* Job file */
ef416fc2 730 cupsd_job_t *job; /* New job */
731 int jobid, /* Current job ID */
732 fileid; /* Current file ID */
733 ipp_attribute_t *attr; /* Job attribute */
734 char method[HTTP_MAX_URI],
735 /* Method portion of URI */
736 username[HTTP_MAX_URI],
737 /* Username portion of URI */
738 host[HTTP_MAX_URI],
739 /* Host portion of URI */
740 resource[HTTP_MAX_URI];
741 /* Resource portion of URI */
742 int port; /* Port portion of URI */
743 const char *dest; /* Destination */
744 mime_type_t **filetypes; /* New filetypes array */
745 int *compressions; /* New compressions array */
746
747
748 /*
749 * First create the job lists...
750 */
751
752 if (!Jobs)
753 Jobs = cupsArrayNew(compare_jobs, NULL);
754
755 if (!ActiveJobs)
756 ActiveJobs = cupsArrayNew(compare_active_jobs, NULL);
757
758 /*
759 * Then open the requests directory...
760 */
761
762 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdLoadAllJobs: Scanning %s...",
763 RequestRoot);
764
765 if ((dir = cupsDirOpen(RequestRoot)) == NULL)
766 {
767 cupsdLogMessage(CUPSD_LOG_ERROR,
768 "cupsdLoadAllJobs: Unable to open spool directory %s: %s",
769 RequestRoot, strerror(errno));
770 return;
771 }
772
773 /*
774 * Read all the c##### files...
775 */
776
777 while ((dent = cupsDirRead(dir)) != NULL)
778 if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c')
779 {
780 /*
781 * Allocate memory for the job...
782 */
783
784 if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
785 {
786 cupsdLogMessage(CUPSD_LOG_ERROR,
787 "cupsdLoadAllJobs: Ran out of memory for jobs!");
788 cupsDirClose(dir);
789 return;
790 }
791
792 if ((job->attrs = ippNew()) == NULL)
793 {
794 free(job);
795 cupsdLogMessage(CUPSD_LOG_ERROR,
796 "cupsdLoadAllJobs: Ran out of memory for job attributes!");
797 cupsDirClose(dir);
798 return;
799 }
800
801 /*
802 * Assign the job ID...
803 */
804
805 job->id = atoi(dent->filename + 1);
806 job->back_pipes[0] = -1;
807 job->back_pipes[1] = -1;
808 job->print_pipes[0] = -1;
809 job->print_pipes[1] = -1;
810
811 cupsdLogMessage(CUPSD_LOG_DEBUG,
812 "cupsdLoadAllJobs: Loading attributes for job %d...",
813 job->id);
814
815 if (job->id >= NextJobId)
816 NextJobId = job->id + 1;
817
818 /*
819 * Load the job control file...
820 */
821
822 snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->filename);
fa73b229 823 if ((fp = cupsFileOpen(filename, "r")) == NULL)
ef416fc2 824 {
825 cupsdLogMessage(CUPSD_LOG_ERROR,
fa73b229 826 "cupsdLoadAllJobs: Unable to open job control file "
827 "\"%s\" - %s!",
ef416fc2 828 filename, strerror(errno));
829 ippDelete(job->attrs);
830 free(job);
831 unlink(filename);
832 continue;
833 }
834 else
835 {
fa73b229 836 if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
837 job->attrs) != IPP_DATA)
ef416fc2 838 {
839 cupsdLogMessage(CUPSD_LOG_ERROR,
fa73b229 840 "cupsdLoadAllJobs: Unable to read job control file "
841 "\"%s\"!",
ef416fc2 842 filename);
fa73b229 843 cupsFileClose(fp);
ef416fc2 844 ippDelete(job->attrs);
845 free(job);
846 unlink(filename);
847 continue;
848 }
849
fa73b229 850 cupsFileClose(fp);
ef416fc2 851 }
852
853 if ((job->state = ippFindAttribute(job->attrs, "job-state", IPP_TAG_ENUM)) == NULL)
854 {
855 cupsdLogMessage(CUPSD_LOG_ERROR,
fa73b229 856 "cupsdLoadAllJobs: Missing or bad job-state attribute "
857 "in control file \"%s\"!",
ef416fc2 858 filename);
859 ippDelete(job->attrs);
860 free(job);
861 unlink(filename);
862 continue;
863 }
864
865 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", IPP_TAG_URI)) == NULL)
866 {
867 cupsdLogMessage(CUPSD_LOG_ERROR,
fa73b229 868 "cupsdLoadAllJobs: No job-printer-uri attribute in "
869 "control file \"%s\"!",
ef416fc2 870 filename);
871 ippDelete(job->attrs);
872 free(job);
873 unlink(filename);
874 continue;
875 }
876
a4d04587 877 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, method,
878 sizeof(method), username, sizeof(username), host,
879 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 880
881 if ((dest = cupsdValidateDest(host, resource, &(job->dtype),
882 NULL)) == NULL)
883 {
884 cupsdLogMessage(CUPSD_LOG_ERROR,
fa73b229 885 "cupsdLoadAllJobs: Unable to queue job for destination "
886 "\"%s\"!",
ef416fc2 887 attr->values[0].string.text);
888 ippDelete(job->attrs);
889 free(job);
890 unlink(filename);
891 continue;
892 }
893
894 cupsdSetString(&job->dest, dest);
895
896 job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed",
897 IPP_TAG_INTEGER);
898 job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
899
fa73b229 900 if ((attr = ippFindAttribute(job->attrs, "job-priority",
901 IPP_TAG_INTEGER)) == NULL)
ef416fc2 902 {
903 cupsdLogMessage(CUPSD_LOG_ERROR,
fa73b229 904 "cupsdLoadAllJobs: Missing or bad job-priority "
905 "attribute in control file \"%s\"!",
ef416fc2 906 filename);
907 ippDelete(job->attrs);
908 free(job);
909 unlink(filename);
910 continue;
911 }
912 job->priority = attr->values[0].integer;
913
fa73b229 914 if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name",
915 IPP_TAG_NAME)) == NULL)
ef416fc2 916 {
917 cupsdLogMessage(CUPSD_LOG_ERROR,
fa73b229 918 "cupsdLoadAllJobs: Missing or bad "
919 "job-originating-user-name attribute in control file "
920 "\"%s\"!",
ef416fc2 921 filename);
922 ippDelete(job->attrs);
923 free(job);
924 unlink(filename);
925 continue;
926 }
927 cupsdSetString(&job->username, attr->values[0].string.text);
928
929 /*
930 * Insert the job into the array, sorting by job priority and ID...
931 */
932
933 cupsArrayAdd(Jobs, job);
934 if (job->state->values[0].integer < IPP_JOB_STOPPED)
935 cupsArrayAdd(ActiveJobs,job);
936
937 /*
938 * Set the job hold-until time and state...
939 */
940
941 if (job->state->values[0].integer == IPP_JOB_HELD)
942 {
fa73b229 943 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
944 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 945 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
946
947 if (attr == NULL)
948 job->state->values[0].integer = IPP_JOB_PENDING;
949 else
950 cupsdSetJobHoldUntil(job, attr->values[0].string.text);
951 }
952 else if (job->state->values[0].integer == IPP_JOB_PROCESSING)
953 job->state->values[0].integer = IPP_JOB_PENDING;
954 }
955
956 /*
957 * Read all the d##### files...
958 */
959
960 cupsDirRewind(dir);
961
962 while ((dent = cupsDirRead(dir)) != NULL)
963 if (strlen(dent->filename) > 7 && dent->filename[0] == 'd' &&
964 strchr(dent->filename, '-'))
965 {
966 /*
967 * Find the job...
968 */
969
970 jobid = atoi(dent->filename + 1);
971 fileid = atoi(strchr(dent->filename, '-') + 1);
972
973 cupsdLogMessage(CUPSD_LOG_DEBUG,
974 "cupsdLoadAllJobs: Auto-typing document file %s...",
975 dent->filename);
976
977 snprintf(filename, sizeof(filename), "%s/%s", RequestRoot, dent->filename);
978
979 if ((job = cupsdFindJob(jobid)) == NULL)
980 {
981 cupsdLogMessage(CUPSD_LOG_ERROR,
982 "cupsdLoadAllJobs: Orphaned print file \"%s\"!",
983 filename);
984 unlink(filename);
985 continue;
986 }
987
988 if (fileid > job->num_files)
989 {
990 if (job->num_files == 0)
991 {
992 compressions = (int *)calloc(fileid, sizeof(int));
993 filetypes = (mime_type_t **)calloc(fileid, sizeof(mime_type_t *));
994 }
995 else
996 {
997 compressions = (int *)realloc(job->compressions,
998 sizeof(int) * fileid);
999 filetypes = (mime_type_t **)realloc(job->filetypes,
1000 sizeof(mime_type_t *) * fileid);
1001 }
1002
1003 if (compressions == NULL || filetypes == NULL)
1004 {
fa73b229 1005 cupsdLogMessage(CUPSD_LOG_ERROR,
1006 "cupsdLoadAllJobs: Ran out of memory for job file "
1007 "types!");
ef416fc2 1008 continue;
1009 }
1010
1011 job->compressions = compressions;
1012 job->filetypes = filetypes;
1013 job->num_files = fileid;
1014 }
1015
1016 job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, filename,
1017 job->compressions + fileid - 1);
1018
1019 if (job->filetypes[fileid - 1] == NULL)
1020 job->filetypes[fileid - 1] = mimeType(MimeDatabase, "application",
1021 "vnd.cups-raw");
1022 }
1023
1024 cupsDirClose(dir);
1025
1026 /*
1027 * Clean out old jobs as needed...
1028 */
1029
1030 cupsdCleanJobs();
1031}
1032
1033
1034/*
1035 * 'cupsdMoveJob()' - Move the specified job to a different destination.
1036 */
1037
1038void
1039cupsdMoveJob(cupsd_job_t *job, /* I - Job */
1040 const char *dest) /* I - Destination */
1041{
1042 ipp_attribute_t *attr; /* job-printer-uri attribute */
1043 cupsd_printer_t *p; /* Destination printer or class */
1044
1045
1046 /*
1047 * Find the printer...
1048 */
1049
1050 if ((p = cupsdFindDest(dest)) == NULL)
1051 return;
1052
1053 /*
1054 * Don't move completed jobs...
1055 */
1056
1057 if (job->state->values[0].integer >= IPP_JOB_PROCESSING)
1058 return;
1059
1060 /*
1061 * Change the destination information...
1062 */
1063
1064 cupsdSetString(&job->dest, dest);
1065 job->dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE |
1066 CUPS_PRINTER_IMPLICIT);
1067
1068 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", IPP_TAG_URI)) != NULL)
1069 cupsdSetString(&(attr->values[0].string.text), p->uri);
1070
1071 cupsdSaveJob(job);
1072}
1073
1074
1075/*
1076 * 'cupsdReleaseJob()' - Release the specified job.
1077 */
1078
1079void
1080cupsdReleaseJob(cupsd_job_t *job) /* I - Job */
1081{
1082 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReleaseJob: id = %d", job->id);
1083
1084 if (job->state->values[0].integer == IPP_JOB_HELD)
1085 {
1086 DEBUG_puts("cupsdReleaseJob: setting state to pending...");
1087
1088 job->state->values[0].integer = IPP_JOB_PENDING;
1089 cupsdSaveJob(job);
1090 cupsdCheckJobs();
1091 }
1092}
1093
1094
1095/*
1096 * 'cupsdRestartJob()' - Restart the specified job.
1097 */
1098
1099void
1100cupsdRestartJob(cupsd_job_t *job) /* I - Job */
1101{
1102 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdRestartJob: id = %d", job->id);
1103
1104 if (job->state->values[0].integer == IPP_JOB_STOPPED || JobFiles)
1105 {
1106 job->tries = 0;
1107 job->state->values[0].integer = IPP_JOB_PENDING;
1108 cupsdSaveJob(job);
1109 cupsdCheckJobs();
1110 }
1111}
1112
1113
1114/*
1115 * 'cupsdSaveJob()' - Save a job to disk.
1116 */
1117
1118void
1119cupsdSaveJob(cupsd_job_t *job) /* I - Job */
1120{
1121 char filename[1024]; /* Job control filename */
fa73b229 1122 cups_file_t *fp; /* Job file */
ef416fc2 1123
1124
1125 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id);
1126
fa73b229 1127 if ((fp = cupsFileOpen(filename, "w")) == NULL)
ef416fc2 1128 {
1129 cupsdLogMessage(CUPSD_LOG_ERROR,
fa73b229 1130 "cupsdSaveJob: Unable to create job control file "
1131 "\"%s\" - %s.",
ef416fc2 1132 filename, strerror(errno));
1133 return;
1134 }
1135
fa73b229 1136 fchmod(cupsFileNumber(fp), 0600);
1137 fchown(cupsFileNumber(fp), RunUser, Group);
ef416fc2 1138
fa73b229 1139 ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, job->attrs);
ef416fc2 1140
fa73b229 1141 cupsFileClose(fp);
ef416fc2 1142}
1143
1144
1145/*
1146 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job...
1147 */
1148
1149void
1150cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */
1151 const char *when) /* I - When to resume */
1152{
1153 time_t curtime; /* Current time */
1154 struct tm *curdate; /* Current date */
1155 int hour; /* Hold hour */
1156 int minute; /* Hold minute */
1157 int second; /* Hold second */
1158
1159
1160 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSetJobHoldUntil(%d, \"%s\")",
1161 job->id, when);
1162
1163 second = 0;
1164
1165 if (!strcmp(when, "indefinite") || !strcmp(when, "authenticated"))
1166 {
1167 /*
1168 * Hold indefinitely...
1169 */
1170
1171 job->hold_until = 0;
1172 }
1173 else if (!strcmp(when, "day-time"))
1174 {
1175 /*
1176 * Hold to 6am the next morning unless local time is < 6pm.
1177 */
1178
1179 curtime = time(NULL);
1180 curdate = localtime(&curtime);
1181
1182 if (curdate->tm_hour < 18)
1183 job->hold_until = curtime;
1184 else
1185 job->hold_until = curtime +
1186 ((29 - curdate->tm_hour) * 60 + 59 -
1187 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1188 }
1189 else if (!strcmp(when, "evening") || strcmp(when, "night"))
1190 {
1191 /*
1192 * Hold to 6pm unless local time is > 6pm or < 6am.
1193 */
1194
1195 curtime = time(NULL);
1196 curdate = localtime(&curtime);
1197
1198 if (curdate->tm_hour < 6 || curdate->tm_hour >= 18)
1199 job->hold_until = curtime;
1200 else
1201 job->hold_until = curtime +
1202 ((17 - curdate->tm_hour) * 60 + 59 -
1203 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1204 }
1205 else if (!strcmp(when, "second-shift"))
1206 {
1207 /*
1208 * Hold to 4pm unless local time is > 4pm.
1209 */
1210
1211 curtime = time(NULL);
1212 curdate = localtime(&curtime);
1213
1214 if (curdate->tm_hour >= 16)
1215 job->hold_until = curtime;
1216 else
1217 job->hold_until = curtime +
1218 ((15 - curdate->tm_hour) * 60 + 59 -
1219 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1220 }
1221 else if (!strcmp(when, "third-shift"))
1222 {
1223 /*
1224 * Hold to 12am unless local time is < 8am.
1225 */
1226
1227 curtime = time(NULL);
1228 curdate = localtime(&curtime);
1229
1230 if (curdate->tm_hour < 8)
1231 job->hold_until = curtime;
1232 else
1233 job->hold_until = curtime +
1234 ((23 - curdate->tm_hour) * 60 + 59 -
1235 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1236 }
1237 else if (!strcmp(when, "weekend"))
1238 {
1239 /*
1240 * Hold to weekend unless we are in the weekend.
1241 */
1242
1243 curtime = time(NULL);
1244 curdate = localtime(&curtime);
1245
1246 if (curdate->tm_wday || curdate->tm_wday == 6)
1247 job->hold_until = curtime;
1248 else
1249 job->hold_until = curtime +
1250 (((5 - curdate->tm_wday) * 24 +
1251 (17 - curdate->tm_hour)) * 60 + 59 -
1252 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
1253 }
1254 else if (sscanf(when, "%d:%d:%d", &hour, &minute, &second) >= 2)
1255 {
1256 /*
1257 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1258 */
1259
1260 curtime = time(NULL);
1261 curdate = gmtime(&curtime);
1262
1263 job->hold_until = curtime +
1264 ((hour - curdate->tm_hour) * 60 + minute -
1265 curdate->tm_min) * 60 + second - curdate->tm_sec;
1266
1267 /*
1268 * Hold until next day as needed...
1269 */
1270
1271 if (job->hold_until < curtime)
1272 job->hold_until += 24 * 60 * 60 * 60;
1273 }
1274
1275 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSetJobHoldUntil: hold_until = %d",
1276 (int)job->hold_until);
1277}
1278
1279
1280/*
1281 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
1282 * the list as needed.
1283 */
1284
1285void
1286cupsdSetJobPriority(
1287 cupsd_job_t *job, /* I - Job ID */
1288 int priority) /* I - New priority (0 to 100) */
1289{
1290 ipp_attribute_t *attr; /* Job attribute */
1291
1292
1293 /*
1294 * Don't change completed jobs...
1295 */
1296
1297 if (job->state->values[0].integer >= IPP_JOB_PROCESSING)
1298 return;
1299
1300 /*
1301 * Set the new priority and re-add the job into the active list...
1302 */
1303
1304 cupsArrayRemove(ActiveJobs, job);
1305
1306 job->priority = priority;
1307
1308 if ((attr = ippFindAttribute(job->attrs, "job-priority", IPP_TAG_INTEGER)) != NULL)
1309 attr->values[0].integer = priority;
1310 else
1311 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
1312 priority);
1313
1314 cupsArrayAdd(ActiveJobs, job);
1315
1316 cupsdSaveJob(job);
1317}
1318
1319
1320/*
1321 * 'cupsdStartJob()' - Start a print job.
1322 */
1323
1324void
1325cupsdStartJob(cupsd_job_t *job, /* I - Job ID */
1326 cupsd_printer_t *printer) /* I - Printer to print job */
1327{
1328 int i; /* Looping var */
1329 int slot; /* Pipe slot */
fa73b229 1330 cups_array_t *filters; /* Filters for job */
1331 mime_filter_t *filter, /* Current filter */
1332 port_monitor; /* Port monitor filter */
ef416fc2 1333 char method[255], /* Method for output */
1334 *optptr, /* Pointer to options */
1335 *valptr; /* Pointer in value string */
1336 ipp_attribute_t *attr; /* Current attribute */
e00b005a 1337 struct stat backinfo; /* Backend file information */
1338 int backroot; /* Run backend as root? */
ef416fc2 1339 int pid; /* Process ID of new filter process */
1340 int banner_page; /* 1 if banner page, 0 otherwise */
1341 int statusfds[2], /* Pipes used between the filters and scheduler */
1342 filterfds[2][2];/* Pipes used between the filters */
1343 int envc; /* Number of environment variables */
1344 char *argv[8], /* Filter command-line arguments */
1345 sani_uri[1024], /* Sanitized DEVICE_URI env var */
1346 filename[1024], /* Job filename */
1347 command[1024], /* Full path to filter/backend command */
1348 jobid[255], /* Job ID string */
1349 title[IPP_MAX_NAME],
1350 /* Job title string */
1351 copies[255], /* # copies string */
e00b005a 1352 *envp[MAX_ENV], /* Environment variables */
ef416fc2 1353 charset[255], /* CHARSET environment variable */
1354 class_name[255],/* CLASS environment variable */
1355 classification[1024],
1356 /* CLASSIFICATION environment variable */
1357 content_type[1024],
1358 /* CONTENT_TYPE environment variable */
1359 device_uri[1024],
1360 /* DEVICE_URI environment variable */
1361 lang[255], /* LANG environment variable */
1362 ppd[1024], /* PPD environment variable */
1363 printer_name[255],
1364 /* PRINTER environment variable */
1365 rip_max_cache[255];
1366 /* RIP_MAX_CACHE environment variable */
1367 static char *options = NULL;/* Full list of options */
1368 static int optlength = 0; /* Length of option buffer */
1369
1370
1371 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob() id = %d, file = %d/%d",
1372 job->id, job->current_file, job->num_files);
1373
1374 if (job->num_files == 0)
1375 {
1376 cupsdLogMessage(CUPSD_LOG_ERROR, "Job ID %d has no files! Cancelling it!",
1377 job->id);
1378
1379 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
1380 "Job canceled because it has no files.");
1381
1382 cupsdCancelJob(job, 0);
1383 return;
1384 }
1385
1386 /*
1387 * Figure out what filters are required to convert from
1388 * the source to the destination type...
1389 */
1390
fa73b229 1391 filters = NULL;
ef416fc2 1392 job->cost = 0;
1393
1394 if (printer->raw)
1395 {
1396 /*
1397 * Remote jobs and raw queues go directly to the printer without
1398 * filtering...
1399 */
1400
1401 cupsdLogMessage(CUPSD_LOG_DEBUG,
1402 "cupsdStartJob: Sending job to queue tagged as raw...");
1403
1404 filters = NULL;
1405 }
1406 else
1407 {
1408 /*
1409 * Local jobs get filtered...
1410 */
1411
1412 filters = mimeFilter(MimeDatabase, job->filetypes[job->current_file],
fa73b229 1413 printer->filetype, &(job->cost), MAX_FILTERS - 1);
ef416fc2 1414
fa73b229 1415 if (!filters)
ef416fc2 1416 {
1417 cupsdLogMessage(CUPSD_LOG_ERROR,
1418 "Unable to convert file %d to printable format for job %d!",
1419 job->current_file, job->id);
1420 cupsdLogMessage(CUPSD_LOG_INFO,
1421 "Hint: Do you have ESP Ghostscript installed?");
1422
1423 if (LogLevel < CUPSD_LOG_DEBUG)
1424 cupsdLogMessage(CUPSD_LOG_INFO,
1425 "Hint: Try setting the LogLevel to \"debug\".");
1426
1427 job->current_file ++;
1428
1429 if (job->current_file == job->num_files)
1430 {
1431 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
1432 "Job canceled because it has no files that can be printed.");
1433
1434 cupsdCancelJob(job, 0);
1435 }
1436
1437 return;
1438 }
1439
1440 /*
1441 * Remove NULL ("-") filters...
1442 */
1443
fa73b229 1444 for (filter = (mime_filter_t *)cupsArrayFirst(filters);
1445 filter;
1446 filter = (mime_filter_t *)cupsArrayNext(filters))
1447 if (!strcmp(filter->filter, "-"))
1448 cupsArrayRemove(filters, filter);
ef416fc2 1449
fa73b229 1450 if (cupsArrayCount(filters) == 0)
ef416fc2 1451 {
fa73b229 1452 cupsArrayDelete(filters);
ef416fc2 1453 filters = NULL;
1454 }
ef416fc2 1455 }
1456
1457 /*
1458 * See if the filter cost is too high...
1459 */
1460
1461 if ((FilterLevel + job->cost) > FilterLimit && FilterLevel > 0 &&
1462 FilterLimit > 0)
1463 {
1464 /*
1465 * Don't print this job quite yet...
1466 */
1467
fa73b229 1468 cupsArrayDelete(filters);
ef416fc2 1469
1470 cupsdLogMessage(CUPSD_LOG_INFO,
1471 "Holding job %d because filter limit has been reached.",
1472 job->id);
1473 cupsdLogMessage(CUPSD_LOG_DEBUG,
1474 "cupsdStartJob: id=%d, file=%d, cost=%d, level=%d, limit=%d",
1475 job->id, job->current_file, job->cost, FilterLevel,
1476 FilterLimit);
1477 return;
1478 }
1479
1480 FilterLevel += job->cost;
1481
1482 /*
1483 * Add decompression filters, if any...
1484 */
1485
1486 if (job->compressions[job->current_file])
1487 {
1488 /*
1489 * Add gziptoany filter to the front of the list...
1490 */
1491
fa73b229 1492 if (!cupsArrayInsert(filters, &gziptoany_filter))
ef416fc2 1493 {
1494 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add decompression filter - %s",
1495 strerror(errno));
1496
1497 if (filters != NULL)
1498 free(filters);
1499
1500 job->current_file ++;
1501
1502 if (job->current_file == job->num_files)
1503 {
1504 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
fa73b229 1505 "Job canceled because the print file could not be "
1506 "decompressed.");
ef416fc2 1507
1508 cupsdCancelJob(job, 0);
1509 }
1510
1511 return;
1512 }
ef416fc2 1513 }
1514
1515 /*
1516 * Add port monitor, if any...
1517 */
1518
1519 if (printer->port_monitor)
1520 {
1521 /*
1522 * Add port monitor to the end of the list...
1523 */
1524
fa73b229 1525 if (!cupsArrayAdd(filters, &port_monitor))
ef416fc2 1526 {
1527 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add port monitor - %s",
1528 strerror(errno));
1529
1530 if (filters != NULL)
1531 free(filters);
1532
1533 job->current_file ++;
1534
1535 if (job->current_file == job->num_files)
1536 {
1537 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
fa73b229 1538 "Job canceled because the port monitor could not be "
1539 "added.");
ef416fc2 1540
1541 cupsdCancelJob(job, 0);
1542 }
1543
1544 return;
1545 }
1546
fa73b229 1547 snprintf(port_monitor.filter, sizeof(port_monitor.filter),
ef416fc2 1548 "%s/monitor/%s", ServerBin, printer->port_monitor);
ef416fc2 1549 }
1550
1551 /*
1552 * Update the printer and job state to "processing"...
1553 */
1554
1555 job->state->values[0].integer = IPP_JOB_PROCESSING;
1556 job->status = 0;
1557 job->printer = printer;
1558 printer->job = job;
1559 cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
1560
1561 if (job->current_file == 0)
1562 {
1563 set_time(job, "time-at-processing");
1564 cupsdOpenPipe(job->back_pipes);
1565 }
1566
1567 /*
1568 * Determine if we are printing a banner page or not...
1569 */
1570
1571 if (job->job_sheets == NULL)
1572 {
1573 cupsdLogMessage(CUPSD_LOG_DEBUG, "No job-sheets attribute.");
1574 if ((job->job_sheets =
1575 ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
1576 cupsdLogMessage(CUPSD_LOG_DEBUG,
1577 "... but someone added one without setting job_sheets!");
1578 }
1579 else if (job->job_sheets->num_values == 1)
1580 cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s",
1581 job->job_sheets->values[0].string.text);
1582 else
1583 cupsdLogMessage(CUPSD_LOG_DEBUG, "job-sheets=%s,%s",
1584 job->job_sheets->values[0].string.text,
1585 job->job_sheets->values[1].string.text);
1586
1587 if (printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
1588 banner_page = 0;
1589 else if (job->job_sheets == NULL)
1590 banner_page = 0;
1591 else if (strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
1592 job->current_file == 0)
1593 banner_page = 1;
1594 else if (job->job_sheets->num_values > 1 &&
1595 strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
1596 job->current_file == (job->num_files - 1))
1597 banner_page = 1;
1598 else
1599 banner_page = 0;
1600
1601 cupsdLogMessage(CUPSD_LOG_DEBUG, "banner_page = %d", banner_page);
1602
1603 /*
1604 * Building the options string is harder than it needs to be, but
1605 * for the moment we need to pass strings for command-line args and
1606 * not IPP attribute pointers... :)
1607 *
1608 * First allocate/reallocate the option buffer as needed...
1609 */
1610
1611 i = ipp_length(job->attrs);
1612
1613 if (i > optlength)
1614 {
1615 if (optlength == 0)
1616 optptr = malloc(i);
1617 else
1618 optptr = realloc(options, i);
1619
1620 if (optptr == NULL)
1621 {
1622 cupsdLogMessage(CUPSD_LOG_CRIT,
1623 "cupsdStartJob: Unable to allocate %d bytes for option buffer for job %d!",
1624 i, job->id);
1625
1626 if (filters != NULL)
1627 free(filters);
1628
1629 FilterLevel -= job->cost;
1630
1631 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
1632 "Job canceled because the server ran out of memory.");
1633
1634 cupsdCancelJob(job, 0);
1635 return;
1636 }
1637
1638 options = optptr;
1639 optlength = i;
1640 }
1641
1642 /*
1643 * Now loop through the attributes and convert them to the textual
1644 * representation used by the filters...
1645 */
1646
1647 optptr = options;
1648 *optptr = '\0';
1649
1650 snprintf(title, sizeof(title), "%s-%d", printer->name, job->id);
1651 strcpy(copies, "1");
1652
1653 for (attr = job->attrs->attrs; attr != NULL; attr = attr->next)
1654 {
1655 if (strcmp(attr->name, "copies") == 0 &&
1656 attr->value_tag == IPP_TAG_INTEGER)
1657 {
1658 /*
1659 * Don't use the # copies attribute if we are printing the job sheets...
1660 */
1661
1662 if (!banner_page)
1663 sprintf(copies, "%d", attr->values[0].integer);
1664 }
1665 else if (strcmp(attr->name, "job-name") == 0 &&
1666 (attr->value_tag == IPP_TAG_NAME ||
1667 attr->value_tag == IPP_TAG_NAMELANG))
1668 strlcpy(title, attr->values[0].string.text, sizeof(title));
1669 else if (attr->group_tag == IPP_TAG_JOB)
1670 {
1671 /*
1672 * Filter out other unwanted attributes...
1673 */
1674
1675 if (attr->value_tag == IPP_TAG_MIMETYPE ||
1676 attr->value_tag == IPP_TAG_NAMELANG ||
1677 attr->value_tag == IPP_TAG_TEXTLANG ||
1678 attr->value_tag == IPP_TAG_URI ||
1679 attr->value_tag == IPP_TAG_URISCHEME ||
1680 attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */
1681 continue;
1682
1683 if (strncmp(attr->name, "time-", 5) == 0)
1684 continue;
1685
1686 if (strncmp(attr->name, "job-", 4) == 0 &&
1687 !(printer->type & CUPS_PRINTER_REMOTE))
1688 continue;
1689
1690 if (strncmp(attr->name, "job-", 4) == 0 &&
1691 strcmp(attr->name, "job-billing") != 0 &&
1692 strcmp(attr->name, "job-sheets") != 0 &&
1693 strcmp(attr->name, "job-hold-until") != 0 &&
1694 strcmp(attr->name, "job-priority") != 0)
1695 continue;
1696
1697 if ((strcmp(attr->name, "page-label") == 0 ||
1698 strcmp(attr->name, "page-border") == 0 ||
1699 strncmp(attr->name, "number-up", 9) == 0 ||
1700 strcmp(attr->name, "page-set") == 0) &&
1701 banner_page)
1702 continue;
1703
1704 /*
1705 * Otherwise add them to the list...
1706 */
1707
1708 if (optptr > options)
1709 strlcat(optptr, " ", optlength - (optptr - options));
1710
1711 if (attr->value_tag != IPP_TAG_BOOLEAN)
1712 {
1713 strlcat(optptr, attr->name, optlength - (optptr - options));
1714 strlcat(optptr, "=", optlength - (optptr - options));
1715 }
1716
1717 for (i = 0; i < attr->num_values; i ++)
1718 {
1719 if (i)
1720 strlcat(optptr, ",", optlength - (optptr - options));
1721
1722 optptr += strlen(optptr);
1723
1724 switch (attr->value_tag)
1725 {
1726 case IPP_TAG_INTEGER :
1727 case IPP_TAG_ENUM :
1728 snprintf(optptr, optlength - (optptr - options),
1729 "%d", attr->values[i].integer);
1730 break;
1731
1732 case IPP_TAG_BOOLEAN :
1733 if (!attr->values[i].boolean)
1734 strlcat(optptr, "no", optlength - (optptr - options));
1735
1736 case IPP_TAG_NOVALUE :
1737 strlcat(optptr, attr->name,
1738 optlength - (optptr - options));
1739 break;
1740
1741 case IPP_TAG_RANGE :
1742 if (attr->values[i].range.lower == attr->values[i].range.upper)
1743 snprintf(optptr, optlength - (optptr - options) - 1,
1744 "%d", attr->values[i].range.lower);
1745 else
1746 snprintf(optptr, optlength - (optptr - options) - 1,
1747 "%d-%d", attr->values[i].range.lower,
1748 attr->values[i].range.upper);
1749 break;
1750
1751 case IPP_TAG_RESOLUTION :
1752 snprintf(optptr, optlength - (optptr - options) - 1,
1753 "%dx%d%s", attr->values[i].resolution.xres,
1754 attr->values[i].resolution.yres,
1755 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
1756 "dpi" : "dpc");
1757 break;
1758
1759 case IPP_TAG_STRING :
1760 case IPP_TAG_TEXT :
1761 case IPP_TAG_NAME :
1762 case IPP_TAG_KEYWORD :
1763 case IPP_TAG_CHARSET :
1764 case IPP_TAG_LANGUAGE :
1765 for (valptr = attr->values[i].string.text; *valptr;)
1766 {
1767 if (strchr(" \t\n\\\'\"", *valptr))
1768 *optptr++ = '\\';
1769 *optptr++ = *valptr++;
1770 }
1771
1772 *optptr = '\0';
1773 break;
1774
1775 default :
1776 break; /* anti-compiler-warning-code */
1777 }
1778 }
1779
1780 optptr += strlen(optptr);
1781 }
1782 }
1783
1784 /*
1785 * Build the command-line arguments for the filters. Each filter
1786 * has 6 or 7 arguments:
1787 *
1788 * argv[0] = printer
1789 * argv[1] = job ID
1790 * argv[2] = username
1791 * argv[3] = title
1792 * argv[4] = # copies
1793 * argv[5] = options
1794 * argv[6] = filename (optional; normally stdin)
1795 *
1796 * This allows legacy printer drivers that use the old System V
1797 * printing interface to be used by CUPS.
1798 */
1799
1800 sprintf(jobid, "%d", job->id);
1801 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
1802 job->id, job->current_file + 1);
1803
1804 argv[0] = printer->name;
1805 argv[1] = jobid;
1806 argv[2] = job->username;
1807 argv[3] = title;
1808 argv[4] = copies;
1809 argv[5] = options;
1810 argv[6] = filename;
1811 argv[7] = NULL;
1812
1813 cupsdLogMessage(CUPSD_LOG_DEBUG,
1814 "cupsdStartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1815 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
1816
1817 /*
1818 * Create environment variable strings for the filters...
1819 */
1820
1821 attr = ippFindAttribute(job->attrs, "attributes-natural-language",
1822 IPP_TAG_LANGUAGE);
1823
1824 switch (strlen(attr->values[0].string.text))
1825 {
1826 default :
1827 /*
1828 * This is an unknown or badly formatted language code; use
1829 * the POSIX locale...
1830 */
1831
1832 strcpy(lang, "LANG=C");
1833 break;
1834
1835 case 2 :
1836 /*
1837 * Just the language code (ll)...
1838 */
1839
1840 snprintf(lang, sizeof(lang), "LANG=%s",
1841 attr->values[0].string.text);
1842 break;
1843
1844 case 5 :
1845 /*
1846 * Language and country code (ll-cc)...
1847 */
1848
1849 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c",
1850 attr->values[0].string.text[0],
1851 attr->values[0].string.text[1],
1852 toupper(attr->values[0].string.text[3] & 255),
1853 toupper(attr->values[0].string.text[4] & 255));
1854 break;
1855 }
1856
1857 attr = ippFindAttribute(job->attrs, "document-format",
1858 IPP_TAG_MIMETYPE);
1859 if (attr != NULL &&
1860 (optptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
1861 snprintf(charset, sizeof(charset), "CHARSET=%s", optptr + 8);
1862 else
1863 {
1864 attr = ippFindAttribute(job->attrs, "attributes-charset",
1865 IPP_TAG_CHARSET);
1866 snprintf(charset, sizeof(charset), "CHARSET=%s",
1867 attr->values[0].string.text);
1868 }
1869
1870 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
1871 job->filetypes[job->current_file]->super,
1872 job->filetypes[job->current_file]->type);
1873 snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s", printer->device_uri);
1874 cupsdSanitizeURI(printer->device_uri, sani_uri, sizeof(sani_uri));
1875 snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot, printer->name);
1876 snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", printer->name);
1877 snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
1878
1879 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
1880
1881 envp[envc ++] = charset;
1882 envp[envc ++] = lang;
1883 envp[envc ++] = ppd;
1884 envp[envc ++] = rip_max_cache;
1885 envp[envc ++] = content_type;
1886 envp[envc ++] = device_uri;
1887 envp[envc ++] = printer_name;
1888
1889 if (Classification && !banner_page)
1890 {
1891 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1892 IPP_TAG_NAME)) == NULL)
1893 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1894 Classification);
1895 else if (attr->num_values > 1 &&
1896 strcmp(attr->values[1].string.text, "none") != 0)
1897 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1898 attr->values[1].string.text);
1899 else
1900 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1901 attr->values[0].string.text);
1902
1903 envp[envc ++] = classification;
1904 }
1905
1906 if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
1907 {
1908 snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest);
1909 envp[envc ++] = class_name;
1910 }
1911
1912 envp[envc] = NULL;
1913
1914 for (i = 0; i < envc; i ++)
1915 if (strncmp(envp[i], "DEVICE_URI=", 11))
1916 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: envp[%d]=\"%s\"",
1917 i, envp[i]);
1918 else
1919 cupsdLogMessage(CUPSD_LOG_DEBUG,
1920 "cupsdStartJob: envp[%d]=\"DEVICE_URI=%s\"", i, sani_uri);
1921
1922 job->current_file ++;
1923
1924 /*
1925 * Now create processes for all of the filters...
1926 */
1927
1928 if (cupsdOpenPipe(statusfds))
1929 {
1930 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create job status pipes - %s.",
1931 strerror(errno));
1932 snprintf(printer->state_message, sizeof(printer->state_message),
1933 "Unable to create status pipes - %s.", strerror(errno));
1934
1935 cupsdAddPrinterHistory(printer);
1936
1937 if (filters != NULL)
1938 free(filters);
1939
1940 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
1941 "Job canceled because the server could not create the job status pipes.");
1942
1943 cupsdCancelJob(job, 0);
1944 return;
1945 }
1946
1947 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: statusfds = [ %d %d ]",
1948 statusfds[0], statusfds[1]);
1949
1950#ifdef FD_CLOEXEC
1951 fcntl(statusfds[0], F_SETFD, FD_CLOEXEC);
1952 fcntl(statusfds[1], F_SETFD, FD_CLOEXEC);
1953#endif /* FD_CLOEXEC */
1954
1955 job->status_buffer = cupsdStatBufNew(statusfds[0], "[Job %d]",
1956 job->id);
1957 job->status = 0;
1958 memset(job->filters, 0, sizeof(job->filters));
1959
1960 filterfds[1][0] = open("/dev/null", O_RDONLY);
1961 filterfds[1][1] = -1;
1962
1963 if (filterfds[1][0] < 0)
1964 {
1965 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"/dev/null\" - %s.",
1966 strerror(errno));
1967 snprintf(printer->state_message, sizeof(printer->state_message),
1968 "Unable to open \"/dev/null\" - %s.", strerror(errno));
1969
1970 cupsdAddPrinterHistory(printer);
1971
1972 if (filters != NULL)
1973 free(filters);
1974
1975 cupsdClosePipe(statusfds);
1976 cupsdCancelJob(job, 0);
1977 return;
1978 }
1979
1980 fcntl(filterfds[1][0], F_SETFD, fcntl(filterfds[1][0], F_GETFD) | FD_CLOEXEC);
1981
1982 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
1983 1, filterfds[1][0], filterfds[1][1]);
1984
fa73b229 1985 for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
1986 filter;
1987 i ++, filter = (mime_filter_t *)cupsArrayNext(filters))
ef416fc2 1988 {
fa73b229 1989 if (filter->filter[0] != '/')
ef416fc2 1990 snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
fa73b229 1991 filter->filter);
ef416fc2 1992 else
fa73b229 1993 strlcpy(command, filter->filter, sizeof(command));
ef416fc2 1994
fa73b229 1995 if (i < (cupsArrayCount(filters) - 1))
ef416fc2 1996 {
1997 if (cupsdOpenPipe(filterfds[slot]))
1998 {
1999 cupsdLogMessage(CUPSD_LOG_ERROR,
2000 "Unable to create job filter pipes - %s.",
2001 strerror(errno));
2002 snprintf(printer->state_message, sizeof(printer->state_message),
2003 "Unable to create filter pipes - %s.", strerror(errno));
2004 cupsdAddPrinterHistory(printer);
2005
fa73b229 2006 cupsArrayDelete(filters);
ef416fc2 2007
2008 cupsdClosePipe(statusfds);
2009 cupsdClosePipe(filterfds[!slot]);
2010
2011 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2012 "Job canceled because the server could not create the filter pipes.");
2013
2014 cupsdCancelJob(job, 0);
2015 return;
2016 }
2017 }
2018 else
2019 {
2020 if (job->current_file == 1)
2021 {
2022 if (strncmp(printer->device_uri, "file:", 5) != 0)
2023 {
2024 if (cupsdOpenPipe(job->print_pipes))
2025 {
2026 cupsdLogMessage(CUPSD_LOG_ERROR,
2027 "Unable to create job backend pipes - %s.",
2028 strerror(errno));
2029 snprintf(printer->state_message, sizeof(printer->state_message),
2030 "Unable to create backend pipes - %s.", strerror(errno));
2031 cupsdAddPrinterHistory(printer);
2032
fa73b229 2033 cupsArrayDelete(filters);
ef416fc2 2034
2035 cupsdClosePipe(statusfds);
2036 cupsdClosePipe(filterfds[!slot]);
2037
2038 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2039 "Job canceled because the server could not create the backend pipes.");
2040
2041 cupsdCancelJob(job, 0);
2042 return;
2043 }
2044 }
2045 else
2046 {
2047 job->print_pipes[0] = -1;
2048 if (!strncmp(printer->device_uri, "file:/dev/", 10) &&
2049 strcmp(printer->device_uri, "file:/dev/null"))
2050 job->print_pipes[1] = open(printer->device_uri + 5,
2051 O_WRONLY | O_EXCL);
2052 else if (!strncmp(printer->device_uri, "file:///dev/", 12) &&
2053 strcmp(printer->device_uri, "file:///dev/null"))
2054 job->print_pipes[1] = open(printer->device_uri + 7,
2055 O_WRONLY | O_EXCL);
2056 else
2057 job->print_pipes[1] = open(printer->device_uri + 5,
2058 O_WRONLY | O_CREAT | O_TRUNC, 0600);
2059
2060 if (job->print_pipes[1] < 0)
2061 {
2062 cupsdLogMessage(CUPSD_LOG_ERROR,
2063 "Unable to open output file \"%s\" - %s.",
2064 printer->device_uri, strerror(errno));
2065 snprintf(printer->state_message, sizeof(printer->state_message),
2066 "Unable to open output file \"%s\" - %s.",
2067 printer->device_uri, strerror(errno));
2068
2069 cupsdAddPrinterHistory(printer);
2070
fa73b229 2071 cupsArrayDelete(filters);
ef416fc2 2072
2073 cupsdClosePipe(statusfds);
2074 cupsdClosePipe(filterfds[!slot]);
2075
2076 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2077 "Job canceled because the server could not open the output file.");
2078
2079 cupsdCancelJob(job, 0);
2080 return;
2081 }
2082
2083 fcntl(job->print_pipes[1], F_SETFD,
2084 fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
2085 }
2086
2087 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartJob: print_pipes = [ %d %d ]",
2088 job->print_pipes[0], job->print_pipes[1]);
2089 }
2090
2091 filterfds[slot][0] = job->print_pipes[0];
2092 filterfds[slot][1] = job->print_pipes[1];
2093 }
2094
2095 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filter = \"%s\"", command);
2096 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2097 slot, filterfds[slot][0], filterfds[slot][1]);
2098
2099 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
2100 filterfds[slot][1], statusfds[1],
2101 job->back_pipes[0], 0, job->filters + i);
2102
2103 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2104 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2105 !slot, filterfds[!slot][0], filterfds[!slot][1]);
2106
2107 cupsdClosePipe(filterfds[!slot]);
2108
2109 if (pid == 0)
2110 {
2111 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
fa73b229 2112 filter->filter, strerror(errno));
ef416fc2 2113 snprintf(printer->state_message, sizeof(printer->state_message),
2114 "Unable to start filter \"%s\" - %s.",
fa73b229 2115 filter->filter, strerror(errno));
ef416fc2 2116
2117 cupsdAddPrinterHistory(printer);
2118
fa73b229 2119 cupsArrayDelete(filters);
ef416fc2 2120
2121 cupsdAddPrinterHistory(printer);
2122
2123 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2124 "Job canceled because the server could not execute a filter.");
2125
2126 cupsdCancelJob(job, 0);
2127 return;
2128 }
2129
2130 cupsdLogMessage(CUPSD_LOG_INFO, "Started filter %s (PID %d) for job %d.",
2131 command, pid, job->id);
2132
2133 argv[6] = NULL;
2134 slot = !slot;
2135 }
2136
fa73b229 2137 cupsArrayDelete(filters);
ef416fc2 2138
2139 /*
2140 * Finally, pipe the final output into a backend process if needed...
2141 */
2142
2143 if (strncmp(printer->device_uri, "file:", 5) != 0)
2144 {
2145 if (job->current_file == 1)
2146 {
2147 sscanf(printer->device_uri, "%254[^:]", method);
2148 snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, method);
2149
e00b005a 2150 /*
2151 * See if the backend needs to run as root...
2152 */
2153
2154 if (RunUser)
2155 backroot = 0;
2156 else if (lstat(command, &backinfo))
2157 backroot = 0;
2158 else
2159 backroot = !(backinfo.st_mode & (S_IRWXG | S_IRWXO));
2160
ef416fc2 2161 argv[0] = sani_uri;
2162
2163 filterfds[slot][0] = -1;
2164 filterfds[slot][1] = open("/dev/null", O_WRONLY);
2165
2166 if (filterfds[slot][1] < 0)
2167 {
2168 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"/dev/null\" - %s.",
2169 strerror(errno));
2170 snprintf(printer->state_message, sizeof(printer->state_message),
2171 "Unable to open \"/dev/null\" - %s.", strerror(errno));
2172
2173 cupsdAddPrinterHistory(printer);
2174
ef416fc2 2175 cupsdClosePipe(statusfds);
2176
2177 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2178 "Job canceled because the server could not open a file.");
2179
2180 cupsdCancelJob(job, 0);
2181 return;
2182 }
2183
2184 fcntl(filterfds[slot][1], F_SETFD,
2185 fcntl(filterfds[slot][1], F_GETFD) | FD_CLOEXEC);
2186
2187 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartJob: backend = \"%s\"",
2188 command);
2189 cupsdLogMessage(CUPSD_LOG_DEBUG,
2190 "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2191 slot, filterfds[slot][0], filterfds[slot][1]);
2192
2193 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
2194 filterfds[slot][1], statusfds[1],
e00b005a 2195 job->back_pipes[1], backroot,
ef416fc2 2196 &(job->backend));
2197
2198 if (pid == 0)
2199 {
2200 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to start backend \"%s\" - %s.",
2201 method, strerror(errno));
2202 snprintf(printer->state_message, sizeof(printer->state_message),
2203 "Unable to start backend \"%s\" - %s.", method, strerror(errno));
2204
2205 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2206 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2207 job->print_pipes[0], job->print_pipes[1]);
2208
2209 cupsdClosePipe(job->print_pipes);
2210
2211 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2212 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2213 job->back_pipes[0], job->back_pipes[1]);
2214
2215 cupsdClosePipe(job->back_pipes);
2216
2217 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2218 "Job canceled because the server could not execute the backend.");
2219
2220 cupsdCancelJob(job, 0);
2221 return;
2222 }
2223 else
2224 {
2225 cupsdLogMessage(CUPSD_LOG_INFO,
2226 "Started backend %s (PID %d) for job %d.",
2227 command, pid, job->id);
2228 }
2229 }
2230
2231 if (job->current_file == job->num_files)
2232 {
2233 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2234 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2235 job->print_pipes[0], job->print_pipes[1]);
2236
2237 cupsdClosePipe(job->print_pipes);
2238
2239 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2240 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2241 job->back_pipes[0], job->back_pipes[1]);
2242
2243 cupsdClosePipe(job->back_pipes);
2244 }
2245 }
2246 else
2247 {
2248 filterfds[slot][0] = -1;
2249 filterfds[slot][1] = -1;
2250
2251 if (job->current_file == job->num_files)
2252 {
2253 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2254 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2255 job->print_pipes[0], job->print_pipes[1]);
2256
2257 cupsdClosePipe(job->print_pipes);
2258 }
2259 }
2260
2261 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2262 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2263 slot, filterfds[slot][0], filterfds[slot][1]);
2264
2265 cupsdClosePipe(filterfds[slot]);
2266
2267 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2268 "cupsdStartJob: Closing status output pipe %d...",
2269 statusfds[1]);
2270
2271 close(statusfds[1]);
2272
2273 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2274 "cupsdStartJob: Adding fd %d to InputSet...",
2275 job->status_buffer->fd);
2276
2277 FD_SET(job->status_buffer->fd, InputSet);
e00b005a 2278
2279 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.",
2280 job->id);
ef416fc2 2281}
2282
2283
2284/*
2285 * 'cupsdStopAllJobs()' - Stop all print jobs.
2286 */
2287
2288void
2289cupsdStopAllJobs(void)
2290{
2291 cupsd_job_t *job; /* Current job */
2292
2293
2294 DEBUG_puts("cupsdStopAllJobs()");
2295
2296 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
2297 job;
2298 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
2299 if (job->state->values[0].integer == IPP_JOB_PROCESSING)
2300 {
2301 cupsdStopJob(job, 1);
2302 job->state->values[0].integer = IPP_JOB_PENDING;
2303 }
2304}
2305
2306
2307/*
2308 * 'cupsdStopJob()' - Stop a print job.
2309 */
2310
2311void
2312cupsdStopJob(cupsd_job_t *job, /* I - Job */
2313 int force) /* I - 1 = Force all filters to stop */
2314{
2315 int i; /* Looping var */
2316
2317
2318 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopJob: id = %d, force = %d",
2319 job->id, force);
2320
2321 if (job->state->values[0].integer != IPP_JOB_PROCESSING)
2322 return;
2323
2324 FilterLevel -= job->cost;
2325
2326 if (job->status < 0 &&
2327 !(job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)) &&
2328 !(job->printer->type & CUPS_PRINTER_FAX) &&
2329 !strcmp(job->printer->error_policy, "stop-printer"))
2330 cupsdSetPrinterState(job->printer, IPP_PRINTER_STOPPED, 1);
2331 else if (job->printer->state != IPP_PRINTER_STOPPED)
2332 cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0);
2333
2334 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopJob: printer state is %d",
2335 job->printer->state);
2336
2337 job->state->values[0].integer = IPP_JOB_STOPPED;
2338 job->printer->job = NULL;
2339 job->printer = NULL;
2340
2341 job->current_file --;
2342
2343 for (i = 0; job->filters[i]; i ++)
2344 if (job->filters[i] > 0)
2345 {
2346 cupsdEndProcess(job->filters[i], force);
2347 job->filters[i] = 0;
2348 }
2349
2350 if (job->backend > 0)
2351 {
2352 cupsdEndProcess(job->backend, force);
2353 job->backend = 0;
2354 }
2355
2356 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2357 "cupsdStopJob: Closing print pipes [ %d %d ]...",
2358 job->print_pipes[0], job->print_pipes[1]);
2359
2360 cupsdClosePipe(job->print_pipes);
2361
2362 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2363 "cupsdStopJob: Closing back pipes [ %d %d ]...",
2364 job->back_pipes[0], job->back_pipes[1]);
2365
2366 cupsdClosePipe(job->back_pipes);
2367
2368 if (job->status_buffer)
2369 {
2370 /*
2371 * Close the pipe and clear the input bit.
2372 */
2373
2374 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2375 "cupsdStopJob: Removing fd %d from InputSet...",
2376 job->status_buffer->fd);
2377
2378 FD_CLR(job->status_buffer->fd, InputSet);
2379
2380 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2381 "cupsdStopJob: Closing status input pipe %d...",
2382 job->status_buffer->fd);
2383
2384 cupsdStatBufDelete(job->status_buffer);
2385
2386 job->status_buffer = NULL;
2387 }
2388}
2389
2390
2391/*
2392 * 'cupsdUpdateJob()' - Read a status update from a job's filters.
2393 */
2394
2395void
2396cupsdUpdateJob(cupsd_job_t *job) /* I - Job to check */
2397{
2398 int i; /* Looping var */
2399 int copies; /* Number of copies printed */
2400 char message[1024], /* Message text */
2401 *ptr; /* Pointer update... */
2402 int loglevel; /* Log level for message */
2403
2404
2405 while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
2406 message, sizeof(message))) != NULL)
2407 {
2408 /*
2409 * Process page and printer state messages as needed...
2410 */
2411
2412 if (loglevel == CUPSD_LOG_PAGE)
2413 {
2414 /*
2415 * Page message; send the message to the page_log file and update the
2416 * job sheet count...
2417 */
2418
2419 if (job->sheets != NULL)
2420 {
2421 if (!strncasecmp(message, "total ", 6))
2422 {
2423 /*
2424 * Got a total count of pages from a backend or filter...
2425 */
2426
2427 copies = atoi(message + 6);
2428 copies -= job->sheets->values[0].integer; /* Just track the delta */
2429 }
2430 else if (!sscanf(message, "%*d%d", &copies))
2431 copies = 1;
2432
2433 job->sheets->values[0].integer += copies;
2434
2435 if (job->printer->page_limit)
2436 cupsdUpdateQuota(job->printer, job->username, copies, 0);
2437 }
2438
2439 cupsdLogPage(job, message);
2440
2441 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
2442 "Printed %d page(s).", job->sheets->values[0].integer);
2443 }
2444 else if (loglevel == CUPSD_LOG_STATE)
e00b005a 2445 {
ef416fc2 2446 cupsdSetPrinterReasons(job->printer, message);
e00b005a 2447 cupsdAddPrinterHistory(job->printer);
2448 }
ef416fc2 2449 else if (loglevel == CUPSD_LOG_ATTR)
2450 {
2451 /*
2452 * Set attribute(s)...
2453 */
2454
2455 /**** TODO ****/
2456 }
e00b005a 2457#ifdef __APPLE__
2458 else if (!strncmp(message, "recoverable:", 12))
2459 {
2460 cupsdSetPrinterReasons(job->printer,
2461 "+com.apple.print.recoverable-warning");
2462
2463 ptr = message + 12;
2464 while (isspace(*ptr & 255))
2465 ptr ++;
2466
2467 cupsdSetString(&job->printer->recoverable, ptr);
2468 cupsdAddPrinterHistory(job->printer);
2469 }
2470 else if (!strncmp(message, "recovered:", 10))
2471 {
2472 cupsdSetPrinterReasons(job->printer,
2473 "-com.apple.print.recoverable-warning");
2474
2475 ptr = message + 10;
2476 while (isspace(*ptr & 255))
2477 ptr ++;
2478
2479 cupsdSetString(&job->printer->recoverable, ptr);
2480 cupsdAddPrinterHistory(job->printer);
2481 }
2482#endif /* __APPLE__ */
2483 else
2484 {
2485 /*
2486 * Some message to show in the printer-state-message attribute...
2487 */
2488
2489 strlcpy(job->printer->state_message, message,
2490 sizeof(job->printer->state_message));
2491 cupsdAddPrinterHistory(job->printer);
2492 }
ef416fc2 2493
2494 if (!strchr(job->status_buffer->buffer, '\n'))
2495 break;
2496 }
2497
2498 if (ptr == NULL)
2499 {
2500 /*
2501 * See if all of the filters and the backend have returned their
2502 * exit statuses.
2503 */
2504
2505 for (i = 0; job->filters[i] < 0; i ++);
2506
2507 if (job->filters[i])
2508 return;
2509
2510 if (job->current_file >= job->num_files && job->backend > 0)
2511 return;
2512
2513 /*
2514 * Handle the end of job stuff...
2515 */
2516
2517 cupsdFinishJob(job);
2518 }
2519}
2520
2521
2522/*
2523 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
2524 */
2525
2526static int /* O - Difference */
2527compare_active_jobs(void *first, /* I - First job */
2528 void *second, /* I - Second job */
2529 void *data) /* I - App data (not used) */
2530{
2531 int diff; /* Difference */
2532
2533
2534 if ((diff = ((cupsd_job_t *)first)->priority - ((cupsd_job_t *)second)->priority) != 0)
2535 return (diff);
2536 else
2537 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
2538}
2539
2540
2541/*
2542 * 'compare_jobs()' - Compare the job IDs of two jobs.
2543 */
2544
2545static int /* O - Difference */
2546compare_jobs(void *first, /* I - First job */
2547 void *second, /* I - Second job */
2548 void *data) /* I - App data (not used) */
2549{
2550 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
2551}
2552
2553
2554/*
2555 * 'ipp_length()' - Compute the size of the buffer needed to hold
2556 * the textual IPP attributes.
2557 */
2558
2559int /* O - Size of buffer to hold IPP attributes */
2560ipp_length(ipp_t *ipp) /* I - IPP request */
2561{
2562 int bytes; /* Number of bytes */
2563 int i; /* Looping var */
2564 ipp_attribute_t *attr; /* Current attribute */
2565
2566
2567 /*
2568 * Loop through all attributes...
2569 */
2570
2571 bytes = 0;
2572
2573 for (attr = ipp->attrs; attr != NULL; attr = attr->next)
2574 {
2575 /*
2576 * Skip attributes that won't be sent to filters...
2577 */
2578
2579 if (attr->value_tag == IPP_TAG_MIMETYPE ||
2580 attr->value_tag == IPP_TAG_NAMELANG ||
2581 attr->value_tag == IPP_TAG_TEXTLANG ||
2582 attr->value_tag == IPP_TAG_URI ||
2583 attr->value_tag == IPP_TAG_URISCHEME)
2584 continue;
2585
2586 if (strncmp(attr->name, "time-", 5) == 0)
2587 continue;
2588
2589 /*
2590 * Add space for a leading space and commas between each value.
2591 * For the first attribute, the leading space isn't used, so the
2592 * extra byte can be used as the nul terminator...
2593 */
2594
2595 bytes ++; /* " " separator */
2596 bytes += attr->num_values; /* "," separators */
2597
2598 /*
2599 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
2600 * other attributes appear as "foo=value1,value2,...,valueN".
2601 */
2602
2603 if (attr->value_tag != IPP_TAG_BOOLEAN)
2604 bytes += strlen(attr->name);
2605 else
2606 bytes += attr->num_values * strlen(attr->name);
2607
2608 /*
2609 * Now add the size required for each value in the attribute...
2610 */
2611
2612 switch (attr->value_tag)
2613 {
2614 case IPP_TAG_INTEGER :
2615 case IPP_TAG_ENUM :
2616 /*
2617 * Minimum value of a signed integer is -2147483647, or 11 digits.
2618 */
2619
2620 bytes += attr->num_values * 11;
2621 break;
2622
2623 case IPP_TAG_BOOLEAN :
2624 /*
2625 * Add two bytes for each false ("no") value...
2626 */
2627
2628 for (i = 0; i < attr->num_values; i ++)
2629 if (!attr->values[i].boolean)
2630 bytes += 2;
2631 break;
2632
2633 case IPP_TAG_RANGE :
2634 /*
2635 * A range is two signed integers separated by a hyphen, or
2636 * 23 characters max.
2637 */
2638
2639 bytes += attr->num_values * 23;
2640 break;
2641
2642 case IPP_TAG_RESOLUTION :
2643 /*
2644 * A resolution is two signed integers separated by an "x" and
2645 * suffixed by the units, or 26 characters max.
2646 */
2647
2648 bytes += attr->num_values * 26;
2649 break;
2650
2651 case IPP_TAG_STRING :
2652 case IPP_TAG_TEXT :
2653 case IPP_TAG_NAME :
2654 case IPP_TAG_KEYWORD :
2655 case IPP_TAG_CHARSET :
2656 case IPP_TAG_LANGUAGE :
2657 /*
2658 * Strings can contain characters that need quoting. We need
2659 * at least 2 * len + 2 characters to cover the quotes and
2660 * any backslashes in the string.
2661 */
2662
2663 for (i = 0; i < attr->num_values; i ++)
2664 bytes += 2 * strlen(attr->values[i].string.text) + 2;
2665 break;
2666
2667 default :
2668 break; /* anti-compiler-warning-code */
2669 }
2670 }
2671
2672 return (bytes);
2673}
2674
2675
2676/*
2677 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2678 */
2679
2680static void
2681set_time(cupsd_job_t *job, /* I - Job to update */
2682 const char *name) /* I - Name of attribute */
2683{
2684 ipp_attribute_t *attr; /* Time attribute */
2685
2686
2687 if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL)
2688 {
2689 attr->value_tag = IPP_TAG_INTEGER;
2690 attr->values[0].integer = time(NULL);
2691 }
2692}
2693
2694
2695/*
2696 * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
2697 */
2698
2699static void
2700set_hold_until(cupsd_job_t *job, /* I - Job to update */
2701 time_t holdtime) /* I - Hold until time */
2702{
2703 ipp_attribute_t *attr; /* job-hold-until attribute */
2704 struct tm *holddate; /* Hold date */
2705 char holdstr[64]; /* Hold time */
2706
2707
2708 /*
2709 * Set the hold_until value and hold the job...
2710 */
2711
2712 cupsdLogMessage(CUPSD_LOG_DEBUG, "set_hold_until: hold_until = %d", (int)holdtime);
2713
2714 job->state->values[0].integer = IPP_JOB_HELD;
2715 job->hold_until = holdtime;
2716
2717 /*
2718 * Update the job-hold-until attribute with a string representing GMT
2719 * time (HH:MM:SS)...
2720 */
2721
2722 holddate = gmtime(&holdtime);
2723 snprintf(holdstr, sizeof(holdstr), "%d:%d:%d", holddate->tm_hour,
2724 holddate->tm_min, holddate->tm_sec);
2725
2726 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
2727 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
2728
2729 /*
2730 * Either add the attribute or update the value of the existing one
2731 */
2732
2733 if (attr == NULL)
2734 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2735 "job-hold-until", NULL, holdstr);
2736 else
2737 cupsdSetString(&attr->values[0].string.text, holdstr);
2738
2739 cupsdSaveJob(job);
2740}
2741
2742
2743/*
e00b005a 2744 * End of "$Id: job.c 5046 2006-02-01 22:11:58Z mike $".
ef416fc2 2745 */