Retrieving the total number of Jobs in process from cupsd

Sebastian James seb at esfnet.co.uk
Sun Apr 22 03:36:58 PDT 2007


> > Hi,
> >
> > I'm trying to write a way to retrieve both the MaxJobs parameter (as set in cupsd.conf) and the total number of jobs in process in cupsd, across all queues.
> >
> > I added an extra IPP operation which I called CUPS_GET_MAXJOBS.
> >
> > This operation is designed to return two integer attributes which I called cupsd-maxjobs and cupsd-numjobs. I chose the type IPP_TAG_EVENT_NOTIFICATION for these attributes, slightly unsurely.
> >
> > The client code, to request this operation (the code running on the same host as cupsd in this case) is:
> >
> > int
> > check_cupsd_for_maxjobs (void)
> > {
> >   /* Find what MaxJobs is. This is held in the scheduler as a global
> >    * variable */
> >   /* Find what current number of jobs is */
> >   /* If we're within 10 of MaxJobs, return 0 */
> >   /* Any errors, return -1 */
> >
> >   http_t * connection;
> >   ipp_t * rqst;
> >   ipp_t * rtn;
> >   ipp_attribute_t * ipp_attributes;
> >   cups_lang_t * lang;
> >
> >   int MyMaxJobs = 0;
> >   int MyNumJobs = 0;
> >
> >   static const char * jobinfo_attributes[] = {
> >     "cupsd-maxjobs",
> >     "cupsd-numjobs"
> >   };
> >   int n_attributes = 2; // Should match the above number of entries
> >
> >   //syslog (LOG_INFO, "At start of %s", __FUNCTION__);
> >
> >   /*
> >    * Setup a connection and request data...
> >    */
> >
> >   if ((connection = httpConnectEncrypt(cupsServer(), ippPort(),
> > 				       cupsEncryption())) == NULL)
> >   {
> >     syslog(LOG_ERR, "%s: Unable to connect to server %s: %s",
> > 	   __FUNCTION__, cupsServer(), strerror(errno));
> >     return -1;
> >   }
> >
> >   rqst = ippNew();
> >   rqst->request.op.operation_id = CUPS_GET_MAXJOBS;
> >   rqst->request.op.request_id   = 1;
> >   lang = cupsLangDefault();
> >
> >   ippAddString(rqst,
> > 	       IPP_TAG_OPERATION,
> > 	       IPP_TAG_CHARSET,
> > 	       "attributes-charset",
> > 	       NULL,
> > 	       cupsLangEncoding(lang));
> >
> >   ippAddString(rqst,
> > 	       IPP_TAG_OPERATION,
> > 	       IPP_TAG_LANGUAGE,
> > 	       "attributes-natural-language",
> > 	       NULL,
> > 	       lang->language);
> >
> >   ippAddStrings(rqst,
> > 		IPP_TAG_OPERATION,
> > 		IPP_TAG_KEYWORD,
> > 		"requested-attributes",
> > 		n_attributes,
> > 		NULL,
> > 		jobinfo_attributes);
> >
> >   rtn = cupsDoRequest (connection, rqst, "/");
> >
> >   if (!rtn) {
> >     syslog (LOG_ERR, "%s: cupsDoRequest() failed: '%s'\n", __FUNCTION__, ippErrorString(cupsLastError()));
> >     cupsLangFree (lang);
> >     return -1;
> >   }
> >
> >   for (ipp_attributes = rtn->attrs; ipp_attributes != NULL; ipp_attributes = ipp_attributes->next) {
> >
> >     while (ipp_attributes != NULL && ipp_attributes->group_tag != IPP_TAG_EVENT_NOTIFICATION) {
> >       // Move on to the next one.
> >       ipp_attributes = ipp_attributes->next;
> >     }
> >
> >     while (ipp_attributes != NULL && ipp_attributes->group_tag == IPP_TAG_EVENT_NOTIFICATION) {
> >
> >       if (!strcmp(ipp_attributes->name, "cupsd-maxjobs") &&
> > 	  ipp_attributes->value_tag == IPP_TAG_INTEGER) {
> > 	MyMaxJobs = ipp_attributes->values[0].integer;
> > 	syslog (LOG_DEBUG, "Got MaxJobs (%d)", MyMaxJobs);
> >       }
> >
> >       if (!strcmp(ipp_attributes->name, "cupsd-numjobs") &&
> > 	  ipp_attributes->value_tag == IPP_TAG_INTEGER) {
> > 	MyNumJobs = ipp_attributes->values[0].integer;
> > 	syslog (LOG_DEBUG, "Got NumJobs (%d)", MyNumJobs);
> >       }
> >
> >       ipp_attributes = ipp_attributes->next;
> >     }
> >   }
> >
> >   cupsLangFree (lang);
> >
> >   if (MyMaxJobs - MyNumJobs > 0) {
> >     /* All is well, cupsd has space to receive the job, return 1 */
> >     return 1;
> >   } else {
> >     return 0;
> >   }
> > }
> >
> > The server code, in scheduler/ipp.c is:
> >
> > /*
> >  * 'get_maxjobs()' - Get the maximum number of jobs that the
> >  * configuration says can be concurrently processed. This is the
> >  * MaxJobs parameter in cupds.conf. We find it here in the global
> >  * variable MaxJobs. Also return the number of jobs currently being
> >  * processed by all queues.
> >  */
> >
> > static void
> > get_maxjobs(client_t *con)		/* I - Client connection */
> > {
> >   job_t *thisJob;
> >   int jobCount = 0;
> >
> >   /* to be L_DEBUG2 */
> >   LogMessage(L_DEBUG2, "get_maxjobs(%p[%d], %x)\n", con, con->http.fd, type);
> >
> >   /* Write MaxJobs global variable contents into cupsd-maxjobs attribute */
> >   ippAddInteger(con->response, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
> > 		"cupsd-maxjobs", MaxJobs);
> >
> >
> >   /* Count all the jobs: */
> >   for (thisJob = Jobs, jobCount = 0; thisJob != NULL; thisJob = thisJob->next) {
> >     if (thisJob->state->values[0].integer <= IPP_JOB_PROCESSING) {
> >       jobCount ++;
> >     }
> >   }
> >   LogMessage (L_INFO, "%s: jobCount = %d", __FUNCTION__, jobCount);
> >
> >   /* And write jobCount into cupsd-numjobs attribute */
> >   ippAddInteger(con->response, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
> > 		"cupsd-numjobs", jobCount);
> >
> >   con->response->request.status.status_code = IPP_OK;
> > }
> >
> > The code works in sending the MaxJobs number to the client, but my job counting code fails - it never counts any jobs.
> >
> > I copied the job count code from scheduler/job.c, GetPrinterJobCount() and just removed the test for destination, so that jobs for all destinations would be counted. (I did this after I tried just returning the global variable "NumJobs" which was always 0. Does anyone know why this doesn't have the value in it?)
> >
> > This must mean than the global list of jobs "Jobs" hasn't been initialised, but if I place the same counting loop in the function print_job() (also in scheduler/ipp.c) then it correctly counts the jobs when print_job() is called.
> >
> > I don't see why Jobs would be empty when I call my get_maxjobs() function in the scheduler - there's only one cupsd process. Looking at scheduler/job.c, the global list "Jobs" is added to when AddJob() is called and also when LoadAllJobs() is called when cupsd is started. Shouldn't "Jobs" always be accessible?
> >
>
> I've answered some of my questions and got closer to the problem:
>
> I had been getting print_job() (with successful page counts) to run by sending my job to cupsd using lp.c. My page counting was failing when I had jobs spooling to cupsd via cups-lpd.c.
>
> lp.c calls cupsPrintFile[s]() which calls cupsPrintFiles2(). This first sends an ipp request of either IPP_PRINT_JOB (if there is 1 file to print) or IPP_CREATE_JOB (if >1 file to print).
>
> The IPP_PRINT_JOB request causes scheduler/ipp.c:print_job() to be called which calls job.c:AddJob() and hence updates the Jobs global. The IPP_CREATE_JOB request causes create_job() to be called in scheduler/ipp.c which calls job.c:AddJob() so this also updates the Jobs global.
>
> However, cups-lpd.c also seems to send an IPP_PRINT_JOB request to print the file - from reading the code in the function print_file(). It should therefore also be causing the AddJob() function to be called in cupsd. But right now I can't see that happening...
>
> Seb

As usual my code was at fault.

In my looping code:

  for (ipp_attributes = rtn->attrs; ipp_attributes != NULL; ipp_attributes = ipp_attributes->next) {

    while (ipp_attributes != NULL && ipp_attributes->group_tag != IPP_TAG_EVENT_NOTIFICATION) {
      // Move on to the next one.
    }

    while (ipp_attributes != NULL && ipp_attributes->group_tag == IPP_TAG_EVENT_NOTIFICATION) {
      /* snip if()s */
      ipp_attributes = ipp_attributes->next;
    }
  }

I was getting an infinite loop, which was stopping cups-lpd.c in its tracks. inetd would then kill the cups-lpd process. I failed to notice because I am spooling jobs to a null backend.





More information about the cups-devel mailing list