Retrieving the total number of Jobs in process from cupsd

Sebastian James seb at esfnet.co.uk
Sun Apr 22 01:22:17 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?

many thanks for reading,

Seb James


p.s.

The reason for doing this is because cups-lpd.c fails if NumJobs>=MaxJobs. The sequence of events is: First, cups-lpd completely receives the print job over the network and saves it to a file, second it tries to pass it to cupsd, and gets IPP_SERVICE_UNAVAILABLE and then it exits, and the job is effectively lost.

A bug fix for this behaviour would be to do this:

1) Check server isn't near MaxJobs, if it is, refuse to receive data
2) Receive data
3) Possibly check again, if server has NumJobs >= MaxJobs, pause, or emit error message and exit.
4) Send data to cupsd and exit.






More information about the cups-devel mailing list