]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/job.c
Import CUPS 1.4svn-r7464.
[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
db0bd74a
MS
2675 port_monitor.src = NULL;
2676 port_monitor.dst = NULL;
2677 port_monitor.cost = 0;
2678
2679 snprintf(port_monitor.filter, sizeof(port_monitor.filter),
2680 "%s/monitor/%s", ServerBin, printer->port_monitor);
2681
e1d6a774 2682 if (!cupsArrayAdd(filters, &port_monitor))
ef416fc2 2683 {
09a101d6 2684 cupsdLogMessage(CUPSD_LOG_ERROR,
2685 "[Job %d] Unable to add port monitor - %s",
2686 job->id, strerror(errno));
ef416fc2 2687
e1d6a774 2688 cupsArrayDelete(filters);
ef416fc2 2689
e1d6a774 2690 job->current_file ++;
ef416fc2 2691
e1d6a774 2692 if (job->current_file == job->num_files)
07725fee 2693 cupsdCancelJob(job, 0, IPP_JOB_ABORTED);
ef416fc2 2694
e1d6a774 2695 return;
e00b005a 2696 }
e1d6a774 2697 }
e00b005a 2698
e1d6a774 2699 /*
2700 * Update the printer and job state to "processing"...
2701 */
ef416fc2 2702
e1d6a774 2703 job->state->values[0].integer = IPP_JOB_PROCESSING;
2704 job->state_value = IPP_JOB_PROCESSING;
f899b121 2705
e1d6a774 2706 job->status = 0;
2707 job->printer = printer;
2708 printer->job = job;
f899b121 2709
e1d6a774 2710 cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
ef416fc2 2711
e1d6a774 2712 if (job->current_file == 0)
ef416fc2 2713 {
f301802f 2714 /*
2715 * Set the processing time...
2716 */
2717
e1d6a774 2718 set_time(job, "time-at-processing");
f301802f 2719
2720 /*
2721 * Create the backchannel pipes and make them non-blocking...
2722 */
2723
e1d6a774 2724 cupsdOpenPipe(job->back_pipes);
f301802f 2725
2726 fcntl(job->back_pipes[0], F_SETFL,
2727 fcntl(job->back_pipes[0], F_GETFL) | O_NONBLOCK);
2728
2729 fcntl(job->back_pipes[1], F_SETFL,
2730 fcntl(job->back_pipes[1], F_GETFL) | O_NONBLOCK);
f7deaa1a 2731
2732 /*
2733 * Create the side-channel pipes and make them non-blocking...
2734 */
2735
2736 socketpair(AF_LOCAL, SOCK_STREAM, 0, job->side_pipes);
2737
2738 fcntl(job->side_pipes[0], F_SETFL,
2739 fcntl(job->side_pipes[0], F_GETFL) | O_NONBLOCK);
2740
2741 fcntl(job->side_pipes[1], F_SETFL,
2742 fcntl(job->side_pipes[1], F_GETFL) | O_NONBLOCK);
e1d6a774 2743 }
ef416fc2 2744
e1d6a774 2745 /*
2746 * Determine if we are printing a banner page or not...
2747 */
ef416fc2 2748
e1d6a774 2749 if (job->job_sheets == NULL)
2750 {
09a101d6 2751 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] No job-sheets attribute.",
2752 job->id);
e1d6a774 2753 if ((job->job_sheets =
2754 ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
2755 cupsdLogMessage(CUPSD_LOG_DEBUG,
09a101d6 2756 "[Job %d] ... but someone added one without setting "
2757 "job_sheets!", job->id);
e1d6a774 2758 }
2759 else if (job->job_sheets->num_values == 1)
09a101d6 2760 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] job-sheets=%s", job->id,
2761 job->job_sheets->values[0].string.text);
e1d6a774 2762 else
09a101d6 2763 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] job-sheets=%s,%s", job->id,
e1d6a774 2764 job->job_sheets->values[0].string.text,
2765 job->job_sheets->values[1].string.text);
ef416fc2 2766
e1d6a774 2767 if (printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
2768 banner_page = 0;
2769 else if (job->job_sheets == NULL)
2770 banner_page = 0;
2771 else if (strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
2772 job->current_file == 0)
2773 banner_page = 1;
2774 else if (job->job_sheets->num_values > 1 &&
2775 strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
2776 job->current_file == (job->num_files - 1))
2777 banner_page = 1;
2778 else
2779 banner_page = 0;
ef416fc2 2780
09a101d6 2781 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] banner_page = %d", job->id,
2782 banner_page);
ef416fc2 2783
e1d6a774 2784 /*
2785 * Building the options string is harder than it needs to be, but
2786 * for the moment we need to pass strings for command-line args and
2787 * not IPP attribute pointers... :)
2788 *
2789 * First allocate/reallocate the option buffer as needed...
2790 */
ef416fc2 2791
e1d6a774 2792 i = ipp_length(job->attrs);
ef416fc2 2793
e1d6a774 2794 if (i > optlength)
2795 {
2796 if (optlength == 0)
2797 optptr = malloc(i);
2798 else
2799 optptr = realloc(options, i);
ef416fc2 2800
e1d6a774 2801 if (optptr == NULL)
2802 {
2803 cupsdLogMessage(CUPSD_LOG_CRIT,
09a101d6 2804 "[Job %d] Unable to allocate %d bytes for option buffer!",
2805 job->id, i);
ef416fc2 2806
e1d6a774 2807 cupsArrayDelete(filters);
ef416fc2 2808
e1d6a774 2809 FilterLevel -= job->cost;
ef416fc2 2810
07725fee 2811 cupsdCancelJob(job, 0, IPP_JOB_ABORTED);
e1d6a774 2812 return;
2813 }
ef416fc2 2814
e1d6a774 2815 options = optptr;
2816 optlength = i;
2817 }
ef416fc2 2818
e1d6a774 2819 /*
2820 * Now loop through the attributes and convert them to the textual
2821 * representation used by the filters...
2822 */
ef416fc2 2823
e1d6a774 2824 optptr = options;
2825 *optptr = '\0';
bd7854cb 2826
e1d6a774 2827 snprintf(title, sizeof(title), "%s-%d", printer->name, job->id);
2828 strcpy(copies, "1");
bd7854cb 2829
e1d6a774 2830 for (attr = job->attrs->attrs; attr != NULL; attr = attr->next)
bd7854cb 2831 {
e1d6a774 2832 if (!strcmp(attr->name, "copies") &&
2833 attr->value_tag == IPP_TAG_INTEGER)
2834 {
2835 /*
2836 * Don't use the # copies attribute if we are printing the job sheets...
2837 */
bd7854cb 2838
e1d6a774 2839 if (!banner_page)
2840 sprintf(copies, "%d", attr->values[0].integer);
2841 }
2842 else if (!strcmp(attr->name, "job-name") &&
2843 (attr->value_tag == IPP_TAG_NAME ||
2844 attr->value_tag == IPP_TAG_NAMELANG))
2845 strlcpy(title, attr->values[0].string.text, sizeof(title));
2846 else if (attr->group_tag == IPP_TAG_JOB)
2847 {
2848 /*
2849 * Filter out other unwanted attributes...
2850 */
bd7854cb 2851
e1d6a774 2852 if (attr->value_tag == IPP_TAG_MIMETYPE ||
2853 attr->value_tag == IPP_TAG_NAMELANG ||
2854 attr->value_tag == IPP_TAG_TEXTLANG ||
2855 (attr->value_tag == IPP_TAG_URI && strcmp(attr->name, "job-uuid")) ||
2856 attr->value_tag == IPP_TAG_URISCHEME ||
2857 attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */
2858 continue;
bd7854cb 2859
e1d6a774 2860 if (!strncmp(attr->name, "time-", 5))
2861 continue;
bd7854cb 2862
e1d6a774 2863 if (!strncmp(attr->name, "job-", 4) && strcmp(attr->name, "job-uuid") &&
2864 !(printer->type & CUPS_PRINTER_REMOTE))
2865 continue;
ef416fc2 2866
e1d6a774 2867 if (!strncmp(attr->name, "job-", 4) &&
2868 strcmp(attr->name, "job-uuid") &&
2869 strcmp(attr->name, "job-billing") &&
2870 strcmp(attr->name, "job-sheets") &&
2871 strcmp(attr->name, "job-hold-until") &&
2872 strcmp(attr->name, "job-priority"))
2873 continue;
ef416fc2 2874
e1d6a774 2875 if ((!strcmp(attr->name, "page-label") ||
2876 !strcmp(attr->name, "page-border") ||
2877 !strncmp(attr->name, "number-up", 9) ||
b94498cf 2878 !strcmp(attr->name, "page-ranges") ||
e1d6a774 2879 !strcmp(attr->name, "page-set") ||
2880 !strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
db1f069b
MS
2881 !strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed") ||
2882 !strcasecmp(attr->name, "com.apple.print.PrintSettings."
2883 "PMTotalSidesImaged..n.") ||
2884 !strcasecmp(attr->name, "com.apple.print.PrintSettings."
2885 "PMTotalBeginPages..n.")) &&
e1d6a774 2886 banner_page)
2887 continue;
ef416fc2 2888
e1d6a774 2889 /*
2890 * Otherwise add them to the list...
2891 */
ef416fc2 2892
e1d6a774 2893 if (optptr > options)
2894 strlcat(optptr, " ", optlength - (optptr - options));
ef416fc2 2895
e1d6a774 2896 if (attr->value_tag != IPP_TAG_BOOLEAN)
2897 {
2898 strlcat(optptr, attr->name, optlength - (optptr - options));
2899 strlcat(optptr, "=", optlength - (optptr - options));
2900 }
ef416fc2 2901
e1d6a774 2902 for (i = 0; i < attr->num_values; i ++)
2903 {
2904 if (i)
2905 strlcat(optptr, ",", optlength - (optptr - options));
ef416fc2 2906
e1d6a774 2907 optptr += strlen(optptr);
ef416fc2 2908
e1d6a774 2909 switch (attr->value_tag)
2910 {
2911 case IPP_TAG_INTEGER :
2912 case IPP_TAG_ENUM :
2913 snprintf(optptr, optlength - (optptr - options),
2914 "%d", attr->values[i].integer);
2915 break;
ef416fc2 2916
e1d6a774 2917 case IPP_TAG_BOOLEAN :
2918 if (!attr->values[i].boolean)
2919 strlcat(optptr, "no", optlength - (optptr - options));
ef416fc2 2920
e1d6a774 2921 case IPP_TAG_NOVALUE :
2922 strlcat(optptr, attr->name,
2923 optlength - (optptr - options));
2924 break;
ef416fc2 2925
e1d6a774 2926 case IPP_TAG_RANGE :
2927 if (attr->values[i].range.lower == attr->values[i].range.upper)
2928 snprintf(optptr, optlength - (optptr - options) - 1,
2929 "%d", attr->values[i].range.lower);
2930 else
2931 snprintf(optptr, optlength - (optptr - options) - 1,
2932 "%d-%d", attr->values[i].range.lower,
2933 attr->values[i].range.upper);
2934 break;
ef416fc2 2935
e1d6a774 2936 case IPP_TAG_RESOLUTION :
2937 snprintf(optptr, optlength - (optptr - options) - 1,
2938 "%dx%d%s", attr->values[i].resolution.xres,
2939 attr->values[i].resolution.yres,
2940 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
2941 "dpi" : "dpc");
2942 break;
ef416fc2 2943
e1d6a774 2944 case IPP_TAG_STRING :
2945 case IPP_TAG_TEXT :
2946 case IPP_TAG_NAME :
2947 case IPP_TAG_KEYWORD :
2948 case IPP_TAG_CHARSET :
2949 case IPP_TAG_LANGUAGE :
2950 case IPP_TAG_URI :
2951 for (valptr = attr->values[i].string.text; *valptr;)
2952 {
2953 if (strchr(" \t\n\\\'\"", *valptr))
2954 *optptr++ = '\\';
2955 *optptr++ = *valptr++;
2956 }
2957
2958 *optptr = '\0';
2959 break;
2960
2961 default :
2962 break; /* anti-compiler-warning-code */
2963 }
2964 }
2965
2966 optptr += strlen(optptr);
2967 }
2968 }
2969
2970 /*
2971 * Build the command-line arguments for the filters. Each filter
2972 * has 6 or 7 arguments:
2973 *
2974 * argv[0] = printer
2975 * argv[1] = job ID
2976 * argv[2] = username
2977 * argv[3] = title
2978 * argv[4] = # copies
2979 * argv[5] = options
2980 * argv[6] = filename (optional; normally stdin)
2981 *
2982 * This allows legacy printer drivers that use the old System V
2983 * printing interface to be used by CUPS.
2984 *
2985 * For remote jobs, we send all of the files in the argument list.
2986 */
2987
d09495fa 2988 if (printer->remote && job->num_files > 1)
e1d6a774 2989 argv = calloc(7 + job->num_files, sizeof(char *));
2990 else
2991 argv = calloc(8, sizeof(char *));
2992
91c84a35
MS
2993 if (!argv)
2994 {
2995 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate argument array!");
2996 cupsArrayDelete(filters);
2997
2998 FilterLevel -= job->cost;
2999
3000 cupsdStopPrinter(printer, 0);
3001 return;
3002 }
3003
e1d6a774 3004 sprintf(jobid, "%d", job->id);
3005
3006 argv[0] = printer->name;
3007 argv[1] = jobid;
3008 argv[2] = job->username;
3009 argv[3] = title;
3010 argv[4] = copies;
3011 argv[5] = options;
3012
d09495fa 3013 if (printer->remote && job->num_files > 1)
e1d6a774 3014 {
3015 for (i = 0; i < job->num_files; i ++)
ef416fc2 3016 {
e1d6a774 3017 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
3018 job->id, i + 1);
3019 argv[6 + i] = strdup(filename);
3020 }
3021 }
3022 else
3023 {
3024 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
3025 job->id, job->current_file + 1);
3026 argv[6] = filename;
3027 }
ef416fc2 3028
e1d6a774 3029 for (i = 0; argv[i]; i ++)
3030 cupsdLogMessage(CUPSD_LOG_DEBUG,
3031 "[Job %d] argv[%d]=\"%s\"", job->id, i, argv[i]);
ef416fc2 3032
e1d6a774 3033 /*
3034 * Create environment variable strings for the filters...
3035 */
ef416fc2 3036
e1d6a774 3037 attr = ippFindAttribute(job->attrs, "attributes-natural-language",
3038 IPP_TAG_LANGUAGE);
ef416fc2 3039
0a682745 3040#ifdef __APPLE__
a4924f6c 3041 strcpy(apple_language, "APPLE_LANGUAGE=");
0a682745
MS
3042 _cupsAppleLanguage(attr->values[0].string.text,
3043 apple_language + 15, sizeof(apple_language) - 15);
3044#endif /* __APPLE__ */
3045
e1d6a774 3046 switch (strlen(attr->values[0].string.text))
3047 {
3048 default :
3049 /*
3050 * This is an unknown or badly formatted language code; use
3051 * the POSIX locale...
3052 */
ef416fc2 3053
e1d6a774 3054 strcpy(lang, "LANG=C");
3055 break;
ef416fc2 3056
e1d6a774 3057 case 2 :
3058 /*
3059 * Just the language code (ll)...
3060 */
ef416fc2 3061
7dfedb92 3062 snprintf(lang, sizeof(lang), "LANG=%s.UTF8",
e1d6a774 3063 attr->values[0].string.text);
3064 break;
ef416fc2 3065
e1d6a774 3066 case 5 :
3067 /*
3068 * Language and country code (ll-cc)...
3069 */
ef416fc2 3070
7dfedb92 3071 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF8",
e1d6a774 3072 attr->values[0].string.text[0],
3073 attr->values[0].string.text[1],
3074 toupper(attr->values[0].string.text[3] & 255),
3075 toupper(attr->values[0].string.text[4] & 255));
3076 break;
3077 }
ef416fc2 3078
e1d6a774 3079 attr = ippFindAttribute(job->attrs, "document-format",
3080 IPP_TAG_MIMETYPE);
3081 if (attr != NULL &&
3082 (optptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
3083 snprintf(charset, sizeof(charset), "CHARSET=%s", optptr + 8);
3084 else
3085 {
3086 attr = ippFindAttribute(job->attrs, "attributes-charset",
3087 IPP_TAG_CHARSET);
3088 snprintf(charset, sizeof(charset), "CHARSET=%s",
3089 attr->values[0].string.text);
ef416fc2 3090 }
3091
e1d6a774 3092 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
3093 job->filetypes[job->current_file]->super,
3094 job->filetypes[job->current_file]->type);
3095 snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s",
3096 printer->device_uri);
3097 cupsdSanitizeURI(printer->device_uri, sani_uri, sizeof(sani_uri));
3098 snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot, printer->name);
3099 snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", printer->name);
3100 snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
ef416fc2 3101
e1d6a774 3102 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
ef416fc2 3103
e1d6a774 3104 envp[envc ++] = charset;
3105 envp[envc ++] = lang;
0a682745
MS
3106#ifdef __APPLE__
3107 envp[envc ++] = apple_language;
3108#endif /* __APPLE__ */
e1d6a774 3109 envp[envc ++] = ppd;
3110 envp[envc ++] = rip_max_cache;
3111 envp[envc ++] = content_type;
3112 envp[envc ++] = device_uri;
3113 envp[envc ++] = printer_name;
bd7854cb 3114
db0bd74a 3115 if (!printer->remote && !printer->raw)
e1d6a774 3116 {
db0bd74a
MS
3117 filter = (mime_filter_t *)cupsArrayLast(filters);
3118
3119 if (printer->port_monitor)
3120 filter = (mime_filter_t *)cupsArrayPrev(filters);
3121
3122 if (filter && filter->dst)
3123 {
3124 snprintf(final_content_type, sizeof(final_content_type),
3125 "FINAL_CONTENT_TYPE=%s/%s",
3126 filter->dst->super, filter->dst->type);
3127 envp[envc ++] = final_content_type;
3128 }
e1d6a774 3129 }
bd7854cb 3130
e1d6a774 3131 if (Classification && !banner_page)
3132 {
3133 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
3134 IPP_TAG_NAME)) == NULL)
3135 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
3136 Classification);
3137 else if (attr->num_values > 1 &&
3138 strcmp(attr->values[1].string.text, "none") != 0)
3139 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
3140 attr->values[1].string.text);
3141 else
3142 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
3143 attr->values[0].string.text);
bd7854cb 3144
e1d6a774 3145 envp[envc ++] = classification;
3146 }
bd7854cb 3147
e1d6a774 3148 if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
bd7854cb 3149 {
e1d6a774 3150 snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest);
3151 envp[envc ++] = class_name;
3152 }
bd7854cb 3153
09a101d6 3154 if (job->auth_username)
3155 envp[envc ++] = job->auth_username;
3156 if (job->auth_domain)
3157 envp[envc ++] = job->auth_domain;
3158 if (job->auth_password)
3159 envp[envc ++] = job->auth_password;
3160
f7deaa1a 3161#ifdef HAVE_GSSAPI
3162 if (job->ccname)
3163 envp[envc ++] = job->ccname;
3164#endif /* HAVE_GSSAPI */
3165
e1d6a774 3166 envp[envc] = NULL;
bd7854cb 3167
e1d6a774 3168 for (i = 0; i < envc; i ++)
db0bd74a
MS
3169 if (!strncmp(envp[i], "AUTH_", 5))
3170 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] envp[%d]=\"AUTH_%c****\"",
09a101d6 3171 job->id, i, envp[i][5]);
3172 else if (strncmp(envp[i], "DEVICE_URI=", 11))
e1d6a774 3173 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] envp[%d]=\"%s\"",
3174 job->id, i, envp[i]);
3175 else
3176 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] envp[%d]=\"DEVICE_URI=%s\"",
3177 job->id, i, sani_uri);
3178
d09495fa 3179 if (printer->remote)
e1d6a774 3180 job->current_file = job->num_files;
3181 else
3182 job->current_file ++;
bd7854cb 3183
3184 /*
e1d6a774 3185 * Now create processes for all of the filters...
bd7854cb 3186 */
3187
e1d6a774 3188 filterfds[0][0] = -1;
3189 filterfds[0][1] = -1;
3190 filterfds[1][0] = -1;
3191 filterfds[1][1] = -1;
bd7854cb 3192
89d46774 3193 if (!job->status_buffer)
bd7854cb 3194 {
89d46774 3195 if (cupsdOpenPipe(job->status_pipes))
3196 {
09a101d6 3197 cupsdLogMessage(CUPSD_LOG_ERROR,
3198 "[Job %d] Unable to create job status pipes - %s.",
3199 job->id, strerror(errno));
89d46774 3200 snprintf(printer->state_message, sizeof(printer->state_message),
3201 "Unable to create status pipes - %s.", strerror(errno));
f7deaa1a 3202
89d46774 3203 cupsdAddPrinterHistory(printer);
f7deaa1a 3204
89d46774 3205 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3206 "Job canceled because the server could not create the job "
3207 "status pipes.");
f7deaa1a 3208
89d46774 3209 goto abort_job;
3210 }
f7deaa1a 3211
09a101d6 3212 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3213 "[Job %d] start_job: status_pipes = [ %d %d ]",
3214 job->id, job->status_pipes[0], job->status_pipes[1]);
f7deaa1a 3215
89d46774 3216 job->status_buffer = cupsdStatBufNew(job->status_pipes[0], "[Job %d]",
3217 job->id);
09a101d6 3218 job->status_level = CUPSD_LOG_INFO;
e1d6a774 3219 }
bd7854cb 3220
89d46774 3221 job->status = 0;
e1d6a774 3222 memset(job->filters, 0, sizeof(job->filters));
bd7854cb 3223
a4924f6c
MS
3224 if (!job->profile)
3225 job->profile = cupsdCreateProfile(job->id);
3226
e1d6a774 3227 for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
3228 filter;
3229 i ++, filter = (mime_filter_t *)cupsArrayNext(filters))
3230 {
3231 if (filter->filter[0] != '/')
3232 snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
3233 filter->filter);
3234 else
3235 strlcpy(command, filter->filter, sizeof(command));
bd7854cb 3236
e1d6a774 3237 if (i < (cupsArrayCount(filters) - 1))
bd7854cb 3238 {
e1d6a774 3239 if (cupsdOpenPipe(filterfds[slot]))
3240 {
3241 cupsdLogMessage(CUPSD_LOG_ERROR,
09a101d6 3242 "[Job %d] Unable to create job filter pipes - %s.",
3243 job->id, strerror(errno));
e1d6a774 3244 snprintf(printer->state_message, sizeof(printer->state_message),
3245 "Unable to create filter pipes - %s.", strerror(errno));
3246 cupsdAddPrinterHistory(printer);
bd7854cb 3247
e1d6a774 3248 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3249 "Job canceled because the server could not create the "
3250 "filter pipes.");
bd7854cb 3251
e1d6a774 3252 goto abort_job;
bd7854cb 3253 }
e1d6a774 3254 }
3255 else
3256 {
3257 if (job->current_file == 1)
bd7854cb 3258 {
e1d6a774 3259 if (strncmp(printer->device_uri, "file:", 5) != 0)
3260 {
3261 if (cupsdOpenPipe(job->print_pipes))
3262 {
3263 cupsdLogMessage(CUPSD_LOG_ERROR,
09a101d6 3264 "[Job %d] Unable to create job backend pipes - %s.",
3265 job->id, strerror(errno));
e1d6a774 3266 snprintf(printer->state_message, sizeof(printer->state_message),
3267 "Unable to create backend pipes - %s.", strerror(errno));
3268 cupsdAddPrinterHistory(printer);
bd7854cb 3269
e1d6a774 3270 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3271 "Job canceled because the server could not create "
3272 "the backend pipes.");
bd7854cb 3273
e1d6a774 3274 goto abort_job;
3275 }
3276 }
3277 else
3278 {
3279 job->print_pipes[0] = -1;
ed486911 3280 if (!strcmp(printer->device_uri, "file:/dev/null") ||
3281 !strcmp(printer->device_uri, "file:///dev/null"))
3282 job->print_pipes[1] = -1;
e1d6a774 3283 else
e1d6a774 3284 {
ed486911 3285 if (!strncmp(printer->device_uri, "file:/dev/", 10))
3286 job->print_pipes[1] = open(printer->device_uri + 5,
3287 O_WRONLY | O_EXCL);
3288 else if (!strncmp(printer->device_uri, "file:///dev/", 12))
3289 job->print_pipes[1] = open(printer->device_uri + 7,
3290 O_WRONLY | O_EXCL);
3291 else if (!strncmp(printer->device_uri, "file:///", 8))
3292 job->print_pipes[1] = open(printer->device_uri + 7,
3293 O_WRONLY | O_CREAT | O_TRUNC, 0600);
3294 else
3295 job->print_pipes[1] = open(printer->device_uri + 5,
3296 O_WRONLY | O_CREAT | O_TRUNC, 0600);
bd7854cb 3297
ed486911 3298 if (job->print_pipes[1] < 0)
3299 {
3300 cupsdLogMessage(CUPSD_LOG_ERROR,
09a101d6 3301 "[Job %d] Unable to open output file \"%s\" - %s.",
3302 job->id, printer->device_uri, strerror(errno));
ed486911 3303 snprintf(printer->state_message, sizeof(printer->state_message),
3304 "Unable to open output file \"%s\" - %s.",
3305 printer->device_uri, strerror(errno));
3306
3307 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3308 "Job canceled because the server could not open the "
3309 "output file.");
3310
3311 goto abort_job;
3312 }
bd7854cb 3313
ed486911 3314 fcntl(job->print_pipes[1], F_SETFD,
3315 fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
3316 }
e1d6a774 3317 }
bd7854cb 3318
e1d6a774 3319 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3320 "[Job %d] start_job: print_pipes = [ %d %d ]",
3321 job->id, job->print_pipes[0], job->print_pipes[1]);
bd7854cb 3322 }
e1d6a774 3323
3324 filterfds[slot][0] = job->print_pipes[0];
3325 filterfds[slot][1] = job->print_pipes[1];
bd7854cb 3326 }
bd7854cb 3327
09a101d6 3328 cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] start_job: filter=\"%s\"",
3329 job->id, command);
e1d6a774 3330 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3331 "[Job %d] start_job: filterfds[%d]=[ %d %d ]",
3332 job->id, slot, filterfds[slot][0], filterfds[slot][1]);
bd7854cb 3333
e1d6a774 3334 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
89d46774 3335 filterfds[slot][1], job->status_pipes[1],
f7deaa1a 3336 job->back_pipes[0], job->side_pipes[0], 0,
a4924f6c 3337 job->profile, job->filters + i);
bd7854cb 3338
e1d6a774 3339 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3340 "[Job %d] start_job: Closing filter pipes for slot %d "
e1d6a774 3341 "[ %d %d ]...",
09a101d6 3342 job->id, !slot, filterfds[!slot][0], filterfds[!slot][1]);
bd7854cb 3343
e1d6a774 3344 cupsdClosePipe(filterfds[!slot]);
bd7854cb 3345
e1d6a774 3346 if (pid == 0)
3347 {
09a101d6 3348 cupsdLogMessage(CUPSD_LOG_ERROR,
3349 "[Job %d] Unable to start filter \"%s\" - %s.",
3350 job->id, filter->filter, strerror(errno));
e1d6a774 3351 snprintf(printer->state_message, sizeof(printer->state_message),
3352 "Unable to start filter \"%s\" - %s.",
3353 filter->filter, strerror(errno));
bd7854cb 3354
e1d6a774 3355 cupsdAddPrinterHistory(printer);
bd7854cb 3356
e1d6a774 3357 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3358 "Job canceled because the server could not execute a "
3359 "filter.");
bd7854cb 3360
e1d6a774 3361 goto abort_job;
3362 }
3363
09a101d6 3364 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Started filter %s (PID %d)",
3365 job->id, command, pid);
e1d6a774 3366
3367 argv[6] = NULL;
3368 slot = !slot;
bd7854cb 3369 }
3370
e1d6a774 3371 cupsArrayDelete(filters);
bd7854cb 3372
e1d6a774 3373 /*
3374 * Finally, pipe the final output into a backend process if needed...
3375 */
bd7854cb 3376
e1d6a774 3377 if (strncmp(printer->device_uri, "file:", 5) != 0)
bd7854cb 3378 {
d9bca400 3379 if (job->current_file == 1 || printer->remote)
bd7854cb 3380 {
e1d6a774 3381 sscanf(printer->device_uri, "%254[^:]", method);
3382 snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, method);
bd7854cb 3383
e1d6a774 3384 /*
3385 * See if the backend needs to run as root...
3386 */
bd7854cb 3387
e1d6a774 3388 if (RunUser)
3389 backroot = 0;
3390 else if (stat(command, &backinfo))
3391 backroot = 0;
3392 else
3393 backroot = !(backinfo.st_mode & (S_IRWXG | S_IRWXO));
bd7854cb 3394
e1d6a774 3395 argv[0] = sani_uri;
bd7854cb 3396
e1d6a774 3397 filterfds[slot][0] = -1;
ed486911 3398 filterfds[slot][1] = -1;
bd7854cb 3399
09a101d6 3400 cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] start_job: backend=\"%s\"",
3401 job->id, command);
e1d6a774 3402 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3403 "[Job %d] start_job: filterfds[%d] = [ %d %d ]", job->id,
e1d6a774 3404 slot, filterfds[slot][0], filterfds[slot][1]);
bd7854cb 3405
e1d6a774 3406 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
89d46774 3407 filterfds[slot][1], job->status_pipes[1],
f7deaa1a 3408 job->back_pipes[1], job->side_pipes[1],
a4924f6c 3409 backroot, job->profile, &(job->backend));
bd7854cb 3410
e1d6a774 3411 if (pid == 0)
bd7854cb 3412 {
09a101d6 3413 cupsdLogMessage(CUPSD_LOG_ERROR,
3414 "[Job %d] Unable to start backend \"%s\" - %s.",
3415 job->id, method, strerror(errno));
e1d6a774 3416 snprintf(printer->state_message, sizeof(printer->state_message),
3417 "Unable to start backend \"%s\" - %s.", method,
3418 strerror(errno));
3419
3420 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
3421 "Job canceled because the server could not execute "
3422 "the backend.");
3423
3424 goto abort_job;
3425 }
3426 else
3427 {
09a101d6 3428 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Started backend %s (PID %d)",
3429 job->id, command, pid);
bd7854cb 3430 }
e1d6a774 3431 }
bd7854cb 3432
e1d6a774 3433 if (job->current_file == job->num_files)
3434 {
3435 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3436 "[Job %d] start_job: Closing print pipes [ %d %d ]...",
3437 job->id, job->print_pipes[0], job->print_pipes[1]);
bd7854cb 3438
e1d6a774 3439 cupsdClosePipe(job->print_pipes);
bd7854cb 3440
e1d6a774 3441 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3442 "[Job %d] start_job: Closing back pipes [ %d %d ]...",
3443 job->id, job->back_pipes[0], job->back_pipes[1]);
bd7854cb 3444
e1d6a774 3445 cupsdClosePipe(job->back_pipes);
89d46774 3446
f7deaa1a 3447 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3448 "[Job %d] start_job: Closing side pipes [ %d %d ]...",
3449 job->id, job->side_pipes[0], job->side_pipes[1]);
f7deaa1a 3450
3451 cupsdClosePipe(job->side_pipes);
3452
89d46774 3453 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3454 "[Job %d] start_job: Closing status output pipe %d...",
3455 job->id, job->status_pipes[1]);
89d46774 3456
3457 close(job->status_pipes[1]);
3458 job->status_pipes[1] = -1;
e1d6a774 3459 }
3460 }
3461 else
3462 {
3463 filterfds[slot][0] = -1;
3464 filterfds[slot][1] = -1;
bd7854cb 3465
e1d6a774 3466 if (job->current_file == job->num_files)
3467 {
3468 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3469 "[Job %d] start_job: Closing print pipes [ %d %d ]...",
3470 job->id, job->print_pipes[0], job->print_pipes[1]);
bd7854cb 3471
e1d6a774 3472 cupsdClosePipe(job->print_pipes);
89d46774 3473
3474 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3475 "[Job %d] start_job: Closing status output pipe %d...",
3476 job->id, job->status_pipes[1]);
89d46774 3477
3478 close(job->status_pipes[1]);
3479 job->status_pipes[1] = -1;
bd7854cb 3480 }
e1d6a774 3481 }
bd7854cb 3482
89d46774 3483 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3484 "[Job %d] start_job: Closing filter pipes for slot %d "
89d46774 3485 "[ %d %d ]...",
09a101d6 3486 job->id, slot, filterfds[slot][0], filterfds[slot][1]);
89d46774 3487 cupsdClosePipe(filterfds[slot]);
bd7854cb 3488
d09495fa 3489 if (printer->remote && job->num_files > 1)
e1d6a774 3490 {
3491 for (i = 0; i < job->num_files; i ++)
3492 free(argv[i + 6]);
3493 }
bd7854cb 3494
e1d6a774 3495 free(argv);
ef416fc2 3496
f899b121 3497 cupsdAddSelect(job->status_buffer->fd, (cupsd_selfunc_t)update_job, NULL,
f7deaa1a 3498 job);
ef416fc2 3499
e1d6a774 3500 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.",
3501 job->id);
ef416fc2 3502
e1d6a774 3503 return;
ef416fc2 3504
3505
3506 /*
e1d6a774 3507 * If we get here, we need to abort the current job and close out all
3508 * files and pipes...
ef416fc2 3509 */
3510
e1d6a774 3511 abort_job:
ef416fc2 3512
e1d6a774 3513 for (slot = 0; slot < 2; slot ++)
3514 {
3515 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3516 "[Job %d] start_job: Closing filter pipes for slot %d "
e1d6a774 3517 "[ %d %d ]...",
09a101d6 3518 job->id, slot, filterfds[slot][0], filterfds[slot][1]);
e1d6a774 3519 cupsdClosePipe(filterfds[slot]);
3520 }
ef416fc2 3521
e1d6a774 3522 cupsdLogMessage(CUPSD_LOG_DEBUG2,
09a101d6 3523 "[Job %d] start_job: Closing status pipes [ %d %d ]...",
3524 job->id, job->status_pipes[0], job->status_pipes[1]);
89d46774 3525 cupsdClosePipe(job->status_pipes);
3526 cupsdStatBufDelete(job->status_buffer);
ef416fc2 3527
b94498cf 3528 job->status_buffer = NULL;
3529
e1d6a774 3530 cupsArrayDelete(filters);
ef416fc2 3531
d09495fa 3532 if (printer->remote && job->num_files > 1)
e1d6a774 3533 {
3534 for (i = 0; i < job->num_files; i ++)
3535 free(argv[i + 6]);
3536 }
ef416fc2 3537
e1d6a774 3538 free(argv);
ef416fc2 3539
e1d6a774 3540 cupsdStopJob(job, 0);
3541}
ef416fc2 3542
e1d6a774 3543
3544/*
3545 * 'unload_job()' - Unload a job from memory.
3546 */
3547
3548static void
3549unload_job(cupsd_job_t *job) /* I - Job */
3550{
3551 if (!job->attrs)
3552 return;
3553
09a101d6 3554 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Unloading...", job->id);
e1d6a774 3555
3556 ippDelete(job->attrs);
3557
cc0d019f
MS
3558 job->attrs = NULL;
3559 job->state = NULL;
3560 job->sheets = NULL;
3561 job->job_sheets = NULL;
3562 job->printer_message = NULL;
3563 job->printer_reasons = NULL;
ef416fc2 3564}
3565
3566
3567/*
f899b121 3568 * 'update_job()' - Read a status update from a job's filters.
3569 */
3570
3571void
09a101d6 3572update_job(cupsd_job_t *job) /* I - Job to check */
f899b121 3573{
3574 int i; /* Looping var */
3575 int copies; /* Number of copies printed */
3576 char message[1024], /* Message text */
3577 *ptr; /* Pointer update... */
3578 int loglevel, /* Log level for message */
3579 event = 0; /* Events? */
3580
3581
3582 while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
3583 message, sizeof(message))) != NULL)
3584 {
3585 /*
3586 * Process page and printer state messages as needed...
3587 */
3588
3589 if (loglevel == CUPSD_LOG_PAGE)
3590 {
3591 /*
3592 * Page message; send the message to the page_log file and update the
3593 * job sheet count...
3594 */
3595
91c84a35 3596 if (job->sheets)
f899b121 3597 {
3598 if (!strncasecmp(message, "total ", 6))
3599 {
3600 /*
3601 * Got a total count of pages from a backend or filter...
3602 */
3603
3604 copies = atoi(message + 6);
3605 copies -= job->sheets->values[0].integer; /* Just track the delta */
3606 }
3607 else if (!sscanf(message, "%*d%d", &copies))
3608 copies = 1;
3609
3610 job->sheets->values[0].integer += copies;
3611
3612 if (job->printer->page_limit)
3613 {
3614 cupsd_quota_t *q = cupsdUpdateQuota(job->printer, job->username,
3615 copies, 0);
3616
3617#ifdef __APPLE__
3618 if (AppleQuotas && q->page_count == -3)
3619 {
3620 /*
3621 * Quota limit exceeded, cancel job in progress immediately...
3622 */
3623
3624 cupsdLogMessage(CUPSD_LOG_INFO,
09a101d6 3625 "[Job %d] Canceled because pages exceed user %s "
3626 "quota limit on printer %s (%s).",
f899b121 3627 job->id, job->username, job->printer->name,
3628 job->printer->info);
3629
3630 cupsdCancelJob(job, 1, IPP_JOB_CANCELED);
3631 return;
3632 }
3633#else
3634 (void)q;
3635#endif /* __APPLE__ */
3636 }
3637 }
3638
3639 cupsdLogPage(job, message);
3640
91c84a35
MS
3641 if (job->sheets)
3642 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
3643 "Printed %d page(s).", job->sheets->values[0].integer);
f899b121 3644 }
3645 else if (loglevel == CUPSD_LOG_STATE)
3646 {
09a101d6 3647 if (!strcmp(message, "paused"))
c24d2134 3648 {
09a101d6 3649 cupsdStopPrinter(job->printer, 1);
c24d2134
MS
3650 return;
3651 }
09a101d6 3652 else
3653 {
3654 cupsdSetPrinterReasons(job->printer, message);
3655 cupsdAddPrinterHistory(job->printer);
d9bca400 3656 event |= CUPSD_EVENT_PRINTER_STATE;
09a101d6 3657 }
bc44d920 3658
3659 update_job_attrs(job);
f899b121 3660 }
3661 else if (loglevel == CUPSD_LOG_ATTR)
3662 {
3663 /*
3664 * Set attribute(s)...
3665 */
3666
3667 int num_attrs; /* Number of attributes */
3668 cups_option_t *attrs; /* Attributes */
3669 const char *attr; /* Attribute */
3670
3671
3672 num_attrs = cupsParseOptions(message, 0, &attrs);
3673
3674 if ((attr = cupsGetOption("auth-info-required", num_attrs,
3675 attrs)) != NULL)
7ff4fea9 3676 {
f899b121 3677 cupsdSetAuthInfoRequired(job->printer, attr, NULL);
7dfedb92 3678 cupsdSetPrinterAttrs(job->printer);
7ff4fea9
MS
3679 cupsdSaveAllPrinters();
3680 }
f899b121 3681
323c5de1 3682 if ((attr = cupsGetOption("printer-alert", num_attrs, attrs)) != NULL)
3683 {
3684 cupsdSetString(&job->printer->alert, attr);
d9bca400 3685 event |= CUPSD_EVENT_PRINTER_STATE;
323c5de1 3686 }
3687
3688 if ((attr = cupsGetOption("printer-alert-description", num_attrs,
3689 attrs)) != NULL)
3690 {
3691 cupsdSetString(&job->printer->alert_description, attr);
d9bca400 3692 event |= CUPSD_EVENT_PRINTER_STATE;
323c5de1 3693 }
3694
5a738aea
MS
3695 if ((attr = cupsGetOption("marker-colors", num_attrs, attrs)) != NULL)
3696 {
3697 cupsdSetPrinterAttr(job->printer, "marker-colors", (char *)attr);
3698 job->printer->marker_time = time(NULL);
3699 event |= CUPSD_EVENT_PRINTER_STATE;
3700 }
3701
3702 if ((attr = cupsGetOption("marker-levels", num_attrs, attrs)) != NULL)
3703 {
3704 cupsdSetPrinterAttr(job->printer, "marker-levels", (char *)attr);
3705 job->printer->marker_time = time(NULL);
3706 event |= CUPSD_EVENT_PRINTER_STATE;
3707 }
3708
3709 if ((attr = cupsGetOption("marker-names", num_attrs, attrs)) != NULL)
3710 {
3711 cupsdSetPrinterAttr(job->printer, "marker-names", (char *)attr);
3712 job->printer->marker_time = time(NULL);
3713 event |= CUPSD_EVENT_PRINTER_STATE;
3714 }
3715
3716 if ((attr = cupsGetOption("marker-types", num_attrs, attrs)) != NULL)
3717 {
3718 cupsdSetPrinterAttr(job->printer, "marker-types", (char *)attr);
3719 job->printer->marker_time = time(NULL);
3720 event |= CUPSD_EVENT_PRINTER_STATE;
3721 }
3722
f899b121 3723 cupsFreeOptions(num_attrs, attrs);
3724 }
3725#ifdef __APPLE__
3726 else if (!strncmp(message, "recoverable:", 12))
3727 {
3728 cupsdSetPrinterReasons(job->printer,
3729 "+com.apple.print.recoverable-warning");
3730
3731 ptr = message + 12;
3732 while (isspace(*ptr & 255))
3733 ptr ++;
3734
3735 cupsdSetString(&job->printer->recoverable, ptr);
3736 cupsdAddPrinterHistory(job->printer);
d9bca400 3737 event |= CUPSD_EVENT_PRINTER_STATE;
f899b121 3738 }
3739 else if (!strncmp(message, "recovered:", 10))
3740 {
3741 cupsdSetPrinterReasons(job->printer,
3742 "-com.apple.print.recoverable-warning");
3743
3744 ptr = message + 10;
3745 while (isspace(*ptr & 255))
3746 ptr ++;
3747
3748 cupsdSetString(&job->printer->recoverable, ptr);
3749 cupsdAddPrinterHistory(job->printer);
d9bca400 3750 event |= CUPSD_EVENT_PRINTER_STATE;
f899b121 3751 }
3752#endif /* __APPLE__ */
09a101d6 3753 else if (loglevel <= job->status_level)
f899b121 3754 {
3755 /*
3756 * Some message to show in the printer-state-message attribute...
3757 */
3758
cc0d019f
MS
3759 if (loglevel != CUPSD_LOG_NOTICE)
3760 job->status_level = loglevel;
09a101d6 3761
f899b121 3762 strlcpy(job->printer->state_message, message,
3763 sizeof(job->printer->state_message));
3764 cupsdAddPrinterHistory(job->printer);
d9bca400 3765 event |= CUPSD_EVENT_PRINTER_STATE;
bc44d920 3766
3767 update_job_attrs(job);
f899b121 3768 }
3769
3770 if (!strchr(job->status_buffer->buffer, '\n'))
3771 break;
3772 }
3773
d9bca400
MS
3774 if (event & CUPSD_EVENT_PRINTER_STATE)
3775 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, job->printer, NULL,
f899b121 3776 (job->printer->type & CUPS_PRINTER_CLASS) ?
3777 "Class \"%s\" state changed." :
3778 "Printer \"%s\" state changed.",
3779 job->printer->name);
3780
3781 if (ptr == NULL && !job->status_buffer->bufused)
3782 {
3783 /*
3784 * See if all of the filters and the backend have returned their
3785 * exit statuses.
3786 */
3787
3788 for (i = 0; job->filters[i] < 0; i ++);
3789
3790 if (job->filters[i])
3791 return;
3792
3793 if (job->current_file >= job->num_files && job->backend > 0)
3794 return;
3795
3796 /*
3797 * Handle the end of job stuff...
3798 */
3799
3800 cupsdFinishJob(job);
3801 }
3802}
3803
3804
3805/*
bc44d920 3806 * 'update_job_attrs()' - Update the job-printer-* attributes.
3807 */
3808
3809void
3810update_job_attrs(cupsd_job_t *job) /* I - Job to update */
3811{
3812 int i; /* Looping var */
3813 int num_reasons; /* Actual number of reasons */
3814 const char * const *reasons; /* Reasons */
3815 static const char *none = "none", /* "none" */
3816 *paused = "paused";
3817 /* "paused" */
3818
3819
3820 /*
3821 * Get/create the job-printer-state-* attributes...
3822 */
3823
3824 if (!job->printer_message)
3825 {
3826 if ((job->printer_message = ippFindAttribute(job->attrs,
3827 "job-printer-state-message",
3828 IPP_TAG_TEXT)) == NULL)
3829 job->printer_message = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_TEXT,
3830 "job-printer-state-message",
3831 NULL, "");
3832 }
3833
3834 if (!job->printer_reasons)
3835 job->printer_reasons = ippFindAttribute(job->attrs,
3836 "job-printer-state-reasons",
3837 IPP_TAG_KEYWORD);
3838
3839 /*
3840 * If the job isn't printing, return now...
3841 */
3842
3843 if (!job->printer)
3844 return;
3845
3846 /*
3847 * Otherwise copy the printer-state-message value...
3848 */
3849
3850 if (job->printer->state_message[0])
3851 cupsdSetString(&(job->printer_message->values[0].string.text),
3852 job->printer->state_message);
3853
3854 /*
3855 * ... and the printer-state-reasons value...
3856 */
3857
3858 if (job->printer->num_reasons == 0)
3859 {
3860 num_reasons = 1;
3861 reasons = job->printer->state == IPP_PRINTER_STOPPED ? &paused : &none;
3862 }
3863 else
3864 {
3865 num_reasons = job->printer->num_reasons;
3866 reasons = (const char * const *)job->printer->reasons;
3867 }
3868
3869 if (!job->printer_reasons || job->printer_reasons->num_values != num_reasons)
3870 {
3871 ippDeleteAttribute(job->attrs, job->printer_reasons);
3872
3873 job->printer_reasons = ippAddStrings(job->attrs,
3874 IPP_TAG_JOB, IPP_TAG_KEYWORD,
3875 "job-printer-state-reasons",
3876 num_reasons, NULL, NULL);
3877 }
3878
3879 for (i = 0; i < num_reasons; i ++)
3880 cupsdSetString(&(job->printer_reasons->values[i].string.text), reasons[i]);
3881}
3882
3883
3884/*
2e4ff8af 3885 * End of "$Id: job.c 7005 2007-10-01 23:45:48Z mike $".
ef416fc2 3886 */