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