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