1/* 2 * "$Id: job.c 12142 2014-08-30 02:35:43Z msweet $" 3 * 4 * Job management routines for the CUPS scheduler. 5 * 6 * Copyright 2007-2014 by Apple Inc. 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 8 * 9 * These coded instructions, statements, and computer programs are the 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/". 14 */ 15 16/* 17 * Include necessary headers... 18 */ 19 20#include "cupsd.h" 21#include <grp.h> 22#include <cups/backend.h> 23#include <cups/dir.h> 24#ifdef __APPLE__ 25# include <IOKit/pwr_mgt/IOPMLib.h> 26# ifdef HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H 27# include <IOKit/pwr_mgt/IOPMLibPrivate.h> 28# endif /* HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H */ 29#endif /* __APPLE__ */ 30 31 32/* 33 * Design Notes for Job Management 34 * ------------------------------- 35 * 36 * STATE CHANGES 37 * 38 * pending Do nothing/check jobs 39 * pending-held Send SIGTERM to filters and backend 40 * processing Do nothing/start job 41 * stopped Send SIGKILL to filters and backend 42 * canceled Send SIGTERM to filters and backend 43 * aborted Finalize 44 * completed Finalize 45 * 46 * Finalize clears the printer <-> job association, deletes the status 47 * buffer, closes all of the pipes, etc. and doesn't get run until all of 48 * the print processes are finished. 49 * 50 * UNLOADING OF JOBS (cupsdUnloadCompletedJobs) 51 * 52 * We unload the job attributes when they are not needed to reduce overall 53 * memory consumption. We don't unload jobs where job->state_value < 54 * IPP_JOB_STOPPED, job->printer != NULL, or job->access_time is recent. 55 * 56 * STARTING OF JOBS (start_job) 57 * 58 * When a job is started, a status buffer, several pipes, a security 59 * profile, and a backend process are created for the life of that job. 60 * These are shared for every file in a job. For remote print jobs, the 61 * IPP backend is provided with every file in the job and no filters are 62 * run. 63 * 64 * The job->printer member tracks which printer is printing a job, which 65 * can be different than the destination in job->dest for classes. The 66 * printer object also has a job pointer to track which job is being 67 * printed. 68 * 69 * PRINTING OF JOB FILES (cupsdContinueJob) 70 * 71 * Each file in a job is filtered by 0 or more programs. After getting the 72 * list of filters needed and the total cost, the job is either passed or 73 * put back to the processing state until the current FilterLevel comes down 74 * enough to allow printing. 75 * 76 * If we can print, we build a string for the print options and run each of 77 * the filters, piping the output from one into the next. 78 * 79 * JOB STATUS UPDATES (update_job) 80 * 81 * The update_job function gets called whenever there are pending messages 82 * on the status pipe. These generally are updates to the marker-*, 83 * printer-state-message, or printer-state-reasons attributes. On EOF, 84 * finalize_job is called to clean up. 85 * 86 * FINALIZING JOBS (finalize_job) 87 * 88 * When all filters and the backend are done, we set the job state to 89 * completed (no errors), aborted (filter errors or abort-job policy), 90 * pending-held (auth required or retry-job policy), or pending 91 * (retry-current-job or stop-printer policies) as appropriate. 92 * 93 * Then we close the pipes and free the status buffers and profiles. 94 * 95 * JOB FILE COMPLETION (process_children in main.c) 96 * 97 * For multiple-file jobs, process_children (in main.c) sees that all 98 * filters have exited and calls in to print the next file if there are 99 * more files in the job, otherwise it waits for the backend to exit and 100 * update_job to do the cleanup. 101 */ 102 103 104/* 105 * Local globals... 106 */ 107 108static mime_filter_t gziptoany_filter = 109 { 110 NULL, /* Source type */ 111 NULL, /* Destination type */ 112 0, /* Cost */ 113 "gziptoany" /* Filter program to run */ 114 }; 115 116 117/* 118 * Local functions... 119 */ 120 121static int compare_active_jobs(void *first, void *second, void *data); 122static int compare_completed_jobs(void *first, void *second, void *data); 123static int compare_jobs(void *first, void *second, void *data); 124static void dump_job_history(cupsd_job_t *job); 125static void finalize_job(cupsd_job_t *job, int set_job_state); 126static void free_job_history(cupsd_job_t *job); 127static char *get_options(cupsd_job_t *job, int banner_page, char *copies, 128 size_t copies_size, char *title, 129 size_t title_size); 130static size_t ipp_length(ipp_t *ipp); 131static void load_job_cache(const char *filename); 132static void load_next_job_id(const char *filename); 133static void load_request_root(void); 134static void remove_job_files(cupsd_job_t *job); 135static void remove_job_history(cupsd_job_t *job); 136static void set_time(cupsd_job_t *job, const char *name); 137static void start_job(cupsd_job_t *job, cupsd_printer_t *printer); 138static void stop_job(cupsd_job_t *job, cupsd_jobaction_t action); 139static void unload_job(cupsd_job_t *job); 140static void update_job(cupsd_job_t *job); 141static void update_job_attrs(cupsd_job_t *job, int do_message); 142 143 144/* 145 * 'cupsdAddJob()' - Add a new job to the job queue. 146 */ 147 148cupsd_job_t * /* O - New job record */ 149cupsdAddJob(int priority, /* I - Job priority */ 150 const char *dest) /* I - Job destination */ 151{ 152 cupsd_job_t *job; /* New job record */ 153 154 155 if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL) 156 return (NULL); 157 158 job->id = NextJobId ++; 159 job->priority = priority; 160 job->back_pipes[0] = -1; 161 job->back_pipes[1] = -1; 162 job->print_pipes[0] = -1; 163 job->print_pipes[1] = -1; 164 job->side_pipes[0] = -1; 165 job->side_pipes[1] = -1; 166 job->status_pipes[0] = -1; 167 job->status_pipes[1] = -1; 168 169 cupsdSetString(&job->dest, dest); 170 171 /* 172 * Add the new job to the "all jobs" and "active jobs" lists... 173 */ 174 175 cupsArrayAdd(Jobs, job); 176 cupsArrayAdd(ActiveJobs, job); 177 178 return (job); 179} 180 181 182/* 183 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user. 184 */ 185 186void 187cupsdCancelJobs(const char *dest, /* I - Destination to cancel */ 188 const char *username, /* I - Username or NULL */ 189 int purge) /* I - Purge jobs? */ 190{ 191 cupsd_job_t *job; /* Current job */ 192 193 194 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); 195 job; 196 job = (cupsd_job_t *)cupsArrayNext(Jobs)) 197 { 198 if ((!job->dest || !job->username) && !cupsdLoadJob(job)) 199 continue; 200 201 if ((!dest || !strcmp(job->dest, dest)) && 202 (!username || !strcmp(job->username, username))) 203 { 204 /* 205 * Cancel all jobs matching this destination/user... 206 */ 207 208 if (purge) 209 cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_PURGE, 210 "Job purged by user."); 211 else if (job->state_value < IPP_JOB_CANCELED) 212 cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT, 213 "Job canceled by user."); 214 } 215 } 216 217 cupsdCheckJobs(); 218} 219 220 221/* 222 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination 223 * is available. 224 */ 225 226void 227cupsdCheckJobs(void) 228{ 229 cupsd_job_t *job; /* Current job in queue */ 230 cupsd_printer_t *printer, /* Printer destination */ 231 *pclass; /* Printer class destination */ 232 ipp_attribute_t *attr; /* Job attribute */ 233 time_t curtime; /* Current time */ 234 235 236 curtime = time(NULL); 237 238 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCheckJobs: %d active jobs, sleeping=%d, ac-power=%d, reload=%d, curtime=%ld", cupsArrayCount(ActiveJobs), Sleeping, ACPower, NeedReload, (long)curtime); 239 240 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); 241 job; 242 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) 243 { 244 cupsdLogMessage(CUPSD_LOG_DEBUG2, 245 "cupsdCheckJobs: Job %d - dest=\"%s\", printer=%p, " 246 "state=%d, cancel_time=%ld, hold_until=%ld, kill_time=%ld, " 247 "pending_cost=%d, pending_timeout=%ld", job->id, job->dest, 248 job->printer, job->state_value, (long)job->cancel_time, 249 (long)job->hold_until, (long)job->kill_time, 250 job->pending_cost, (long)job->pending_timeout); 251 252 /* 253 * Kill jobs if they are unresponsive... 254 */ 255 256 if (job->kill_time && job->kill_time <= curtime) 257 { 258 if (!job->completed) 259 cupsdLogJob(job, CUPSD_LOG_ERROR, "Stopping unresponsive job."); 260 261 stop_job(job, CUPSD_JOB_FORCE); 262 continue; 263 } 264 265 /* 266 * Cancel stuck jobs... 267 */ 268 269 if (job->cancel_time && job->cancel_time <= curtime) 270 { 271 int cancel_after; /* job-cancel-after value */ 272 273 attr = ippFindAttribute(job->attrs, "job-cancel-after", IPP_TAG_INTEGER); 274 cancel_after = attr ? ippGetInteger(attr, 0) : MaxJobTime; 275 276 if (job->completed) 277 cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_FORCE, "Marking stuck job as completed after %d seconds.", cancel_after); 278 else 279 cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT, "Canceling stuck job after %d seconds.", cancel_after); 280 continue; 281 } 282 283 /* 284 * Start held jobs if they are ready... 285 */ 286 287 if (job->state_value == IPP_JOB_HELD && 288 job->hold_until && 289 job->hold_until < curtime) 290 { 291 if (job->pending_timeout) 292 { 293 /* 294 * This job is pending; check that we don't have an active Send-Document 295 * operation in progress on any of the client connections, then timeout 296 * the job so we can start printing... 297 */ 298 299 cupsd_client_t *con; /* Current client connection */ 300 301 for (con = (cupsd_client_t *)cupsArrayFirst(Clients); 302 con; 303 con = (cupsd_client_t *)cupsArrayNext(Clients)) 304 if (con->request && 305 con->request->request.op.operation_id == IPP_SEND_DOCUMENT) 306 break; 307 308 if (con) 309 continue; 310 311 if (cupsdTimeoutJob(job)) 312 continue; 313 } 314 315 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT, 316 "Job submission timed out."); 317 } 318 319 /* 320 * Continue jobs that are waiting on the FilterLimit... 321 */ 322 323 if (job->pending_cost > 0 && 324 ((FilterLevel + job->pending_cost) < FilterLimit || FilterLevel == 0)) 325 cupsdContinueJob(job); 326 327 /* 328 * Start pending jobs if the destination is available... 329 */ 330 331 if (job->state_value == IPP_JOB_PENDING && !NeedReload && 332 (!Sleeping || ACPower) && !DoingShutdown && !job->printer) 333 { 334 printer = cupsdFindDest(job->dest); 335 pclass = NULL; 336 337 while (printer && (printer->type & CUPS_PRINTER_CLASS)) 338 { 339 /* 340 * If the class is remote, just pass it to the remote server... 341 */ 342 343 pclass = printer; 344 345 if (pclass->state == IPP_PRINTER_STOPPED) 346 printer = NULL; 347 else if (pclass->type & CUPS_PRINTER_REMOTE) 348 break; 349 else 350 printer = cupsdFindAvailablePrinter(printer->name); 351 } 352 353 if (!printer && !pclass) 354 { 355 /* 356 * Whoa, the printer and/or class for this destination went away; 357 * cancel the job... 358 */ 359 360 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE, 361 "Job aborted because the destination printer/class " 362 "has gone away."); 363 } 364 else if (printer && !printer->holding_new_jobs) 365 { 366 /* 367 * See if the printer is available or remote and not printing a job; 368 * if so, start the job... 369 */ 370 371 if (pclass) 372 { 373 /* 374 * Add/update a job-actual-printer-uri attribute for this job 375 * so that we know which printer actually printed the job... 376 */ 377 378 if ((attr = ippFindAttribute(job->attrs, "job-actual-printer-uri", 379 IPP_TAG_URI)) != NULL) 380 cupsdSetString(&attr->values[0].string.text, printer->uri); 381 else 382 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, 383 "job-actual-printer-uri", NULL, printer->uri); 384 385 job->dirty = 1; 386 cupsdMarkDirty(CUPSD_DIRTY_JOBS); 387 } 388 389 if (!printer->job && printer->state == IPP_PRINTER_IDLE) 390 { 391 /* 392 * Start the job... 393 */ 394 395 start_job(job, printer); 396 } 397 } 398 } 399 } 400} 401 402 403/* 404 * 'cupsdCleanJobs()' - Clean out old jobs. 405 */ 406 407void 408cupsdCleanJobs(void) 409{ 410 cupsd_job_t *job; /* Current job */ 411 time_t curtime; /* Current time */ 412 413 414 cupsdLogMessage(CUPSD_LOG_DEBUG2, 415 "cupsdCleanJobs: MaxJobs=%d, JobHistory=%d, JobFiles=%d", 416 MaxJobs, JobHistory, JobFiles); 417 418 if (MaxJobs <= 0 && JobHistory == INT_MAX && JobFiles == INT_MAX) 419 return; 420 421 curtime = time(NULL); 422 JobHistoryUpdate = 0; 423 424 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); 425 job; 426 job = (cupsd_job_t *)cupsArrayNext(Jobs)) 427 { 428 if (job->state_value >= IPP_JOB_CANCELED && !job->printer) 429 { 430 /* 431 * Expire old jobs (or job files)... 432 */ 433 434 if ((MaxJobs > 0 && cupsArrayCount(Jobs) >= MaxJobs) || 435 (job->history_time && job->history_time <= curtime)) 436 { 437 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Removing from history."); 438 cupsdDeleteJob(job, CUPSD_JOB_PURGE); 439 } 440 else if (job->file_time && job->file_time <= curtime) 441 { 442 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Removing document files."); 443 remove_job_files(job); 444 445 if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate) 446 JobHistoryUpdate = job->history_time; 447 } 448 else 449 { 450 if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate) 451 JobHistoryUpdate = job->history_time; 452 453 if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate) 454 JobHistoryUpdate = job->file_time; 455 } 456 } 457 } 458 459 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCleanJobs: JobHistoryUpdate=%ld", 460 (long)JobHistoryUpdate); 461} 462 463 464/* 465 * 'cupsdContinueJob()' - Continue printing with the next file in a job. 466 */ 467 468void 469cupsdContinueJob(cupsd_job_t *job) /* I - Job */ 470{ 471 int i; /* Looping var */ 472 int slot; /* Pipe slot */ 473 cups_array_t *filters = NULL,/* Filters for job */ 474 *prefilters; /* Filters with prefilters */ 475 mime_filter_t *filter, /* Current filter */ 476 *prefilter, /* Prefilter */ 477 port_monitor; /* Port monitor filter */ 478 char scheme[255]; /* Device URI scheme */ 479 ipp_attribute_t *attr; /* Current attribute */ 480 const char *ptr, /* Pointer into value */ 481 *abort_message; /* Abort message */ 482 ipp_jstate_t abort_state = IPP_JOB_STOPPED; 483 /* New job state on abort */ 484 struct stat backinfo; /* Backend file information */ 485 int backroot; /* Run backend as root? */ 486 int pid; /* Process ID of new filter process */ 487 int banner_page; /* 1 if banner page, 0 otherwise */ 488 int filterfds[2][2] = { { -1, -1 }, { -1, -1 } }; 489 /* Pipes used between filters */ 490 int envc; /* Number of environment variables */ 491 struct stat fileinfo; /* Job file information */ 492 int argc; /* Number of arguments */ 493 char **argv = NULL, /* Filter command-line arguments */ 494 filename[1024], /* Job filename */ 495 command[1024], /* Full path to command */ 496 jobid[255], /* Job ID string */ 497 title[IPP_MAX_NAME], 498 /* Job title string */ 499 copies[255], /* # copies string */ 500 *options, /* Options string */ 501 *envp[MAX_ENV + 21], 502 /* Environment variables */ 503 charset[255], /* CHARSET env variable */ 504 class_name[255],/* CLASS env variable */ 505 classification[1024], 506 /* CLASSIFICATION env variable */ 507 content_type[1024], 508 /* CONTENT_TYPE env variable */ 509 device_uri[1024], 510 /* DEVICE_URI env variable */ 511 final_content_type[1024] = "", 512 /* FINAL_CONTENT_TYPE env variable */ 513 lang[255], /* LANG env variable */ 514#ifdef __APPLE__ 515 apple_language[255], 516 /* APPLE_LANGUAGE env variable */ 517#endif /* __APPLE__ */ 518 auth_info_required[255], 519 /* AUTH_INFO_REQUIRED env variable */ 520 ppd[1024], /* PPD env variable */ 521 printer_info[255], 522 /* PRINTER_INFO env variable */ 523 printer_location[255], 524 /* PRINTER_LOCATION env variable */ 525 printer_name[255], 526 /* PRINTER env variable */ 527 *printer_state_reasons = NULL, 528 /* PRINTER_STATE_REASONS env var */ 529 rip_max_cache[255]; 530 /* RIP_MAX_CACHE env variable */ 531 532 533 cupsdLogMessage(CUPSD_LOG_DEBUG2, 534 "cupsdContinueJob(job=%p(%d)): current_file=%d, num_files=%d", 535 job, job->id, job->current_file, job->num_files); 536 537 /* 538 * Figure out what filters are required to convert from 539 * the source to the destination type... 540 */ 541 542 FilterLevel -= job->cost; 543 544 job->cost = 0; 545 job->pending_cost = 0; 546 547 memset(job->filters, 0, sizeof(job->filters)); 548 549 if (job->printer->raw) 550 { 551 /* 552 * Remote jobs and raw queues go directly to the printer without 553 * filtering... 554 */ 555 556 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Sending job to queue tagged as raw..."); 557 } 558 else 559 { 560 /* 561 * Local jobs get filtered... 562 */ 563 564 mime_type_t *dst = job->printer->filetype; 565 /* Destination file type */ 566 567 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, 568 job->id, job->current_file + 1); 569 if (stat(filename, &fileinfo)) 570 fileinfo.st_size = 0; 571 572 if (job->retry_as_raster) 573 { 574 /* 575 * Need to figure out whether the printer supports image/pwg-raster or 576 * image/urf, and use the corresponding type... 577 */ 578 579 char type[MIME_MAX_TYPE]; /* MIME media type for printer */ 580 581 snprintf(type, sizeof(type), "%s/image/urf", job->printer->name); 582 if ((dst = mimeType(MimeDatabase, "printer", type)) == NULL) 583 { 584 snprintf(type, sizeof(type), "%s/image/pwg-raster", job->printer->name); 585 dst = mimeType(MimeDatabase, "printer", type); 586 } 587 588 if (dst) 589 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Retrying job as \"%s\".", strchr(dst->type, '/') + 1); 590 else 591 cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to retry job using a supported raster format."); 592 } 593 594 filters = mimeFilter2(MimeDatabase, job->filetypes[job->current_file], (size_t)fileinfo.st_size, dst, &(job->cost)); 595 596 if (!filters) 597 { 598 cupsdLogJob(job, CUPSD_LOG_ERROR, 599 "Unable to convert file %d to printable format.", 600 job->current_file); 601 602 abort_message = "Aborting job because it cannot be printed."; 603 abort_state = IPP_JOB_ABORTED; 604 605 ippSetString(job->attrs, &job->reasons, 0, "document-unprintable-error"); 606 goto abort_job; 607 } 608 609 /* 610 * Figure out the final content type... 611 */ 612 613 cupsdLogJob(job, CUPSD_LOG_DEBUG, "%d filters for job:", 614 cupsArrayCount(filters)); 615 for (filter = (mime_filter_t *)cupsArrayFirst(filters); 616 filter; 617 filter = (mime_filter_t *)cupsArrayNext(filters)) 618 cupsdLogJob(job, CUPSD_LOG_DEBUG, "%s (%s/%s to %s/%s, cost %d)", 619 filter->filter, 620 filter->src ? filter->src->super : "???", 621 filter->src ? filter->src->type : "???", 622 filter->dst ? filter->dst->super : "???", 623 filter->dst ? filter->dst->type : "???", 624 filter->cost); 625 626 if (!job->printer->remote) 627 { 628 for (filter = (mime_filter_t *)cupsArrayLast(filters); 629 filter && filter->dst; 630 filter = (mime_filter_t *)cupsArrayPrev(filters)) 631 if (strcmp(filter->dst->super, "printer") || 632 strcmp(filter->dst->type, job->printer->name)) 633 break; 634 635 if (filter && filter->dst) 636 { 637 if ((ptr = strchr(filter->dst->type, '/')) != NULL) 638 snprintf(final_content_type, sizeof(final_content_type), 639 "FINAL_CONTENT_TYPE=%s", ptr + 1); 640 else 641 snprintf(final_content_type, sizeof(final_content_type), 642 "FINAL_CONTENT_TYPE=%s/%s", filter->dst->super, 643 filter->dst->type); 644 } 645 else 646 snprintf(final_content_type, sizeof(final_content_type), 647 "FINAL_CONTENT_TYPE=printer/%s", job->printer->name); 648 } 649 650 /* 651 * Remove NULL ("-") filters... 652 */ 653 654 for (filter = (mime_filter_t *)cupsArrayFirst(filters); 655 filter; 656 filter = (mime_filter_t *)cupsArrayNext(filters)) 657 if (!strcmp(filter->filter, "-")) 658 cupsArrayRemove(filters, filter); 659 660 if (cupsArrayCount(filters) == 0) 661 { 662 cupsArrayDelete(filters); 663 filters = NULL; 664 } 665 666 /* 667 * If this printer has any pre-filters, insert the required pre-filter 668 * in the filters array... 669 */ 670 671 if (job->printer->prefiltertype && filters) 672 { 673 prefilters = cupsArrayNew(NULL, NULL); 674 675 for (filter = (mime_filter_t *)cupsArrayFirst(filters); 676 filter; 677 filter = (mime_filter_t *)cupsArrayNext(filters)) 678 { 679 if ((prefilter = mimeFilterLookup(MimeDatabase, filter->src, 680 job->printer->prefiltertype))) 681 { 682 cupsArrayAdd(prefilters, prefilter); 683 job->cost += prefilter->cost; 684 } 685 686 cupsArrayAdd(prefilters, filter); 687 } 688 689 cupsArrayDelete(filters); 690 filters = prefilters; 691 } 692 } 693 694 /* 695 * Set a minimum cost of 100 for all jobs so that FilterLimit 696 * works with raw queues and other low-cost paths. 697 */ 698 699 if (job->cost < 100) 700 job->cost = 100; 701 702 /* 703 * See if the filter cost is too high... 704 */ 705 706 if ((FilterLevel + job->cost) > FilterLimit && FilterLevel > 0 && 707 FilterLimit > 0) 708 { 709 /* 710 * Don't print this job quite yet... 711 */ 712 713 cupsArrayDelete(filters); 714 715 cupsdLogJob(job, CUPSD_LOG_INFO, 716 "Holding because filter limit has been reached."); 717 cupsdLogJob(job, CUPSD_LOG_DEBUG2, 718 "cupsdContinueJob: file=%d, cost=%d, level=%d, limit=%d", 719 job->current_file, job->cost, FilterLevel, 720 FilterLimit); 721 722 job->pending_cost = job->cost; 723 job->cost = 0; 724 return; 725 } 726 727 FilterLevel += job->cost; 728 729 /* 730 * Add decompression/raw filter as needed... 731 */ 732 733 if (job->compressions[job->current_file] && 734 (!job->printer->remote || job->num_files == 1)) 735 { 736 /* 737 * Add gziptoany filter to the front of the list... 738 */ 739 740 if (!filters) 741 filters = cupsArrayNew(NULL, NULL); 742 743 if (!cupsArrayInsert(filters, &gziptoany_filter)) 744 { 745 cupsdLogJob(job, CUPSD_LOG_DEBUG, 746 "Unable to add decompression filter - %s", strerror(errno)); 747 748 cupsArrayDelete(filters); 749 750 abort_message = "Stopping job because the scheduler ran out of memory."; 751 752 goto abort_job; 753 } 754 } 755 756 /* 757 * Add port monitor, if any... 758 */ 759 760 if (job->printer->port_monitor) 761 { 762 /* 763 * Add port monitor to the end of the list... 764 */ 765 766 if (!filters) 767 filters = cupsArrayNew(NULL, NULL); 768 769 port_monitor.src = NULL; 770 port_monitor.dst = NULL; 771 port_monitor.cost = 0; 772 773 snprintf(port_monitor.filter, sizeof(port_monitor.filter), 774 "%s/monitor/%s", ServerBin, job->printer->port_monitor); 775 776 if (!cupsArrayAdd(filters, &port_monitor)) 777 { 778 cupsdLogJob(job, CUPSD_LOG_DEBUG, 779 "Unable to add port monitor - %s", strerror(errno)); 780 781 abort_message = "Stopping job because the scheduler ran out of memory."; 782 783 goto abort_job; 784 } 785 } 786 787 /* 788 * Make sure we don't go over the "MAX_FILTERS" limit... 789 */ 790 791 if (cupsArrayCount(filters) > MAX_FILTERS) 792 { 793 cupsdLogJob(job, CUPSD_LOG_DEBUG, 794 "Too many filters (%d > %d), unable to print.", 795 cupsArrayCount(filters), MAX_FILTERS); 796 797 abort_message = "Aborting job because it needs too many filters to print."; 798 abort_state = IPP_JOB_ABORTED; 799 800 ippSetString(job->attrs, &job->reasons, 0, "document-unprintable-error"); 801 802 goto abort_job; 803 } 804 805 /* 806 * Determine if we are printing a banner page or not... 807 */ 808 809 if (job->job_sheets == NULL) 810 { 811 cupsdLogJob(job, CUPSD_LOG_DEBUG, "No job-sheets attribute."); 812 if ((job->job_sheets = 813 ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL) 814 cupsdLogJob(job, CUPSD_LOG_DEBUG, 815 "... but someone added one without setting job_sheets."); 816 } 817 else if (job->job_sheets->num_values == 1) 818 cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-sheets=%s", 819 job->job_sheets->values[0].string.text); 820 else 821 cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-sheets=%s,%s", 822 job->job_sheets->values[0].string.text, 823 job->job_sheets->values[1].string.text); 824 825 if (job->printer->type & CUPS_PRINTER_REMOTE) 826 banner_page = 0; 827 else if (job->job_sheets == NULL) 828 banner_page = 0; 829 else if (_cups_strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 && 830 job->current_file == 0) 831 banner_page = 1; 832 else if (job->job_sheets->num_values > 1 && 833 _cups_strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 && 834 job->current_file == (job->num_files - 1)) 835 banner_page = 1; 836 else 837 banner_page = 0; 838 839 if ((options = get_options(job, banner_page, copies, sizeof(copies), title, 840 sizeof(title))) == NULL) 841 { 842 abort_message = "Stopping job because the scheduler ran out of memory."; 843 844 goto abort_job; 845 } 846 847 /* 848 * Build the command-line arguments for the filters. Each filter 849 * has 6 or 7 arguments: 850 * 851 * argv[0] = printer 852 * argv[1] = job ID 853 * argv[2] = username 854 * argv[3] = title 855 * argv[4] = # copies 856 * argv[5] = options 857 * argv[6] = filename (optional; normally stdin) 858 * 859 * This allows legacy printer drivers that use the old System V 860 * printing interface to be used by CUPS. 861 * 862 * For remote jobs, we send all of the files in the argument list. 863 */ 864 865 if (job->printer->remote) 866 argc = 6 + job->num_files; 867 else 868 argc = 7; 869 870 if ((argv = calloc((size_t)argc + 1, sizeof(char *))) == NULL) 871 { 872 cupsdLogMessage(CUPSD_LOG_DEBUG, "Unable to allocate argument array - %s", 873 strerror(errno)); 874 875 abort_message = "Stopping job because the scheduler ran out of memory."; 876 877 goto abort_job; 878 } 879 880 sprintf(jobid, "%d", job->id); 881 882 argv[0] = job->printer->name; 883 argv[1] = jobid; 884 argv[2] = job->username; 885 argv[3] = title; 886 argv[4] = copies; 887 argv[5] = options; 888 889 if (job->printer->remote && job->num_files > 1) 890 { 891 for (i = 0; i < job->num_files; i ++) 892 { 893 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, 894 job->id, i + 1); 895 argv[6 + i] = strdup(filename); 896 } 897 } 898 else 899 { 900 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, 901 job->id, job->current_file + 1); 902 argv[6] = strdup(filename); 903 } 904 905 for (i = 0; argv[i]; i ++) 906 cupsdLogJob(job, CUPSD_LOG_DEBUG, "argv[%d]=\"%s\"", i, argv[i]); 907 908 /* 909 * Create environment variable strings for the filters... 910 */ 911 912 attr = ippFindAttribute(job->attrs, "attributes-natural-language", 913 IPP_TAG_LANGUAGE); 914 915#ifdef __APPLE__ 916 strlcpy(apple_language, "APPLE_LANGUAGE=", sizeof(apple_language)); 917 _cupsAppleLanguage(attr->values[0].string.text, 918 apple_language + 15, sizeof(apple_language) - 15); 919#endif /* __APPLE__ */ 920 921 switch (strlen(attr->values[0].string.text)) 922 { 923 default : 924 /* 925 * This is an unknown or badly formatted language code; use 926 * the POSIX locale... 927 */ 928 929 strlcpy(lang, "LANG=C", sizeof(lang)); 930 break; 931 932 case 2 : 933 /* 934 * Just the language code (ll)... 935 */ 936 937 snprintf(lang, sizeof(lang), "LANG=%s.UTF-8", 938 attr->values[0].string.text); 939 break; 940 941 case 5 : 942 /* 943 * Language and country code (ll-cc)... 944 */ 945 946 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF-8", 947 attr->values[0].string.text[0], 948 attr->values[0].string.text[1], 949 toupper(attr->values[0].string.text[3] & 255), 950 toupper(attr->values[0].string.text[4] & 255)); 951 break; 952 } 953 954 if ((attr = ippFindAttribute(job->attrs, "document-format", 955 IPP_TAG_MIMETYPE)) != NULL && 956 (ptr = strstr(attr->values[0].string.text, "charset=")) != NULL) 957 snprintf(charset, sizeof(charset), "CHARSET=%s", ptr + 8); 958 else 959 strlcpy(charset, "CHARSET=utf-8", sizeof(charset)); 960 961 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s", 962 job->filetypes[job->current_file]->super, 963 job->filetypes[job->current_file]->type); 964 snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s", 965 job->printer->device_uri); 966 snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot, 967 job->printer->name); 968 snprintf(printer_info, sizeof(printer_name), "PRINTER_INFO=%s", 969 job->printer->info ? job->printer->info : ""); 970 snprintf(printer_location, sizeof(printer_name), "PRINTER_LOCATION=%s", 971 job->printer->location ? job->printer->location : ""); 972 snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", job->printer->name); 973 if (job->printer->num_reasons > 0) 974 { 975 char *psrptr; /* Pointer into PRINTER_STATE_REASONS */ 976 size_t psrlen; /* Size of PRINTER_STATE_REASONS */ 977 978 for (psrlen = 22, i = 0; i < job->printer->num_reasons; i ++) 979 psrlen += strlen(job->printer->reasons[i]) + 1; 980 981 if ((printer_state_reasons = malloc(psrlen)) != NULL) 982 { 983 /* 984 * All of these strcpy's are safe because we allocated the psr string... 985 */ 986 987 strlcpy(printer_state_reasons, "PRINTER_STATE_REASONS=", psrlen); 988 for (psrptr = printer_state_reasons + 22, i = 0; 989 i < job->printer->num_reasons; 990 i ++) 991 { 992 if (i) 993 *psrptr++ = ','; 994 strlcpy(psrptr, job->printer->reasons[i], psrlen - (size_t)(psrptr - printer_state_reasons)); 995 psrptr += strlen(psrptr); 996 } 997 } 998 } 999 snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache); 1000 1001 if (job->printer->num_auth_info_required == 1) 1002 snprintf(auth_info_required, sizeof(auth_info_required), 1003 "AUTH_INFO_REQUIRED=%s", 1004 job->printer->auth_info_required[0]); 1005 else if (job->printer->num_auth_info_required == 2) 1006 snprintf(auth_info_required, sizeof(auth_info_required), 1007 "AUTH_INFO_REQUIRED=%s,%s", 1008 job->printer->auth_info_required[0], 1009 job->printer->auth_info_required[1]); 1010 else if (job->printer->num_auth_info_required == 3) 1011 snprintf(auth_info_required, sizeof(auth_info_required), 1012 "AUTH_INFO_REQUIRED=%s,%s,%s", 1013 job->printer->auth_info_required[0], 1014 job->printer->auth_info_required[1], 1015 job->printer->auth_info_required[2]); 1016 else if (job->printer->num_auth_info_required == 4) 1017 snprintf(auth_info_required, sizeof(auth_info_required), 1018 "AUTH_INFO_REQUIRED=%s,%s,%s,%s", 1019 job->printer->auth_info_required[0], 1020 job->printer->auth_info_required[1], 1021 job->printer->auth_info_required[2], 1022 job->printer->auth_info_required[3]); 1023 else 1024 strlcpy(auth_info_required, "AUTH_INFO_REQUIRED=none", 1025 sizeof(auth_info_required)); 1026 1027 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0]))); 1028 1029 envp[envc ++] = charset; 1030 envp[envc ++] = lang; 1031#ifdef __APPLE__ 1032 envp[envc ++] = apple_language; 1033#endif /* __APPLE__ */ 1034 envp[envc ++] = ppd; 1035 envp[envc ++] = rip_max_cache; 1036 envp[envc ++] = content_type; 1037 envp[envc ++] = device_uri; 1038 envp[envc ++] = printer_info; 1039 envp[envc ++] = printer_location; 1040 envp[envc ++] = printer_name; 1041 envp[envc ++] = printer_state_reasons ? printer_state_reasons : 1042 "PRINTER_STATE_REASONS=none"; 1043 envp[envc ++] = banner_page ? "CUPS_FILETYPE=job-sheet" : 1044 "CUPS_FILETYPE=document"; 1045 1046 if (final_content_type[0]) 1047 envp[envc ++] = final_content_type; 1048 1049 if (Classification && !banner_page) 1050 { 1051 if ((attr = ippFindAttribute(job->attrs, "job-sheets", 1052 IPP_TAG_NAME)) == NULL) 1053 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s", 1054 Classification); 1055 else if (attr->num_values > 1 && 1056 strcmp(attr->values[1].string.text, "none") != 0) 1057 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s", 1058 attr->values[1].string.text); 1059 else 1060 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s", 1061 attr->values[0].string.text); 1062 1063 envp[envc ++] = classification; 1064 } 1065 1066 if (job->dtype & CUPS_PRINTER_CLASS) 1067 { 1068 snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest); 1069 envp[envc ++] = class_name; 1070 } 1071 1072 envp[envc ++] = auth_info_required; 1073 1074 for (i = 0; 1075 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0])); 1076 i ++) 1077 if (job->auth_env[i]) 1078 envp[envc ++] = job->auth_env[i]; 1079 else 1080 break; 1081 1082 if (job->auth_uid) 1083 envp[envc ++] = job->auth_uid; 1084 1085 envp[envc] = NULL; 1086 1087 for (i = 0; i < envc; i ++) 1088 if (!strncmp(envp[i], "AUTH_", 5)) 1089 cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"AUTH_%c****\"", i, 1090 envp[i][5]); 1091 else if (strncmp(envp[i], "DEVICE_URI=", 11)) 1092 cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"%s\"", i, envp[i]); 1093 else 1094 cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"DEVICE_URI=%s\"", i, 1095 job->printer->sanitized_device_uri); 1096 1097 if (job->printer->remote) 1098 job->current_file = job->num_files; 1099 else 1100 job->current_file ++; 1101 1102 /* 1103 * Now create processes for all of the filters... 1104 */ 1105 1106 for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters); 1107 filter; 1108 i ++, filter = (mime_filter_t *)cupsArrayNext(filters)) 1109 { 1110 if (filter->filter[0] != '/') 1111 snprintf(command, sizeof(command), "%s/filter/%s", ServerBin, 1112 filter->filter); 1113 else 1114 strlcpy(command, filter->filter, sizeof(command)); 1115 1116 if (i < (cupsArrayCount(filters) - 1)) 1117 { 1118 if (cupsdOpenPipe(filterfds[slot])) 1119 { 1120 abort_message = "Stopping job because the scheduler could not create " 1121 "the filter pipes."; 1122 1123 goto abort_job; 1124 } 1125 } 1126 else 1127 { 1128 if (job->current_file == 1 || 1129 (job->printer->pc && job->printer->pc->single_file)) 1130 { 1131 if (strncmp(job->printer->device_uri, "file:", 5) != 0) 1132 { 1133 if (cupsdOpenPipe(job->print_pipes)) 1134 { 1135 abort_message = "Stopping job because the scheduler could not " 1136 "create the backend pipes."; 1137 1138 goto abort_job; 1139 } 1140 } 1141 else 1142 { 1143 job->print_pipes[0] = -1; 1144 if (!strcmp(job->printer->device_uri, "file:/dev/null") || 1145 !strcmp(job->printer->device_uri, "file:///dev/null")) 1146 job->print_pipes[1] = -1; 1147 else 1148 { 1149 if (!strncmp(job->printer->device_uri, "file:/dev/", 10)) 1150 job->print_pipes[1] = open(job->printer->device_uri + 5, 1151 O_WRONLY | O_EXCL); 1152 else if (!strncmp(job->printer->device_uri, "file:///dev/", 12)) 1153 job->print_pipes[1] = open(job->printer->device_uri + 7, 1154 O_WRONLY | O_EXCL); 1155 else if (!strncmp(job->printer->device_uri, "file:///", 8)) 1156 job->print_pipes[1] = open(job->printer->device_uri + 7, 1157 O_WRONLY | O_CREAT | O_TRUNC, 0600); 1158 else 1159 job->print_pipes[1] = open(job->printer->device_uri + 5, 1160 O_WRONLY | O_CREAT | O_TRUNC, 0600); 1161 1162 if (job->print_pipes[1] < 0) 1163 { 1164 abort_message = "Stopping job because the scheduler could not " 1165 "open the output file."; 1166 1167 goto abort_job; 1168 } 1169 1170 fcntl(job->print_pipes[1], F_SETFD, 1171 fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC); 1172 } 1173 } 1174 } 1175 1176 filterfds[slot][0] = job->print_pipes[0]; 1177 filterfds[slot][1] = job->print_pipes[1]; 1178 } 1179 1180 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0], 1181 filterfds[slot][1], job->status_pipes[1], 1182 job->back_pipes[0], job->side_pipes[0], 0, 1183 job->profile, job, job->filters + i); 1184 1185 cupsdClosePipe(filterfds[!slot]); 1186 1187 if (pid == 0) 1188 { 1189 cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.", 1190 filter->filter, strerror(errno)); 1191 1192 abort_message = "Stopping job because the scheduler could not execute a " 1193 "filter."; 1194 1195 goto abort_job; 1196 } 1197 1198 cupsdLogJob(job, CUPSD_LOG_INFO, "Started filter %s (PID %d)", command, 1199 pid); 1200 1201 if (argv[6]) 1202 { 1203 free(argv[6]); 1204 argv[6] = NULL; 1205 } 1206 1207 slot = !slot; 1208 } 1209 1210 cupsArrayDelete(filters); 1211 filters = NULL; 1212 1213 /* 1214 * Finally, pipe the final output into a backend process if needed... 1215 */ 1216 1217 if (strncmp(job->printer->device_uri, "file:", 5) != 0) 1218 { 1219 if (job->current_file == 1 || job->printer->remote || 1220 (job->printer->pc && job->printer->pc->single_file)) 1221 { 1222 sscanf(job->printer->device_uri, "%254[^:]", scheme); 1223 snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, scheme); 1224 1225 /* 1226 * See if the backend needs to run as root... 1227 */ 1228 1229 if (RunUser) 1230 backroot = 0; 1231 else if (stat(command, &backinfo)) 1232 backroot = 0; 1233 else 1234 backroot = !(backinfo.st_mode & (S_IWGRP | S_IRWXO)); 1235 1236 argv[0] = job->printer->sanitized_device_uri; 1237 1238 filterfds[slot][0] = -1; 1239 filterfds[slot][1] = -1; 1240 1241 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0], 1242 filterfds[slot][1], job->status_pipes[1], 1243 job->back_pipes[1], job->side_pipes[1], 1244 backroot, job->bprofile, job, &(job->backend)); 1245 1246 if (pid == 0) 1247 { 1248 abort_message = "Stopping job because the sheduler could not execute " 1249 "the backend."; 1250 1251 goto abort_job; 1252 } 1253 else 1254 { 1255 cupsdLogJob(job, CUPSD_LOG_INFO, "Started backend %s (PID %d)", 1256 command, pid); 1257 } 1258 } 1259 1260 if (job->current_file == job->num_files || 1261 (job->printer->pc && job->printer->pc->single_file)) 1262 cupsdClosePipe(job->print_pipes); 1263 1264 if (job->current_file == job->num_files) 1265 { 1266 cupsdClosePipe(job->back_pipes); 1267 cupsdClosePipe(job->side_pipes); 1268 1269 close(job->status_pipes[1]); 1270 job->status_pipes[1] = -1; 1271 } 1272 } 1273 else 1274 { 1275 filterfds[slot][0] = -1; 1276 filterfds[slot][1] = -1; 1277 1278 if (job->current_file == job->num_files || 1279 (job->printer->pc && job->printer->pc->single_file)) 1280 cupsdClosePipe(job->print_pipes); 1281 1282 if (job->current_file == job->num_files) 1283 { 1284 close(job->status_pipes[1]); 1285 job->status_pipes[1] = -1; 1286 } 1287 } 1288 1289 cupsdClosePipe(filterfds[slot]); 1290 1291 for (i = 6; i < argc; i ++) 1292 if (argv[i]) 1293 free(argv[i]); 1294 1295 free(argv); 1296 1297 if (printer_state_reasons) 1298 free(printer_state_reasons); 1299 1300 cupsdAddSelect(job->status_buffer->fd, (cupsd_selfunc_t)update_job, NULL, 1301 job); 1302 1303 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.", 1304 job->id); 1305 1306 return; 1307 1308 1309 /* 1310 * If we get here, we need to abort the current job and close out all 1311 * files and pipes... 1312 */ 1313 1314 abort_job: 1315 1316 FilterLevel -= job->cost; 1317 job->cost = 0; 1318 1319 for (slot = 0; slot < 2; slot ++) 1320 cupsdClosePipe(filterfds[slot]); 1321 1322 cupsArrayDelete(filters); 1323 1324 if (argv) 1325 { 1326 for (i = 6; i < argc; i ++) 1327 if (argv[i]) 1328 free(argv[i]); 1329 } 1330 1331 if (printer_state_reasons) 1332 free(printer_state_reasons); 1333 1334 cupsdClosePipe(job->print_pipes); 1335 cupsdClosePipe(job->back_pipes); 1336 cupsdClosePipe(job->side_pipes); 1337 1338 cupsdRemoveSelect(job->status_pipes[0]); 1339 cupsdClosePipe(job->status_pipes); 1340 cupsdStatBufDelete(job->status_buffer); 1341 job->status_buffer = NULL; 1342 1343 /* 1344 * Update the printer and job state. 1345 */ 1346 1347 cupsdSetJobState(job, abort_state, CUPSD_JOB_DEFAULT, "%s", abort_message); 1348 cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0); 1349 update_job_attrs(job, 0); 1350 1351 if (job->history) 1352 free_job_history(job); 1353 1354 cupsArrayRemove(PrintingJobs, job); 1355 1356 /* 1357 * Clear the printer <-> job association... 1358 */ 1359 1360 job->printer->job = NULL; 1361 job->printer = NULL; 1362} 1363 1364 1365/* 1366 * 'cupsdDeleteJob()' - Free all memory used by a job. 1367 */ 1368 1369void 1370cupsdDeleteJob(cupsd_job_t *job, /* I - Job */ 1371 cupsd_jobaction_t action)/* I - Action */ 1372{ 1373 int i; /* Looping var */ 1374 1375 1376 if (job->printer) 1377 finalize_job(job, 1); 1378 1379 if (action == CUPSD_JOB_PURGE) 1380 remove_job_history(job); 1381 1382 cupsdClearString(&job->username); 1383 cupsdClearString(&job->dest); 1384 for (i = 0; 1385 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0])); 1386 i ++) 1387 cupsdClearString(job->auth_env + i); 1388 cupsdClearString(&job->auth_uid); 1389 1390 if (action == CUPSD_JOB_PURGE) 1391 remove_job_files(job); 1392 else if (job->num_files > 0) 1393 { 1394 free(job->compressions); 1395 free(job->filetypes); 1396 1397 job->num_files = 0; 1398 } 1399 1400 if (job->history) 1401 free_job_history(job); 1402 1403 unload_job(job); 1404 1405 cupsArrayRemove(Jobs, job); 1406 cupsArrayRemove(ActiveJobs, job); 1407 cupsArrayRemove(PrintingJobs, job); 1408 1409 free(job); 1410} 1411 1412 1413/* 1414 * 'cupsdFreeAllJobs()' - Free all jobs from memory. 1415 */ 1416 1417void 1418cupsdFreeAllJobs(void) 1419{ 1420 cupsd_job_t *job; /* Current job */ 1421 1422 1423 if (!Jobs) 1424 return; 1425 1426 cupsdHoldSignals(); 1427 1428 cupsdStopAllJobs(CUPSD_JOB_FORCE, 0); 1429 cupsdSaveAllJobs(); 1430 1431 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); 1432 job; 1433 job = (cupsd_job_t *)cupsArrayNext(Jobs)) 1434 cupsdDeleteJob(job, CUPSD_JOB_DEFAULT); 1435 1436 cupsdReleaseSignals(); 1437} 1438 1439 1440/* 1441 * 'cupsdFindJob()' - Find the specified job. 1442 */ 1443 1444cupsd_job_t * /* O - Job data */ 1445cupsdFindJob(int id) /* I - Job ID */ 1446{ 1447 cupsd_job_t key; /* Search key */ 1448 1449 1450 key.id = id; 1451 1452 return ((cupsd_job_t *)cupsArrayFind(Jobs, &key)); 1453} 1454 1455 1456/* 1457 * 'cupsdGetCompletedJobs()'- Generate a completed jobs list. 1458 */ 1459 1460cups_array_t * /* O - Array of jobs */ 1461cupsdGetCompletedJobs( 1462 cupsd_printer_t *p) /* I - Printer */ 1463{ 1464 cups_array_t *list; /* Array of jobs */ 1465 cupsd_job_t *job; /* Current job */ 1466 1467 1468 list = cupsArrayNew(compare_completed_jobs, NULL); 1469 1470 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); 1471 job; 1472 job = (cupsd_job_t *)cupsArrayNext(Jobs)) 1473 if ((!p || !_cups_strcasecmp(p->name, job->dest)) && job->state_value >= IPP_JOB_STOPPED && job->completed_time) 1474 cupsArrayAdd(list, job); 1475 1476 return (list); 1477} 1478 1479 1480/* 1481 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing, 1482 * or held jobs in a printer or class. 1483 */ 1484 1485int /* O - Job count */ 1486cupsdGetPrinterJobCount( 1487 const char *dest) /* I - Printer or class name */ 1488{ 1489 int count; /* Job count */ 1490 cupsd_job_t *job; /* Current job */ 1491 1492 1493 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0; 1494 job; 1495 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) 1496 if (job->dest && !_cups_strcasecmp(job->dest, dest)) 1497 count ++; 1498 1499 return (count); 1500} 1501 1502 1503/* 1504 * 'cupsdGetUserJobCount()' - Get the number of pending, processing, 1505 * or held jobs for a user. 1506 */ 1507 1508int /* O - Job count */ 1509cupsdGetUserJobCount( 1510 const char *username) /* I - Username */ 1511{ 1512 int count; /* Job count */ 1513 cupsd_job_t *job; /* Current job */ 1514 1515 1516 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0; 1517 job; 1518 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) 1519 if (!_cups_strcasecmp(job->username, username)) 1520 count ++; 1521 1522 return (count); 1523} 1524 1525 1526/* 1527 * 'cupsdLoadAllJobs()' - Load all jobs from disk. 1528 */ 1529 1530void 1531cupsdLoadAllJobs(void) 1532{ 1533 char filename[1024]; /* Full filename of job.cache file */ 1534 struct stat fileinfo, /* Information on job.cache file */ 1535 dirinfo; /* Information on RequestRoot dir */ 1536 1537 1538 1539 /* 1540 * Create the job arrays as needed... 1541 */ 1542 1543 if (!Jobs) 1544 Jobs = cupsArrayNew(compare_jobs, NULL); 1545 1546 if (!ActiveJobs) 1547 ActiveJobs = cupsArrayNew(compare_active_jobs, NULL); 1548 1549 if (!PrintingJobs) 1550 PrintingJobs = cupsArrayNew(compare_jobs, NULL); 1551 1552 /* 1553 * See whether the job.cache file is older than the RequestRoot directory... 1554 */ 1555 1556 snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir); 1557 1558 if (stat(filename, &fileinfo)) 1559 { 1560 fileinfo.st_mtime = 0; 1561 1562 if (errno != ENOENT) 1563 cupsdLogMessage(CUPSD_LOG_ERROR, 1564 "Unable to get file information for \"%s\" - %s", 1565 filename, strerror(errno)); 1566 } 1567 1568 if (stat(RequestRoot, &dirinfo)) 1569 { 1570 dirinfo.st_mtime = 0; 1571 1572 if (errno != ENOENT) 1573 cupsdLogMessage(CUPSD_LOG_ERROR, 1574 "Unable to get directory information for \"%s\" - %s", 1575 RequestRoot, strerror(errno)); 1576 } 1577 1578 /* 1579 * Load the most recent source for job data... 1580 */ 1581 1582 if (dirinfo.st_mtime > fileinfo.st_mtime) 1583 { 1584 load_request_root(); 1585 1586 load_next_job_id(filename); 1587 } 1588 else 1589 load_job_cache(filename); 1590 1591 /* 1592 * Clean out old jobs as needed... 1593 */ 1594 1595 if (MaxJobs > 0 && cupsArrayCount(Jobs) >= MaxJobs) 1596 cupsdCleanJobs(); 1597} 1598 1599 1600/* 1601 * 'cupsdLoadJob()' - Load a single job. 1602 */ 1603 1604int /* O - 1 on success, 0 on failure */ 1605cupsdLoadJob(cupsd_job_t *job) /* I - Job */ 1606{ 1607 int i; /* Looping var */ 1608 char jobfile[1024]; /* Job filename */ 1609 cups_file_t *fp; /* Job file */ 1610 int fileid; /* Current file ID */ 1611 ipp_attribute_t *attr; /* Job attribute */ 1612 const char *dest; /* Destination name */ 1613 cupsd_printer_t *destptr; /* Pointer to destination */ 1614 mime_type_t **filetypes; /* New filetypes array */ 1615 int *compressions; /* New compressions array */ 1616 1617 1618 if (job->attrs) 1619 { 1620 if (job->state_value > IPP_JOB_STOPPED) 1621 job->access_time = time(NULL); 1622 1623 return (1); 1624 } 1625 1626 if ((job->attrs = ippNew()) == NULL) 1627 { 1628 cupsdLogJob(job, CUPSD_LOG_ERROR, "Ran out of memory for job attributes."); 1629 return (0); 1630 } 1631 1632 /* 1633 * Load job attributes... 1634 */ 1635 1636 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Loading attributes..."); 1637 1638 snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, job->id); 1639 if ((fp = cupsdOpenConfFile(jobfile)) == NULL) 1640 goto error; 1641 1642 if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, job->attrs) != IPP_DATA) 1643 { 1644 cupsdLogJob(job, CUPSD_LOG_ERROR, 1645 "Unable to read job control file \"%s\".", jobfile); 1646 cupsFileClose(fp); 1647 goto error; 1648 } 1649 1650 cupsFileClose(fp); 1651 1652 /* 1653 * Copy attribute data to the job object... 1654 */ 1655 1656 if (!ippFindAttribute(job->attrs, "time-at-creation", IPP_TAG_INTEGER)) 1657 { 1658 cupsdLogJob(job, CUPSD_LOG_ERROR, 1659 "Missing or bad time-at-creation attribute in control file."); 1660 goto error; 1661 } 1662 1663 if ((job->state = ippFindAttribute(job->attrs, "job-state", 1664 IPP_TAG_ENUM)) == NULL) 1665 { 1666 cupsdLogJob(job, CUPSD_LOG_ERROR, 1667 "Missing or bad job-state attribute in control file."); 1668 goto error; 1669 } 1670 1671 job->state_value = (ipp_jstate_t)job->state->values[0].integer; 1672 job->file_time = 0; 1673 job->history_time = 0; 1674 1675 if ((attr = ippFindAttribute(job->attrs, "time-at-creation", IPP_TAG_INTEGER)) != NULL) 1676 job->creation_time = attr->values[0].integer; 1677 1678 if (job->state_value >= IPP_JOB_CANCELED && (attr = ippFindAttribute(job->attrs, "time-at-completed", IPP_TAG_INTEGER)) != NULL) 1679 { 1680 job->completed_time = attr->values[0].integer; 1681 1682 if (JobHistory < INT_MAX) 1683 job->history_time = attr->values[0].integer + JobHistory; 1684 else 1685 job->history_time = INT_MAX; 1686 1687 if (job->history_time < time(NULL)) 1688 goto error; /* Expired, remove from history */ 1689 1690 if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate) 1691 JobHistoryUpdate = job->history_time; 1692 1693 if (JobFiles < INT_MAX) 1694 job->file_time = attr->values[0].integer + JobFiles; 1695 else 1696 job->file_time = INT_MAX; 1697 1698 if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate) 1699 JobHistoryUpdate = job->file_time; 1700 1701 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdLoadJob: JobHistoryUpdate=%ld", 1702 (long)JobHistoryUpdate); 1703 } 1704 1705 if (!job->dest) 1706 { 1707 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", 1708 IPP_TAG_URI)) == NULL) 1709 { 1710 cupsdLogJob(job, CUPSD_LOG_ERROR, 1711 "No job-printer-uri attribute in control file."); 1712 goto error; 1713 } 1714 1715 if ((dest = cupsdValidateDest(attr->values[0].string.text, &(job->dtype), 1716 &destptr)) == NULL) 1717 { 1718 cupsdLogJob(job, CUPSD_LOG_ERROR, 1719 "Unable to queue job for destination \"%s\".", 1720 attr->values[0].string.text); 1721 goto error; 1722 } 1723 1724 cupsdSetString(&job->dest, dest); 1725 } 1726 else if ((destptr = cupsdFindDest(job->dest)) == NULL) 1727 { 1728 cupsdLogJob(job, CUPSD_LOG_ERROR, 1729 "Unable to queue job for destination \"%s\".", 1730 job->dest); 1731 goto error; 1732 } 1733 1734 if ((job->reasons = ippFindAttribute(job->attrs, "job-state-reasons", 1735 IPP_TAG_KEYWORD)) == NULL) 1736 { 1737 const char *reason; /* job-state-reason keyword */ 1738 1739 cupsdLogJob(job, CUPSD_LOG_DEBUG, 1740 "Adding missing job-state-reasons attribute to control file."); 1741 1742 switch (job->state_value) 1743 { 1744 default : 1745 case IPP_JOB_PENDING : 1746 if (destptr->state == IPP_PRINTER_STOPPED) 1747 reason = "printer-stopped"; 1748 else 1749 reason = "none"; 1750 break; 1751 1752 case IPP_JOB_HELD : 1753 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", 1754 IPP_TAG_ZERO)) != NULL && 1755 (attr->value_tag == IPP_TAG_NAME || 1756 attr->value_tag == IPP_TAG_NAMELANG || 1757 attr->value_tag == IPP_TAG_KEYWORD) && 1758 strcmp(attr->values[0].string.text, "no-hold")) 1759 reason = "job-hold-until-specified"; 1760 else 1761 reason = "job-incoming"; 1762 break; 1763 1764 case IPP_JOB_PROCESSING : 1765 reason = "job-printing"; 1766 break; 1767 1768 case IPP_JOB_STOPPED : 1769 reason = "job-stopped"; 1770 break; 1771 1772 case IPP_JOB_CANCELED : 1773 reason = "job-canceled-by-user"; 1774 break; 1775 1776 case IPP_JOB_ABORTED : 1777 reason = "aborted-by-system"; 1778 break; 1779 1780 case IPP_JOB_COMPLETED : 1781 reason = "job-completed-successfully"; 1782 break; 1783 } 1784 1785 job->reasons = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD, 1786 "job-state-reasons", NULL, reason); 1787 } 1788 else if (job->state_value == IPP_JOB_PENDING) 1789 { 1790 if (destptr->state == IPP_PRINTER_STOPPED) 1791 ippSetString(job->attrs, &job->reasons, 0, "printer-stopped"); 1792 else 1793 ippSetString(job->attrs, &job->reasons, 0, "none"); 1794 } 1795 1796 job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed", 1797 IPP_TAG_INTEGER); 1798 job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME); 1799 1800 if (!job->priority) 1801 { 1802 if ((attr = ippFindAttribute(job->attrs, "job-priority", 1803 IPP_TAG_INTEGER)) == NULL) 1804 { 1805 cupsdLogJob(job, CUPSD_LOG_ERROR, 1806 "Missing or bad job-priority attribute in control file."); 1807 goto error; 1808 } 1809 1810 job->priority = attr->values[0].integer; 1811 } 1812 1813 if (!job->username) 1814 { 1815 if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name", 1816 IPP_TAG_NAME)) == NULL) 1817 { 1818 cupsdLogJob(job, CUPSD_LOG_ERROR, 1819 "Missing or bad job-originating-user-name " 1820 "attribute in control file."); 1821 goto error; 1822 } 1823 1824 cupsdSetString(&job->username, attr->values[0].string.text); 1825 } 1826 1827 if (!job->name) 1828 { 1829 if ((attr = ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME)) != NULL) 1830 cupsdSetString(&job->name, attr->values[0].string.text); 1831 } 1832 1833 /* 1834 * Set the job hold-until time and state... 1835 */ 1836 1837 if (job->state_value == IPP_JOB_HELD) 1838 { 1839 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", 1840 IPP_TAG_KEYWORD)) == NULL) 1841 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME); 1842 1843 if (attr) 1844 cupsdSetJobHoldUntil(job, attr->values[0].string.text, CUPSD_JOB_DEFAULT); 1845 else 1846 { 1847 job->state->values[0].integer = IPP_JOB_PENDING; 1848 job->state_value = IPP_JOB_PENDING; 1849 } 1850 } 1851 else if (job->state_value == IPP_JOB_PROCESSING) 1852 { 1853 job->state->values[0].integer = IPP_JOB_PENDING; 1854 job->state_value = IPP_JOB_PENDING; 1855 } 1856 1857 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL) 1858 job->koctets = attr->values[0].integer; 1859 1860 if (!job->num_files) 1861 { 1862 /* 1863 * Find all the d##### files... 1864 */ 1865 1866 for (fileid = 1; fileid < 10000; fileid ++) 1867 { 1868 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot, 1869 job->id, fileid); 1870 1871 if (access(jobfile, 0)) 1872 break; 1873 1874 cupsdLogJob(job, CUPSD_LOG_DEBUG, 1875 "Auto-typing document file \"%s\"...", jobfile); 1876 1877 if (fileid > job->num_files) 1878 { 1879 if (job->num_files == 0) 1880 { 1881 compressions = (int *)calloc((size_t)fileid, sizeof(int)); 1882 filetypes = (mime_type_t **)calloc((size_t)fileid, sizeof(mime_type_t *)); 1883 } 1884 else 1885 { 1886 compressions = (int *)realloc(job->compressions, sizeof(int) * (size_t)fileid); 1887 filetypes = (mime_type_t **)realloc(job->filetypes, sizeof(mime_type_t *) * (size_t)fileid); 1888 } 1889 1890 if (compressions) 1891 job->compressions = compressions; 1892 1893 if (filetypes) 1894 job->filetypes = filetypes; 1895 1896 if (!compressions || !filetypes) 1897 { 1898 cupsdLogJob(job, CUPSD_LOG_ERROR, 1899 "Ran out of memory for job file types."); 1900 1901 ippDelete(job->attrs); 1902 job->attrs = NULL; 1903 1904 if (job->compressions) 1905 { 1906 free(job->compressions); 1907 job->compressions = NULL; 1908 } 1909 1910 if (job->filetypes) 1911 { 1912 free(job->filetypes); 1913 job->filetypes = NULL; 1914 } 1915 1916 job->num_files = 0; 1917 return (0); 1918 } 1919 1920 job->num_files = fileid; 1921 } 1922 1923 job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, jobfile, NULL, 1924 job->compressions + fileid - 1); 1925 1926 if (!job->filetypes[fileid - 1]) 1927 job->filetypes[fileid - 1] = mimeType(MimeDatabase, "application", 1928 "vnd.cups-raw"); 1929 } 1930 } 1931 1932 /* 1933 * Load authentication information as needed... 1934 */ 1935 1936 if (job->state_value < IPP_JOB_STOPPED) 1937 { 1938 snprintf(jobfile, sizeof(jobfile), "%s/a%05d", RequestRoot, job->id); 1939 1940 for (i = 0; 1941 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0])); 1942 i ++) 1943 cupsdClearString(job->auth_env + i); 1944 cupsdClearString(&job->auth_uid); 1945 1946 if ((fp = cupsFileOpen(jobfile, "r")) != NULL) 1947 { 1948 int bytes, /* Size of auth data */ 1949 linenum = 1; /* Current line number */ 1950 char line[65536], /* Line from file */ 1951 *value, /* Value from line */ 1952 data[65536]; /* Decoded data */ 1953 1954 1955 if (cupsFileGets(fp, line, sizeof(line)) && 1956 !strcmp(line, "CUPSD-AUTH-V3")) 1957 { 1958 i = 0; 1959 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) 1960 { 1961 /* 1962 * Decode value... 1963 */ 1964 1965 if (strcmp(line, "negotiate") && strcmp(line, "uid")) 1966 { 1967 bytes = sizeof(data); 1968 httpDecode64_2(data, &bytes, value); 1969 } 1970 1971 /* 1972 * Assign environment variables... 1973 */ 1974 1975 if (!strcmp(line, "uid")) 1976 { 1977 cupsdSetStringf(&job->auth_uid, "AUTH_UID=%s", value); 1978 continue; 1979 } 1980 else if (i >= (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]))) 1981 break; 1982 1983 if (!strcmp(line, "username")) 1984 cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s", data); 1985 else if (!strcmp(line, "domain")) 1986 cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s", data); 1987 else if (!strcmp(line, "password")) 1988 cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s", data); 1989 else if (!strcmp(line, "negotiate")) 1990 cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s", value); 1991 else 1992 continue; 1993 1994 i ++; 1995 } 1996 } 1997 1998 cupsFileClose(fp); 1999 } 2000 } 2001 2002 job->access_time = time(NULL); 2003 return (1); 2004 2005 /* 2006 * If we get here then something bad happened... 2007 */ 2008 2009 error: 2010 2011 ippDelete(job->attrs); 2012 job->attrs = NULL; 2013 2014 remove_job_history(job); 2015 remove_job_files(job); 2016 2017 return (0); 2018} 2019 2020 2021/* 2022 * 'cupsdMoveJob()' - Move the specified job to a different destination. 2023 */ 2024 2025void 2026cupsdMoveJob(cupsd_job_t *job, /* I - Job */ 2027 cupsd_printer_t *p) /* I - Destination printer or class */ 2028{ 2029 ipp_attribute_t *attr; /* job-printer-uri attribute */ 2030 const char *olddest; /* Old destination */ 2031 cupsd_printer_t *oldp; /* Old pointer */ 2032 2033 2034 /* 2035 * Don't move completed jobs... 2036 */ 2037 2038 if (job->state_value > IPP_JOB_STOPPED) 2039 return; 2040 2041 /* 2042 * Get the old destination... 2043 */ 2044 2045 olddest = job->dest; 2046 2047 if (job->printer) 2048 oldp = job->printer; 2049 else 2050 oldp = cupsdFindDest(olddest); 2051 2052 /* 2053 * Change the destination information... 2054 */ 2055 2056 if (job->state_value > IPP_JOB_HELD) 2057 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT, 2058 "Stopping job prior to move."); 2059 2060 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, oldp, job, 2061 "Job #%d moved from %s to %s.", job->id, olddest, 2062 p->name); 2063 2064 cupsdSetString(&job->dest, p->name); 2065 job->dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE); 2066 2067 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", 2068 IPP_TAG_URI)) != NULL) 2069 cupsdSetString(&(attr->values[0].string.text), p->uri); 2070 2071 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job, 2072 "Job #%d moved from %s to %s.", job->id, olddest, 2073 p->name); 2074 2075 job->dirty = 1; 2076 cupsdMarkDirty(CUPSD_DIRTY_JOBS); 2077} 2078 2079 2080/* 2081 * 'cupsdReleaseJob()' - Release the specified job. 2082 */ 2083 2084void 2085cupsdReleaseJob(cupsd_job_t *job) /* I - Job */ 2086{ 2087 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReleaseJob(job=%p(%d))", job, 2088 job->id); 2089 2090 if (job->state_value == IPP_JOB_HELD) 2091 { 2092 /* 2093 * Add trailing banner as needed... 2094 */ 2095 2096 if (job->pending_timeout) 2097 cupsdTimeoutJob(job); 2098 2099 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT, 2100 "Job released by user."); 2101 } 2102} 2103 2104 2105/* 2106 * 'cupsdRestartJob()' - Restart the specified job. 2107 */ 2108 2109void 2110cupsdRestartJob(cupsd_job_t *job) /* I - Job */ 2111{ 2112 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRestartJob(job=%p(%d))", job, 2113 job->id); 2114 2115 if (job->state_value == IPP_JOB_STOPPED || job->num_files) 2116 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT, 2117 "Job restarted by user."); 2118} 2119 2120 2121/* 2122 * 'cupsdSaveAllJobs()' - Save a summary of all jobs to disk. 2123 */ 2124 2125void 2126cupsdSaveAllJobs(void) 2127{ 2128 int i; /* Looping var */ 2129 cups_file_t *fp; /* job.cache file */ 2130 char filename[1024], /* job.cache filename */ 2131 temp[1024]; /* Temporary string */ 2132 cupsd_job_t *job; /* Current job */ 2133 time_t curtime; /* Current time */ 2134 struct tm *curdate; /* Current date */ 2135 2136 2137 snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir); 2138 if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL) 2139 return; 2140 2141 cupsdLogMessage(CUPSD_LOG_INFO, "Saving job.cache..."); 2142 2143 /* 2144 * Write a small header to the file... 2145 */ 2146 2147 curtime = time(NULL); 2148 curdate = localtime(&curtime); 2149 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate); 2150 2151 cupsFilePuts(fp, "# Job cache file for " CUPS_SVERSION "\n"); 2152 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp); 2153 cupsFilePrintf(fp, "NextJobId %d\n", NextJobId); 2154 2155 /* 2156 * Write each job known to the system... 2157 */ 2158 2159 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); 2160 job; 2161 job = (cupsd_job_t *)cupsArrayNext(Jobs)) 2162 { 2163 cupsFilePrintf(fp, "<Job %d>\n", job->id); 2164 cupsFilePrintf(fp, "State %d\n", job->state_value); 2165 cupsFilePrintf(fp, "Created %ld\n", (long)job->creation_time); 2166 if (job->completed_time) 2167 cupsFilePrintf(fp, "Completed %ld\n", (long)job->completed_time); 2168 cupsFilePrintf(fp, "Priority %d\n", job->priority); 2169 if (job->hold_until) 2170 cupsFilePrintf(fp, "HoldUntil %ld\n", (long)job->hold_until); 2171 cupsFilePrintf(fp, "Username %s\n", job->username); 2172 if (job->name) 2173 cupsFilePutConf(fp, "Name", job->name); 2174 cupsFilePrintf(fp, "Destination %s\n", job->dest); 2175 cupsFilePrintf(fp, "DestType %d\n", job->dtype); 2176 cupsFilePrintf(fp, "KOctets %d\n", job->koctets); 2177 cupsFilePrintf(fp, "NumFiles %d\n", job->num_files); 2178 for (i = 0; i < job->num_files; i ++) 2179 cupsFilePrintf(fp, "File %d %s/%s %d\n", i + 1, job->filetypes[i]->super, 2180 job->filetypes[i]->type, job->compressions[i]); 2181 cupsFilePuts(fp, "</Job>\n"); 2182 } 2183 2184 cupsdCloseCreatedConfFile(fp, filename); 2185} 2186 2187 2188/* 2189 * 'cupsdSaveJob()' - Save a job to disk. 2190 */ 2191 2192void 2193cupsdSaveJob(cupsd_job_t *job) /* I - Job */ 2194{ 2195 char filename[1024]; /* Job control filename */ 2196 cups_file_t *fp; /* Job file */ 2197 2198 2199 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSaveJob(job=%p(%d)): job->attrs=%p", 2200 job, job->id, job->attrs); 2201 2202 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id); 2203 2204 if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm & 0600)) == NULL) 2205 return; 2206 2207 fchown(cupsFileNumber(fp), RunUser, Group); 2208 2209 job->attrs->state = IPP_IDLE; 2210 2211 if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, 2212 job->attrs) != IPP_DATA) 2213 { 2214 cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to write job control file."); 2215 cupsFileClose(fp); 2216 return; 2217 } 2218 2219 if (!cupsdCloseCreatedConfFile(fp, filename)) 2220 { 2221 /* 2222 * Remove backup file and mark this job as clean... 2223 */ 2224 2225 strlcat(filename, ".O", sizeof(filename)); 2226 unlink(filename); 2227 2228 job->dirty = 0; 2229 } 2230} 2231 2232 2233/* 2234 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job. 2235 */ 2236 2237void 2238cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */ 2239 const char *when, /* I - When to resume */ 2240 int update)/* I - Update job-hold-until attr? */ 2241{ 2242 time_t curtime; /* Current time */ 2243 struct tm *curdate; /* Current date */ 2244 int hour; /* Hold hour */ 2245 int minute; /* Hold minute */ 2246 int second = 0; /* Hold second */ 2247 2248 2249 cupsdLogMessage(CUPSD_LOG_DEBUG2, 2250 "cupsdSetJobHoldUntil(job=%p(%d), when=\"%s\", update=%d)", 2251 job, job->id, when, update); 2252 2253 if (update) 2254 { 2255 /* 2256 * Update the job-hold-until attribute... 2257 */ 2258 2259 ipp_attribute_t *attr; /* job-hold-until attribute */ 2260 2261 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", 2262 IPP_TAG_KEYWORD)) == NULL) 2263 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME); 2264 2265 if (attr) 2266 cupsdSetString(&(attr->values[0].string.text), when); 2267 else 2268 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD, 2269 "job-hold-until", NULL, when); 2270 2271 if (attr) 2272 { 2273 if (isdigit(when[0] & 255)) 2274 attr->value_tag = IPP_TAG_NAME; 2275 else 2276 attr->value_tag = IPP_TAG_KEYWORD; 2277 2278 job->dirty = 1; 2279 cupsdMarkDirty(CUPSD_DIRTY_JOBS); 2280 } 2281 2282 } 2283 2284 ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified"); 2285 2286 /* 2287 * Update the hold time... 2288 */ 2289 2290 job->cancel_time = 0; 2291 2292 if (!strcmp(when, "indefinite") || !strcmp(when, "auth-info-required")) 2293 { 2294 /* 2295 * Hold indefinitely... 2296 */ 2297 2298 job->hold_until = 0; 2299 2300 if (MaxHoldTime > 0) 2301 job->cancel_time = time(NULL) + MaxHoldTime; 2302 } 2303 else if (!strcmp(when, "day-time")) 2304 { 2305 /* 2306 * Hold to 6am the next morning unless local time is < 6pm. 2307 */ 2308 2309 curtime = time(NULL); 2310 curdate = localtime(&curtime); 2311 2312 if (curdate->tm_hour < 18) 2313 job->hold_until = curtime; 2314 else 2315 job->hold_until = curtime + 2316 ((29 - curdate->tm_hour) * 60 + 59 - 2317 curdate->tm_min) * 60 + 60 - curdate->tm_sec; 2318 } 2319 else if (!strcmp(when, "evening") || !strcmp(when, "night")) 2320 { 2321 /* 2322 * Hold to 6pm unless local time is > 6pm or < 6am. 2323 */ 2324 2325 curtime = time(NULL); 2326 curdate = localtime(&curtime); 2327 2328 if (curdate->tm_hour < 6 || curdate->tm_hour >= 18) 2329 job->hold_until = curtime; 2330 else 2331 job->hold_until = curtime + 2332 ((17 - curdate->tm_hour) * 60 + 59 - 2333 curdate->tm_min) * 60 + 60 - curdate->tm_sec; 2334 } 2335 else if (!strcmp(when, "second-shift")) 2336 { 2337 /* 2338 * Hold to 4pm unless local time is > 4pm. 2339 */ 2340 2341 curtime = time(NULL); 2342 curdate = localtime(&curtime); 2343 2344 if (curdate->tm_hour >= 16) 2345 job->hold_until = curtime; 2346 else 2347 job->hold_until = curtime + 2348 ((15 - curdate->tm_hour) * 60 + 59 - 2349 curdate->tm_min) * 60 + 60 - curdate->tm_sec; 2350 } 2351 else if (!strcmp(when, "third-shift")) 2352 { 2353 /* 2354 * Hold to 12am unless local time is < 8am. 2355 */ 2356 2357 curtime = time(NULL); 2358 curdate = localtime(&curtime); 2359 2360 if (curdate->tm_hour < 8) 2361 job->hold_until = curtime; 2362 else 2363 job->hold_until = curtime + 2364 ((23 - curdate->tm_hour) * 60 + 59 - 2365 curdate->tm_min) * 60 + 60 - curdate->tm_sec; 2366 } 2367 else if (!strcmp(when, "weekend")) 2368 { 2369 /* 2370 * Hold to weekend unless we are in the weekend. 2371 */ 2372 2373 curtime = time(NULL); 2374 curdate = localtime(&curtime); 2375 2376 if (curdate->tm_wday == 0 || curdate->tm_wday == 6) 2377 job->hold_until = curtime; 2378 else 2379 job->hold_until = curtime + 2380 (((5 - curdate->tm_wday) * 24 + 2381 (17 - curdate->tm_hour)) * 60 + 59 - 2382 curdate->tm_min) * 60 + 60 - curdate->tm_sec; 2383 } 2384 else if (sscanf(when, "%d:%d:%d", &hour, &minute, &second) >= 2) 2385 { 2386 /* 2387 * Hold to specified GMT time (HH:MM or HH:MM:SS)... 2388 */ 2389 2390 curtime = time(NULL); 2391 curdate = gmtime(&curtime); 2392 2393 job->hold_until = curtime + 2394 ((hour - curdate->tm_hour) * 60 + minute - 2395 curdate->tm_min) * 60 + second - curdate->tm_sec; 2396 2397 /* 2398 * Hold until next day as needed... 2399 */ 2400 2401 if (job->hold_until < curtime) 2402 job->hold_until += 24 * 60 * 60; 2403 } 2404 2405 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil: hold_until=%d", 2406 (int)job->hold_until); 2407} 2408 2409 2410/* 2411 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in 2412 * the list as needed. 2413 */ 2414 2415void 2416cupsdSetJobPriority( 2417 cupsd_job_t *job, /* I - Job ID */ 2418 int priority) /* I - New priority (0 to 100) */ 2419{ 2420 ipp_attribute_t *attr; /* Job attribute */ 2421 2422 2423 /* 2424 * Don't change completed jobs... 2425 */ 2426 2427 if (job->state_value >= IPP_JOB_PROCESSING) 2428 return; 2429 2430 /* 2431 * Set the new priority and re-add the job into the active list... 2432 */ 2433 2434 cupsArrayRemove(ActiveJobs, job); 2435 2436 job->priority = priority; 2437 2438 if ((attr = ippFindAttribute(job->attrs, "job-priority", 2439 IPP_TAG_INTEGER)) != NULL) 2440 attr->values[0].integer = priority; 2441 else 2442 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority", 2443 priority); 2444 2445 cupsArrayAdd(ActiveJobs, job); 2446 2447 job->dirty = 1; 2448 cupsdMarkDirty(CUPSD_DIRTY_JOBS); 2449} 2450 2451 2452/* 2453 * 'cupsdSetJobState()' - Set the state of the specified print job. 2454 */ 2455 2456void 2457cupsdSetJobState( 2458 cupsd_job_t *job, /* I - Job to cancel */ 2459 ipp_jstate_t newstate, /* I - New job state */ 2460 cupsd_jobaction_t action, /* I - Action to take */ 2461 const char *message, /* I - Message to log */ 2462 ...) /* I - Additional arguments as needed */ 2463{ 2464 int i; /* Looping var */ 2465 ipp_jstate_t oldstate; /* Old state */ 2466 char filename[1024]; /* Job filename */ 2467 ipp_attribute_t *attr; /* Job attribute */ 2468 2469 2470 cupsdLogMessage(CUPSD_LOG_DEBUG2, 2471 "cupsdSetJobState(job=%p(%d), state=%d, newstate=%d, " 2472 "action=%d, message=\"%s\")", job, job->id, job->state_value, 2473 newstate, action, message ? message : "(null)"); 2474 2475 2476 /* 2477 * Make sure we have the job attributes... 2478 */ 2479 2480 if (!cupsdLoadJob(job)) 2481 return; 2482 2483 /* 2484 * Don't do anything if the state is unchanged and we aren't purging the 2485 * job... 2486 */ 2487 2488 oldstate = job->state_value; 2489 if (newstate == oldstate && action != CUPSD_JOB_PURGE) 2490 return; 2491 2492 /* 2493 * Stop any processes that are working on the current job... 2494 */ 2495 2496 if (oldstate == IPP_JOB_PROCESSING) 2497 stop_job(job, action); 2498 2499 /* 2500 * Set the new job state... 2501 */ 2502 2503 job->state_value = newstate; 2504 2505 if (job->state) 2506 job->state->values[0].integer = newstate; 2507 2508 switch (newstate) 2509 { 2510 case IPP_JOB_PENDING : 2511 /* 2512 * Update job-hold-until as needed... 2513 */ 2514 2515 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", 2516 IPP_TAG_KEYWORD)) == NULL) 2517 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME); 2518 2519 if (attr) 2520 { 2521 attr->value_tag = IPP_TAG_KEYWORD; 2522 cupsdSetString(&(attr->values[0].string.text), "no-hold"); 2523 } 2524 2525 default : 2526 break; 2527 2528 case IPP_JOB_ABORTED : 2529 case IPP_JOB_CANCELED : 2530 case IPP_JOB_COMPLETED : 2531 set_time(job, "time-at-completed"); 2532 ippSetString(job->attrs, &job->reasons, 0, "processing-to-stop-point"); 2533 break; 2534 } 2535 2536 /* 2537 * Log message as needed... 2538 */ 2539 2540 if (message) 2541 { 2542 char buffer[2048]; /* Message buffer */ 2543 va_list ap; /* Pointer to additional arguments */ 2544 2545 va_start(ap, message); 2546 vsnprintf(buffer, sizeof(buffer), message, ap); 2547 va_end(ap); 2548 2549 if (newstate > IPP_JOB_STOPPED) 2550 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, "%s", buffer); 2551 else 2552 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "%s", buffer); 2553 2554 if (newstate == IPP_JOB_STOPPED || newstate == IPP_JOB_ABORTED) 2555 cupsdLogJob(job, CUPSD_LOG_ERROR, "%s", buffer); 2556 else 2557 cupsdLogJob(job, CUPSD_LOG_INFO, "%s", buffer); 2558 } 2559 2560 /* 2561 * Handle post-state-change actions... 2562 */ 2563 2564 switch (newstate) 2565 { 2566 case IPP_JOB_PROCESSING : 2567 /* 2568 * Add the job to the "printing" list... 2569 */ 2570 2571 if (!cupsArrayFind(PrintingJobs, job)) 2572 cupsArrayAdd(PrintingJobs, job); 2573 2574 /* 2575 * Set the processing time... 2576 */ 2577 2578 set_time(job, "time-at-processing"); 2579 2580 case IPP_JOB_PENDING : 2581 case IPP_JOB_HELD : 2582 case IPP_JOB_STOPPED : 2583 /* 2584 * Make sure the job is in the active list... 2585 */ 2586 2587 if (!cupsArrayFind(ActiveJobs, job)) 2588 cupsArrayAdd(ActiveJobs, job); 2589 2590 /* 2591 * Save the job state to disk... 2592 */ 2593 2594 job->dirty = 1; 2595 cupsdMarkDirty(CUPSD_DIRTY_JOBS); 2596 break; 2597 2598 case IPP_JOB_ABORTED : 2599 case IPP_JOB_CANCELED : 2600 case IPP_JOB_COMPLETED : 2601 if (newstate == IPP_JOB_CANCELED) 2602 { 2603 /* 2604 * Remove the job from the active list if there are no processes still 2605 * running for it... 2606 */ 2607 2608 for (i = 0; job->filters[i] < 0; i++); 2609 2610 if (!job->filters[i] && job->backend <= 0) 2611 cupsArrayRemove(ActiveJobs, job); 2612 } 2613 else 2614 { 2615 /* 2616 * Otherwise just remove the job from the active list immediately... 2617 */ 2618 2619 cupsArrayRemove(ActiveJobs, job); 2620 } 2621 2622 /* 2623 * Expire job subscriptions since the job is now "completed"... 2624 */ 2625 2626 cupsdExpireSubscriptions(NULL, job); 2627 2628#ifdef __APPLE__ 2629 /* 2630 * If we are going to sleep and the PrintingJobs count is now 0, allow the 2631 * sleep to happen immediately... 2632 */ 2633 2634 if (Sleeping && cupsArrayCount(PrintingJobs) == 0) 2635 cupsdAllowSleep(); 2636#endif /* __APPLE__ */ 2637 2638 /* 2639 * Remove any authentication data... 2640 */ 2641 2642 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id); 2643 if (cupsdRemoveFile(filename) && errno != ENOENT) 2644 cupsdLogMessage(CUPSD_LOG_ERROR, 2645 "Unable to remove authentication cache: %s", 2646 strerror(errno)); 2647 2648 for (i = 0; 2649 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0])); 2650 i ++) 2651 cupsdClearString(job->auth_env + i); 2652 2653 cupsdClearString(&job->auth_uid); 2654 2655 /* 2656 * Remove the print file for good if we aren't preserving jobs or 2657 * files... 2658 */ 2659 2660 if (!JobHistory || !JobFiles || action == CUPSD_JOB_PURGE) 2661 remove_job_files(job); 2662 2663 if (JobHistory && action != CUPSD_JOB_PURGE) 2664 { 2665 /* 2666 * Save job state info... 2667 */ 2668 2669 job->dirty = 1; 2670 cupsdMarkDirty(CUPSD_DIRTY_JOBS); 2671 } 2672 else if (!job->printer) 2673 { 2674 /* 2675 * Delete the job immediately if not actively printing... 2676 */ 2677 2678 cupsdDeleteJob(job, CUPSD_JOB_PURGE); 2679 job = NULL; 2680 } 2681 break; 2682 } 2683 2684 /* 2685 * Finalize the job immediately if we forced things... 2686 */ 2687 2688 if (action >= CUPSD_JOB_FORCE && job && job->printer) 2689 finalize_job(job, 0); 2690 2691 /* 2692 * Update the server "busy" state... 2693 */ 2694 2695 cupsdSetBusyState(); 2696} 2697 2698 2699/* 2700 * 'cupsdStopAllJobs()' - Stop all print jobs. 2701 */ 2702 2703void 2704cupsdStopAllJobs( 2705 cupsd_jobaction_t action, /* I - Action */ 2706 int kill_delay) /* I - Number of seconds before we kill */ 2707{ 2708 cupsd_job_t *job; /* Current job */ 2709 2710 2711 DEBUG_puts("cupsdStopAllJobs()"); 2712 2713 for (job = (cupsd_job_t *)cupsArrayFirst(PrintingJobs); 2714 job; 2715 job = (cupsd_job_t *)cupsArrayNext(PrintingJobs)) 2716 { 2717 if (job->completed) 2718 { 2719 cupsdSetJobState(job, IPP_JOB_COMPLETED, CUPSD_JOB_FORCE, NULL); 2720 } 2721 else 2722 { 2723 if (kill_delay) 2724 job->kill_time = time(NULL) + kill_delay; 2725 2726 cupsdSetJobState(job, IPP_JOB_PENDING, action, NULL); 2727 } 2728 } 2729} 2730 2731 2732/* 2733 * 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory. 2734 */ 2735 2736void 2737cupsdUnloadCompletedJobs(void) 2738{ 2739 cupsd_job_t *job; /* Current job */ 2740 time_t expire; /* Expiration time */ 2741 2742 2743 expire = time(NULL) - 60; 2744 2745 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); 2746 job; 2747 job = (cupsd_job_t *)cupsArrayNext(Jobs)) 2748 if (job->attrs && job->state_value >= IPP_JOB_STOPPED && !job->printer && 2749 job->access_time < expire) 2750 { 2751 if (job->dirty) 2752 cupsdSaveJob(job); 2753 2754 unload_job(job); 2755 } 2756} 2757 2758 2759/* 2760 * 'cupsdUpdateJobs()' - Update the history/file files for all jobs. 2761 */ 2762 2763void 2764cupsdUpdateJobs(void) 2765{ 2766 cupsd_job_t *job; /* Current job */ 2767 time_t curtime; /* Current time */ 2768 ipp_attribute_t *attr; /* time-at-completed attribute */ 2769 2770 2771 curtime = time(NULL); 2772 JobHistoryUpdate = 0; 2773 2774 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); 2775 job; 2776 job = (cupsd_job_t *)cupsArrayNext(Jobs)) 2777 { 2778 if (job->state_value >= IPP_JOB_CANCELED && 2779 (attr = ippFindAttribute(job->attrs, "time-at-completed", 2780 IPP_TAG_INTEGER)) != NULL) 2781 { 2782 /* 2783 * Update history/file expiration times... 2784 */ 2785 2786 if (JobHistory < INT_MAX) 2787 job->history_time = attr->values[0].integer + JobHistory; 2788 else 2789 job->history_time = INT_MAX; 2790 2791 if (job->history_time < curtime) 2792 { 2793 cupsdDeleteJob(job, CUPSD_JOB_PURGE); 2794 continue; 2795 } 2796 2797 if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate) 2798 JobHistoryUpdate = job->history_time; 2799 2800 if (JobFiles < INT_MAX) 2801 job->file_time = attr->values[0].integer + JobFiles; 2802 else 2803 job->file_time = INT_MAX; 2804 2805 if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate) 2806 JobHistoryUpdate = job->file_time; 2807 } 2808 } 2809 2810 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdUpdateAllJobs: JobHistoryUpdate=%ld", 2811 (long)JobHistoryUpdate); 2812} 2813 2814 2815/* 2816 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs. 2817 */ 2818 2819static int /* O - Difference */ 2820compare_active_jobs(void *first, /* I - First job */ 2821 void *second, /* I - Second job */ 2822 void *data) /* I - App data (not used) */ 2823{ 2824 int diff; /* Difference */ 2825 2826 2827 (void)data; 2828 2829 if ((diff = ((cupsd_job_t *)second)->priority - 2830 ((cupsd_job_t *)first)->priority) != 0) 2831 return (diff); 2832 else 2833 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id); 2834} 2835 2836 2837/* 2838 * 'compare_completed_jobs()' - Compare the job IDs and completion times of two jobs. 2839 */ 2840 2841static int /* O - Difference */ 2842compare_completed_jobs(void *first, /* I - First job */ 2843 void *second, /* I - Second job */ 2844 void *data) /* I - App data (not used) */ 2845{ 2846 int diff; /* Difference */ 2847 2848 2849 (void)data; 2850 2851 if ((diff = ((cupsd_job_t *)second)->completed_time - 2852 ((cupsd_job_t *)first)->completed_time) != 0) 2853 return (diff); 2854 else 2855 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id); 2856} 2857 2858 2859/* 2860 * 'compare_jobs()' - Compare the job IDs of two jobs. 2861 */ 2862 2863static int /* O - Difference */ 2864compare_jobs(void *first, /* I - First job */ 2865 void *second, /* I - Second job */ 2866 void *data) /* I - App data (not used) */ 2867{ 2868 (void)data; 2869 2870 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id); 2871} 2872 2873 2874/* 2875 * 'dump_job_history()' - Dump any debug messages for a job. 2876 */ 2877 2878static void 2879dump_job_history(cupsd_job_t *job) /* I - Job */ 2880{ 2881 int i, /* Looping var */ 2882 oldsize; /* Current MaxLogSize */ 2883 struct tm *date; /* Date/time value */ 2884 cupsd_joblog_t *message; /* Current message */ 2885 char temp[2048], /* Log message */ 2886 *ptr, /* Pointer into log message */ 2887 start[256], /* Start time */ 2888 end[256]; /* End time */ 2889 cupsd_printer_t *printer; /* Printer for job */ 2890 2891 2892 /* 2893 * See if we have anything to dump... 2894 */ 2895 2896 if (!job->history) 2897 return; 2898 2899 /* 2900 * Disable log rotation temporarily... 2901 */ 2902 2903 oldsize = MaxLogSize; 2904 MaxLogSize = 0; 2905 2906 /* 2907 * Copy the debug messages to the log... 2908 */ 2909 2910 message = (cupsd_joblog_t *)cupsArrayFirst(job->history); 2911 date = localtime(&(message->time)); 2912 strftime(start, sizeof(start), "%X", date); 2913 2914 message = (cupsd_joblog_t *)cupsArrayLast(job->history); 2915 date = localtime(&(message->time)); 2916 strftime(end, sizeof(end), "%X", date); 2917 2918 snprintf(temp, sizeof(temp), 2919 "[Job %d] The following messages were recorded from %s to %s", 2920 job->id, start, end); 2921 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp); 2922 2923 for (message = (cupsd_joblog_t *)cupsArrayFirst(job->history); 2924 message; 2925 message = (cupsd_joblog_t *)cupsArrayNext(job->history)) 2926 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, message->message); 2927 2928 snprintf(temp, sizeof(temp), "[Job %d] End of messages", job->id); 2929 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp); 2930 2931 /* 2932 * Log the printer state values... 2933 */ 2934 2935 if ((printer = job->printer) == NULL) 2936 printer = cupsdFindDest(job->dest); 2937 2938 if (printer) 2939 { 2940 snprintf(temp, sizeof(temp), "[Job %d] printer-state=%d(%s)", job->id, 2941 printer->state, 2942 printer->state == IPP_PRINTER_IDLE ? "idle" : 2943 printer->state == IPP_PRINTER_PROCESSING ? "processing" : 2944 "stopped"); 2945 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp); 2946 2947 snprintf(temp, sizeof(temp), "[Job %d] printer-state-message=\"%s\"", 2948 job->id, printer->state_message); 2949 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp); 2950 2951 snprintf(temp, sizeof(temp), "[Job %d] printer-state-reasons=", job->id); 2952 ptr = temp + strlen(temp); 2953 if (printer->num_reasons == 0) 2954 strlcpy(ptr, "none", sizeof(temp) - (size_t)(ptr - temp)); 2955 else 2956 { 2957 for (i = 0; 2958 i < printer->num_reasons && ptr < (temp + sizeof(temp) - 2); 2959 i ++) 2960 { 2961 if (i) 2962 *ptr++ = ','; 2963 2964 strlcpy(ptr, printer->reasons[i], sizeof(temp) - (size_t)(ptr - temp)); 2965 ptr += strlen(ptr); 2966 } 2967 } 2968 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp); 2969 } 2970 2971 /* 2972 * Restore log file rotation... 2973 */ 2974 2975 MaxLogSize = oldsize; 2976 2977 /* 2978 * Free all messages... 2979 */ 2980 2981 free_job_history(job); 2982} 2983 2984 2985/* 2986 * 'free_job_history()' - Free any log history. 2987 */ 2988 2989static void 2990free_job_history(cupsd_job_t *job) /* I - Job */ 2991{ 2992 char *message; /* Current message */ 2993 2994 2995 if (!job->history) 2996 return; 2997 2998 for (message = (char *)cupsArrayFirst(job->history); 2999 message; 3000 message = (char *)cupsArrayNext(job->history)) 3001 free(message); 3002 3003 cupsArrayDelete(job->history); 3004 job->history = NULL; 3005} 3006 3007 3008/* 3009 * 'finalize_job()' - Cleanup after job filter processes and support data. 3010 */ 3011 3012static void 3013finalize_job(cupsd_job_t *job, /* I - Job */ 3014 int set_job_state) /* I - 1 = set the job state */ 3015{ 3016 ipp_pstate_t printer_state; /* New printer state value */ 3017 ipp_jstate_t job_state; /* New job state value */ 3018 const char *message; /* Message for job state */ 3019 char buffer[1024]; /* Buffer for formatted messages */ 3020 3021 3022 cupsdLogMessage(CUPSD_LOG_DEBUG2, "finalize_job(job=%p(%d))", job, job->id); 3023 3024 /* 3025 * Clear the "connecting-to-device" and "cups-waiting-for-job-completed" 3026 * reasons, which are only valid when a printer is processing, along with any 3027 * remote printing job state... 3028 */ 3029 3030 cupsdSetPrinterReasons(job->printer, "-connecting-to-device," 3031 "cups-waiting-for-job-completed," 3032 "cups-remote-pending," 3033 "cups-remote-pending-held," 3034 "cups-remote-processing," 3035 "cups-remote-stopped," 3036 "cups-remote-canceled," 3037 "cups-remote-aborted," 3038 "cups-remote-completed"); 3039 3040 /* 3041 * Similarly, clear the "offline-report" reason for non-USB devices since we 3042 * rarely have current information for network devices... 3043 */ 3044 3045 if (strncmp(job->printer->device_uri, "usb:", 4) && 3046 strncmp(job->printer->device_uri, "ippusb:", 7)) 3047 cupsdSetPrinterReasons(job->printer, "-offline-report"); 3048 3049 /* 3050 * Free the security profile... 3051 */ 3052 3053 cupsdDestroyProfile(job->profile); 3054 job->profile = NULL; 3055 cupsdDestroyProfile(job->bprofile); 3056 job->bprofile = NULL; 3057 3058 /* 3059 * Clear the unresponsive job watchdog timers... 3060 */ 3061 3062 job->cancel_time = 0; 3063 job->kill_time = 0; 3064 3065 /* 3066 * Close pipes and status buffer... 3067 */ 3068 3069 cupsdClosePipe(job->print_pipes); 3070 cupsdClosePipe(job->back_pipes); 3071 cupsdClosePipe(job->side_pipes); 3072 3073 cupsdRemoveSelect(job->status_pipes[0]); 3074 cupsdClosePipe(job->status_pipes); 3075 cupsdStatBufDelete(job->status_buffer); 3076 job->status_buffer = NULL; 3077 3078 /* 3079 * Process the exit status... 3080 */ 3081 3082 if (job->printer->state == IPP_PRINTER_PROCESSING) 3083 printer_state = IPP_PRINTER_IDLE; 3084 else 3085 printer_state = job->printer->state; 3086 3087 switch (job_state = job->state_value) 3088 { 3089 case IPP_JOB_PENDING : 3090 message = "Job paused."; 3091 break; 3092 3093 case IPP_JOB_HELD : 3094 message = "Job held."; 3095 break; 3096 3097 default : 3098 case IPP_JOB_PROCESSING : 3099 case IPP_JOB_COMPLETED : 3100 job_state = IPP_JOB_COMPLETED; 3101 message = "Job completed."; 3102 3103 if (!job->status) 3104 ippSetString(job->attrs, &job->reasons, 0, 3105 "job-completed-successfully"); 3106 break; 3107 3108 case IPP_JOB_STOPPED : 3109 message = "Job stopped."; 3110 3111 ippSetString(job->attrs, &job->reasons, 0, "job-stopped"); 3112 break; 3113 3114 case IPP_JOB_CANCELED : 3115 message = "Job canceled."; 3116 3117 ippSetString(job->attrs, &job->reasons, 0, "job-canceled-by-user"); 3118 break; 3119 3120 case IPP_JOB_ABORTED : 3121 message = "Job aborted."; 3122 break; 3123 } 3124 3125 if (job->status < 0) 3126 { 3127 /* 3128 * Backend had errors... 3129 */ 3130 3131 int exit_code; /* Exit code from backend */ 3132 3133 /* 3134 * Convert the status to an exit code. Due to the way the W* macros are 3135 * implemented on MacOS X (bug?), we have to store the exit status in a 3136 * variable first and then convert... 3137 */ 3138 3139 exit_code = -job->status; 3140 if (WIFEXITED(exit_code)) 3141 exit_code = WEXITSTATUS(exit_code); 3142 else 3143 { 3144 ippSetString(job->attrs, &job->reasons, 0, "cups-backend-crashed"); 3145 exit_code = job->status; 3146 } 3147 3148 cupsdLogJob(job, CUPSD_LOG_INFO, "Backend returned status %d (%s)", 3149 exit_code, 3150 exit_code == CUPS_BACKEND_FAILED ? "failed" : 3151 exit_code == CUPS_BACKEND_AUTH_REQUIRED ? 3152 "authentication required" : 3153 exit_code == CUPS_BACKEND_HOLD ? "hold job" : 3154 exit_code == CUPS_BACKEND_STOP ? "stop printer" : 3155 exit_code == CUPS_BACKEND_CANCEL ? "cancel job" : 3156 exit_code == CUPS_BACKEND_RETRY ? "retry job later" : 3157 exit_code == CUPS_BACKEND_RETRY_CURRENT ? "retry job immediately" : 3158 exit_code < 0 ? "crashed" : "unknown"); 3159 3160 /* 3161 * Do what needs to be done... 3162 */ 3163 3164 switch (exit_code) 3165 { 3166 default : 3167 case CUPS_BACKEND_FAILED : 3168 /* 3169 * Backend failure, use the error-policy to determine how to 3170 * act... 3171 */ 3172 3173 if (job->dtype & CUPS_PRINTER_CLASS) 3174 { 3175 /* 3176 * Queued on a class - mark the job as pending and we'll retry on 3177 * another printer... 3178 */ 3179 3180 if (job_state == IPP_JOB_COMPLETED) 3181 { 3182 job_state = IPP_JOB_PENDING; 3183 message = "Retrying job on another printer."; 3184 3185 ippSetString(job->attrs, &job->reasons, 0, 3186 "resources-are-not-ready"); 3187 } 3188 } 3189 else if (!strcmp(job->printer->error_policy, "retry-current-job")) 3190 { 3191 /* 3192 * The error policy is "retry-current-job" - mark the job as pending 3193 * and we'll retry on the same printer... 3194 */ 3195 3196 if (job_state == IPP_JOB_COMPLETED) 3197 { 3198 job_state = IPP_JOB_PENDING; 3199 message = "Retrying job on same printer."; 3200 3201 ippSetString(job->attrs, &job->reasons, 0, "none"); 3202 } 3203 } 3204 else if ((job->printer->type & CUPS_PRINTER_FAX) || 3205 !strcmp(job->printer->error_policy, "retry-job")) 3206 { 3207 if (job_state == IPP_JOB_COMPLETED) 3208 { 3209 /* 3210 * The job was queued on a fax or the error policy is "retry-job" - 3211 * hold the job if the number of retries is less than the 3212 * JobRetryLimit, otherwise abort the job. 3213 */ 3214 3215 job->tries ++; 3216 3217 if (job->tries > JobRetryLimit && JobRetryLimit > 0) 3218 { 3219 /* 3220 * Too many tries... 3221 */ 3222 3223 snprintf(buffer, sizeof(buffer), 3224 "Job aborted after %d unsuccessful attempts.", 3225 JobRetryLimit); 3226 job_state = IPP_JOB_ABORTED; 3227 message = buffer; 3228 3229 ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system"); 3230 } 3231 else 3232 { 3233 /* 3234 * Try again in N seconds... 3235 */ 3236 3237 snprintf(buffer, sizeof(buffer), 3238 "Job held for %d seconds since it could not be sent.", 3239 JobRetryInterval); 3240 3241 job->hold_until = time(NULL) + JobRetryInterval; 3242 job_state = IPP_JOB_HELD; 3243 message = buffer; 3244 3245 ippSetString(job->attrs, &job->reasons, 0, 3246 "resources-are-not-ready"); 3247 } 3248 } 3249 } 3250 else if (!strcmp(job->printer->error_policy, "abort-job") && 3251 job_state == IPP_JOB_COMPLETED) 3252 { 3253 job_state = IPP_JOB_ABORTED; 3254 message = "Job aborted due to backend errors; please consult " 3255 "the error_log file for details."; 3256 3257 ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system"); 3258 } 3259 else if (job->state_value == IPP_JOB_PROCESSING) 3260 { 3261 job_state = IPP_JOB_PENDING; 3262 printer_state = IPP_PRINTER_STOPPED; 3263 message = "Printer stopped due to backend errors; please " 3264 "consult the error_log file for details."; 3265 3266 ippSetString(job->attrs, &job->reasons, 0, "none"); 3267 } 3268 break; 3269 3270 case CUPS_BACKEND_CANCEL : 3271 /* 3272 * Cancel the job... 3273 */ 3274 3275 if (job_state == IPP_JOB_COMPLETED) 3276 { 3277 job_state = IPP_JOB_CANCELED; 3278 message = "Job canceled at printer."; 3279 3280 ippSetString(job->attrs, &job->reasons, 0, "canceled-at-device"); 3281 } 3282 break; 3283 3284 case CUPS_BACKEND_HOLD : 3285 if (job_state == IPP_JOB_COMPLETED) 3286 { 3287 /* 3288 * Hold the job... 3289 */ 3290 3291 const char *reason = ippGetString(job->reasons, 0, NULL); 3292 3293 cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-state-reasons=\"%s\"", 3294 reason); 3295 3296 if (!reason || strncmp(reason, "account-", 8)) 3297 { 3298 cupsdSetJobHoldUntil(job, "indefinite", 1); 3299 3300 ippSetString(job->attrs, &job->reasons, 0, 3301 "job-hold-until-specified"); 3302 message = "Job held indefinitely due to backend errors; please " 3303 "consult the error_log file for details."; 3304 } 3305 else if (!strcmp(reason, "account-info-needed")) 3306 { 3307 cupsdSetJobHoldUntil(job, "indefinite", 0); 3308 3309 message = "Job held indefinitely - account information is " 3310 "required."; 3311 } 3312 else if (!strcmp(reason, "account-closed")) 3313 { 3314 cupsdSetJobHoldUntil(job, "indefinite", 0); 3315 3316 message = "Job held indefinitely - account has been closed."; 3317 } 3318 else if (!strcmp(reason, "account-limit-reached")) 3319 { 3320 cupsdSetJobHoldUntil(job, "indefinite", 0); 3321 3322 message = "Job held indefinitely - account limit has been " 3323 "reached."; 3324 } 3325 else 3326 { 3327 cupsdSetJobHoldUntil(job, "indefinite", 0); 3328 3329 message = "Job held indefinitely - account authorization failed."; 3330 } 3331 3332 job_state = IPP_JOB_HELD; 3333 } 3334 break; 3335 3336 case CUPS_BACKEND_STOP : 3337 /* 3338 * Stop the printer... 3339 */ 3340 3341 printer_state = IPP_PRINTER_STOPPED; 3342 message = "Printer stopped due to backend errors; please " 3343 "consult the error_log file for details."; 3344 3345 if (job_state == IPP_JOB_COMPLETED) 3346 { 3347 job_state = IPP_JOB_PENDING; 3348 3349 ippSetString(job->attrs, &job->reasons, 0, 3350 "resources-are-not-ready"); 3351 } 3352 break; 3353 3354 case CUPS_BACKEND_AUTH_REQUIRED : 3355 /* 3356 * Hold the job for authentication... 3357 */ 3358 3359 if (job_state == IPP_JOB_COMPLETED) 3360 { 3361 cupsdSetJobHoldUntil(job, "auth-info-required", 1); 3362 3363 job_state = IPP_JOB_HELD; 3364 message = "Job held for authentication."; 3365 3366 if (strncmp(job->reasons->values[0].string.text, "account-", 8)) 3367 ippSetString(job->attrs, &job->reasons, 0, 3368 "cups-held-for-authentication"); 3369 } 3370 break; 3371 3372 case CUPS_BACKEND_RETRY : 3373 if (job_state == IPP_JOB_COMPLETED) 3374 { 3375 /* 3376 * Hold the job if the number of retries is less than the 3377 * JobRetryLimit, otherwise abort the job. 3378 */ 3379 3380 job->tries ++; 3381 3382 if (job->tries > JobRetryLimit && JobRetryLimit > 0) 3383 { 3384 /* 3385 * Too many tries... 3386 */ 3387 3388 snprintf(buffer, sizeof(buffer), 3389 "Job aborted after %d unsuccessful attempts.", 3390 JobRetryLimit); 3391 job_state = IPP_JOB_ABORTED; 3392 message = buffer; 3393 3394 ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system"); 3395 } 3396 else 3397 { 3398 /* 3399 * Try again in N seconds... 3400 */ 3401 3402 snprintf(buffer, sizeof(buffer), 3403 "Job held for %d seconds since it could not be sent.", 3404 JobRetryInterval); 3405 3406 job->hold_until = time(NULL) + JobRetryInterval; 3407 job_state = IPP_JOB_HELD; 3408 message = buffer; 3409 3410 ippSetString(job->attrs, &job->reasons, 0, 3411 "resources-are-not-ready"); 3412 } 3413 } 3414 break; 3415 3416 case CUPS_BACKEND_RETRY_CURRENT : 3417 /* 3418 * Mark the job as pending and retry on the same printer... 3419 */ 3420 3421 if (job_state == IPP_JOB_COMPLETED) 3422 { 3423 job_state = IPP_JOB_PENDING; 3424 message = "Retrying job on same printer."; 3425 3426 ippSetString(job->attrs, &job->reasons, 0, "none"); 3427 } 3428 break; 3429 } 3430 } 3431 else if (job->status > 0) 3432 { 3433 /* 3434 * Filter had errors; stop job... 3435 */ 3436 3437 if (job_state == IPP_JOB_COMPLETED) 3438 { 3439 job_state = IPP_JOB_STOPPED; 3440 message = "Job stopped due to filter errors; please consult the " 3441 "error_log file for details."; 3442 3443 if (WIFSIGNALED(job->status)) 3444 ippSetString(job->attrs, &job->reasons, 0, "cups-filter-crashed"); 3445 else 3446 ippSetString(job->attrs, &job->reasons, 0, "job-completed-with-errors"); 3447 } 3448 } 3449 3450 /* 3451 * Update the printer and job state. 3452 */ 3453 3454 if (set_job_state && job_state != job->state_value) 3455 cupsdSetJobState(job, job_state, CUPSD_JOB_DEFAULT, "%s", message); 3456 3457 cupsdSetPrinterState(job->printer, printer_state, 3458 printer_state == IPP_PRINTER_STOPPED); 3459 update_job_attrs(job, 0); 3460 3461 if (job->history) 3462 { 3463 if (job->status && 3464 (job->state_value == IPP_JOB_ABORTED || 3465 job->state_value == IPP_JOB_STOPPED)) 3466 dump_job_history(job); 3467 else 3468 free_job_history(job); 3469 } 3470 3471 cupsArrayRemove(PrintingJobs, job); 3472 3473 /* 3474 * Apply any PPD updates... 3475 */ 3476 3477 if (job->num_keywords) 3478 { 3479 if (cupsdUpdatePrinterPPD(job->printer, job->num_keywords, job->keywords)) 3480 cupsdSetPrinterAttrs(job->printer); 3481 3482 cupsFreeOptions(job->num_keywords, job->keywords); 3483 3484 job->num_keywords = 0; 3485 job->keywords = NULL; 3486 } 3487 3488 /* 3489 * Clear the printer <-> job association... 3490 */ 3491 3492 job->printer->job = NULL; 3493 job->printer = NULL; 3494 3495 /* 3496 * Try printing another job... 3497 */ 3498 3499 if (printer_state != IPP_PRINTER_STOPPED) 3500 cupsdCheckJobs(); 3501} 3502 3503 3504/* 3505 * 'get_options()' - Get a string containing the job options. 3506 */ 3507 3508static char * /* O - Options string */ 3509get_options(cupsd_job_t *job, /* I - Job */ 3510 int banner_page, /* I - Printing a banner page? */ 3511 char *copies, /* I - Copies buffer */ 3512 size_t copies_size, /* I - Size of copies buffer */ 3513 char *title, /* I - Title buffer */ 3514 size_t title_size) /* I - Size of title buffer */ 3515{ 3516 int i; /* Looping var */ 3517 size_t newlength; /* New option buffer length */ 3518 char *optptr, /* Pointer to options */ 3519 *valptr; /* Pointer in value string */ 3520 ipp_attribute_t *attr; /* Current attribute */ 3521 _ppd_cache_t *pc; /* PPD cache and mapping data */ 3522 int num_pwgppds; /* Number of PWG->PPD options */ 3523 cups_option_t *pwgppds, /* PWG->PPD options */ 3524 *pwgppd, /* Current PWG->PPD option */ 3525 *preset; /* Current preset option */ 3526 int print_color_mode, 3527 /* Output mode (if any) */ 3528 print_quality; /* Print quality (if any) */ 3529 const char *ppd; /* PPD option choice */ 3530 int exact; /* Did we get an exact match? */ 3531 static char *options = NULL;/* Full list of options */ 3532 static size_t optlength = 0; /* Length of option buffer */ 3533 3534 3535 /* 3536 * Building the options string is harder than it needs to be, but for the 3537 * moment we need to pass strings for command-line args and not IPP attribute 3538 * pointers... :) 3539 * 3540 * First build an options array for any PWG->PPD mapped option/choice pairs. 3541 */ 3542 3543 pc = job->printer->pc; 3544 num_pwgppds = 0; 3545 pwgppds = NULL; 3546 3547 if (pc && 3548 !ippFindAttribute(job->attrs, 3549 "com.apple.print.DocumentTicket.PMSpoolFormat", 3550 IPP_TAG_ZERO) && 3551 !ippFindAttribute(job->attrs, "APPrinterPreset", IPP_TAG_ZERO) && 3552 (ippFindAttribute(job->attrs, "print-color-mode", IPP_TAG_ZERO) || 3553 ippFindAttribute(job->attrs, "print-quality", IPP_TAG_ZERO))) 3554 { 3555 /* 3556 * Map print-color-mode and print-quality to a preset... 3557 */ 3558 3559 if ((attr = ippFindAttribute(job->attrs, "print-color-mode", 3560 IPP_TAG_KEYWORD)) != NULL && 3561 !strcmp(attr->values[0].string.text, "monochrome")) 3562 print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME; 3563 else 3564 print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR; 3565 3566 if ((attr = ippFindAttribute(job->attrs, "print-quality", 3567 IPP_TAG_ENUM)) != NULL && 3568 attr->values[0].integer >= IPP_QUALITY_DRAFT && 3569 attr->values[0].integer <= IPP_QUALITY_HIGH) 3570 print_quality = attr->values[0].integer - IPP_QUALITY_DRAFT; 3571 else 3572 print_quality = _PWG_PRINT_QUALITY_NORMAL; 3573 3574 if (pc->num_presets[print_color_mode][print_quality] == 0) 3575 { 3576 /* 3577 * Try to find a preset that works so that we maximize the chances of us 3578 * getting a good print using IPP attributes. 3579 */ 3580 3581 if (pc->num_presets[print_color_mode][_PWG_PRINT_QUALITY_NORMAL] > 0) 3582 print_quality = _PWG_PRINT_QUALITY_NORMAL; 3583 else if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][print_quality] > 0) 3584 print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR; 3585 else 3586 { 3587 print_quality = _PWG_PRINT_QUALITY_NORMAL; 3588 print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR; 3589 } 3590 } 3591 3592 if (pc->num_presets[print_color_mode][print_quality] > 0) 3593 { 3594 /* 3595 * Copy the preset options as long as the corresponding names are not 3596 * already defined in the IPP request... 3597 */ 3598 3599 for (i = pc->num_presets[print_color_mode][print_quality], 3600 preset = pc->presets[print_color_mode][print_quality]; 3601 i > 0; 3602 i --, preset ++) 3603 { 3604 if (!ippFindAttribute(job->attrs, preset->name, IPP_TAG_ZERO)) 3605 num_pwgppds = cupsAddOption(preset->name, preset->value, num_pwgppds, 3606 &pwgppds); 3607 } 3608 } 3609 } 3610 3611 if (pc) 3612 { 3613 if (!ippFindAttribute(job->attrs, "InputSlot", IPP_TAG_ZERO) && 3614 !ippFindAttribute(job->attrs, "HPPaperSource", IPP_TAG_ZERO)) 3615 { 3616 if ((ppd = _ppdCacheGetInputSlot(pc, job->attrs, NULL)) != NULL) 3617 num_pwgppds = cupsAddOption(pc->source_option, ppd, num_pwgppds, 3618 &pwgppds); 3619 } 3620 if (!ippFindAttribute(job->attrs, "MediaType", IPP_TAG_ZERO) && 3621 (ppd = _ppdCacheGetMediaType(pc, job->attrs, NULL)) != NULL) 3622 num_pwgppds = cupsAddOption("MediaType", ppd, num_pwgppds, &pwgppds); 3623 3624 if (!ippFindAttribute(job->attrs, "PageRegion", IPP_TAG_ZERO) && 3625 !ippFindAttribute(job->attrs, "PageSize", IPP_TAG_ZERO) && 3626 (ppd = _ppdCacheGetPageSize(pc, job->attrs, NULL, &exact)) != NULL) 3627 { 3628 num_pwgppds = cupsAddOption("PageSize", ppd, num_pwgppds, &pwgppds); 3629 3630 if (!ippFindAttribute(job->attrs, "media", IPP_TAG_ZERO)) 3631 num_pwgppds = cupsAddOption("media", ppd, num_pwgppds, &pwgppds); 3632 } 3633 3634 if (!ippFindAttribute(job->attrs, "OutputBin", IPP_TAG_ZERO) && 3635 (attr = ippFindAttribute(job->attrs, "output-bin", 3636 IPP_TAG_ZERO)) != NULL && 3637 (attr->value_tag == IPP_TAG_KEYWORD || 3638 attr->value_tag == IPP_TAG_NAME) && 3639 (ppd = _ppdCacheGetOutputBin(pc, attr->values[0].string.text)) != NULL) 3640 { 3641 /* 3642 * Map output-bin to OutputBin option... 3643 */ 3644 3645 num_pwgppds = cupsAddOption("OutputBin", ppd, num_pwgppds, &pwgppds); 3646 } 3647 3648 if (pc->sides_option && 3649 !ippFindAttribute(job->attrs, pc->sides_option, IPP_TAG_ZERO) && 3650 (attr = ippFindAttribute(job->attrs, "sides", IPP_TAG_KEYWORD)) != NULL) 3651 { 3652 /* 3653 * Map sides to duplex option... 3654 */ 3655 3656 if (!strcmp(attr->values[0].string.text, "one-sided")) 3657 num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_1sided, 3658 num_pwgppds, &pwgppds); 3659 else if (!strcmp(attr->values[0].string.text, "two-sided-long-edge")) 3660 num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_long, 3661 num_pwgppds, &pwgppds); 3662 else if (!strcmp(attr->values[0].string.text, "two-sided-short-edge")) 3663 num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_short, 3664 num_pwgppds, &pwgppds); 3665 } 3666 3667 /* 3668 * Map finishings values... 3669 */ 3670 3671 num_pwgppds = _ppdCacheGetFinishingOptions(pc, job->attrs, 3672 IPP_FINISHINGS_NONE, num_pwgppds, 3673 &pwgppds); 3674 } 3675 3676 /* 3677 * Figure out how much room we need... 3678 */ 3679 3680 newlength = ipp_length(job->attrs); 3681 3682 for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++) 3683 newlength += 1 + strlen(pwgppd->name) + 1 + strlen(pwgppd->value); 3684 3685 /* 3686 * Then allocate/reallocate the option buffer as needed... 3687 */ 3688 3689 if (newlength == 0) /* This can never happen, but Clang */ 3690 newlength = 1; /* thinks it can... */ 3691 3692 if (newlength > optlength || !options) 3693 { 3694 if (!options) 3695 optptr = malloc(newlength); 3696 else 3697 optptr = realloc(options, newlength); 3698 3699 if (!optptr) 3700 { 3701 cupsdLogJob(job, CUPSD_LOG_CRIT, 3702 "Unable to allocate " CUPS_LLFMT " bytes for option buffer.", 3703 CUPS_LLCAST newlength); 3704 return (NULL); 3705 } 3706 3707 options = optptr; 3708 optlength = newlength; 3709 } 3710 3711 /* 3712 * Now loop through the attributes and convert them to the textual 3713 * representation used by the filters... 3714 */ 3715 3716 optptr = options; 3717 *optptr = '\0'; 3718 3719 snprintf(title, title_size, "%s-%d", job->printer->name, job->id); 3720 strlcpy(copies, "1", copies_size); 3721 3722 for (attr = job->attrs->attrs; attr != NULL; attr = attr->next) 3723 { 3724 if (!strcmp(attr->name, "copies") && 3725 attr->value_tag == IPP_TAG_INTEGER) 3726 { 3727 /* 3728 * Don't use the # copies attribute if we are printing the job sheets... 3729 */ 3730 3731 if (!banner_page) 3732 snprintf(copies, copies_size, "%d", attr->values[0].integer); 3733 } 3734 else if (!strcmp(attr->name, "job-name") && 3735 (attr->value_tag == IPP_TAG_NAME || 3736 attr->value_tag == IPP_TAG_NAMELANG)) 3737 strlcpy(title, attr->values[0].string.text, title_size); 3738 else if (attr->group_tag == IPP_TAG_JOB) 3739 { 3740 /* 3741 * Filter out other unwanted attributes... 3742 */ 3743 3744 if (attr->value_tag == IPP_TAG_NOVALUE || 3745 attr->value_tag == IPP_TAG_MIMETYPE || 3746 attr->value_tag == IPP_TAG_NAMELANG || 3747 attr->value_tag == IPP_TAG_TEXTLANG || 3748 (attr->value_tag == IPP_TAG_URI && strcmp(attr->name, "job-uuid") && 3749 strcmp(attr->name, "job-authorization-uri")) || 3750 attr->value_tag == IPP_TAG_URISCHEME || 3751 attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */ 3752 continue; 3753 3754 if (!strcmp(attr->name, "job-hold-until") || 3755 !strcmp(attr->name, "job-id") || 3756 !strcmp(attr->name, "job-k-octets") || 3757 !strcmp(attr->name, "job-media-sheets") || 3758 !strcmp(attr->name, "job-media-sheets-completed") || 3759 !strcmp(attr->name, "job-state") || 3760 !strcmp(attr->name, "job-state-reasons")) 3761 continue; 3762 3763 if (!strncmp(attr->name, "job-", 4) && 3764 strcmp(attr->name, "job-account-id") && 3765 strcmp(attr->name, "job-accounting-user-id") && 3766 strcmp(attr->name, "job-authorization-uri") && 3767 strcmp(attr->name, "job-billing") && 3768 strcmp(attr->name, "job-impressions") && 3769 strcmp(attr->name, "job-originating-host-name") && 3770 strcmp(attr->name, "job-password") && 3771 strcmp(attr->name, "job-password-encryption") && 3772 strcmp(attr->name, "job-uuid") && 3773 !(job->printer->type & CUPS_PRINTER_REMOTE)) 3774 continue; 3775 3776 if ((!strcmp(attr->name, "job-impressions") || 3777 !strcmp(attr->name, "page-label") || 3778 !strcmp(attr->name, "page-border") || 3779 !strncmp(attr->name, "number-up", 9) || 3780 !strcmp(attr->name, "page-ranges") || 3781 !strcmp(attr->name, "page-set") || 3782 !_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") || 3783 !_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed") || 3784 !_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings." 3785 "PMTotalSidesImaged..n.") || 3786 !_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings." 3787 "PMTotalBeginPages..n.")) && 3788 banner_page) 3789 continue; 3790 3791 /* 3792 * Otherwise add them to the list... 3793 */ 3794 3795 if (optptr > options) 3796 strlcat(optptr, " ", optlength - (size_t)(optptr - options)); 3797 3798 if (attr->value_tag != IPP_TAG_BOOLEAN) 3799 { 3800 strlcat(optptr, attr->name, optlength - (size_t)(optptr - options)); 3801 strlcat(optptr, "=", optlength - (size_t)(optptr - options)); 3802 } 3803 3804 for (i = 0; i < attr->num_values; i ++) 3805 { 3806 if (i) 3807 strlcat(optptr, ",", optlength - (size_t)(optptr - options)); 3808 3809 optptr += strlen(optptr); 3810 3811 switch (attr->value_tag) 3812 { 3813 case IPP_TAG_INTEGER : 3814 case IPP_TAG_ENUM : 3815 snprintf(optptr, optlength - (size_t)(optptr - options), 3816 "%d", attr->values[i].integer); 3817 break; 3818 3819 case IPP_TAG_BOOLEAN : 3820 if (!attr->values[i].boolean) 3821 strlcat(optptr, "no", optlength - (size_t)(optptr - options)); 3822 3823 strlcat(optptr, attr->name, optlength - (size_t)(optptr - options)); 3824 break; 3825 3826 case IPP_TAG_RANGE : 3827 if (attr->values[i].range.lower == attr->values[i].range.upper) 3828 snprintf(optptr, optlength - (size_t)(optptr - options) - 1, 3829 "%d", attr->values[i].range.lower); 3830 else 3831 snprintf(optptr, optlength - (size_t)(optptr - options) - 1, 3832 "%d-%d", attr->values[i].range.lower, 3833 attr->values[i].range.upper); 3834 break; 3835 3836 case IPP_TAG_RESOLUTION : 3837 snprintf(optptr, optlength - (size_t)(optptr - options) - 1, 3838 "%dx%d%s", attr->values[i].resolution.xres, 3839 attr->values[i].resolution.yres, 3840 attr->values[i].resolution.units == IPP_RES_PER_INCH ? 3841 "dpi" : "dpcm"); 3842 break; 3843 3844 case IPP_TAG_STRING : 3845 case IPP_TAG_TEXT : 3846 case IPP_TAG_NAME : 3847 case IPP_TAG_KEYWORD : 3848 case IPP_TAG_CHARSET : 3849 case IPP_TAG_LANGUAGE : 3850 case IPP_TAG_URI : 3851 for (valptr = attr->values[i].string.text; *valptr;) 3852 { 3853 if (strchr(" \t\n\\\'\"", *valptr)) 3854 *optptr++ = '\\'; 3855 *optptr++ = *valptr++; 3856 } 3857 3858 *optptr = '\0'; 3859 break; 3860 3861 default : 3862 break; /* anti-compiler-warning-code */ 3863 } 3864 } 3865 3866 optptr += strlen(optptr); 3867 } 3868 } 3869 3870 /* 3871 * Finally loop through the PWG->PPD mapped options and add them... 3872 */ 3873 3874 for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++) 3875 { 3876 *optptr++ = ' '; 3877 strlcpy(optptr, pwgppd->name, optlength - (size_t)(optptr - options)); 3878 optptr += strlen(optptr); 3879 *optptr++ = '='; 3880 strlcpy(optptr, pwgppd->value, optlength - (size_t)(optptr - options)); 3881 optptr += strlen(optptr); 3882 } 3883 3884 cupsFreeOptions(num_pwgppds, pwgppds); 3885 3886 /* 3887 * Return the options string... 3888 */ 3889 3890 return (options); 3891} 3892 3893 3894/* 3895 * 'ipp_length()' - Compute the size of the buffer needed to hold 3896 * the textual IPP attributes. 3897 */ 3898 3899static size_t /* O - Size of attribute buffer */ 3900ipp_length(ipp_t *ipp) /* I - IPP request */ 3901{ 3902 size_t bytes; /* Number of bytes */ 3903 int i; /* Looping var */ 3904 ipp_attribute_t *attr; /* Current attribute */ 3905 3906 3907 /* 3908 * Loop through all attributes... 3909 */ 3910 3911 bytes = 0; 3912 3913 for (attr = ipp->attrs; attr != NULL; attr = attr->next) 3914 { 3915 /* 3916 * Skip attributes that won't be sent to filters... 3917 */ 3918 3919 if (attr->value_tag == IPP_TAG_NOVALUE || 3920 attr->value_tag == IPP_TAG_MIMETYPE || 3921 attr->value_tag == IPP_TAG_NAMELANG || 3922 attr->value_tag == IPP_TAG_TEXTLANG || 3923 attr->value_tag == IPP_TAG_URI || 3924 attr->value_tag == IPP_TAG_URISCHEME) 3925 continue; 3926 3927 /* 3928 * Add space for a leading space and commas between each value. 3929 * For the first attribute, the leading space isn't used, so the 3930 * extra byte can be used as the nul terminator... 3931 */ 3932 3933 bytes ++; /* " " separator */ 3934 bytes += (size_t)attr->num_values; /* "," separators */ 3935 3936 /* 3937 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while 3938 * other attributes appear as "foo=value1,value2,...,valueN". 3939 */ 3940 3941 if (attr->value_tag != IPP_TAG_BOOLEAN) 3942 bytes += strlen(attr->name); 3943 else 3944 bytes += (size_t)attr->num_values * strlen(attr->name); 3945 3946 /* 3947 * Now add the size required for each value in the attribute... 3948 */ 3949 3950 switch (attr->value_tag) 3951 { 3952 case IPP_TAG_INTEGER : 3953 case IPP_TAG_ENUM : 3954 /* 3955 * Minimum value of a signed integer is -2147483647, or 11 digits. 3956 */ 3957 3958 bytes += (size_t)attr->num_values * 11; 3959 break; 3960 3961 case IPP_TAG_BOOLEAN : 3962 /* 3963 * Add two bytes for each false ("no") value... 3964 */ 3965 3966 for (i = 0; i < attr->num_values; i ++) 3967 if (!attr->values[i].boolean) 3968 bytes += 2; 3969 break; 3970 3971 case IPP_TAG_RANGE : 3972 /* 3973 * A range is two signed integers separated by a hyphen, or 3974 * 23 characters max. 3975 */ 3976 3977 bytes += (size_t)attr->num_values * 23; 3978 break; 3979 3980 case IPP_TAG_RESOLUTION : 3981 /* 3982 * A resolution is two signed integers separated by an "x" and 3983 * suffixed by the units, or 26 characters max. 3984 */ 3985 3986 bytes += (size_t)attr->num_values * 26; 3987 break; 3988 3989 case IPP_TAG_STRING : 3990 case IPP_TAG_TEXT : 3991 case IPP_TAG_NAME : 3992 case IPP_TAG_KEYWORD : 3993 case IPP_TAG_CHARSET : 3994 case IPP_TAG_LANGUAGE : 3995 case IPP_TAG_URI : 3996 /* 3997 * Strings can contain characters that need quoting. We need 3998 * at least 2 * len + 2 characters to cover the quotes and 3999 * any backslashes in the string. 4000 */ 4001 4002 for (i = 0; i < attr->num_values; i ++) 4003 bytes += 2 * strlen(attr->values[i].string.text) + 2; 4004 break; 4005 4006 default : 4007 break; /* anti-compiler-warning-code */ 4008 } 4009 } 4010 4011 return (bytes); 4012} 4013 4014 4015/* 4016 * 'load_job_cache()' - Load jobs from the job.cache file. 4017 */ 4018 4019static void 4020load_job_cache(const char *filename) /* I - job.cache filename */ 4021{ 4022 cups_file_t *fp; /* job.cache file */ 4023 char line[1024], /* Line buffer */ 4024 *value; /* Value on line */ 4025 int linenum; /* Line number in file */ 4026 cupsd_job_t *job; /* Current job */ 4027 int jobid; /* Job ID */ 4028 char jobfile[1024]; /* Job filename */ 4029 4030 4031 /* 4032 * Open the job.cache file... 4033 */ 4034 4035 if ((fp = cupsdOpenConfFile(filename)) == NULL) 4036 { 4037 load_request_root(); 4038 return; 4039 } 4040 4041 /* 4042 * Read entries from the job cache file and create jobs as needed. 4043 */ 4044 4045 cupsdLogMessage(CUPSD_LOG_INFO, "Loading job cache file \"%s\"...", 4046 filename); 4047 4048 linenum = 0; 4049 job = NULL; 4050 4051 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) 4052 { 4053 if (!_cups_strcasecmp(line, "NextJobId")) 4054 { 4055 if (value) 4056 NextJobId = atoi(value); 4057 } 4058 else if (!_cups_strcasecmp(line, "<Job")) 4059 { 4060 if (job) 4061 { 4062 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing </Job> directive on line %d.", 4063 linenum); 4064 continue; 4065 } 4066 4067 if (!value) 4068 { 4069 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing job ID on line %d.", linenum); 4070 continue; 4071 } 4072 4073 jobid = atoi(value); 4074 4075 if (jobid < 1) 4076 { 4077 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad job ID %d on line %d.", jobid, 4078 linenum); 4079 continue; 4080 } 4081 4082 snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, jobid); 4083 if (access(jobfile, 0)) 4084 { 4085 snprintf(jobfile, sizeof(jobfile), "%s/c%05d.N", RequestRoot, jobid); 4086 if (access(jobfile, 0)) 4087 { 4088 cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Files have gone away.", 4089 jobid); 4090 continue; 4091 } 4092 } 4093 4094 job = calloc(1, sizeof(cupsd_job_t)); 4095 if (!job) 4096 { 4097 cupsdLogMessage(CUPSD_LOG_EMERG, 4098 "[Job %d] Unable to allocate memory for job.", jobid); 4099 break; 4100 } 4101 4102 job->id = jobid; 4103 job->back_pipes[0] = -1; 4104 job->back_pipes[1] = -1; 4105 job->print_pipes[0] = -1; 4106 job->print_pipes[1] = -1; 4107 job->side_pipes[0] = -1; 4108 job->side_pipes[1] = -1; 4109 job->status_pipes[0] = -1; 4110 job->status_pipes[1] = -1; 4111 4112 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Loading from cache..."); 4113 } 4114 else if (!job) 4115 { 4116 cupsdLogMessage(CUPSD_LOG_ERROR, 4117 "Missing <Job #> directive on line %d.", linenum); 4118 continue; 4119 } 4120 else if (!_cups_strcasecmp(line, "</Job>")) 4121 { 4122 cupsArrayAdd(Jobs, job); 4123 4124 if (job->state_value <= IPP_JOB_STOPPED && cupsdLoadJob(job)) 4125 cupsArrayAdd(ActiveJobs, job); 4126 else if (job->state_value > IPP_JOB_STOPPED) 4127 { 4128 if (!job->completed_time || !job->creation_time || !job->name || !job->koctets) 4129 { 4130 cupsdLoadJob(job); 4131 unload_job(job); 4132 } 4133 } 4134 4135 job = NULL; 4136 } 4137 else if (!value) 4138 { 4139 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d.", linenum); 4140 continue; 4141 } 4142 else if (!_cups_strcasecmp(line, "State")) 4143 { 4144 job->state_value = (ipp_jstate_t)atoi(value); 4145 4146 if (job->state_value < IPP_JOB_PENDING) 4147 job->state_value = IPP_JOB_PENDING; 4148 else if (job->state_value > IPP_JOB_COMPLETED) 4149 job->state_value = IPP_JOB_COMPLETED; 4150 } 4151 else if (!_cups_strcasecmp(line, "Name")) 4152 { 4153 cupsdSetString(&(job->name), value); 4154 } 4155 else if (!_cups_strcasecmp(line, "Created")) 4156 { 4157 job->creation_time = strtol(value, NULL, 10); 4158 } 4159 else if (!_cups_strcasecmp(line, "Completed")) 4160 { 4161 job->completed_time = strtol(value, NULL, 10); 4162 } 4163 else if (!_cups_strcasecmp(line, "HoldUntil")) 4164 { 4165 job->hold_until = strtol(value, NULL, 10); 4166 } 4167 else if (!_cups_strcasecmp(line, "Priority")) 4168 { 4169 job->priority = atoi(value); 4170 } 4171 else if (!_cups_strcasecmp(line, "Username")) 4172 { 4173 cupsdSetString(&job->username, value); 4174 } 4175 else if (!_cups_strcasecmp(line, "Destination")) 4176 { 4177 cupsdSetString(&job->dest, value); 4178 } 4179 else if (!_cups_strcasecmp(line, "DestType")) 4180 { 4181 job->dtype = (cups_ptype_t)atoi(value); 4182 } 4183 else if (!_cups_strcasecmp(line, "KOctets")) 4184 { 4185 job->koctets = atoi(value); 4186 } 4187 else if (!_cups_strcasecmp(line, "NumFiles")) 4188 { 4189 job->num_files = atoi(value); 4190 4191 if (job->num_files < 0) 4192 { 4193 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad NumFiles value %d on line %d.", 4194 job->num_files, linenum); 4195 job->num_files = 0; 4196 continue; 4197 } 4198 4199 if (job->num_files > 0) 4200 { 4201 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-001", RequestRoot, 4202 job->id); 4203 if (access(jobfile, 0)) 4204 { 4205 cupsdLogJob(job, CUPSD_LOG_INFO, "Data files have gone away."); 4206 job->num_files = 0; 4207 continue; 4208 } 4209 4210 job->filetypes = calloc((size_t)job->num_files, sizeof(mime_type_t *)); 4211 job->compressions = calloc((size_t)job->num_files, sizeof(int)); 4212 4213 if (!job->filetypes || !job->compressions) 4214 { 4215 cupsdLogJob(job, CUPSD_LOG_EMERG, 4216 "Unable to allocate memory for %d files.", 4217 job->num_files); 4218 break; 4219 } 4220 } 4221 } 4222 else if (!_cups_strcasecmp(line, "File")) 4223 { 4224 int number, /* File number */ 4225 compression; /* Compression value */ 4226 char super[MIME_MAX_SUPER], /* MIME super type */ 4227 type[MIME_MAX_TYPE]; /* MIME type */ 4228 4229 4230 if (sscanf(value, "%d%*[ \t]%15[^/]/%255s%d", &number, super, type, 4231 &compression) != 4) 4232 { 4233 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File on line %d.", linenum); 4234 continue; 4235 } 4236 4237 if (number < 1 || number > job->num_files) 4238 { 4239 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File number %d on line %d.", 4240 number, linenum); 4241 continue; 4242 } 4243 4244 number --; 4245 4246 job->compressions[number] = compression; 4247 job->filetypes[number] = mimeType(MimeDatabase, super, type); 4248 4249 if (!job->filetypes[number]) 4250 { 4251 /* 4252 * If the original MIME type is unknown, auto-type it! 4253 */ 4254 4255 cupsdLogJob(job, CUPSD_LOG_ERROR, 4256 "Unknown MIME type %s/%s for file %d.", 4257 super, type, number + 1); 4258 4259 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot, 4260 job->id, number + 1); 4261 job->filetypes[number] = mimeFileType(MimeDatabase, jobfile, NULL, 4262 job->compressions + number); 4263 4264 /* 4265 * If that didn't work, assume it is raw... 4266 */ 4267 4268 if (!job->filetypes[number]) 4269 job->filetypes[number] = mimeType(MimeDatabase, "application", 4270 "vnd.cups-raw"); 4271 } 4272 } 4273 else 4274 cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown %s directive on line %d.", 4275 line, linenum); 4276 } 4277 4278 if (job) 4279 { 4280 cupsdLogMessage(CUPSD_LOG_ERROR, 4281 "Missing </Job> directive on line %d.", linenum); 4282 cupsdDeleteJob(job, CUPSD_JOB_PURGE); 4283 } 4284 4285 cupsFileClose(fp); 4286} 4287 4288 4289/* 4290 * 'load_next_job_id()' - Load the NextJobId value from the job.cache file. 4291 */ 4292 4293static void 4294load_next_job_id(const char *filename) /* I - job.cache filename */ 4295{ 4296 cups_file_t *fp; /* job.cache file */ 4297 char line[1024], /* Line buffer */ 4298 *value; /* Value on line */ 4299 int linenum; /* Line number in file */ 4300 int next_job_id; /* NextJobId value from line */ 4301 4302 4303 /* 4304 * Read the NextJobId directive from the job.cache file and use 4305 * the value (if any). 4306 */ 4307 4308 if ((fp = cupsFileOpen(filename, "r")) == NULL) 4309 { 4310 if (errno != ENOENT) 4311 cupsdLogMessage(CUPSD_LOG_ERROR, 4312 "Unable to open job cache file \"%s\": %s", 4313 filename, strerror(errno)); 4314 4315 return; 4316 } 4317 4318 cupsdLogMessage(CUPSD_LOG_INFO, 4319 "Loading NextJobId from job cache file \"%s\"...", filename); 4320 4321 linenum = 0; 4322 4323 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) 4324 { 4325 if (!_cups_strcasecmp(line, "NextJobId")) 4326 { 4327 if (value) 4328 { 4329 next_job_id = atoi(value); 4330 4331 if (next_job_id > NextJobId) 4332 NextJobId = next_job_id; 4333 } 4334 break; 4335 } 4336 } 4337 4338 cupsFileClose(fp); 4339} 4340 4341 4342/* 4343 * 'load_request_root()' - Load jobs from the RequestRoot directory. 4344 */ 4345 4346static void 4347load_request_root(void) 4348{ 4349 cups_dir_t *dir; /* Directory */ 4350 cups_dentry_t *dent; /* Directory entry */ 4351 cupsd_job_t *job; /* New job */ 4352 4353 4354 /* 4355 * Open the requests directory... 4356 */ 4357 4358 cupsdLogMessage(CUPSD_LOG_DEBUG, "Scanning %s for jobs...", RequestRoot); 4359 4360 if ((dir = cupsDirOpen(RequestRoot)) == NULL) 4361 { 4362 cupsdLogMessage(CUPSD_LOG_ERROR, 4363 "Unable to open spool directory \"%s\": %s", 4364 RequestRoot, strerror(errno)); 4365 return; 4366 } 4367 4368 /* 4369 * Read all the c##### files... 4370 */ 4371 4372 while ((dent = cupsDirRead(dir)) != NULL) 4373 if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c') 4374 { 4375 /* 4376 * Allocate memory for the job... 4377 */ 4378 4379 if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL) 4380 { 4381 cupsdLogMessage(CUPSD_LOG_ERROR, "Ran out of memory for jobs."); 4382 cupsDirClose(dir); 4383 return; 4384 } 4385 4386 /* 4387 * Assign the job ID... 4388 */ 4389 4390 job->id = atoi(dent->filename + 1); 4391 job->back_pipes[0] = -1; 4392 job->back_pipes[1] = -1; 4393 job->print_pipes[0] = -1; 4394 job->print_pipes[1] = -1; 4395 job->side_pipes[0] = -1; 4396 job->side_pipes[1] = -1; 4397 job->status_pipes[0] = -1; 4398 job->status_pipes[1] = -1; 4399 4400 if (job->id >= NextJobId) 4401 NextJobId = job->id + 1; 4402 4403 /* 4404 * Load the job... 4405 */ 4406 4407 if (cupsdLoadJob(job)) 4408 { 4409 /* 4410 * Insert the job into the array, sorting by job priority and ID... 4411 */ 4412 4413 cupsArrayAdd(Jobs, job); 4414 4415 if (job->state_value <= IPP_JOB_STOPPED) 4416 cupsArrayAdd(ActiveJobs, job); 4417 else 4418 unload_job(job); 4419 } 4420 else 4421 free(job); 4422 } 4423 4424 cupsDirClose(dir); 4425} 4426 4427 4428/* 4429 * 'remove_job_files()' - Remove the document files for a job. 4430 */ 4431 4432static void 4433remove_job_files(cupsd_job_t *job) /* I - Job */ 4434{ 4435 int i; /* Looping var */ 4436 char filename[1024]; /* Document filename */ 4437 4438 4439 if (job->num_files <= 0) 4440 return; 4441 4442 for (i = 1; i <= job->num_files; i ++) 4443 { 4444 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, 4445 job->id, i); 4446 cupsdUnlinkOrRemoveFile(filename); 4447 } 4448 4449 free(job->filetypes); 4450 free(job->compressions); 4451 4452 job->file_time = 0; 4453 job->num_files = 0; 4454 job->filetypes = NULL; 4455 job->compressions = NULL; 4456 4457 LastEvent |= CUPSD_EVENT_PRINTER_STATE_CHANGED; 4458} 4459 4460 4461/* 4462 * 'remove_job_history()' - Remove the control file for a job. 4463 */ 4464 4465static void 4466remove_job_history(cupsd_job_t *job) /* I - Job */ 4467{ 4468 char filename[1024]; /* Control filename */ 4469 4470 4471 /* 4472 * Remove the job info file... 4473 */ 4474 4475 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, 4476 job->id); 4477 cupsdUnlinkOrRemoveFile(filename); 4478 4479 LastEvent |= CUPSD_EVENT_PRINTER_STATE_CHANGED; 4480} 4481 4482 4483/* 4484 * 'set_time()' - Set one of the "time-at-xyz" attributes. 4485 */ 4486 4487static void 4488set_time(cupsd_job_t *job, /* I - Job to update */ 4489 const char *name) /* I - Name of attribute */ 4490{ 4491 ipp_attribute_t *attr; /* Time attribute */ 4492 time_t curtime; /* Current time */ 4493 4494 4495 curtime = time(NULL); 4496 4497 cupsdLogJob(job, CUPSD_LOG_DEBUG, "%s=%ld", name, (long)curtime); 4498 4499 if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL) 4500 { 4501 attr->value_tag = IPP_TAG_INTEGER; 4502 attr->values[0].integer = curtime; 4503 } 4504 4505 if (!strcmp(name, "time-at-completed")) 4506 { 4507 job->completed_time = curtime; 4508 4509 if (JobHistory < INT_MAX && attr) 4510 job->history_time = attr->values[0].integer + JobHistory; 4511 else 4512 job->history_time = INT_MAX; 4513 4514 if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate) 4515 JobHistoryUpdate = job->history_time; 4516 4517 if (JobFiles < INT_MAX && attr) 4518 job->file_time = attr->values[0].integer + JobFiles; 4519 else 4520 job->file_time = INT_MAX; 4521 4522 if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate) 4523 JobHistoryUpdate = job->file_time; 4524 4525 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_time: JobHistoryUpdate=%ld", 4526 (long)JobHistoryUpdate); 4527 } 4528} 4529 4530 4531/* 4532 * 'start_job()' - Start a print job. 4533 */ 4534 4535static void 4536start_job(cupsd_job_t *job, /* I - Job ID */ 4537 cupsd_printer_t *printer) /* I - Printer to print job */ 4538{ 4539 const char *filename; /* Support filename */ 4540 ipp_attribute_t *cancel_after = ippFindAttribute(job->attrs, 4541 "job-cancel-after", 4542 IPP_TAG_INTEGER); 4543 /* job-cancel-after attribute */ 4544 4545 4546 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job(job=%p(%d), printer=%p(%s))", 4547 job, job->id, printer, printer->name); 4548 4549 /* 4550 * Make sure we have some files around before we try to print... 4551 */ 4552 4553 if (job->num_files == 0) 4554 { 4555 ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system"); 4556 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_DEFAULT, 4557 "Aborting job because it has no files."); 4558 return; 4559 } 4560 4561 /* 4562 * Update the printer and job state to "processing"... 4563 */ 4564 4565 if (!cupsdLoadJob(job)) 4566 return; 4567 4568 if (!job->printer_message) 4569 job->printer_message = ippFindAttribute(job->attrs, 4570 "job-printer-state-message", 4571 IPP_TAG_TEXT); 4572 if (job->printer_message) 4573 cupsdSetString(&(job->printer_message->values[0].string.text), ""); 4574 4575 ippSetString(job->attrs, &job->reasons, 0, "job-printing"); 4576 cupsdSetJobState(job, IPP_JOB_PROCESSING, CUPSD_JOB_DEFAULT, NULL); 4577 cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0); 4578 cupsdSetPrinterReasons(printer, "-cups-remote-pending," 4579 "cups-remote-pending-held," 4580 "cups-remote-processing," 4581 "cups-remote-stopped," 4582 "cups-remote-canceled," 4583 "cups-remote-aborted," 4584 "cups-remote-completed"); 4585 4586 job->cost = 0; 4587 job->current_file = 0; 4588 job->file_time = 0; 4589 job->history_time = 0; 4590 job->progress = 0; 4591 job->printer = printer; 4592 printer->job = job; 4593 4594 if (cancel_after) 4595 job->cancel_time = time(NULL) + ippGetInteger(cancel_after, 0); 4596 else if (MaxJobTime > 0) 4597 job->cancel_time = time(NULL) + MaxJobTime; 4598 else 4599 job->cancel_time = 0; 4600 4601 /* 4602 * Check for support files... 4603 */ 4604 4605 cupsdSetPrinterReasons(job->printer, "-cups-missing-filter-warning," 4606 "cups-insecure-filter-warning"); 4607 4608 if (printer->pc) 4609 { 4610 for (filename = (const char *)cupsArrayFirst(printer->pc->support_files); 4611 filename; 4612 filename = (const char *)cupsArrayNext(printer->pc->support_files)) 4613 { 4614 if (_cupsFileCheck(filename, _CUPS_FILE_CHECK_FILE, !RunUser, 4615 cupsdLogFCMessage, printer)) 4616 break; 4617 } 4618 } 4619 4620 /* 4621 * Setup the last exit status and security profiles... 4622 */ 4623 4624 job->status = 0; 4625 job->profile = cupsdCreateProfile(job->id, 0); 4626 job->bprofile = cupsdCreateProfile(job->id, 1); 4627 4628 /* 4629 * Create the status pipes and buffer... 4630 */ 4631 4632 if (cupsdOpenPipe(job->status_pipes)) 4633 { 4634 cupsdLogJob(job, CUPSD_LOG_DEBUG, 4635 "Unable to create job status pipes - %s.", strerror(errno)); 4636 4637 cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT, 4638 "Job stopped because the scheduler could not create the " 4639 "job status pipes."); 4640 4641 cupsdDestroyProfile(job->profile); 4642 job->profile = NULL; 4643 cupsdDestroyProfile(job->bprofile); 4644 job->bprofile = NULL; 4645 return; 4646 } 4647 4648 job->status_buffer = cupsdStatBufNew(job->status_pipes[0], NULL); 4649 job->status_level = CUPSD_LOG_INFO; 4650 4651 /* 4652 * Create the backchannel pipes and make them non-blocking... 4653 */ 4654 4655 if (cupsdOpenPipe(job->back_pipes)) 4656 { 4657 cupsdLogJob(job, CUPSD_LOG_DEBUG, 4658 "Unable to create back-channel pipes - %s.", strerror(errno)); 4659 4660 cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT, 4661 "Job stopped because the scheduler could not create the " 4662 "back-channel pipes."); 4663 4664 cupsdClosePipe(job->status_pipes); 4665 cupsdStatBufDelete(job->status_buffer); 4666 job->status_buffer = NULL; 4667 4668 cupsdDestroyProfile(job->profile); 4669 job->profile = NULL; 4670 cupsdDestroyProfile(job->bprofile); 4671 job->bprofile = NULL; 4672 return; 4673 } 4674 4675 fcntl(job->back_pipes[0], F_SETFL, 4676 fcntl(job->back_pipes[0], F_GETFL) | O_NONBLOCK); 4677 fcntl(job->back_pipes[1], F_SETFL, 4678 fcntl(job->back_pipes[1], F_GETFL) | O_NONBLOCK); 4679 4680 /* 4681 * Create the side-channel pipes and make them non-blocking... 4682 */ 4683 4684 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, job->side_pipes)) 4685 { 4686 cupsdLogJob(job, CUPSD_LOG_DEBUG, 4687 "Unable to create side-channel pipes - %s.", strerror(errno)); 4688 4689 cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT, 4690 "Job stopped because the scheduler could not create the " 4691 "side-channel pipes."); 4692 4693 cupsdClosePipe(job->back_pipes); 4694 4695 cupsdClosePipe(job->status_pipes); 4696 cupsdStatBufDelete(job->status_buffer); 4697 job->status_buffer = NULL; 4698 4699 cupsdDestroyProfile(job->profile); 4700 job->profile = NULL; 4701 cupsdDestroyProfile(job->bprofile); 4702 job->bprofile = NULL; 4703 return; 4704 } 4705 4706 fcntl(job->side_pipes[0], F_SETFL, 4707 fcntl(job->side_pipes[0], F_GETFL) | O_NONBLOCK); 4708 fcntl(job->side_pipes[1], F_SETFL, 4709 fcntl(job->side_pipes[1], F_GETFL) | O_NONBLOCK); 4710 4711 fcntl(job->side_pipes[0], F_SETFD, 4712 fcntl(job->side_pipes[0], F_GETFD) | FD_CLOEXEC); 4713 fcntl(job->side_pipes[1], F_SETFD, 4714 fcntl(job->side_pipes[1], F_GETFD) | FD_CLOEXEC); 4715 4716 /* 4717 * Now start the first file in the job... 4718 */ 4719 4720 cupsdContinueJob(job); 4721} 4722 4723 4724/* 4725 * 'stop_job()' - Stop a print job. 4726 */ 4727 4728static void 4729stop_job(cupsd_job_t *job, /* I - Job */ 4730 cupsd_jobaction_t action) /* I - Action */ 4731{ 4732 int i; /* Looping var */ 4733 4734 4735 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_job(job=%p(%d), action=%d)", job, 4736 job->id, action); 4737 4738 FilterLevel -= job->cost; 4739 job->cost = 0; 4740 4741 if (action == CUPSD_JOB_DEFAULT && !job->kill_time) 4742 job->kill_time = time(NULL) + JobKillDelay; 4743 else if (action >= CUPSD_JOB_FORCE) 4744 job->kill_time = 0; 4745 4746 for (i = 0; job->filters[i]; i ++) 4747 if (job->filters[i] > 0) 4748 { 4749 cupsdEndProcess(job->filters[i], action >= CUPSD_JOB_FORCE); 4750 4751 if (action >= CUPSD_JOB_FORCE) 4752 job->filters[i] = -job->filters[i]; 4753 } 4754 4755 if (job->backend > 0) 4756 { 4757 cupsdEndProcess(job->backend, action >= CUPSD_JOB_FORCE); 4758 4759 if (action >= CUPSD_JOB_FORCE) 4760 job->backend = -job->backend; 4761 } 4762 4763 if (action >= CUPSD_JOB_FORCE) 4764 { 4765 /* 4766 * Clear job status... 4767 */ 4768 4769 job->status = 0; 4770 } 4771} 4772 4773 4774/* 4775 * 'unload_job()' - Unload a job from memory. 4776 */ 4777 4778static void 4779unload_job(cupsd_job_t *job) /* I - Job */ 4780{ 4781 if (!job->attrs) 4782 return; 4783 4784 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Unloading..."); 4785 4786 ippDelete(job->attrs); 4787 4788 job->attrs = NULL; 4789 job->state = NULL; 4790 job->reasons = NULL; 4791 job->sheets = NULL; 4792 job->job_sheets = NULL; 4793 job->printer_message = NULL; 4794 job->printer_reasons = NULL; 4795} 4796 4797 4798/* 4799 * 'update_job()' - Read a status update from a job's filters. 4800 */ 4801 4802void 4803update_job(cupsd_job_t *job) /* I - Job to check */ 4804{ 4805 int i; /* Looping var */ 4806 int copies; /* Number of copies printed */ 4807 char message[CUPSD_SB_BUFFER_SIZE], 4808 /* Message text */ 4809 *ptr; /* Pointer update... */ 4810 int loglevel, /* Log level for message */ 4811 event = 0; /* Events? */ 4812 static const char * const levels[] = /* Log levels */ 4813 { 4814 "NONE", 4815 "EMERG", 4816 "ALERT", 4817 "CRIT", 4818 "ERROR", 4819 "WARN", 4820 "NOTICE", 4821 "INFO", 4822 "DEBUG", 4823 "DEBUG2" 4824 }; 4825 4826 4827 /* 4828 * Get the printer associated with this job; if the printer is stopped for 4829 * any reason then job->printer will be reset to NULL, so make sure we have 4830 * a valid pointer... 4831 */ 4832 4833 while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel, 4834 message, sizeof(message))) != NULL) 4835 { 4836 /* 4837 * Process page and printer state messages as needed... 4838 */ 4839 4840 if (loglevel == CUPSD_LOG_PAGE) 4841 { 4842 /* 4843 * Page message; send the message to the page_log file and update the 4844 * job sheet count... 4845 */ 4846 4847 cupsdLogJob(job, CUPSD_LOG_DEBUG, "PAGE: %s", message); 4848 4849 if (job->sheets) 4850 { 4851 if (!_cups_strncasecmp(message, "total ", 6)) 4852 { 4853 /* 4854 * Got a total count of pages from a backend or filter... 4855 */ 4856 4857 copies = atoi(message + 6); 4858 copies -= job->sheets->values[0].integer; /* Just track the delta */ 4859 } 4860 else if (!sscanf(message, "%*d%d", &copies)) 4861 copies = 1; 4862 4863 job->sheets->values[0].integer += copies; 4864 4865 if (job->printer->page_limit) 4866 cupsdUpdateQuota(job->printer, job->username, copies, 0); 4867 } 4868 4869 cupsdLogPage(job, message); 4870 4871 if (job->sheets) 4872 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job, 4873 "Printed %d page(s).", job->sheets->values[0].integer); 4874 } 4875 else if (loglevel == CUPSD_LOG_JOBSTATE) 4876 { 4877 /* 4878 * Support "keyword" to set job-state-reasons to the specified keyword. 4879 * This is sufficient for the current paid printing stuff. 4880 */ 4881 4882 cupsdLogJob(job, CUPSD_LOG_DEBUG, "JOBSTATE: %s", message); 4883 4884 if (!strcmp(message, "cups-retry-as-raster")) 4885 job->retry_as_raster = 1; 4886 else 4887 ippSetString(job->attrs, &job->reasons, 0, message); 4888 } 4889 else if (loglevel == CUPSD_LOG_STATE) 4890 { 4891 cupsdLogJob(job, CUPSD_LOG_DEBUG, "STATE: %s", message); 4892 4893 if (!strcmp(message, "paused")) 4894 { 4895 cupsdStopPrinter(job->printer, 1); 4896 return; 4897 } 4898 else if (message[0] && cupsdSetPrinterReasons(job->printer, message)) 4899 { 4900 event |= CUPSD_EVENT_PRINTER_STATE; 4901 4902 if (MaxJobTime > 0) 4903 { 4904 /* 4905 * Reset cancel time after connecting to the device... 4906 */ 4907 4908 for (i = 0; i < job->printer->num_reasons; i ++) 4909 if (!strcmp(job->printer->reasons[i], "connecting-to-device")) 4910 break; 4911 4912 if (i >= job->printer->num_reasons) 4913 { 4914 ipp_attribute_t *cancel_after = ippFindAttribute(job->attrs, 4915 "job-cancel-after", 4916 IPP_TAG_INTEGER); 4917 /* job-cancel-after attribute */ 4918 4919 if (cancel_after) 4920 job->cancel_time = time(NULL) + ippGetInteger(cancel_after, 0); 4921 else 4922 job->cancel_time = time(NULL) + MaxJobTime; 4923 } 4924 } 4925 } 4926 4927 update_job_attrs(job, 0); 4928 } 4929 else if (loglevel == CUPSD_LOG_ATTR) 4930 { 4931 /* 4932 * Set attribute(s)... 4933 */ 4934 4935 int num_attrs; /* Number of attributes */ 4936 cups_option_t *attrs; /* Attributes */ 4937 const char *attr; /* Attribute */ 4938 4939 cupsdLogJob(job, CUPSD_LOG_DEBUG, "ATTR: %s", message); 4940 4941 num_attrs = cupsParseOptions(message, 0, &attrs); 4942 4943 if ((attr = cupsGetOption("auth-info-default", num_attrs, 4944 attrs)) != NULL) 4945 { 4946 job->printer->num_options = cupsAddOption("auth-info", attr, 4947 job->printer->num_options, 4948 &(job->printer->options)); 4949 cupsdSetPrinterAttrs(job->printer); 4950 4951 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); 4952 } 4953 4954 if ((attr = cupsGetOption("auth-info-required", num_attrs, 4955 attrs)) != NULL) 4956 { 4957 cupsdSetAuthInfoRequired(job->printer, attr, NULL); 4958 cupsdSetPrinterAttrs(job->printer); 4959 4960 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); 4961 } 4962 4963 if ((attr = cupsGetOption("job-media-progress", num_attrs, 4964 attrs)) != NULL) 4965 { 4966 int progress = atoi(attr); 4967 4968 4969 if (progress >= 0 && progress <= 100) 4970 { 4971 job->progress = progress; 4972 4973 if (job->sheets) 4974 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job, 4975 "Printing page %d, %d%%", 4976 job->sheets->values[0].integer, job->progress); 4977 } 4978 } 4979 4980 if ((attr = cupsGetOption("printer-alert", num_attrs, attrs)) != NULL) 4981 { 4982 cupsdSetString(&job->printer->alert, attr); 4983 event |= CUPSD_EVENT_PRINTER_STATE; 4984 } 4985 4986 if ((attr = cupsGetOption("printer-alert-description", num_attrs, 4987 attrs)) != NULL) 4988 { 4989 cupsdSetString(&job->printer->alert_description, attr); 4990 event |= CUPSD_EVENT_PRINTER_STATE; 4991 } 4992 4993 if ((attr = cupsGetOption("marker-colors", num_attrs, attrs)) != NULL) 4994 { 4995 cupsdSetPrinterAttr(job->printer, "marker-colors", (char *)attr); 4996 job->printer->marker_time = time(NULL); 4997 event |= CUPSD_EVENT_PRINTER_STATE; 4998 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); 4999 } 5000 5001 if ((attr = cupsGetOption("marker-levels", num_attrs, attrs)) != NULL) 5002 { 5003 cupsdSetPrinterAttr(job->printer, "marker-levels", (char *)attr); 5004 job->printer->marker_time = time(NULL); 5005 event |= CUPSD_EVENT_PRINTER_STATE; 5006 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); 5007 } 5008 5009 if ((attr = cupsGetOption("marker-low-levels", num_attrs, attrs)) != NULL) 5010 { 5011 cupsdSetPrinterAttr(job->printer, "marker-low-levels", (char *)attr); 5012 job->printer->marker_time = time(NULL); 5013 event |= CUPSD_EVENT_PRINTER_STATE; 5014 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); 5015 } 5016 5017 if ((attr = cupsGetOption("marker-high-levels", num_attrs, attrs)) != NULL) 5018 { 5019 cupsdSetPrinterAttr(job->printer, "marker-high-levels", (char *)attr); 5020 job->printer->marker_time = time(NULL); 5021 event |= CUPSD_EVENT_PRINTER_STATE; 5022 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); 5023 } 5024 5025 if ((attr = cupsGetOption("marker-message", num_attrs, attrs)) != NULL) 5026 { 5027 cupsdSetPrinterAttr(job->printer, "marker-message", (char *)attr); 5028 job->printer->marker_time = time(NULL); 5029 event |= CUPSD_EVENT_PRINTER_STATE; 5030 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); 5031 } 5032 5033 if ((attr = cupsGetOption("marker-names", num_attrs, attrs)) != NULL) 5034 { 5035 cupsdSetPrinterAttr(job->printer, "marker-names", (char *)attr); 5036 job->printer->marker_time = time(NULL); 5037 event |= CUPSD_EVENT_PRINTER_STATE; 5038 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); 5039 } 5040 5041 if ((attr = cupsGetOption("marker-types", num_attrs, attrs)) != NULL) 5042 { 5043 cupsdSetPrinterAttr(job->printer, "marker-types", (char *)attr); 5044 job->printer->marker_time = time(NULL); 5045 event |= CUPSD_EVENT_PRINTER_STATE; 5046 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); 5047 } 5048 5049 cupsFreeOptions(num_attrs, attrs); 5050 } 5051 else if (loglevel == CUPSD_LOG_PPD) 5052 { 5053 /* 5054 * Set attribute(s)... 5055 */ 5056 5057 cupsdLogJob(job, CUPSD_LOG_DEBUG, "PPD: %s", message); 5058 5059 job->num_keywords = cupsParseOptions(message, job->num_keywords, 5060 &job->keywords); 5061 } 5062 else 5063 { 5064 /* 5065 * Strip legacy message prefix... 5066 */ 5067 5068 if (!strncmp(message, "recoverable:", 12)) 5069 { 5070 ptr = message + 12; 5071 while (isspace(*ptr & 255)) 5072 ptr ++; 5073 } 5074 else if (!strncmp(message, "recovered:", 10)) 5075 { 5076 ptr = message + 10; 5077 while (isspace(*ptr & 255)) 5078 ptr ++; 5079 } 5080 else 5081 ptr = message; 5082 5083 if (*ptr) 5084 cupsdLogJob(job, loglevel, "%s", ptr); 5085 5086 if (loglevel < CUPSD_LOG_DEBUG && 5087 strcmp(job->printer->state_message, ptr)) 5088 { 5089 strlcpy(job->printer->state_message, ptr, 5090 sizeof(job->printer->state_message)); 5091 5092 event |= CUPSD_EVENT_PRINTER_STATE | CUPSD_EVENT_JOB_PROGRESS; 5093 5094 if (loglevel <= job->status_level && job->status_level > CUPSD_LOG_ERROR) 5095 { 5096 /* 5097 * Some messages show in the job-printer-state-message attribute... 5098 */ 5099 5100 if (loglevel != CUPSD_LOG_NOTICE) 5101 job->status_level = loglevel; 5102 5103 update_job_attrs(job, 1); 5104 5105 cupsdLogJob(job, CUPSD_LOG_DEBUG, 5106 "Set job-printer-state-message to \"%s\", " 5107 "current level=%s", 5108 job->printer_message->values[0].string.text, 5109 levels[job->status_level]); 5110 } 5111 } 5112 } 5113 5114 if (!strchr(job->status_buffer->buffer, '\n')) 5115 break; 5116 } 5117 5118 if (event & CUPSD_EVENT_JOB_PROGRESS) 5119 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job, 5120 "%s", job->printer->state_message); 5121 if (event & CUPSD_EVENT_PRINTER_STATE) 5122 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, job->printer, NULL, 5123 (job->printer->type & CUPS_PRINTER_CLASS) ? 5124 "Class \"%s\" state changed." : 5125 "Printer \"%s\" state changed.", 5126 job->printer->name); 5127 5128 5129 if (ptr == NULL && !job->status_buffer->bufused) 5130 { 5131 /* 5132 * See if all of the filters and the backend have returned their 5133 * exit statuses. 5134 */ 5135 5136 for (i = 0; job->filters[i] < 0; i ++); 5137 5138 if (job->filters[i]) 5139 { 5140 /* 5141 * EOF but we haven't collected the exit status of all filters... 5142 */ 5143 5144 cupsdCheckProcess(); 5145 return; 5146 } 5147 5148 if (job->current_file >= job->num_files && job->backend > 0) 5149 { 5150 /* 5151 * EOF but we haven't collected the exit status of the backend... 5152 */ 5153 5154 cupsdCheckProcess(); 5155 return; 5156 } 5157 5158 /* 5159 * Handle the end of job stuff... 5160 */ 5161 5162 finalize_job(job, 1); 5163 5164 /* 5165 * Check for new jobs... 5166 */ 5167 5168 cupsdCheckJobs(); 5169 } 5170} 5171 5172 5173/* 5174 * 'update_job_attrs()' - Update the job-printer-* attributes. 5175 */ 5176 5177void 5178update_job_attrs(cupsd_job_t *job, /* I - Job to update */ 5179 int do_message)/* I - 1 = copy job-printer-state message */ 5180{ 5181 int i; /* Looping var */ 5182 int num_reasons; /* Actual number of reasons */ 5183 const char * const *reasons; /* Reasons */ 5184 static const char *none = "none"; /* "none" reason */ 5185 5186 5187 /* 5188 * Get/create the job-printer-state-* attributes... 5189 */ 5190 5191 if (!job->printer_message) 5192 { 5193 if ((job->printer_message = ippFindAttribute(job->attrs, 5194 "job-printer-state-message", 5195 IPP_TAG_TEXT)) == NULL) 5196 job->printer_message = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_TEXT, 5197 "job-printer-state-message", 5198 NULL, ""); 5199 } 5200 5201 if (!job->printer_reasons) 5202 job->printer_reasons = ippFindAttribute(job->attrs, 5203 "job-printer-state-reasons", 5204 IPP_TAG_KEYWORD); 5205 5206 /* 5207 * Copy or clear the printer-state-message value as needed... 5208 */ 5209 5210 if (job->state_value != IPP_JOB_PROCESSING && 5211 job->status_level == CUPSD_LOG_INFO) 5212 { 5213 cupsdSetString(&(job->printer_message->values[0].string.text), ""); 5214 5215 job->dirty = 1; 5216 cupsdMarkDirty(CUPSD_DIRTY_JOBS); 5217 } 5218 else if (job->printer->state_message[0] && do_message) 5219 { 5220 cupsdSetString(&(job->printer_message->values[0].string.text), 5221 job->printer->state_message); 5222 5223 job->dirty = 1; 5224 cupsdMarkDirty(CUPSD_DIRTY_JOBS); 5225 } 5226 5227 /* 5228 * ... and the printer-state-reasons value... 5229 */ 5230 5231 if (job->printer->num_reasons == 0) 5232 { 5233 num_reasons = 1; 5234 reasons = &none; 5235 } 5236 else 5237 { 5238 num_reasons = job->printer->num_reasons; 5239 reasons = (const char * const *)job->printer->reasons; 5240 } 5241 5242 if (!job->printer_reasons || job->printer_reasons->num_values != num_reasons) 5243 { 5244 /* 5245 * Replace/create a job-printer-state-reasons attribute... 5246 */ 5247 5248 ippDeleteAttribute(job->attrs, job->printer_reasons); 5249 5250 job->printer_reasons = ippAddStrings(job->attrs, 5251 IPP_TAG_JOB, IPP_TAG_KEYWORD, 5252 "job-printer-state-reasons", 5253 num_reasons, NULL, NULL); 5254 } 5255 else 5256 { 5257 /* 5258 * Don't bother clearing the reason strings if they are the same... 5259 */ 5260 5261 for (i = 0; i < num_reasons; i ++) 5262 if (strcmp(job->printer_reasons->values[i].string.text, reasons[i])) 5263 break; 5264 5265 if (i >= num_reasons) 5266 return; 5267 5268 /* 5269 * Not the same, so free the current strings... 5270 */ 5271 5272 for (i = 0; i < num_reasons; i ++) 5273 _cupsStrFree(job->printer_reasons->values[i].string.text); 5274 } 5275 5276 /* 5277 * Copy the reasons... 5278 */ 5279 5280 for (i = 0; i < num_reasons; i ++) 5281 job->printer_reasons->values[i].string.text = _cupsStrAlloc(reasons[i]); 5282 5283 job->dirty = 1; 5284 cupsdMarkDirty(CUPSD_DIRTY_JOBS); 5285} 5286 5287 5288/* 5289 * End of "$Id: job.c 12142 2014-08-30 02:35:43Z msweet $". 5290 */ 5291