static void process_children(void) { int status; /* Exit status of child */ int pid, /* Process ID of child */ job_id; /* Job ID of child */ cupsd_job_t *job; /* Current job */ int i; /* Looping var */ char name[1024]; /* Process name */ const char *type; /* Type of program */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "process_children()"); /* * Reset the dead_children flag... */ dead_children = 0; /* * Collect the exit status of some children... */ #ifdef HAVE_WAITPID while ((pid = waitpid(-1, &status, WNOHANG)) > 0) #elif defined(HAVE_WAIT3) while ((pid = wait3(&status, WNOHANG, NULL)) > 0) #else if ((pid = wait(&status)) > 0) #endif /* HAVE_WAITPID */ { /* * Collect the name of the process that finished... */ cupsdFinishProcess(pid, name, sizeof(name), &job_id); /* * Delete certificates for CGI processes... */ if (pid) cupsdDeleteCert(pid); /* * Handle completed job filters... */ if (job_id > 0) job = cupsdFindJob(job_id); else job = NULL; if (job) { for (i = 0; job->filters[i]; i ++) if (job->filters[i] == pid) break; if (job->filters[i] || job->backend == pid) { /* * OK, this process has gone away; what's left? */ if (job->filters[i]) { job->filters[i] = -pid; type = "Filter"; } else { job->backend = -pid; type = "Backend"; } if (status && status != SIGTERM && status != SIGKILL && status != SIGPIPE) { /* * An error occurred; save the exit status so we know to stop * the printer or cancel the job when all of the filters finish... * * A negative status indicates that the backend failed and the * printer needs to be stopped. * * In order to preserve the most serious status, we always log * when a process dies due to a signal (e.g. SIGABRT, SIGSEGV, * and SIGBUS) and prefer to log the backend exit status over a * filter's. */ int old_status = abs(job->status); if (WIFSIGNALED(status) || /* This process crashed, or */ !job->status || /* No process had a status, or */ (!job->filters[i] && WIFEXITED(old_status))) { /* Backend and filter didn't crash */ if (job->filters[i]) job->status = status; /* Filter failed */ else job->status = -status; /* Backend failed */ } if (job->state_value == IPP_JOB_PROCESSING && job->status_level > CUPSD_LOG_ERROR && (job->filters[i] || !WIFEXITED(status))) { char message[1024]; /* New printer-state-message */ job->status_level = CUPSD_LOG_ERROR; snprintf(message, sizeof(message), "%s failed", type); if (job->printer) { strlcpy(job->printer->state_message, message, sizeof(job->printer->state_message)); } if (!job->attrs) cupsdLoadJob(job); if (!job->printer_message && job->attrs) { if ((job->printer_message = ippFindAttribute(job->attrs, "job-printer-state-message", IPP_TAG_TEXT)) == NULL) job->printer_message = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_TEXT, "job-printer-state-message", NULL, NULL); } if (job->printer_message) cupsdSetString(&(job->printer_message->values[0].string.text), message); } } /* * If this is not the last file in a job, see if all of the * filters are done, and if so move to the next file. */ if (job->current_file < job->num_files && job->printer) { for (i = 0; job->filters[i] < 0; i ++); if (!job->filters[i] && (!job->printer->pc || !job->printer->pc->single_file || job->backend <= 0)) { /* * Process the next file... */ cupsdContinueJob(job); } } else if (job->state_value >= IPP_JOB_CANCELED) { /* * Remove the job from the active list if there are no processes still * running for it... */ for (i = 0; job->filters[i] < 0; i++); if (!job->filters[i] && job->backend <= 0) cupsArrayRemove(ActiveJobs, job); } } } /* * Show the exit status as needed, ignoring SIGTERM and SIGKILL errors * since they come when we kill/end a process... */ if (status == SIGTERM || status == SIGKILL) { cupsdLogJob(job, CUPSD_LOG_DEBUG, "PID %d (%s) was terminated normally with signal %d.", pid, name, status); } else if (status == SIGPIPE) { cupsdLogJob(job, CUPSD_LOG_DEBUG, "PID %d (%s) did not catch or ignore signal %d.", pid, name, status); } else if (status) { if (WIFEXITED(status)) { int code = WEXITSTATUS(status); /* Exit code */ if (code > 100) cupsdLogJob(job, CUPSD_LOG_DEBUG, "PID %d (%s) stopped with status %d (%s)", pid, name, code, strerror(code - 100)); else cupsdLogJob(job, CUPSD_LOG_DEBUG, "PID %d (%s) stopped with status %d.", pid, name, code); } else cupsdLogJob(job, CUPSD_LOG_DEBUG, "PID %d (%s) crashed on signal %d.", pid, name, WTERMSIG(status)); if (LogLevel < CUPSD_LOG_DEBUG) cupsdLogJob(job, CUPSD_LOG_INFO, "Hint: Try setting the LogLevel to \"debug\" to find out " "more."); } else cupsdLogJob(job, CUPSD_LOG_DEBUG, "PID %d (%s) exited with no errors.", pid, name); } /* * If wait*() is interrupted by a signal, tell main() to call us again... */ if (pid < 0 && errno == EINTR) dead_children = 1; }
cupsd_quota_t * /* O - Quota data */ cupsdUpdateQuota( cupsd_printer_t *p, /* I - Printer */ const char *username, /* I - User */ int pages, /* I - Number of pages */ int k) /* I - Number of kilobytes */ { cupsd_quota_t *q; /* Quota data */ cupsd_job_t *job; /* Current job */ time_t curtime; /* Current time */ ipp_attribute_t *attr; /* Job attribute */ if (!p || !username) return (NULL); if (!p->k_limit && !p->page_limit) return (NULL); if ((q = cupsdFindQuota(p, username)) == NULL) return (NULL); cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdUpdateQuota: p=%s username=%s pages=%d k=%d", p->name, username, pages, k); #if defined(__APPLE__) && defined(HAVE_DLFCN_H) /* * Use Apple PrintService quota enforcement if installed (X Server only) */ if (AppleQuotas && PSQUpdateQuotaProc) { q->page_count = (*PSQUpdateQuotaProc)(p->name, p->info, username, pages, 0); return (q); } #endif /* __APPLE__ && HAVE_DLFCN_H */ curtime = time(NULL); if (curtime < q->next_update) { q->page_count += pages; q->k_count += k; return (q); } if (p->quota_period) curtime -= p->quota_period; else curtime = 0; q->next_update = 0; q->page_count = 0; q->k_count = 0; for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); job; job = (cupsd_job_t *)cupsArrayNext(Jobs)) { /* * We only care about the current printer/class and user... */ if (strcasecmp(job->dest, p->name) != 0 || strcasecmp(job->username, q->username) != 0) continue; /* * Make sure attributes are loaded; we always call cupsdLoadJob() to ensure * the access_time member is updated so the job isn't unloaded right away... */ if (!cupsdLoadJob(job)) continue; if ((attr = ippFindAttribute(job->attrs, "time-at-completion", IPP_TAG_INTEGER)) == NULL) if ((attr = ippFindAttribute(job->attrs, "time-at-processing", IPP_TAG_INTEGER)) == NULL) attr = ippFindAttribute(job->attrs, "time-at-creation", IPP_TAG_INTEGER); if (attr->values[0].integer < curtime) { /* * This job is too old to count towards the quota, ignore it... */ if (JobAutoPurge && !job->printer && job->state_value > IPP_JOB_STOPPED) cupsdDeleteJob(job, CUPSD_JOB_PURGE); continue; } if (q->next_update == 0) q->next_update = attr->values[0].integer + p->quota_period; if ((attr = ippFindAttribute(job->attrs, "job-media-sheets-completed", IPP_TAG_INTEGER)) != NULL) q->page_count += attr->values[0].integer; if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL) q->k_count += attr->values[0].integer; } return (q); }