Index: doc/index.html.in =================================================================== --- doc/index.html.in (revision 10356) +++ doc/index.html.in (working copy) @@ -100,7 +100,7 @@   CUPS and the CUPS logo are trademarks of -Apple Inc. CUPS is copyright 2007-2011 Apple +Apple Inc. CUPS is copyright 2007-2012 Apple Inc. All rights reserved. Index: doc/help/ref-cupsd-conf.html.in =================================================================== --- doc/help/ref-cupsd-conf.html.in (revision 10356) +++ doc/help/ref-cupsd-conf.html.in (working copy) @@ -42,7 +42,13 @@

You can also edit this file from the CUPS web interface, which automatically handles restarting the scheduler.

+
Note: +

The specification of time units ("w" for weeks, "h" for hours, etc.) in the various time interval directives is new in CUPS 1.6. Prior releases of CUPS only supported time intervals in seconds.

+ +
+ +

AccessLog

Examples

@@ -632,19 +638,21 @@

Examples

+DirtyCleanInterval 1w
+DirtyCleanInterval 1d
+DirtyCleanInterval 1h
+DirtyCleanInterval 1m
 DirtyCleanInterval 30
 DirtyCleanInterval 0
 

Description

-

The DirtyCleanInterval directive specifies the number of -seconds to wait before updating configuration and state files for printers, -classes, subscriptions, and jobs. The default is 30 seconds. A value of 0 -causes the update to occur as soon as possible, typically within a few -milliseconds.

+

The DirtyCleanInterval directive specifies the amount of time to wait before updating configuration and state files for printers, classes, subscriptions, and jobs in seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix). A value of 0 causes the update to occur as soon as possible, typically within a few milliseconds.

+

The default value is 30 (30 seconds).

+

DocumentRoot

Examples

@@ -1022,35 +1030,39 @@

Examples

+JobRetryInterval 1w
+JobRetryInterval 1d
+JobRetryInterval 1h
+JobRetryInterval 1m
 JobRetryInterval 30
-JobRetryInterval 120
 

Description

-

The JobRetryInterval directive specifies the -number of seconds to wait before retrying a job. This is -typically used for fax queues but can also be used with normal -print queues whose error policy is retry-job. The -default is 30 seconds.

+

The JobRetryInterval directive specifies the amount of time to wait before retrying a job in seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix). This is typically used for fax queues but can also be used with normal print queues whose error policy is retry-job or retry-current-job.

+

The default is 30 (30 seconds).

+

CUPS 1.4/Mac OS X 10.6JobKillDelay

Examples

+JobKillDelay 1w
+JobKillDelay 1d
+JobKillDelay 1h
+JobKillDelay 1m
 JobKillDelay 30
-JobKillDelay 120
 

Description

-

The JobKillDelay directive specifies the number of seconds to -wait before killing the filters and backend associated with a canceled or held -job. The default is 30 seconds.

+

The JobKillDelay directive specifies the amount of time to wait before killing the filters and backend associated with a canceled or held job in seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix).

+

The default is 30 (30 seconds).

+

CUPS 1.2/Mac OS X 10.5JobRetryLimit

Examples

@@ -1095,17 +1107,20 @@

Examples

-KeepAliveTimeout 60
+KeepAliveTimeout 1w
+KeepAliveTimeout 1d
+KeepAliveTimeout 1h
+KeepAliveTimeout 1m
 KeepAliveTimeout 30
 

Description

-

The KeepAliveTimeout directive controls how long -a persistent HTTP connection will remain open after the last -request. The default is 30 seconds.

+

The KeepAliveTimeout directive controls how long a persistent HTTP connection will remain open after the last request in seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix).

+

The default is 30 (30 seconds).

+

CUPS 1.1.7Limit (Location)

Examples

@@ -1681,14 +1696,16 @@
 MaxHoldTime 10800
+MaxHoldTime 3h
+MaxHoldTime 180m
 MaxHoldTime 0
 

Description

-

The MaxHoldTime directive controls the maximum number of seconds allowed for a job to remain in the "indefinite" hold state. The job is canceled automatically if it remains held indefinitely longer than the specified number of seconds.

+

The MaxHoldTime directive controls the maximum number of seconds allowed for a job to remain in the "indefinite" hold state. The job is canceled automatically if it remains held indefinitely longer than the specified time interval in seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix).

-

The default setting is 0 which disables this functionality.

+

The default setting is 0 which disables this functionality.

MaxJobs

@@ -1763,6 +1780,8 @@
 MaxJobTime 10800
+MaxJobTime 3h
+MaxJobTime 180m
 MaxJobTime 0
 
@@ -1770,10 +1789,9 @@

The MaxJobTime directive controls the maximum number of seconds allowed for a job to complete printing before it is considered "stuck". -The job is canceled automatically if it takes longer than the specified number of seconds to complete.

+The job is canceled automatically if it takes longer than the specified time to complete in seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix).

-

Setting the maximum time to 0 disables this functionality. The default -setting is 10800 seconds (3 hours).

+

Setting the maximum time to 0 disables this functionality. The default setting is 3h (3 hours).

MaxLogSize

@@ -1825,17 +1843,20 @@

Examples

-MultipleOperationTimeout 60
+MultipleOperationTimeout 1w
+MultipleOperationTimeout 1d
+MultipleOperationTimeout 1h
+MultipleOperationTimeout 5m
 MultipleOperationTimeout 300
-MultipleOperationTimeout 86400
 

Description

-

The MultipleOperationTimeout directive sets the maximum amount -of time between files in a multi-file print job. The default is 300 seconds.

+

The MultipleOperationTimeout directive sets the maximum amount of time between files in a multi-file print job in seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix).

+

The default is 5m (five minutes).

+

Order

Examples

@@ -2026,22 +2047,26 @@
 PreserveJobHistory On
 PreserveJobHistory Off
+PreserveJobHistory 1w
+PreserveJobHistory 7d
+PreserveJobHistory 168h
+PreserveJobHistory 10080m
+PreserveJobHistory 604800
 

Description

-

The PreserveJobHistory directive controls whether -the history of completed, canceled, or aborted print jobs is -stored on disk.

+

The PreserveJobHistory directive controls whether the history of completed, canceled, or aborted print jobs is retained by the scheduler. A value of On preserves job information until the administrator purges it with the cancel command. A value of Off removes the job information as soon as each job is completed, canceled, or aborted. Numeric values preserve job information for the specified number of seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix).

-

A value of On (the default) preserves job -information until the administrator purges it with the -cancel command.

+

The default value is On.

-

A value of Off removes the job information as -soon as each job is completed, canceled, or aborted.

+
Note: +

The MaxJobs, MaxJobsPerPrinter, and MaxJobsPerUser directives can cause job history to be discarded to make room for new jobs.

+
+ +

PreserveJobFiles

Examples

@@ -2049,23 +2074,28 @@
 PreserveJobFiles On
 PreserveJobFiles Off
+PreserveJobFiles 1w
+PreserveJobFiles 7d
+PreserveJobFiles 168h
+PreserveJobFiles 10080m
+PreserveJobFiles 604800
 

Description

-

The PreserveJobFiles directive controls whether -the document files of completed, canceled, or aborted print jobs -are stored on disk.

+

The PreserveJobFiles directive controls whether the document files of completed, canceled, or aborted print jobs are retained. Jobs can be restarted (and reprinted) as desired until they are purged.

-

A value of On preserves job files until the -administrator purges them with the cancel command. -Jobs can be restarted (and reprinted) as desired until they are -purged.

+

A value of On preserves job files until the administrator purges them with the cancel command. A value of Off removes the job files as soon as each job is completed, canceled, or aborted. Numeric values preserve job files for the specified number of seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix).

-

A value of Off (the default) removes the job -files as soon as each job is completed, canceled, or aborted.

+

The default value is 1d (one day).

+
Note: +

The MaxJobs, MaxJobsPerPrinter, MaxJobsPerUser, and PreserveJobHistory directives can cause job files to be discarded sooner than specified.

+ +
+ +

Printcap

Examples

@@ -2239,19 +2269,20 @@
 RootCertDuration 0
+RootCertDuration 1w
+RootCertDuration 1d
+RootCertDuration 1h
+RootCertDuration 5m
 RootCertDuration 300
 

Description

-

The RootCertDuration directive specifies the -number of seconds the root certificate remains valid. -The scheduler will generate a new certificate as needed when the -number of seconds has expired. If set to 0, the root certificate -is generated only once on startup or on a restart. The default is -300 seconds.

+

The RootCertDuration directive specifies the amount of time the root certificate remains valid in seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix). The scheduler will generate a new certificate as needed when the given time interval has expired. If set to 0, the root certificate is generated only once on startup or on a restart.

+

The default is 5m (five minutes).

+

CUPS 1.1.7Satisfy

Examples

@@ -2649,17 +2680,21 @@

Examples

+Timeout 1w
+Timeout 1d
+Timeout 1h
+Timeout 5m
 Timeout 300
-Timeout 90
 

Description

The Timeout directive controls the amount of time -to wait before an active HTTP or IPP request times out. The -default timeout is 300 seconds.

+to wait before an active HTTP or IPP request times out in seconds (no suffix), minutes ("m" suffix), hours ("h" suffix), days ("d" suffix), or weeks ("w" suffix).

+

The default timeout is 5m (five minutes).

+

CUPS 1.2/Mac OS X 10.5UseNetworkDefault

Examples

Index: templates/admin.tmpl =================================================================== --- templates/admin.tmpl (revision 10356) +++ templates/admin.tmpl (working copy) @@ -53,10 +53,13 @@ Allow remote administration
{have_gssapi? Use Kerberos authentication (FAQ)
:} Allow users to cancel any job (not just their own)
- Preserve job history
-        Number of jobs\: + Preserve job history
+        Maximum jobs (0 for no limit)\:
-         Preserve job print files
+        Retain Metadata\: +
+        Retain Documents\: +
Save debugging information for troubleshooting
        Max log file size\:

Index: templates/trailer.tmpl =================================================================== --- templates/trailer.tmpl (revision 10356) +++ templates/trailer.tmpl (working copy) @@ -1,7 +1,7 @@   CUPS and the CUPS logo are trademarks of -Apple Inc. CUPS is copyright 2007-2011 Apple +Apple Inc. CUPS is copyright 2007-2012 Apple Inc. All rights reserved. Index: cgi-bin/admin.c =================================================================== --- cgi-bin/admin.c (revision 10356) +++ cgi-bin/admin.c (working copy) @@ -1557,6 +1557,8 @@ int advanced, /* Advanced settings shown? */ changed; /* Have settings changed? */ const char *debug_logging, /* DEBUG_LOGGING value */ + *preserve_jobs = NULL, + /* PRESERVE_JOBS value */ *remote_admin, /* REMOTE_ADMIN value */ *remote_any, /* REMOTE_ANY value */ *share_printers,/* SHARE_PRINTERS value */ @@ -1611,19 +1613,36 @@ */ browse_web_if = cgiGetVariable("BROWSE_WEB_IF") ? "Yes" : "No"; - preserve_job_history = cgiGetVariable("PRESERVE_JOB_HISTORY") ? "Yes" : "No"; - preserve_job_files = cgiGetVariable("PRESERVE_JOB_FILES") ? "Yes" : "No"; max_clients = cgiGetVariable("MAX_CLIENTS"); - max_jobs = cgiGetVariable("MAX_JOBS"); max_log_size = cgiGetVariable("MAX_LOG_SIZE"); + preserve_jobs = cgiGetVariable("PRESERVE_JOBS"); + if (preserve_jobs) + { + max_jobs = cgiGetVariable("MAX_JOBS"); + preserve_job_history = cgiGetVariable("PRESERVE_JOB_HISTORY"); + preserve_job_files = cgiGetVariable("PRESERVE_JOB_FILES"); + + if (!max_jobs || atoi(max_jobs) < 0) + max_jobs = "500"; + + if (!preserve_job_history) + preserve_job_history = "On"; + + if (!preserve_job_files) + preserve_job_files = "1d"; + } + else + { + max_jobs = "0"; + preserve_job_history = "No"; + preserve_job_files = "No"; + } + if (!max_clients || atoi(max_clients) <= 0) max_clients = "100"; - if (!max_jobs || atoi(max_jobs) <= 0) - max_jobs = "500"; - - if (!max_log_size || atof(max_log_size) <= 0.0) + if (!max_log_size || atoi(max_log_size) <= 0.0) max_log_size = "1m"; } @@ -1674,7 +1693,7 @@ if ((current_preserve_job_files = cupsGetOption("PreserveJobFiles", num_settings, settings)) == NULL) - current_preserve_job_files = "No"; + current_preserve_job_files = "1d"; if ((current_max_clients = cupsGetOption("MaxClients", num_settings, settings)) == NULL) @@ -2636,18 +2655,25 @@ settings)) == NULL) val = "Yes"; - if (!_cups_strcasecmp(val, "yes") || !_cups_strcasecmp(val, "on") || - !_cups_strcasecmp(val, "true")) + if (val && + (!_cups_strcasecmp(val, "0") || !_cups_strcasecmp(val, "no") || + !_cups_strcasecmp(val, "off") || !_cups_strcasecmp(val, "false") || + !_cups_strcasecmp(val, "disabled"))) { - cgiSetVariable("PRESERVE_JOB_HISTORY", "CHECKED"); + cgiSetVariable("PRESERVE_JOB_HISTORY", "0"); + cgiSetVariable("PRESERVE_JOB_FILES", "0"); + } + else + { + cgiSetVariable("PRESERVE_JOBS", "CHECKED"); + cgiSetVariable("PRESERVE_JOB_HISTORY", val); if ((val = cupsGetOption("PreserveJobFiles", num_settings, settings)) == NULL) - val = "No"; + val = "1d"; - if (!_cups_strcasecmp(val, "yes") || !_cups_strcasecmp(val, "on") || - !_cups_strcasecmp(val, "true")) - cgiSetVariable("PRESERVE_JOB_FILES", "CHECKED"); + cgiSetVariable("PRESERVE_JOB_FILES", val); + } if ((val = cupsGetOption("MaxClients", num_settings, settings)) == NULL) Index: scheduler/conf.c =================================================================== --- scheduler/conf.c (revision 10356) +++ scheduler/conf.c (working copy) @@ -65,6 +65,7 @@ typedef enum { CUPSD_VARTYPE_INTEGER, /* Integer option */ + CUPSD_VARTYPE_TIME, /* Time interval option */ CUPSD_VARTYPE_STRING, /* String option */ CUPSD_VARTYPE_BOOLEAN, /* Boolean option */ CUPSD_VARTYPE_PATHNAME /* File/directory name option */ @@ -99,11 +100,11 @@ { "ConfigFilePerm", &ConfigFilePerm, CUPSD_VARTYPE_INTEGER }, { "DataDir", &DataDir, CUPSD_VARTYPE_STRING }, { "DefaultLanguage", &DefaultLanguage, CUPSD_VARTYPE_STRING }, - { "DefaultLeaseDuration", &DefaultLeaseDuration, CUPSD_VARTYPE_INTEGER }, + { "DefaultLeaseDuration", &DefaultLeaseDuration, CUPSD_VARTYPE_TIME }, { "DefaultPaperSize", &DefaultPaperSize, CUPSD_VARTYPE_STRING }, { "DefaultPolicy", &DefaultPolicy, CUPSD_VARTYPE_STRING }, { "DefaultShared", &DefaultShared, CUPSD_VARTYPE_BOOLEAN }, - { "DirtyCleanInterval", &DirtyCleanInterval, CUPSD_VARTYPE_INTEGER }, + { "DirtyCleanInterval", &DirtyCleanInterval, CUPSD_VARTYPE_TIME }, { "DocumentRoot", &DocumentRoot, CUPSD_VARTYPE_STRING }, { "ErrorLog", &ErrorLog, CUPSD_VARTYPE_STRING }, { "ErrorPolicy", &ErrorPolicy, CUPSD_VARTYPE_STRING }, @@ -114,13 +115,13 @@ #ifdef HAVE_GSSAPI { "GSSServiceName", &GSSServiceName, CUPSD_VARTYPE_STRING }, #endif /* HAVE_GSSAPI */ - { "JobKillDelay", &JobKillDelay, CUPSD_VARTYPE_INTEGER }, + { "JobKillDelay", &JobKillDelay, CUPSD_VARTYPE_TIME }, { "JobRetryLimit", &JobRetryLimit, CUPSD_VARTYPE_INTEGER }, - { "JobRetryInterval", &JobRetryInterval, CUPSD_VARTYPE_INTEGER }, - { "KeepAliveTimeout", &KeepAliveTimeout, CUPSD_VARTYPE_INTEGER }, + { "JobRetryInterval", &JobRetryInterval, CUPSD_VARTYPE_TIME }, + { "KeepAliveTimeout", &KeepAliveTimeout, CUPSD_VARTYPE_TIME }, { "KeepAlive", &KeepAlive, CUPSD_VARTYPE_BOOLEAN }, #ifdef HAVE_LAUNCHD - { "LaunchdTimeout", &LaunchdTimeout, CUPSD_VARTYPE_INTEGER }, + { "LaunchdTimeout", &LaunchdTimeout, CUPSD_VARTYPE_TIME }, #endif /* HAVE_LAUNCHD */ { "LimitRequestBody", &MaxRequestSize, CUPSD_VARTYPE_INTEGER }, { "ListenBackLog", &ListenBackLog, CUPSD_VARTYPE_INTEGER }, @@ -132,30 +133,30 @@ { "MaxClientsPerHost", &MaxClientsPerHost, CUPSD_VARTYPE_INTEGER }, { "MaxCopies", &MaxCopies, CUPSD_VARTYPE_INTEGER }, { "MaxEvents", &MaxEvents, CUPSD_VARTYPE_INTEGER }, - { "MaxHoldTime", &MaxHoldTime, CUPSD_VARTYPE_INTEGER }, + { "MaxHoldTime", &MaxHoldTime, CUPSD_VARTYPE_TIME }, { "MaxJobs", &MaxJobs, CUPSD_VARTYPE_INTEGER }, { "MaxJobsPerPrinter", &MaxJobsPerPrinter, CUPSD_VARTYPE_INTEGER }, { "MaxJobsPerUser", &MaxJobsPerUser, CUPSD_VARTYPE_INTEGER }, { "MaxJobTime", &MaxJobTime, CUPSD_VARTYPE_INTEGER }, - { "MaxLeaseDuration", &MaxLeaseDuration, CUPSD_VARTYPE_INTEGER }, + { "MaxLeaseDuration", &MaxLeaseDuration, CUPSD_VARTYPE_TIME }, { "MaxLogSize", &MaxLogSize, CUPSD_VARTYPE_INTEGER }, { "MaxRequestSize", &MaxRequestSize, CUPSD_VARTYPE_INTEGER }, { "MaxSubscriptions", &MaxSubscriptions, CUPSD_VARTYPE_INTEGER }, { "MaxSubscriptionsPerJob", &MaxSubscriptionsPerJob, CUPSD_VARTYPE_INTEGER }, { "MaxSubscriptionsPerPrinter",&MaxSubscriptionsPerPrinter, CUPSD_VARTYPE_INTEGER }, { "MaxSubscriptionsPerUser", &MaxSubscriptionsPerUser, CUPSD_VARTYPE_INTEGER }, - { "MultipleOperationTimeout", &MultipleOperationTimeout, CUPSD_VARTYPE_INTEGER }, + { "MultipleOperationTimeout", &MultipleOperationTimeout, CUPSD_VARTYPE_TIME }, { "PageLog", &PageLog, CUPSD_VARTYPE_STRING }, { "PageLogFormat", &PageLogFormat, CUPSD_VARTYPE_STRING }, - { "PreserveJobFiles", &JobFiles, CUPSD_VARTYPE_BOOLEAN }, - { "PreserveJobHistory", &JobHistory, CUPSD_VARTYPE_BOOLEAN }, + { "PreserveJobFiles", &JobFiles, CUPSD_VARTYPE_TIME }, + { "PreserveJobHistory", &JobHistory, CUPSD_VARTYPE_TIME }, { "Printcap", &Printcap, CUPSD_VARTYPE_STRING }, { "PrintcapGUI", &PrintcapGUI, CUPSD_VARTYPE_STRING }, - { "ReloadTimeout", &ReloadTimeout, CUPSD_VARTYPE_INTEGER }, + { "ReloadTimeout", &ReloadTimeout, CUPSD_VARTYPE_TIME }, { "RemoteRoot", &RemoteRoot, CUPSD_VARTYPE_STRING }, { "RequestRoot", &RequestRoot, CUPSD_VARTYPE_STRING }, { "RIPCache", &RIPCache, CUPSD_VARTYPE_STRING }, - { "RootCertDuration", &RootCertDuration, CUPSD_VARTYPE_INTEGER }, + { "RootCertDuration", &RootCertDuration, CUPSD_VARTYPE_TIME }, { "ServerAdmin", &ServerAdmin, CUPSD_VARTYPE_STRING }, { "ServerBin", &ServerBin, CUPSD_VARTYPE_PATHNAME }, #ifdef HAVE_SSL @@ -172,7 +173,7 @@ { "SystemGroupAuthKey", &SystemGroupAuthKey, CUPSD_VARTYPE_STRING }, #endif /* HAVE_AUTHORIZATION_H */ { "TempDir", &TempDir, CUPSD_VARTYPE_PATHNAME }, - { "Timeout", &Timeout, CUPSD_VARTYPE_INTEGER }, + { "Timeout", &Timeout, CUPSD_VARTYPE_TIME }, { "WebInterface", &WebInterface, CUPSD_VARTYPE_BOOLEAN } }; #define NUM_VARS (sizeof(variables) / sizeof(variables[0])) @@ -1558,6 +1559,12 @@ cupsdCreateCommonData(); /* + * Update all jobs as needed... + */ + + cupsdUpdateJobs(); + + /* * Update all printers as needed... */ @@ -3140,12 +3147,15 @@ cupsdLogMessage(CUPSD_LOG_ERROR, "Missing integer value for %s on line %d.", line, linenum); + else if (!isdigit(*value & 255)) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Bad integer value for %s on line %d.", + line, linenum); else { int n; /* Number */ char *units; /* Units */ - n = strtol(value, &units, 0); if (units && *units) @@ -3158,6 +3168,13 @@ n *= 1024; else if (tolower(units[0] & 255) == 't') n *= 262144; + else + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unknown integer value for %s on line %d.", + line, linenum); + break; + } } if (n < 0) @@ -3169,6 +3186,61 @@ } break; + case CUPSD_VARTYPE_TIME : + if (!value) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Missing time interval value for %s on line %d.", + line, linenum); + else if (!_cups_strncasecmp(line, "PreserveJob", 11) && + (!_cups_strcasecmp(value, "true") || + !_cups_strcasecmp(value, "on") || + !_cups_strcasecmp(value, "enabled") || + !_cups_strcasecmp(value, "yes"))) + *((int *)var->ptr) = INT_MAX; + else if (!_cups_strcasecmp(value, "false") || + !_cups_strcasecmp(value, "off") || + !_cups_strcasecmp(value, "disabled") || + !_cups_strcasecmp(value, "no")) + *((int *)var->ptr) = 0; + else if (!isdigit(*value & 255)) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unknown time interval value for %s on line %d.", + line, linenum); + else + { + double n; /* Number */ + char *units; /* Units */ + + n = strtod(value, &units); + + if (units && *units) + { + if (tolower(units[0] & 255) == 'w') + n *= 7 * 24 * 60 * 60; + else if (tolower(units[0] & 255) == 'd') + n *= 24 * 60 * 60; + else if (tolower(units[0] & 255) == 'h') + n *= 60 * 60; + else if (tolower(units[0] & 255) == 'm') + n *= 60; + else + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unknown time interval value for %s on line " + "%d.", line, linenum); + break; + } + } + + if (n < 0.0 || n > INT_MAX) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Bad time value for %s on line %d.", + line, linenum); + else + *((int *)var->ptr) = (int)n; + } + break; + case CUPSD_VARTYPE_BOOLEAN : if (!value) cupsdLogMessage(CUPSD_LOG_ERROR, Index: scheduler/printers.c =================================================================== --- scheduler/printers.c (revision 10356) +++ scheduler/printers.c (working copy) @@ -640,8 +640,7 @@ /* operations-supported */ ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "operations-supported", - sizeof(ops) / sizeof(ops[0]) + JobFiles - 1, ops); + "operations-supported", sizeof(ops) / sizeof(ops[0]), ops); /* orientation-requested-supported */ ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM, Index: scheduler/job.c =================================================================== --- scheduler/job.c (revision 10356) +++ scheduler/job.c (working copy) @@ -14,54 +14,58 @@ * * Contents: * - * cupsdAddJob() - Add a new job to the job queue. - * cupsdCancelJobs() - Cancel all jobs for the given - * destination/user. - * cupsdCheckJobs() - Check the pending jobs and start any if the - * destination is available. - * cupsdCleanJobs() - Clean out old jobs. - * cupsdContinueJob() - Continue printing with the next file in a job. - * cupsdDeleteJob() - Free all memory used by a job. - * cupsdFreeAllJobs() - Free all jobs from memory. - * cupsdFindJob() - Find the specified job. - * cupsdGetPrinterJobCount() - Get the number of pending, processing, or held - * jobs in a printer or class. - * cupsdGetUserJobCount() - Get the number of pending, processing, or held - * jobs for a user. - * cupsdLoadAllJobs() - Load all jobs from disk. - * cupsdLoadJob() - Load a single job. - * cupsdMoveJob() - Move the specified job to a different - * destination. - * cupsdReleaseJob() - Release the specified job. - * cupsdRestartJob() - Restart the specified job. - * cupsdSaveAllJobs() - Save a summary of all jobs to disk. - * cupsdSaveJob() - Save a job to disk. - * cupsdSetJobHoldUntil() - Set the hold time for a job. - * cupsdSetJobPriority() - Set the priority of a job, moving it up/down - * in the list as needed. - * cupsdSetJobState() - Set the state of the specified print job. - * cupsdStopAllJobs() - Stop all print jobs. +* cupsdAddJob() - Add a new job to the job queue. + * cupsdCancelJobs() - Cancel all jobs for the given + * destination/user. + * cupsdCheckJobs() - Check the pending jobs and start any if the + * destination is available. + * cupsdCleanJobs() - Clean out old jobs. + * cupsdContinueJob() - Continue printing with the next file in a + * job. + * cupsdDeleteJob() - Free all memory used by a job. + * cupsdFreeAllJobs() - Free all jobs from memory. + * cupsdFindJob() - Find the specified job. + * cupsdGetPrinterJobCount() - Get the number of pending, processing, or + * held jobs in a printer or class. + * cupsdGetUserJobCount() - Get the number of pending, processing, or + * held jobs for a user. + * cupsdLoadAllJobs() - Load all jobs from disk. + * cupsdLoadJob() - Load a single job. + * cupsdMoveJob() - Move the specified job to a different + * destination. + * cupsdReleaseJob() - Release the specified job. + * cupsdRestartJob() - Restart the specified job. + * cupsdSaveAllJobs() - Save a summary of all jobs to disk. + * cupsdSaveJob() - Save a job to disk. + * cupsdSetJobHoldUntil() - Set the hold time for a job. + * cupsdSetJobPriority() - Set the priority of a job, moving it up/down + * in the list as needed. + * cupsdSetJobState() - Set the state of the specified print job. + * cupsdStopAllJobs() - Stop all print jobs. * cupsdUnloadCompletedJobs() - Flush completed job history from memory. - * compare_active_jobs() - Compare the job IDs and priorities of two - * jobs. - * compare_jobs() - Compare the job IDs of two jobs. - * dump_job_history() - Dump any debug messages for a job. - * free_job_history() - Free any log history. - * finalize_job() - Cleanup after job filter processes and support - * data. - * get_options() - Get a string containing the job options. - * ipp_length() - Compute the size of the buffer needed to hold - * the textual IPP attributes. - * load_job_cache() - Load jobs from the job.cache file. - * load_next_job_id() - Load the NextJobId value from the job.cache - * file. - * load_request_root() - Load jobs from the RequestRoot directory. - * set_time() - Set one of the "time-at-xyz" attributes. - * start_job() - Start a print job. - * stop_job() - Stop a print job. - * unload_job() - Unload a job from memory. - * update_job() - Read a status update from a job's filters. - * update_job_attrs() - Update the job-printer-* attributes. + * cupsdUpdateJobs() - Update the history/file files for all jobs. + * compare_active_jobs() - Compare the job IDs and priorities of two + * jobs. + * compare_jobs() - Compare the job IDs of two jobs. + * dump_job_history() - Dump any debug messages for a job. + * free_job_history() - Free any log history. + * finalize_job() - Cleanup after job filter processes and + * support data. + * get_options() - Get a string containing the job options. + * ipp_length() - Compute the size of the buffer needed to hold + * the textual IPP attributes. + * load_job_cache() - Load jobs from the job.cache file. + * load_next_job_id() - Load the NextJobId value from the job.cache + * file. + * load_request_root() - Load jobs from the RequestRoot directory. + * remove_job_files() - Remove the document files for a job. + * remove_job_history() - Remove the control file for a job. + * set_time() - Set one of the "time-at-xyz" attributes. + * start_job() - Start a print job. + * stop_job() - Stop a print job. + * unload_job() - Unload a job from memory. + * update_job() - Read a status update from a job's filters. + * update_job_attrs() - Update the job-printer-* attributes. */ /* @@ -181,6 +185,8 @@ static void load_job_cache(const char *filename); static void load_next_job_id(const char *filename); static void load_request_root(void); +static void remove_job_files(cupsd_job_t *job); +static void remove_job_history(cupsd_job_t *job); static void set_time(cupsd_job_t *job, const char *name); static void start_job(cupsd_job_t *job, cupsd_printer_t *printer); static void stop_job(cupsd_job_t *job, cupsd_jobaction_t action); @@ -447,16 +453,56 @@ cupsdCleanJobs(void) { cupsd_job_t *job; /* Current job */ + time_t curtime; /* Current time */ - if (MaxJobs <= 0 && JobHistory) + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdCleanJobs: MaxJobs=%d, JobHistory=%d, JobFiles=%d", + MaxJobs, JobHistory, JobFiles); + + if (MaxJobs <= 0 && JobHistory == INT_MAX && JobFiles == INT_MAX) return; + curtime = time(NULL); + JobHistoryUpdate = 0; + for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); - job && (cupsArrayCount(Jobs) >= MaxJobs || !JobHistory); + job; job = (cupsd_job_t *)cupsArrayNext(Jobs)) + { if (job->state_value >= IPP_JOB_CANCELED && !job->printer) - cupsdDeleteJob(job, CUPSD_JOB_PURGE); + { + /* + * Expire old jobs (or job files)... + */ + + if ((MaxJobs > 0 && cupsArrayCount(Jobs) >= MaxJobs) || + (job->history_time && job->history_time <= curtime)) + { + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Removing from history."); + cupsdDeleteJob(job, CUPSD_JOB_PURGE); + } + else if (job->file_time && job->file_time <= curtime) + { + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Removing document files."); + remove_job_files(job); + + if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate) + JobHistoryUpdate = job->history_time; + } + else + { + if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate) + JobHistoryUpdate = job->history_time; + + if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate) + JobHistoryUpdate = job->file_time; + } + } + } + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCleanJobs: JobHistoryUpdate=%ld", + (long)JobHistoryUpdate); } @@ -1327,26 +1373,14 @@ cupsd_jobaction_t action)/* I - Action */ { int i; /* Looping var */ - char filename[1024]; /* Job filename */ if (job->printer) finalize_job(job, 1); if (action == CUPSD_JOB_PURGE) - { - /* - * Remove the job info file... - */ + remove_job_history(job); - snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, - job->id); - if (Classification) - cupsdRemoveFile(filename); - else - unlink(filename); - } - cupsdClearString(&job->username); cupsdClearString(&job->dest); for (i = 0; @@ -1355,27 +1389,14 @@ cupsdClearString(job->auth_env + i); cupsdClearString(&job->auth_uid); - if (job->num_files > 0) + if (action == CUPSD_JOB_PURGE) + remove_job_files(job); + else if (job->num_files > 0) { free(job->compressions); free(job->filetypes); - if (action == CUPSD_JOB_PURGE) - { - while (job->num_files > 0) - { - snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, - job->id, job->num_files); - if (Classification) - cupsdRemoveFile(filename); - else - unlink(filename); - - job->num_files --; - } - } - else - job->num_files = 0; + job->num_files = 0; } if (job->history) @@ -1593,23 +1614,9 @@ cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading attributes...", job->id); snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, job->id); - if ((fp = cupsFileOpen(jobfile, "r")) == NULL) - { - char newfile[1024]; /* New job filename */ + if ((fp = cupsdOpenConfFile(jobfile)) == NULL) + goto error; - snprintf(newfile, sizeof(newfile), "%s/c%05d.N", RequestRoot, job->id); - if ((fp = cupsFileOpen(newfile, "r")) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Job %d] Unable to open job control file \"%s\": %s", - job->id, jobfile, strerror(errno)); - goto error; - } - - unlink(jobfile); - rename(newfile, jobfile); - } - if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, job->attrs) != IPP_DATA) { cupsdLogMessage(CUPSD_LOG_ERROR, @@ -1642,8 +1649,37 @@ goto error; } - job->state_value = (ipp_jstate_t)job->state->values[0].integer; + job->state_value = (ipp_jstate_t)job->state->values[0].integer; + job->file_time = 0; + job->history_time = 0; + if (job->state_value >= IPP_JOB_CANCELED && + (attr = ippFindAttribute(job->attrs, "time-at-completed", + IPP_TAG_INTEGER)) != NULL) + { + if (JobHistory < INT_MAX) + job->history_time = attr->values[0].integer + JobHistory; + else + job->history_time = INT_MAX; + + if (job->history_time < time(NULL)) + goto error; /* Expired, remove from history */ + + if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate) + JobHistoryUpdate = job->history_time; + + if (JobFiles < INT_MAX) + job->file_time = attr->values[0].integer + JobFiles; + else + job->file_time = INT_MAX; + + if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate) + JobHistoryUpdate = job->file_time; + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdLoadJob: JobHistoryUpdate=%ld", + (long)JobHistoryUpdate); + } + if (!job->dest) { if ((attr = ippFindAttribute(job->attrs, "job-printer-uri", @@ -1950,25 +1986,9 @@ ippDelete(job->attrs); job->attrs = NULL; - if (job->compressions) - { - free(job->compressions); - job->compressions = NULL; - } + remove_job_history(job); + remove_job_files(job); - if (job->filetypes) - { - free(job->filetypes); - job->filetypes = NULL; - } - - job->num_files = 0; - - if (Classification) - cupsdRemoveFile(jobfile); - else - unlink(jobfile); - return (0); } @@ -2140,8 +2160,7 @@ void cupsdSaveJob(cupsd_job_t *job) /* I - Job */ { - char filename[1024], /* Job control filename */ - newfile[1024]; /* New job control filename */ + char filename[1024]; /* Job control filename */ cups_file_t *fp; /* Job file */ @@ -2149,17 +2168,10 @@ job, job->id, job->attrs); snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id); - snprintf(newfile, sizeof(newfile), "%s/c%05d.N", RequestRoot, job->id); - if ((fp = cupsFileOpen(newfile, "w")) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Job %d] Unable to create job control file \"%s\": %s", - job->id, newfile, strerror(errno)); + if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm & 0600)) == NULL) return; - } - fchmod(cupsFileNumber(fp), 0600); fchown(cupsFileNumber(fp), RunUser, Group); job->attrs->state = IPP_IDLE; @@ -2170,24 +2182,11 @@ cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Unable to write job control file.", job->id); cupsFileClose(fp); - unlink(newfile); return; } - if (cupsFileClose(fp)) - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Job %d] Unable to close job control file: %s", - job->id, strerror(errno)); - else - { - unlink(filename); - if (rename(newfile, filename)) - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Job %d] Unable to finalize job control file: %s", - job->id, strerror(errno)); - else - job->dirty = 0; - } + if (!cupsdCloseCreatedConfFile(fp, filename)) + job->dirty = 0; } @@ -2616,28 +2615,8 @@ */ if (!JobHistory || !JobFiles || action == CUPSD_JOB_PURGE) - { - for (i = 1; i <= job->num_files; i ++) - { - snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, - job->id, i); - if (Classification) - cupsdRemoveFile(filename); - else - unlink(filename); - } + remove_job_files(job); - if (job->num_files > 0) - { - free(job->filetypes); - free(job->compressions); - - job->num_files = 0; - job->filetypes = NULL; - job->compressions = NULL; - } - } - if (JobHistory && action != CUPSD_JOB_PURGE) { /* @@ -2728,6 +2707,62 @@ /* + * 'cupsdUpdateJobs()' - Update the history/file files for all jobs. + */ + +void +cupsdUpdateJobs(void) +{ + cupsd_job_t *job; /* Current job */ + time_t curtime; /* Current time */ + ipp_attribute_t *attr; /* time-at-completed attribute */ + + + curtime = time(NULL); + JobHistoryUpdate = 0; + + for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); + job; + job = (cupsd_job_t *)cupsArrayNext(Jobs)) + { + if (job->state_value >= IPP_JOB_CANCELED && + (attr = ippFindAttribute(job->attrs, "time-at-completed", + IPP_TAG_INTEGER)) != NULL) + { + /* + * Update history/file expiration times... + */ + + if (JobHistory < INT_MAX) + job->history_time = attr->values[0].integer + JobHistory; + else + job->history_time = INT_MAX; + + if (job->history_time < curtime) + { + cupsdDeleteJob(job, CUPSD_JOB_PURGE); + continue; + } + + if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate) + JobHistoryUpdate = job->history_time; + + if (JobFiles < INT_MAX) + job->file_time = attr->values[0].integer + JobFiles; + else + job->file_time = INT_MAX; + + if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate) + JobHistoryUpdate = job->file_time; + } + } + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdUpdateAllJobs: JobHistoryUpdate=%ld", + (long)JobHistoryUpdate); +} + + +/* * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs. */ @@ -4221,6 +4256,67 @@ /* + * 'remove_job_files()' - Remove the document files for a job. + */ + +static void +remove_job_files(cupsd_job_t *job) /* I - Job */ +{ + int i; /* Looping var */ + char filename[1024]; /* Document filename */ + + + if (job->num_files <= 0) + return; + + for (i = 1; i <= job->num_files; i ++) + { + snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, + job->id, i); + if (Classification) + cupsdRemoveFile(filename); + else + unlink(filename); + } + + free(job->filetypes); + free(job->compressions); + + job->file_time = 0; + job->num_files = 0; + job->filetypes = NULL; + job->compressions = NULL; + + LastEvent |= CUPSD_EVENT_PRINTER_STATE_CHANGED; +} + + +/* + * 'remove_job_history()' - Remove the control file for a job. + */ + +static void +remove_job_history(cupsd_job_t *job) /* I - Job */ +{ + char filename[1024]; /* Control filename */ + + + /* + * Remove the job info file... + */ + + snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, + job->id); + if (Classification) + cupsdRemoveFile(filename); + else + unlink(filename); + + LastEvent |= CUPSD_EVENT_PRINTER_STATE_CHANGED; +} + + +/* * 'set_time()' - Set one of the "time-at-xyz" attributes. */ @@ -4229,13 +4325,40 @@ const char *name) /* I - Name of attribute */ { ipp_attribute_t *attr; /* Time attribute */ + time_t curtime; /* Current time */ + curtime = time(NULL); + + cupsdLogJob(job, CUPSD_LOG_DEBUG, "%s=%ld", name, (long)curtime); + if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL) { attr->value_tag = IPP_TAG_INTEGER; - attr->values[0].integer = time(NULL); + attr->values[0].integer = curtime; } + + if (!strcmp(name, "time-at-completed")) + { + if (JobHistory < INT_MAX) + job->history_time = attr->values[0].integer + JobHistory; + else + job->history_time = INT_MAX; + + if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate) + JobHistoryUpdate = job->history_time; + + if (JobFiles < INT_MAX) + job->file_time = attr->values[0].integer + JobFiles; + else + job->file_time = INT_MAX; + + if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate) + JobHistoryUpdate = job->file_time; + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_time: JobHistoryUpdate=%ld", + (long)JobHistoryUpdate); + } } @@ -4289,6 +4412,8 @@ job->cost = 0; job->current_file = 0; + job->file_time = 0; + job->history_time = 0; job->progress = 0; job->printer = printer; printer->job = job; Index: scheduler/job.h =================================================================== --- scheduler/job.h (revision 10356) +++ scheduler/job.h (working copy) @@ -47,8 +47,10 @@ ipp_attribute_t *sheets; /* job-media-sheets-completed */ time_t access_time, /* Last access time */ cancel_time, /* When to cancel/send SIGTERM */ - kill_time, /* When to send SIGKILL */ - hold_until; /* Hold expiration date/time */ + file_time, /* Job file retain time */ + history_time, /* Job history retain time */ + hold_until, /* Hold expiration date/time */ + kill_time; /* When to send SIGKILL */ ipp_attribute_t *state; /* Job state */ ipp_attribute_t *reasons; /* Job state reasons */ ipp_attribute_t *job_sheets; /* Job sheets (NULL if none) */ @@ -91,10 +93,12 @@ * Globals... */ -VAR int JobHistory VALUE(1); +VAR int JobHistory VALUE(INT_MAX); /* Preserve job history? */ -VAR int JobFiles VALUE(0); +VAR int JobFiles VALUE(86400); /* Preserve job files? */ +VAR time_t JobHistoryUpdate VALUE(0); + /* Time for next job history update */ VAR int MaxJobs VALUE(0), /* Max number of jobs */ MaxActiveJobs VALUE(0), @@ -161,6 +165,7 @@ int kill_delay); extern int cupsdTimeoutJob(cupsd_job_t *job); extern void cupsdUnloadCompletedJobs(void); +extern void cupsdUpdateJobs(void); /* Index: scheduler/cupsd.h =================================================================== --- scheduler/cupsd.h (revision 10356) +++ scheduler/cupsd.h (working copy) @@ -82,12 +82,10 @@ * Defaults... */ -#define DEFAULT_HISTORY 1 /* Preserve job history? */ -#define DEFAULT_FILES 0 /* Preserve job files? */ +#define DEFAULT_HISTORY INT_MAX /* Preserve job history? */ +#define DEFAULT_FILES 86400 /* Preserve job files? */ #define DEFAULT_TIMEOUT 300 /* Timeout during requests/updates */ #define DEFAULT_KEEPALIVE 30 /* Timeout between requests */ -#define DEFAULT_INTERVAL 30 /* Interval between browse updates */ -#define DEFAULT_CHARSET "utf-8" /* Default charset */ /* Index: scheduler/main.c =================================================================== --- scheduler/main.c (revision 10356) +++ scheduler/main.c (working copy) @@ -981,11 +981,17 @@ if ((current_time - senddoc_time) >= 10) { cupsdCheckJobs(); - cupsdCleanJobs(); senddoc_time = current_time; } /* + * Clean job history... + */ + + if (JobHistoryUpdate && current_time >= JobHistoryUpdate) + cupsdCleanJobs(); + + /* * Log statistics at most once a minute when in debug mode... */ @@ -1773,6 +1779,9 @@ #endif /* HAVE_AVAHI */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "select_timeout: JobHistoryUpdate=%ld", + (long)JobHistoryUpdate); + /* * Check to see if any of the clients have pending data to be * processed; if so, the timeout should be 0... @@ -1865,29 +1874,38 @@ } /* - * Check for any active jobs... + * Check for any job activity... */ + if (JobHistoryUpdate && timeout > JobHistoryUpdate) + { + timeout = JobHistoryUpdate; + why = "update job history"; + } + for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); job; job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) { + if (job->cancel_time && job->cancel_time < timeout) + { + timeout = job->cancel_time; + why = "cancel stuck jobs"; + } + if (job->kill_time && job->kill_time < timeout) { timeout = job->kill_time; why = "kill unresponsive jobs"; } - else if (job->cancel_time && job->cancel_time < timeout) + + if (job->state_value == IPP_JOB_HELD && job->hold_until < timeout) { - timeout = job->cancel_time; - why = "cancel stuck jobs"; - } - else if (job->state_value == IPP_JOB_HELD && job->hold_until < timeout) - { timeout = job->hold_until; why = "release held jobs"; } - else if (job->state_value == IPP_JOB_PENDING && timeout > (now + 10)) + + if (job->state_value == IPP_JOB_PENDING && timeout > (now + 10)) { timeout = now + 10; why = "start pending jobs";