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