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