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