]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/job.c
Merge changes from CUPS 1.4svn-r8606.
[thirdparty/cups.git] / scheduler / job.c
index 5adf0e050e50f3de252f0c208e8024683e14e314..19e0af1aa043bcb524b2a083c70a251a70bde5b1 100644 (file)
@@ -271,23 +271,36 @@ cupsdCheckJobs(void)
   cupsd_printer_t      *printer,       /* Printer destination */
                        *pclass;        /* Printer class destination */
   ipp_attribute_t      *attr;          /* Job attribute */
+  time_t               curtime;        /* Current time */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d",
                   cupsArrayCount(ActiveJobs), Sleeping, NeedReload);
 
+  curtime = time(NULL);
+
   for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
        job;
        job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
   {
+   /*
+    * Kill jobs if they are unresponsive...
+    */
+
+    if (job->kill_time && job->kill_time <= curtime)
+    {
+      stop_job(job, CUPSD_JOB_FORCE);
+      continue;
+    }
+
    /*
     * Start held jobs if they are ready...
     */
 
     if (job->state_value == IPP_JOB_HELD &&
         job->hold_until &&
-       job->hold_until < time(NULL))
+       job->hold_until < curtime)
     {
       if (job->pending_timeout)
       {
@@ -318,6 +331,14 @@ cupsdCheckJobs(void)
                        "Job submission timed out.");
     }
 
+   /*
+    * Continue jobs that are waiting on the FilterLimit...
+    */
+
+    if (job->pending_cost > 0 &&
+       ((FilterLevel + job->pending_cost) < FilterLimit || FilterLevel == 0))
+      cupsdContinueJob(job);
+
    /*
     * Start pending jobs if the destination is available...
     */
@@ -428,7 +449,7 @@ cupsdContinueJob(cupsd_job_t *job)  /* I - Job */
 {
   int                  i;              /* Looping var */
   int                  slot;           /* Pipe slot */
-  cups_array_t         *filters,       /* Filters for job */
+  cups_array_t         *filters = NULL,/* Filters for job */
                        *prefilters;    /* Filters with prefilters */
   mime_filter_t                *filter,        /* Current filter */
                        *prefilter,     /* Prefilter */
@@ -437,13 +458,16 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
   ipp_attribute_t      *attr;          /* Current attribute */
   const char           *ptr,           /* Pointer into value */
                        *abort_message; /* Abort message */
+  ipp_jstate_t         abort_state = IPP_JOB_STOPPED;
+                                       /* New job state on abort */
   struct stat          backinfo;       /* Backend file information */
   int                  backroot;       /* Run backend as root? */
   int                  pid;            /* Process ID of new filter process */
   int                  banner_page;    /* 1 if banner page, 0 otherwise */
-  int                  filterfds[2][2];/* Pipes used between filters */
+  int                  filterfds[2][2] = { { -1, -1 }, { -1, -1 } };
+                                       /* Pipes used between filters */
   int                  envc;           /* Number of environment variables */
-  char                 **argv,         /* Filter command-line arguments */
+  char                 **argv = NULL,  /* Filter command-line arguments */
                        filename[1024], /* Job filename */
                        command[1024],  /* Full path to command */
                        jobid[255],     /* Job ID string */
@@ -490,7 +514,11 @@ cupsdContinueJob(cupsd_job_t *job) /* I - Job */
 
   FilterLevel -= job->cost;
 
-  filters = NULL;
+  job->cost         = 0;
+  job->pending_cost = 0;
+
+  memset(job->filters, 0, sizeof(job->filters));
+
 
   if (job->printer->raw)
   {
@@ -500,8 +528,6 @@ cupsdContinueJob(cupsd_job_t *job)  /* I - Job */
     */
 
     cupsdLogJob(job, CUPSD_LOG_DEBUG, "Sending job to queue tagged as raw...");
-
-    filters = NULL;
   }
   else
   {
@@ -518,13 +544,10 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
                  "Unable to convert file %d to printable format!",
                  job->current_file);
 
-      job->current_file ++;
+      abort_message = "Aborting job because it cannot be printed.";
+      abort_state   = IPP_JOB_ABORTED;
 
-      if (job->current_file == job->num_files)
-        cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_DEFAULT,
-                        "Aborting job because it cannot be printed.");
-
-      return;
+      goto abort_job;
     }
 
    /*
@@ -598,6 +621,9 @@ cupsdContinueJob(cupsd_job_t *job)  /* I - Job */
                "cupsdContinueJob: file=%d, cost=%d, level=%d, limit=%d",
                job->current_file, job->cost, FilterLevel,
                FilterLimit);
+
+    job->pending_cost = job->cost;
+    job->cost         = 0;
     return;
   }
 
@@ -625,12 +651,9 @@ cupsdContinueJob(cupsd_job_t *job) /* I - Job */
 
       cupsArrayDelete(filters);
 
-      cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
-                      "Stopping job because the scheduler ran out of "
-                      "memory.");
+      abort_message = "Stopping job because the scheduler ran out of memory.";
 
-      FilterLevel -= job->cost;
-      return;
+      goto abort_job;
     }
   }
 
@@ -659,14 +682,9 @@ cupsdContinueJob(cupsd_job_t *job) /* I - Job */
       cupsdLogJob(job, CUPSD_LOG_DEBUG,
                  "Unable to add port monitor - %s", strerror(errno));
 
-      cupsArrayDelete(filters);
-
-      cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
-                      "Stopping job because the scheduler ran out of "
-                      "memory.");
+      abort_message = "Stopping job because the scheduler ran out of memory.";
 
-      FilterLevel -= job->cost;
-      return;
+      goto abort_job;
     }
   }
 
@@ -680,13 +698,10 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
                "Too many filters (%d > %d), unable to print!",
                cupsArrayCount(filters), MAX_FILTERS);
 
-    cupsArrayDelete(filters);
-    cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
-                    "Stopping job because it needs too many filters to "
-                    "print.");
+    abort_message = "Aborting job because it needs too many filters to print.";
+    abort_state   = IPP_JOB_ABORTED;
 
-    FilterLevel -= job->cost;
-    return;
+    goto abort_job;
   }
 
  /*
@@ -726,12 +741,9 @@ cupsdContinueJob(cupsd_job_t *job) /* I - Job */
   if ((options = get_options(job, banner_page, copies, sizeof(copies), title,
                              sizeof(title))) == NULL)
   {
-    cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
-                    "Stopping job because the scheduler ran out of memory.");
-    cupsArrayDelete(filters);
+    abort_message = "Stopping job because the scheduler ran out of memory.";
 
-    FilterLevel -= job->cost;
-    return;
+    goto abort_job;
   }
 
  /*
@@ -761,12 +773,10 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
   {
     cupsdLogMessage(CUPSD_LOG_DEBUG, "Unable to allocate argument array - %s",
                     strerror(errno));
-    cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
-                    "Stopping job because the scheduler ran out of memory.");
-    cupsArrayDelete(filters);
 
-    FilterLevel -= job->cost;
-    return;
+    abort_message = "Stopping job because the scheduler ran out of memory.";
+
+    goto abort_job;
   }
 
   sprintf(jobid, "%d", job->id);
@@ -953,13 +963,6 @@ cupsdContinueJob(cupsd_job_t *job) /* I - Job */
   * Now create processes for all of the filters...
   */
 
-  filterfds[0][0] = -1;
-  filterfds[0][1] = -1;
-  filterfds[1][0] = -1;
-  filterfds[1][1] = -1;
-
-  memset(job->filters, 0, sizeof(job->filters));
-
   for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
        filter;
        i ++, filter = (mime_filter_t *)cupsArrayNext(filters))
@@ -1036,7 +1039,7 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
     pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
                             filterfds[slot][1], job->status_pipes[1],
                            job->back_pipes[0], job->side_pipes[0], 0,
-                           job->profile, job->id, job->filters + i);
+                           job->profile, job, job->filters + i);
 
     cupsdClosePipe(filterfds[!slot]);
 
@@ -1045,7 +1048,7 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
       cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
                  filter->filter, strerror(errno));
 
-      abort_message = "Stopped job because the scheduler could not execute a "
+      abort_message = "Stopping job because the scheduler could not execute a "
                      "filter.";
 
       goto abort_job;
@@ -1091,7 +1094,7 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
       pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
                              filterfds[slot][1], job->status_pipes[1],
                              job->back_pipes[1], job->side_pipes[1],
-                             backroot, job->profile, job->id, &(job->backend));
+                             backroot, job->profile, job, &(job->backend));
 
       if (pid == 0)
       {
@@ -1147,6 +1150,16 @@ cupsdContinueJob(cupsd_job_t *job)       /* I - Job */
   cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.",
                 job->id);
 
+ /*
+  * If we get here than we are able to run the printer driver filters, so clear
+  * the missing and insecure warnings...
+  */
+
+  if (cupsdSetPrinterReasons(job->printer, "-cups-missing-filter-warning,"
+                                          "cups-insecure-filter-warning"))
+    cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, job->printer, NULL,
+                  "Printer drivers now functional.");
+
   return;
 
 
@@ -1157,25 +1170,50 @@ cupsdContinueJob(cupsd_job_t *job)      /* I - Job */
 
   abort_job:
 
+  FilterLevel -= job->cost;
+  job->cost = 0;
+
   for (slot = 0; slot < 2; slot ++)
     cupsdClosePipe(filterfds[slot]);
 
+  cupsArrayDelete(filters);
+
+  if (argv)
+  {
+    if (job->printer->remote && job->num_files > 1)
+    {
+      for (i = 0; i < job->num_files; i ++)
+       free(argv[i + 6]);
+    }
+
+    free(argv);
+  }
+
+  cupsdClosePipe(job->print_pipes);
+  cupsdClosePipe(job->back_pipes);
+  cupsdClosePipe(job->side_pipes);
+
+  cupsdRemoveSelect(job->status_pipes[0]);
   cupsdClosePipe(job->status_pipes);
   cupsdStatBufDelete(job->status_buffer);
   job->status_buffer = NULL;
 
-  cupsArrayDelete(filters);
+ /*
+  * Update the printer and job state.
+  */
 
-  if (job->printer->remote && job->num_files > 1)
-  {
-    for (i = 0; i < job->num_files; i ++)
-      free(argv[i + 6]);
-  }
+  cupsdSetJobState(job, abort_state, CUPSD_JOB_DEFAULT, "%s", abort_message);
+  cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0);
+  update_job_attrs(job, 0);
 
-  free(argv);
+  cupsArrayRemove(PrintingJobs, job);
 
-  cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT, "%s",
-                   abort_message);
+ /*
+  * Clear the printer <-> job association...
+  */
+
+  job->printer->job = NULL;
+  job->printer      = NULL;
 }
 
 
@@ -1256,7 +1294,7 @@ cupsdFreeAllJobs(void)
 
   cupsdHoldSignals();
 
-  cupsdStopAllJobs(1);
+  cupsdStopAllJobs(CUPSD_JOB_FORCE, 0);
   cupsdSaveAllJobs();
 
   for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
@@ -1839,6 +1877,7 @@ cupsdSaveAllJobs(void)
     cupsFilePrintf(fp, "<Job %d>\n", job->id);
     cupsFilePrintf(fp, "State %d\n", job->state_value);
     cupsFilePrintf(fp, "Priority %d\n", job->priority);
+    cupsFilePrintf(fp, "HoldUntil %d\n", (int)job->hold_until);
     cupsFilePrintf(fp, "Username %s\n", job->username);
     cupsFilePrintf(fp, "Destination %s\n", job->dest);
     cupsFilePrintf(fp, "DestType %d\n", job->dtype);
@@ -2337,6 +2376,13 @@ cupsdSetJobState(
        break;
   }
 
+ /*
+  * Finalize the job immediately if we forced things...
+  */
+
+  if (action == CUPSD_JOB_FORCE)
+    finalize_job(job);
+
  /*
   * Update the server "busy" state...
   */
@@ -2351,7 +2397,8 @@ cupsdSetJobState(
 
 void
 cupsdStopAllJobs(
-    cupsd_jobaction_t action)          /* I - Action */
+    cupsd_jobaction_t action,          /* I - Action */
+    int               kill_delay)      /* I - Number of seconds before we kill */
 {
   cupsd_job_t  *job;                   /* Current job */
 
@@ -2361,7 +2408,12 @@ cupsdStopAllJobs(
   for (job = (cupsd_job_t *)cupsArrayFirst(PrintingJobs);
        job;
        job = (cupsd_job_t *)cupsArrayNext(PrintingJobs))
+  {
+    if (kill_delay)
+      job->kill_time = time(NULL) + kill_delay;
+
     cupsdSetJobState(job, IPP_JOB_PENDING, action, NULL);
+  }
 }
 
 
@@ -2466,13 +2518,12 @@ finalize_job(cupsd_job_t *job)          /* I - Job */
   * Close pipes and status buffer...
   */
 
-  cupsdRemoveSelect(job->status_buffer->fd);
-
   cupsdClosePipe(job->print_pipes);
   cupsdClosePipe(job->back_pipes);
   cupsdClosePipe(job->side_pipes);
-  cupsdClosePipe(job->status_pipes);
 
+  cupsdRemoveSelect(job->status_pipes[0]);
+  cupsdClosePipe(job->status_pipes);
   cupsdStatBufDelete(job->status_buffer);
   job->status_buffer = NULL;
 
@@ -2498,8 +2549,8 @@ finalize_job(cupsd_job_t *job)            /* I - Job */
     default :
     case IPP_JOB_PROCESSING :
     case IPP_JOB_COMPLETED :
-       job_state     = IPP_JOB_COMPLETED;
-       message       = "Job completed.";
+       job_state = IPP_JOB_COMPLETED;
+       message   = "Job completed.";
         break;
 
     case IPP_JOB_STOPPED :
@@ -2566,8 +2617,11 @@ finalize_job(cupsd_job_t *job)           /* I - Job */
            * another printer...
            */
 
-            job_state = IPP_JOB_PENDING;
-           message   = "Retrying job on another printer.";
+            if (job_state == IPP_JOB_COMPLETED)
+           {
+             job_state = IPP_JOB_PENDING;
+             message   = "Retrying job on another printer.";
+           }
           }
          else if (!strcmp(job->printer->error_policy, "retry-current-job"))
          {
@@ -2576,59 +2630,66 @@ finalize_job(cupsd_job_t *job)          /* I - Job */
            * and we'll retry on the same printer...
            */
 
-            job_state = IPP_JOB_PENDING;
-           message   = "Retrying job on same printer.";
+            if (job_state == IPP_JOB_COMPLETED)
+           {
+             job_state = IPP_JOB_PENDING;
+             message   = "Retrying job on same printer.";
+           }
           }
          else if ((job->printer->type & CUPS_PRINTER_FAX) ||
                   !strcmp(job->printer->error_policy, "retry-job"))
          {
-          /*
-           * The job was queued on a fax or the error policy is "retry-job" -
-           * hold the job if the number of retries is less than the
-           * JobRetryLimit, otherwise abort the job.
-           */
-
-           job->tries ++;
-
-           if (job->tries >= JobRetryLimit)
+            if (job_state == IPP_JOB_COMPLETED)
            {
             /*
-             * Too many tries...
+             * The job was queued on a fax or the error policy is "retry-job" -
+             * hold the job if the number of retries is less than the
+             * JobRetryLimit, otherwise abort the job.
              */
 
-              snprintf(buffer, sizeof(buffer),
-                      "Job aborted after %d unsuccessful attempts.",
-                      JobRetryLimit);
-              job_state = IPP_JOB_ABORTED;
-             message   = buffer;
-           }
-           else
-           {
-            /*
-             * Try again in N seconds...
-             */
+             job->tries ++;
 
-             set_hold_until(job, time(NULL) + JobRetryInterval);
+             if (job->tries >= JobRetryLimit)
+             {
+              /*
+               * Too many tries...
+               */
+
+               snprintf(buffer, sizeof(buffer),
+                        "Job aborted after %d unsuccessful attempts.",
+                        JobRetryLimit);
+               job_state = IPP_JOB_ABORTED;
+               message   = buffer;
+             }
+             else
+             {
+              /*
+               * Try again in N seconds...
+               */
 
-              snprintf(buffer, sizeof(buffer),
-                      "Job held for %d seconds since it could not be sent.",
-                      JobRetryInterval);
-              job_state = IPP_JOB_HELD;
-             message   = buffer;
-           }
+               set_hold_until(job, time(NULL) + JobRetryInterval);
+
+               snprintf(buffer, sizeof(buffer),
+                        "Job held for %d seconds since it could not be sent.",
+                        JobRetryInterval);
+               job_state = IPP_JOB_HELD;
+               message   = buffer;
+             }
+            }
          }
-         else if (!strcmp(job->printer->error_policy, "abort-job"))
+         else if (!strcmp(job->printer->error_policy, "abort-job") &&
+                  job_state == IPP_JOB_COMPLETED)
          {
            job_state = IPP_JOB_ABORTED;
            message   = "Job aborted due to backend errors; please consult "
                        "the error_log file for details.";
          }
-         else
+         else if (job->state_value == IPP_JOB_PROCESSING)
           {
+            job_state     = IPP_JOB_PENDING;
            printer_state = IPP_PRINTER_STOPPED;
-           job_state     = IPP_JOB_PENDING;
            message       = "Printer stopped due to backend errors; please "
-                           "consult the error_log file for details.";
+                           "consult the error_log file for details.";
          }
           break;
 
@@ -2637,21 +2698,27 @@ finalize_job(cupsd_job_t *job)          /* I - Job */
          * Abort the job...
          */
 
-         job_state = IPP_JOB_ABORTED;
-         message   = "Job aborted due to backend errors; please consult "
-                     "the error_log file for details.";
+         if (job_state == IPP_JOB_COMPLETED)
+         {
+           job_state = IPP_JOB_ABORTED;
+           message   = "Job aborted due to backend errors; please consult "
+                       "the error_log file for details.";
+         }
           break;
 
       case CUPS_BACKEND_HOLD :
-         /*
-         * Hold the job...
-         */
+         if (job_state == IPP_JOB_COMPLETED)
+         {
+          /*
+           * Hold the job...
+           */
 
-         cupsdSetJobHoldUntil(job, "indefinite", 1);
+           cupsdSetJobHoldUntil(job, "indefinite", 1);
 
-         job_state = IPP_JOB_HELD;
-         message   = "Job held indefinitely due to backend errors; please "
-                     "consult the error_log file for details.";
+           job_state = IPP_JOB_HELD;
+           message   = "Job held indefinitely due to backend errors; please "
+                       "consult the error_log file for details.";
+          }
           break;
 
       case CUPS_BACKEND_STOP :
@@ -2660,9 +2727,11 @@ finalize_job(cupsd_job_t *job)           /* I - Job */
          */
 
          printer_state = IPP_PRINTER_STOPPED;
-         job_state     = IPP_JOB_PENDING;
          message       = "Printer stopped due to backend errors; please "
                          "consult the error_log file for details.";
+
+         if (job_state == IPP_JOB_COMPLETED)
+           job_state = IPP_JOB_PENDING;
           break;
 
       case CUPS_BACKEND_AUTH_REQUIRED :
@@ -2670,10 +2739,13 @@ finalize_job(cupsd_job_t *job)          /* I - Job */
          * Hold the job for authentication...
          */
 
-         cupsdSetJobHoldUntil(job, "auth-info-required", 1);
+         if (job_state == IPP_JOB_COMPLETED)
+         {
+           cupsdSetJobHoldUntil(job, "auth-info-required", 1);
 
-         job_state = IPP_JOB_HELD;
-         message   = "Job held for authentication.";
+           job_state = IPP_JOB_HELD;
+           message   = "Job held for authentication.";
+          }
           break;
     }
   }
@@ -2683,9 +2755,12 @@ finalize_job(cupsd_job_t *job)           /* I - Job */
     * Filter had errors; stop job...
     */
 
-    job_state = IPP_JOB_STOPPED;
-    message   = "Job stopped due to filter errors; please consult the "
-               "error_log file for details.";
+    if (job_state == IPP_JOB_COMPLETED)
+    {
+      job_state = IPP_JOB_STOPPED;
+      message   = "Job stopped due to filter errors; please consult the "
+                 "error_log file for details.";
+    }
   }
 
  /*
@@ -3173,6 +3248,10 @@ load_job_cache(const char *filename)     /* I - job.cache filename */
       else if (job->state_value > IPP_JOB_COMPLETED)
         job->state_value = IPP_JOB_COMPLETED;
     }
+    else if (!strcasecmp(line, "HoldUntil"))
+    {
+      job->hold_until = atoi(value);
+    }
     else if (!strcasecmp(line, "Priority"))
     {
       job->priority = atoi(value);
@@ -3558,6 +3637,9 @@ start_job(cupsd_job_t     *job,           /* I - Job ID */
   job->status_buffer = cupsdStatBufNew(job->status_pipes[0], NULL);
   job->status_level  = CUPSD_LOG_INFO;
 
+  if (job->printer_message)
+    cupsdSetString(&(job->printer_message->values[0].string.text), "");
+
  /*
   * Create the backchannel pipes and make them non-blocking...
   */
@@ -3639,6 +3721,11 @@ stop_job(cupsd_job_t       *job, /* I - Job */
   FilterLevel -= job->cost;
   job->cost   = 0;
 
+  if (action == CUPSD_JOB_DEFAULT && !job->kill_time)
+    job->kill_time = time(NULL) + JobKillDelay;
+  else if (action == CUPSD_JOB_FORCE)
+    job->kill_time = 0;
+
   for (i = 0; job->filters[i]; i ++)
     if (job->filters[i] > 0)
       cupsdEndProcess(job->filters[i], action == CUPSD_JOB_FORCE);
@@ -3952,8 +4039,7 @@ update_job(cupsd_job_t *job)              /* I - Job to check */
 #endif /* __APPLE__ */
     else
     {
-      if (loglevel != CUPSD_LOG_INFO && loglevel > LogLevel)
-       cupsdLogJob(job, loglevel, "%d %s", loglevel, message);
+      cupsdLogJob(job, loglevel, "%s", message);
 
       if (loglevel < CUPSD_LOG_DEBUG)
       {
@@ -3961,9 +4047,9 @@ update_job(cupsd_job_t *job)              /* I - Job to check */
                sizeof(job->printer->state_message));
        cupsdAddPrinterHistory(job->printer);
 
-       event |= CUPSD_EVENT_PRINTER_STATE;
+       event |= CUPSD_EVENT_PRINTER_STATE | CUPSD_EVENT_JOB_PROGRESS;
 
-       if (loglevel <= job->status_level)
+       if (loglevel < job->status_level)
        {
         /*
          * Some messages show in the job-printer-state-message attribute...
@@ -3994,6 +4080,10 @@ update_job(cupsd_job_t *job)             /* I - Job to check */
                      "Printer \"%s\" state changed.",
                  job->printer->name);
 
+  if (event & CUPSD_EVENT_JOB_PROGRESS)
+    cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
+                  "%s", job->printer->state_message);
+
   if (ptr == NULL && !job->status_buffer->bufused)
   {
    /*