cupsd_policy_t * /* O - Policy */ cupsdAddPolicy(const char *policy) /* I - Name of policy */ { cupsd_policy_t *temp; /* Pointer to policy */ if (!policy) return (NULL); if (!Policies) Policies = cupsArrayNew3((cups_array_func_t)compare_policies, NULL, (cups_ahash_func_t)NULL, 0, (cups_acopy_func_t)NULL, (cups_afree_func_t)free_policy); if (!Policies) return (NULL); if ((temp = calloc(1, sizeof(cupsd_policy_t))) != NULL) { cupsdSetString(&temp->name, policy); cupsArrayAdd(Policies, temp); } return (temp); }
cupsd_printer_t * /* O - New class */ cupsdAddClass(const char *name) /* I - Name of class */ { cupsd_printer_t *c; /* New class */ /* * Add the printer and set the type to "class"... */ if ((c = cupsdAddPrinter(name)) != NULL) { /* * Change from a printer to a class... */ c->type = CUPS_PRINTER_CLASS; cupsdSetStringf(&c->uri, "ipp://%s:%d/classes/%s", ServerName, RemotePort, name); cupsdSetString(&c->error_policy, "retry-current-job"); } return (c); }
void cupsdInitEnv(void) { /* * Clear existing environment variables... */ clear_env(); #if defined(__APPLE__) /* * Add special voodoo magic for MacOS X - this allows MacOS X * programs to access their bundle resources properly... * * This string is replaced in cupsdStartProcess()... */ cupsdSetString(common_env, "<CFProcessPath>"); num_common_env = 1; #endif /* __APPLE__ */ }
static void dnssdRegisterCallback( DNSServiceRef sdRef, /* I - DNS Service reference */ DNSServiceFlags flags, /* I - Reserved for future use */ DNSServiceErrorType errorCode, /* I - Error code */ const char *name, /* I - Service name */ const char *regtype, /* I - Service type */ const char *domain, /* I - Domain. ".local" for now */ void *context) /* I - Printer */ { cupsd_printer_t *p = (cupsd_printer_t *)context; /* Current printer */ (void)sdRef; (void)flags; (void)domain; cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterCallback(%s, %s) for %s (%s)", name, regtype, p ? p->name : "Web Interface", p ? (p->reg_name ? p->reg_name : "(null)") : "NA"); if (errorCode) { cupsdLogMessage(CUPSD_LOG_ERROR, "DNSServiceRegister failed with error %d", (int)errorCode); return; } else if (p && (!p->reg_name || _cups_strcasecmp(name, p->reg_name))) { cupsdLogMessage(CUPSD_LOG_INFO, "Using service name \"%s\" for \"%s\"", name, p->name); cupsArrayRemove(DNSSDPrinters, p); cupsdSetString(&p->reg_name, name); cupsArrayAdd(DNSSDPrinters, p); LastEvent |= CUPSD_EVENT_PRINTER_MODIFIED; } }
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; }
int /* O - Exit status */ main(int argc, /* I - Number of command-line args */ char *argv[]) /* I - Command-line arguments */ { int i; /* Looping var */ char *opt; /* Option character */ int fg; /* Run in the foreground */ int fds; /* Number of ready descriptors */ cupsd_client_t *con; /* Current client */ cupsd_job_t *job; /* Current job */ cupsd_listener_t *lis; /* Current listener */ time_t current_time, /* Current time */ activity, /* Client activity timer */ senddoc_time, /* Send-Document time */ expire_time, /* Subscription expire time */ report_time, /* Malloc/client/job report time */ event_time; /* Last event notification time */ long timeout; /* Timeout for cupsdDoSelect() */ struct rlimit limit; /* Runtime limit */ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* Actions for POSIX signals */ #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ #ifdef __sgi cups_file_t *fp; /* Fake lpsched lock file */ struct stat statbuf; /* Needed for checking lpsched FIFO */ #endif /* __sgi */ int run_as_child = 0; /* Needed for background fork/exec */ #ifdef __APPLE__ int use_sysman = !getuid(); /* Use system management functions? */ #else time_t netif_time = 0; /* Time since last network update */ #endif /* __APPLE__ */ #if HAVE_LAUNCHD int launchd_idle_exit; /* Idle exit on select timeout? */ #endif /* HAVE_LAUNCHD */ #ifdef HAVE_GETEUID /* * Check for setuid invocation, which we do not support! */ if (getuid() != geteuid()) { fputs("cupsd: Cannot run as a setuid program\n", stderr); return (1); } #endif /* HAVE_GETEUID */ /* * Check for command-line arguments... */ fg = 0; #ifdef HAVE_LAUNCHD if (getenv("CUPSD_LAUNCHD")) { Launchd = 1; fg = 1; } #endif /* HAVE_LAUNCHD */ for (i = 1; i < argc; i ++) if (argv[i][0] == '-') for (opt = argv[i] + 1; *opt != '\0'; opt ++) switch (*opt) { case 'C' : /* Run as child with config file */ run_as_child = 1; fg = -1; case 'c' : /* Configuration file */ i ++; if (i >= argc) { _cupsLangPuts(stderr, _("cupsd: Expected config filename " "after \"-c\" option.")); usage(1); } if (argv[i][0] == '/') { /* * Absolute directory... */ cupsdSetString(&ConfigurationFile, argv[i]); } else { /* * Relative directory... */ char *current; /* Current directory */ /* * Allocate a buffer for the current working directory to * reduce run-time stack usage; this approximates the * behavior of some implementations of getcwd() when they * are passed a NULL pointer. */ if ((current = malloc(1024)) == NULL) { _cupsLangPuts(stderr, _("cupsd: Unable to get current directory.")); return (1); } if (!getcwd(current, 1024)) { _cupsLangPuts(stderr, _("cupsd: Unable to get current directory.")); free(current); return (1); } cupsdSetStringf(&ConfigurationFile, "%s/%s", current, argv[i]); free(current); } break; case 'f' : /* Run in foreground... */ fg = 1; break; case 'F' : /* Run in foreground, but disconnect from terminal... */ fg = -1; break; case 'h' : /* Show usage/help */ usage(0); break; case 'l' : /* Started by launchd... */ #ifdef HAVE_LAUNCHD Launchd = 1; fg = 1; #else _cupsLangPuts(stderr, _("cupsd: launchd(8) support not compiled " "in, running in normal mode.")); fg = 0; #endif /* HAVE_LAUNCHD */ break; case 'p' : /* Stop immediately for profiling */ fputs("cupsd: -p (startup profiling) is for internal testing " "use only!\n", stderr); stop_scheduler = 1; fg = 1; break; case 'P' : /* Disable security profiles */ fputs("cupsd: -P (disable security profiles) is for internal " "testing use only!\n", stderr); UseProfiles = 0; break; #ifdef __APPLE__ case 'S' : /* Disable system management functions */ fputs("cupsd: -S (disable system management) for internal " "testing use only!\n", stderr); use_sysman = 0; break; #endif /* __APPLE__ */ case 't' : /* Test the cupsd.conf file... */ TestConfigFile = 1; fg = 1; break; default : /* Unknown option */ _cupsLangPrintf(stderr, _("cupsd: Unknown option \"%c\" - " "aborting."), *opt); usage(1); break; } else { _cupsLangPrintf(stderr, _("cupsd: Unknown argument \"%s\" - aborting."), argv[i]); usage(1); } if (!ConfigurationFile) cupsdSetString(&ConfigurationFile, CUPS_SERVERROOT "/cupsd.conf"); /* * If the user hasn't specified "-f", run in the background... */ if (!fg) { /* * Setup signal handlers for the parent... */ #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ sigset(SIGUSR1, parent_handler); sigset(SIGCHLD, parent_handler); sigset(SIGHUP, SIG_IGN); #elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGUSR1); action.sa_handler = parent_handler; sigaction(SIGUSR1, &action, NULL); sigaction(SIGCHLD, &action, NULL); sigemptyset(&action.sa_mask); action.sa_handler = SIG_IGN; sigaction(SIGHUP, &action, NULL); #else signal(SIGUSR1, parent_handler); signal(SIGCLD, parent_handler); signal(SIGHUP, SIG_IGN); #endif /* HAVE_SIGSET */ if (fork() > 0) { /* * OK, wait for the child to startup and send us SIGUSR1 or to crash * and the OS send us SIGCHLD... We also need to ignore SIGHUP which * might be sent by the init script to restart the scheduler... */ for (; parent_signal == 0;) sleep(1); if (parent_signal == SIGUSR1) return (0); if (wait(&i) < 0) { perror("cupsd"); return (1); } else if (WIFEXITED(i)) { fprintf(stderr, "cupsd: Child exited with status %d\n", WEXITSTATUS(i)); return (2); } else { fprintf(stderr, "cupsd: Child exited on signal %d\n", WTERMSIG(i)); return (3); } } #ifdef __OpenBSD__ /* * Call _thread_sys_closefrom() so the child process doesn't reset the * parent's file descriptors to be blocking. This is a workaround for a * limitation of userland libpthread on OpenBSD. */ _thread_sys_closefrom(0); #endif /* __OpenBSD__ */ /* * Since CoreFoundation and DBUS both create fork-unsafe data on execution of * a program, and since this kind of really unfriendly behavior seems to be * more common these days in system libraries, we need to re-execute the * background cupsd with the "-C" option to avoid problems. Unfortunately, * we also have to assume that argv[0] contains the name of the cupsd * executable - there is no portable way to get the real pathname... */ execlp(argv[0], argv[0], "-C", ConfigurationFile, (char *)0); exit(errno); } if (fg < 1) { /* * Make sure we aren't tying up any filesystems... */ chdir("/"); #ifndef DEBUG /* * Disable core dumps... */ getrlimit(RLIMIT_CORE, &limit); limit.rlim_cur = 0; setrlimit(RLIMIT_CORE, &limit); /* * Disconnect from the controlling terminal... */ setsid(); /* * Close all open files... */ getrlimit(RLIMIT_NOFILE, &limit); for (i = 0; i < limit.rlim_cur && i < 1024; i ++) close(i); /* * Redirect stdin/out/err to /dev/null... */ if ((i = open("/dev/null", O_RDONLY)) != 0) { dup2(i, 0); close(i); } if ((i = open("/dev/null", O_WRONLY)) != 1) { dup2(i, 1); close(i); } if ((i = open("/dev/null", O_WRONLY)) != 2) { dup2(i, 2); close(i); } #endif /* DEBUG */ } /* * Set the timezone info... */ tzset(); #ifdef LC_TIME setlocale(LC_TIME, ""); #endif /* LC_TIME */ /* * Set the maximum number of files... */ getrlimit(RLIMIT_NOFILE, &limit); #if !defined(HAVE_POLL) && !defined(HAVE_EPOLL) && !defined(HAVE_KQUEUE) if (limit.rlim_max > FD_SETSIZE) MaxFDs = FD_SETSIZE; else #endif /* !HAVE_POLL && !HAVE_EPOLL && !HAVE_KQUEUE */ #ifdef RLIM_INFINITY if (limit.rlim_max == RLIM_INFINITY) MaxFDs = 16384; else #endif /* RLIM_INFINITY */ MaxFDs = limit.rlim_max; limit.rlim_cur = MaxFDs; setrlimit(RLIMIT_NOFILE, &limit); cupsdStartSelect(); /* * Read configuration... */ if (!cupsdReadConfiguration()) { if (TestConfigFile) printf("%s contains errors\n", ConfigurationFile); else syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!", ConfigurationFile); return (1); } else if (TestConfigFile) { printf("%s is OK\n", ConfigurationFile); return (0); } /* * Clean out old temp files and printer cache data. */ if (!strncmp(TempDir, RequestRoot, strlen(RequestRoot))) cupsdCleanFiles(TempDir, NULL); cupsdCleanFiles(CacheDir, "*.ipp"); #if HAVE_LAUNCHD if (Launchd) { /* * If we were started by launchd get the listen sockets file descriptors... */ launchd_checkin(); launchd_checkout(); } #endif /* HAVE_LAUNCHD */ /* * Startup the server... */ httpInitialize(); cupsdStartServer(); /* * Catch hangup and child signals and ignore broken pipes... */ #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ sigset(SIGCHLD, sigchld_handler); sigset(SIGHUP, sighup_handler); sigset(SIGPIPE, SIG_IGN); sigset(SIGTERM, sigterm_handler); #elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGTERM); sigaddset(&action.sa_mask, SIGCHLD); action.sa_handler = sigchld_handler; sigaction(SIGCHLD, &action, NULL); sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGHUP); action.sa_handler = sighup_handler; sigaction(SIGHUP, &action, NULL); sigemptyset(&action.sa_mask); action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &action, NULL); sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGTERM); sigaddset(&action.sa_mask, SIGCHLD); action.sa_handler = sigterm_handler; sigaction(SIGTERM, &action, NULL); #else signal(SIGCLD, sigchld_handler); /* No, SIGCLD isn't a typo... */ signal(SIGHUP, sighup_handler); signal(SIGPIPE, SIG_IGN); signal(SIGTERM, sigterm_handler); #endif /* HAVE_SIGSET */ #ifdef __sgi /* * Try to create a fake lpsched lock file if one is not already there. * Some Adobe applications need it under IRIX in order to enable * printing... */ if ((fp = cupsFileOpen("/var/spool/lp/SCHEDLOCK", "w")) == NULL) { syslog(LOG_LPR, "Unable to create fake lpsched lock file " "\"/var/spool/lp/SCHEDLOCK\"\' - %s!", strerror(errno)); } else { fchmod(cupsFileNumber(fp), 0644); fchown(cupsFileNumber(fp), User, Group); cupsFileClose(fp); } #endif /* __sgi */ /* * Initialize authentication certificates... */ cupsdInitCerts(); /* * If we are running in the background, signal the parent process that * we are up and running... */ if (!fg || run_as_child) { /* * Send a signal to the parent process, but only if the parent is * not PID 1 (init). This avoids accidentally shutting down the * system on OpenBSD if you CTRL-C the server before it is up... */ i = getppid(); /* Save parent PID to avoid race condition */ if (i != 1) kill(i, SIGUSR1); } #ifdef __APPLE__ /* * Start power management framework... */ if (use_sysman) cupsdStartSystemMonitor(); #endif /* __APPLE__ */ /* * Send server-started event... */ #ifdef HAVE_LAUNCHD if (Launchd) cupsdAddEvent(CUPSD_EVENT_SERVER_STARTED, NULL, NULL, "Scheduler started via launchd."); else #endif /* HAVE_LAUNCHD */ if (fg) cupsdAddEvent(CUPSD_EVENT_SERVER_STARTED, NULL, NULL, "Scheduler started in foreground."); else cupsdAddEvent(CUPSD_EVENT_SERVER_STARTED, NULL, NULL, "Scheduler started in background."); /* * Start any pending print jobs... */ cupsdCheckJobs(); /* * Loop forever... */ current_time = time(NULL); event_time = current_time; expire_time = current_time; fds = 1; report_time = 0; senddoc_time = current_time; while (!stop_scheduler) { /* * Check if there are dead children to handle... */ if (dead_children) process_children(); /* * Check if we need to load the server configuration file... */ if (NeedReload) { /* * Close any idle clients... */ if (cupsArrayCount(Clients) > 0) { for (con = (cupsd_client_t *)cupsArrayFirst(Clients); con; con = (cupsd_client_t *)cupsArrayNext(Clients)) if (con->http.state == HTTP_WAITING) cupsdCloseClient(con); else con->http.keep_alive = HTTP_KEEPALIVE_OFF; cupsdPauseListening(); } /* * Restart if all clients are closed and all jobs finished, or * if the reload timeout has elapsed... */ if ((cupsArrayCount(Clients) == 0 && (cupsArrayCount(PrintingJobs) == 0 || NeedReload != RELOAD_ALL)) || (time(NULL) - ReloadTime) >= ReloadTimeout) { /* * Shutdown the server... */ DoingShutdown = 1; cupsdStopServer(); /* * Read configuration... */ if (!cupsdReadConfiguration()) { syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!", ConfigurationFile); break; } #if HAVE_LAUNCHD if (Launchd) { /* * If we were started by launchd, get the listen socket file * descriptors... */ launchd_checkin(); launchd_checkout(); } #endif /* HAVE_LAUNCHD */ /* * Startup the server... */ DoingShutdown = 0; cupsdStartServer(); /* * Send a server-restarted event... */ cupsdAddEvent(CUPSD_EVENT_SERVER_RESTARTED, NULL, NULL, "Scheduler restarted."); } } /* * Check for available input or ready output. If cupsdDoSelect() * returns 0 or -1, something bad happened and we should exit * immediately. * * Note that we at least have one listening socket open at all * times. */ if ((timeout = select_timeout(fds)) > 1 && LastEvent) timeout = 1; #if HAVE_LAUNCHD /* * If no other work is scheduled and we're being controlled by * launchd then timeout after 'LaunchdTimeout' seconds of * inactivity... */ if (timeout == 86400 && Launchd && LaunchdTimeout && !cupsArrayCount(ActiveJobs) && (!Browsing || !BrowseLocalProtocols || !cupsArrayCount(Printers))) { timeout = LaunchdTimeout; launchd_idle_exit = 1; } else launchd_idle_exit = 0; #endif /* HAVE_LAUNCHD */ if ((fds = cupsdDoSelect(timeout)) < 0) { /* * Got an error from select! */ #ifdef HAVE_DNSSD cupsd_printer_t *p; /* Current printer */ #endif /* HAVE_DNSSD */ if (errno == EINTR) /* Just interrupted by a signal */ continue; /* * Log all sorts of debug info to help track down the problem. */ cupsdLogMessage(CUPSD_LOG_EMERG, "cupsdDoSelect() failed - %s!", strerror(errno)); for (i = 0, con = (cupsd_client_t *)cupsArrayFirst(Clients); con; i ++, con = (cupsd_client_t *)cupsArrayNext(Clients)) cupsdLogMessage(CUPSD_LOG_EMERG, "Clients[%d] = %d, file = %d, state = %d", i, con->http.fd, con->file, con->http.state); for (i = 0, lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); lis; i ++, lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) cupsdLogMessage(CUPSD_LOG_EMERG, "Listeners[%d] = %d", i, lis->fd); cupsdLogMessage(CUPSD_LOG_EMERG, "CGIPipes[0] = %d", CGIPipes[0]); #ifdef __APPLE__ cupsdLogMessage(CUPSD_LOG_EMERG, "SysEventPipes[0] = %d", SysEventPipes[0]); #endif /* __APPLE__ */ for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); job; job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) cupsdLogMessage(CUPSD_LOG_EMERG, "Jobs[%d] = %d < [%d %d] > [%d %d]", job->id, job->status_buffer ? job->status_buffer->fd : -1, job->print_pipes[0], job->print_pipes[1], job->back_pipes[0], job->back_pipes[1]); #ifdef HAVE_DNSSD for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); p; p = (cupsd_printer_t *)cupsArrayNext(Printers)) cupsdLogMessage(CUPSD_LOG_EMERG, "printer[%s] reg_name=\"%s\"", p->name, p->reg_name ? p->reg_name : "(null)"); #endif /* HAVE_DNSSD */ break; } current_time = time(NULL); /* * Write dirty config/state files... */ if (DirtyCleanTime && current_time >= DirtyCleanTime) cupsdCleanDirty(); #ifdef __APPLE__ /* * If we are going to sleep and still have pending jobs, stop them after * a period of time... */ if (SleepJobs > 0 && current_time >= SleepJobs && cupsArrayCount(PrintingJobs) > 0) { SleepJobs = 0; cupsdStopAllJobs(CUPSD_JOB_DEFAULT, 5); } #endif /* __APPLE__ */ #ifndef __APPLE__ /* * Update the network interfaces once a minute... */ if ((current_time - netif_time) >= 60) { netif_time = current_time; NetIFUpdate = 1; } #endif /* !__APPLE__ */ #if HAVE_LAUNCHD /* * If no other work was scheduled and we're being controlled by launchd * then timeout after 'LaunchdTimeout' seconds of inactivity... */ if (!fds && launchd_idle_exit) { cupsdLogMessage(CUPSD_LOG_INFO, "Printer sharing is off and there are no jobs pending, " "will restart on demand."); stop_scheduler = 1; break; } #endif /* HAVE_LAUNCHD */ /* * Resume listening for new connections as needed... */ if (ListeningPaused && ListeningPaused <= current_time && cupsArrayCount(Clients) < MaxClients) cupsdResumeListening(); /* * Expire subscriptions and unload completed jobs as needed... */ if (current_time > expire_time) { if (cupsArrayCount(Subscriptions) > 0) cupsdExpireSubscriptions(NULL, NULL); cupsdUnloadCompletedJobs(); expire_time = current_time; } #ifndef HAVE_AUTHORIZATION_H /* * Update the root certificate once every 5 minutes if we have client * connections... */ if ((current_time - RootCertTime) >= RootCertDuration && RootCertDuration && !RunUser && cupsArrayCount(Clients)) { /* * Update the root certificate... */ cupsdDeleteCert(0); cupsdAddCert(0, "root", NULL); } #endif /* !HAVE_AUTHORIZATION_H */ /* * Check for new data on the client sockets... */ for (con = (cupsd_client_t *)cupsArrayFirst(Clients); con; con = (cupsd_client_t *)cupsArrayNext(Clients)) { /* * Process pending data in the input buffer... */ if (con->http.used) { cupsdReadClient(con); continue; } /* * Check the activity and close old clients... */ activity = current_time - Timeout; if (con->http.activity < activity && !con->pipe_pid) { cupsdLogMessage(CUPSD_LOG_DEBUG, "Closing client %d after %d seconds of inactivity...", con->http.fd, Timeout); cupsdCloseClient(con); continue; } } /* * Update any pending multi-file documents... */ if ((current_time - senddoc_time) >= 10) { cupsdCheckJobs(); senddoc_time = current_time; } /* * Clean job history... */ if (JobHistoryUpdate && current_time >= JobHistoryUpdate) cupsdCleanJobs(); /* * Log statistics at most once a minute when in debug mode... */ if ((current_time - report_time) >= 60 && LogLevel >= CUPSD_LOG_DEBUG) { size_t string_count, /* String count */ alloc_bytes, /* Allocated string bytes */ total_bytes; /* Total string bytes */ #ifdef HAVE_MALLINFO struct mallinfo mem; /* Malloc information */ mem = mallinfo(); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: malloc-arena=%lu", mem.arena); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: malloc-used=%lu", mem.usmblks + mem.uordblks); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: malloc-free=%lu", mem.fsmblks + mem.fordblks); #endif /* HAVE_MALLINFO */ cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: clients=%d", cupsArrayCount(Clients)); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: jobs=%d", cupsArrayCount(Jobs)); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: jobs-active=%d", cupsArrayCount(ActiveJobs)); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: printers=%d", cupsArrayCount(Printers)); string_count = _cupsStrStatistics(&alloc_bytes, &total_bytes); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: stringpool-string-count=" CUPS_LLFMT, CUPS_LLCAST string_count); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: stringpool-alloc-bytes=" CUPS_LLFMT, CUPS_LLCAST alloc_bytes); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: stringpool-total-bytes=" CUPS_LLFMT, CUPS_LLCAST total_bytes); report_time = current_time; } /* * Handle OS-specific event notification for any events that have * accumulated. Don't send these more than once a second... */ if (LastEvent && (current_time - event_time) >= 1) { #ifdef HAVE_NOTIFY_POST if (LastEvent & (CUPSD_EVENT_PRINTER_ADDED | CUPSD_EVENT_PRINTER_DELETED | CUPSD_EVENT_PRINTER_MODIFIED)) { cupsdLogMessage(CUPSD_LOG_DEBUG2, "notify_post(\"com.apple.printerListChange\")"); notify_post("com.apple.printerListChange"); } if (LastEvent & CUPSD_EVENT_PRINTER_STATE_CHANGED) { cupsdLogMessage(CUPSD_LOG_DEBUG2, "notify_post(\"com.apple.printerHistoryChange\")"); notify_post("com.apple.printerHistoryChange"); } if (LastEvent & (CUPSD_EVENT_JOB_STATE_CHANGED | CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_PROGRESS)) { cupsdLogMessage(CUPSD_LOG_DEBUG2, "notify_post(\"com.apple.jobChange\")"); notify_post("com.apple.jobChange"); } #endif /* HAVE_NOTIFY_POST */ /* * Reset the accumulated events... */ LastEvent = CUPSD_EVENT_NONE; event_time = current_time; } } /* * Log a message based on what happened... */ if (stop_scheduler) { cupsdLogMessage(CUPSD_LOG_INFO, "Scheduler shutting down normally."); cupsdAddEvent(CUPSD_EVENT_SERVER_STOPPED, NULL, NULL, "Scheduler shutting down normally."); } else { cupsdLogMessage(CUPSD_LOG_ERROR, "Scheduler shutting down due to program error."); cupsdAddEvent(CUPSD_EVENT_SERVER_STOPPED, NULL, NULL, "Scheduler shutting down due to program error."); } /* * Close all network clients... */ DoingShutdown = 1; cupsdStopServer(); #ifdef HAVE_LAUNCHD /* * Update the launchd KeepAlive file as needed... */ if (Launchd) launchd_checkout(); #endif /* HAVE_LAUNCHD */ /* * Stop all jobs... */ cupsdFreeAllJobs(); #ifdef __APPLE__ /* * Stop monitoring system event monitoring... */ if (use_sysman) cupsdStopSystemMonitor(); #endif /* __APPLE__ */ #ifdef HAVE_GSSAPI /* * Free the scheduler's Kerberos context... */ # ifdef __APPLE__ /* * If the weak-linked GSSAPI/Kerberos library is not present, don't try * to use it... */ if (krb5_init_context != NULL) # endif /* __APPLE__ */ if (KerberosContext) krb5_free_context(KerberosContext); #endif /* HAVE_GSSAPI */ #ifdef __sgi /* * Remove the fake IRIX lpsched lock file, but only if the existing * file is not a FIFO which indicates that the real IRIX lpsched is * running... */ if (!stat("/var/spool/lp/FIFO", &statbuf)) if (!S_ISFIFO(statbuf.st_mode)) unlink("/var/spool/lp/SCHEDLOCK"); #endif /* __sgi */ cupsdStopSelect(); return (!stop_scheduler); }
static void dnssdUpdateDNSSDName(int from_callback) /* I - Called from callback? */ { char webif[1024]; /* Web interface share name */ # ifdef __APPLE__ SCDynamicStoreRef sc; /* Context for dynamic store */ CFDictionaryRef btmm; /* Back-to-My-Mac domains */ CFStringEncoding nameEncoding; /* Encoding of computer name */ CFStringRef nameRef; /* Host name CFString */ char nameBuffer[1024]; /* C-string buffer */ # endif /* __APPLE__ */ /* * Only share the web interface and printers when non-local listening is * enabled... */ if (!DNSSDPort) { /* * Get the port we use for registrations. If we are not listening on any * non-local ports, there is no sense sharing local printers via Bonjour... */ cupsd_listener_t *lis; /* Current listening socket */ for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); lis; lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) { if (httpAddrLocalhost(&(lis->address))) continue; DNSSDPort = httpAddrPort(&(lis->address)); break; } } if (!DNSSDPort) return; /* * Get the computer name as a c-string... */ # ifdef __APPLE__ sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), NULL, NULL); if (sc) { /* * Get the computer name from the dynamic store... */ cupsdClearString(&DNSSDComputerName); if ((nameRef = SCDynamicStoreCopyComputerName(sc, &nameEncoding)) != NULL) { if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer), kCFStringEncodingUTF8)) { cupsdLogMessage(CUPSD_LOG_DEBUG, "Dynamic store computer name is \"%s\".", nameBuffer); cupsdSetString(&DNSSDComputerName, nameBuffer); } CFRelease(nameRef); } if (!DNSSDComputerName) { /* * Use the ServerName instead... */ cupsdLogMessage(CUPSD_LOG_DEBUG, "Using ServerName \"%s\" as computer name.", ServerName); cupsdSetString(&DNSSDComputerName, ServerName); } /* * Get the local hostname from the dynamic store... */ cupsdClearString(&DNSSDHostName); if ((nameRef = SCDynamicStoreCopyLocalHostName(sc)) != NULL) { if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer), kCFStringEncodingUTF8)) { cupsdLogMessage(CUPSD_LOG_DEBUG, "Dynamic store host name is \"%s\".", nameBuffer); cupsdSetString(&DNSSDHostName, nameBuffer); } CFRelease(nameRef); } if (!DNSSDHostName) { /* * Use the ServerName instead... */ cupsdLogMessage(CUPSD_LOG_DEBUG, "Using ServerName \"%s\" as host name.", ServerName); cupsdSetString(&DNSSDHostName, ServerName); } /* * Get any Back-to-My-Mac domains and add them as aliases... */ cupsdFreeAliases(DNSSDAlias); DNSSDAlias = NULL; btmm = SCDynamicStoreCopyValue(sc, CFSTR("Setup:/Network/BackToMyMac")); if (btmm && CFGetTypeID(btmm) == CFDictionaryGetTypeID()) { cupsdLogMessage(CUPSD_LOG_DEBUG, "%d Back to My Mac aliases to add.", (int)CFDictionaryGetCount(btmm)); CFDictionaryApplyFunction(btmm, dnssdAddAlias, NULL); } else if (btmm) cupsdLogMessage(CUPSD_LOG_ERROR, "Bad Back to My Mac data in dynamic store!"); else cupsdLogMessage(CUPSD_LOG_DEBUG, "No Back to My Mac aliases to add."); if (btmm) CFRelease(btmm); CFRelease(sc); } else # endif /* __APPLE__ */ # ifdef HAVE_AVAHI if (DNSSDClient) { const char *host_name = avahi_client_get_host_name(DNSSDClient); const char *host_fqdn = avahi_client_get_host_name_fqdn(DNSSDClient); cupsdSetString(&DNSSDComputerName, host_name ? host_name : ServerName); if (host_fqdn) cupsdSetString(&DNSSDHostName, host_fqdn); else if (strchr(ServerName, '.')) cupsdSetString(&DNSSDHostName, ServerName); else cupsdSetStringf(&DNSSDHostName, "%s.local", ServerName); } else # endif /* HAVE_AVAHI */ { cupsdSetString(&DNSSDComputerName, ServerName); if (strchr(ServerName, '.')) cupsdSetString(&DNSSDHostName, ServerName); else cupsdSetStringf(&DNSSDHostName, "%s.local", ServerName); } /* * Then (re)register the web interface if enabled... */ if (BrowseWebIF) { if (DNSSDComputerName) snprintf(webif, sizeof(webif), "CUPS @ %s", DNSSDComputerName); else strlcpy(webif, "CUPS", sizeof(webif)); dnssdDeregisterInstance(&WebIFSrv, from_callback); dnssdRegisterInstance(&WebIFSrv, NULL, webif, "_http._tcp", "_printer", DNSSDPort, NULL, 1, from_callback); } }
static void dnssdRegisterPrinter( cupsd_printer_t *p, /* I - Printer */ int from_callback) /* I - Called from callback? */ { char name[256]; /* Service name */ int printer_port; /* LPD port number */ int status; /* Registration status */ cupsd_txt_t ipp_txt, /* IPP(S) TXT record */ printer_txt; /* LPD TXT record */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) %s", p->name, !p->ipp_srv ? "new" : "update"); # ifdef HAVE_AVAHI if (!avahi_running) return; # endif /* HAVE_AVAHI */ /* * Remove the current registrations if we have them and then return if * per-printer sharing was just disabled... */ dnssdDeregisterPrinter(p, 0, from_callback); if (!p->shared) return; /* * Set the registered name as needed; the registered name takes the form of * "<printer-info> @ <computer name>"... */ if (!p->reg_name) { if (p->info && strlen(p->info) > 0) { if (DNSSDComputerName) snprintf(name, sizeof(name), "%s @ %s", p->info, DNSSDComputerName); else strlcpy(name, p->info, sizeof(name)); } else if (DNSSDComputerName) snprintf(name, sizeof(name), "%s @ %s", p->name, DNSSDComputerName); else strlcpy(name, p->name, sizeof(name)); } else strlcpy(name, p->reg_name, sizeof(name)); /* * Register IPP and LPD... * * We always must register the "_printer" service type in order to reserve * our name, but use port number 0 if we haven't actually configured cups-lpd * to share via LPD... */ ipp_txt = dnssdBuildTxtRecord(p, 0); printer_txt = dnssdBuildTxtRecord(p, 1); if (BrowseLocalProtocols & BROWSE_LPD) printer_port = 515; else printer_port = 0; status = dnssdRegisterInstance(NULL, p, name, "_printer._tcp", NULL, printer_port, &printer_txt, 0, from_callback); # ifdef HAVE_SSL if (status) dnssdRegisterInstance(NULL, p, name, "_ipps._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 0, from_callback); # endif /* HAVE_SSL */ if (status) { /* * Use the "_fax-ipp" service type for fax queues, otherwise use "_ipp"... */ if (p->type & CUPS_PRINTER_FAX) status = dnssdRegisterInstance(NULL, p, name, "_fax-ipp._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 1, from_callback); else status = dnssdRegisterInstance(NULL, p, name, "_ipp._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 1, from_callback); } dnssdFreeTxtRecord(&ipp_txt); dnssdFreeTxtRecord(&printer_txt); if (status) { /* * Save the registered name and add the printer to the array of DNS-SD * printers... */ cupsdSetString(&p->reg_name, name); cupsArrayAdd(DNSSDPrinters, p); } else { /* * Registration failed for this printer... */ dnssdDeregisterInstance(&p->ipp_srv, from_callback); # ifdef HAVE_DNSSD # ifdef HAVE_SSL dnssdDeregisterInstance(&p->ipps_srv, from_callback); # endif /* HAVE_SSL */ dnssdDeregisterInstance(&p->printer_srv, from_callback); # endif /* HAVE_DNSSD */ } }
void cupsdLoadAllSubscriptions(void) { int i; /* Looping var */ cups_file_t *fp; /* subscriptions.conf file */ int linenum; /* Current line number */ char line[1024], /* Line from file */ *value, /* Pointer to value */ *valueptr; /* Pointer into value */ cupsd_subscription_t *sub; /* Current subscription */ int hex; /* Non-zero if reading hex data */ int delete_sub; /* Delete subscription? */ /* * Open the subscriptions.conf file... */ snprintf(line, sizeof(line), "%s/subscriptions.conf", ServerRoot); if ((fp = cupsdOpenConfFile(line)) == NULL) return; /* * Read all of the lines from the file... */ linenum = 0; sub = NULL; delete_sub = 0; while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) { if (!_cups_strcasecmp(line, "NextSubscriptionId") && value) { /* * NextSubscriptionId NNN */ i = atoi(value); if (i >= NextSubscriptionId && i > 0) NextSubscriptionId = i; } else if (!_cups_strcasecmp(line, "<Subscription")) { /* * <Subscription #> */ if (!sub && value && isdigit(value[0] & 255)) { sub = cupsdAddSubscription(CUPSD_EVENT_NONE, NULL, NULL, NULL, atoi(value)); } else { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } } else if (!_cups_strcasecmp(line, "</Subscription>")) { if (!sub) { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } if (delete_sub) cupsdDeleteSubscription(sub, 0); sub = NULL; delete_sub = 0; } else if (!sub) { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); } else if (!_cups_strcasecmp(line, "Events")) { /* * Events name * Events name name name ... */ if (!value) { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } while (*value) { /* * Separate event names... */ for (valueptr = value; !isspace(*valueptr) && *valueptr; valueptr ++); while (isspace(*valueptr & 255)) *valueptr++ = '\0'; /* * See if the name exists... */ if ((sub->mask |= cupsdEventValue(value)) == CUPSD_EVENT_NONE) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown event name \'%s\' on line %d of subscriptions.conf.", value, linenum); break; } value = valueptr; } } else if (!_cups_strcasecmp(line, "Owner")) { /* * Owner */ if (value) cupsdSetString(&sub->owner, value); else { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } } else if (!_cups_strcasecmp(line, "Recipient")) { /* * Recipient uri */ if (value) cupsdSetString(&sub->recipient, value); else { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } } else if (!_cups_strcasecmp(line, "JobId")) { /* * JobId # */ if (value && isdigit(*value & 255)) { if ((sub->job = cupsdFindJob(atoi(value))) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, "Job %s not found on line %d of subscriptions.conf.", value, linenum); delete_sub = 1; } } else { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } } else if (!_cups_strcasecmp(line, "PrinterName")) { /* * PrinterName name */ if (value) { if ((sub->dest = cupsdFindDest(value)) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, "Printer \'%s\' not found on line %d of subscriptions.conf.", value, linenum); delete_sub = 1; } } else { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } } else if (!_cups_strcasecmp(line, "UserData")) { /* * UserData encoded-string */ if (value) { for (i = 0, valueptr = value, hex = 0; i < 63 && *valueptr; i ++) { if (*valueptr == '<' && !hex) { hex = 1; valueptr ++; } if (hex) { if (isxdigit(valueptr[0]) && isxdigit(valueptr[1])) { if (isdigit(valueptr[0])) sub->user_data[i] = (unsigned char)((valueptr[0] - '0') << 4); else sub->user_data[i] = (unsigned char)((tolower(valueptr[0]) - 'a' + 10) << 4); if (isdigit(valueptr[1])) sub->user_data[i] |= valueptr[1] - '0'; else sub->user_data[i] |= tolower(valueptr[1]) - 'a' + 10; valueptr += 2; if (*valueptr == '>') { hex = 0; valueptr ++; } } else break; } else sub->user_data[i] = (unsigned char)*valueptr++; } if (*valueptr) { cupsdLogMessage(CUPSD_LOG_ERROR, "Bad UserData \'%s\' on line %d of subscriptions.conf.", value, linenum); } else sub->user_data_len = i; } else { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } } else if (!_cups_strcasecmp(line, "LeaseDuration")) { /* * LeaseDuration # */ if (value && isdigit(*value & 255)) { sub->lease = atoi(value); sub->expire = sub->lease ? time(NULL) + sub->lease : 0; } else { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } } else if (!_cups_strcasecmp(line, "Interval")) { /* * Interval # */ if (value && isdigit(*value & 255)) sub->interval = atoi(value); else { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } } else if (!_cups_strcasecmp(line, "ExpirationTime")) { /* * ExpirationTime # */ if (value && isdigit(*value & 255)) sub->expire = atoi(value); else { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } } else if (!_cups_strcasecmp(line, "NextEventId")) { /* * NextEventId # */ if (value && isdigit(*value & 255)) sub->next_event_id = sub->first_event_id = atoi(value); else { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of subscriptions.conf.", linenum); break; } } else { /* * Something else we don't understand... */ cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown configuration directive %s on line %d of subscriptions.conf.", line, linenum); } } cupsFileClose(fp); }
cupsd_subscription_t * /* O - New subscription object */ cupsdAddSubscription( unsigned mask, /* I - Event mask */ cupsd_printer_t *dest, /* I - Printer, if any */ cupsd_job_t *job, /* I - Job, if any */ const char *uri, /* I - notify-recipient-uri, if any */ int sub_id) /* I - notify-subscription-id or 0 */ { cupsd_subscription_t *temp; /* New subscription object */ cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), " "uri=\"%s\")", mask, dest, dest ? dest->name : "", job, job ? job->id : 0, uri ? uri : "(null)"); if (!Subscriptions) Subscriptions = cupsArrayNew((cups_array_func_t)cupsd_compare_subscriptions, NULL); if (!Subscriptions) { cupsdLogMessage(CUPSD_LOG_CRIT, "Unable to allocate memory for subscriptions - %s", strerror(errno)); return (NULL); } /* * Limit the number of subscriptions... */ if (MaxSubscriptions > 0 && cupsArrayCount(Subscriptions) >= MaxSubscriptions) { cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAddSubscription: Reached MaxSubscriptions %d " "(count=%d)", MaxSubscriptions, cupsArrayCount(Subscriptions)); return (NULL); } if (MaxSubscriptionsPerJob > 0 && job) { int count; /* Number of job subscriptions */ for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0; temp; temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions)) if (temp->job == job) count ++; if (count >= MaxSubscriptionsPerJob) { cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d " "for job #%d (count=%d)", MaxSubscriptionsPerJob, job->id, count); return (NULL); } } if (MaxSubscriptionsPerPrinter > 0 && dest) { int count; /* Number of printer subscriptions */ for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0; temp; temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions)) if (temp->dest == dest) count ++; if (count >= MaxSubscriptionsPerPrinter) { cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAddSubscription: Reached " "MaxSubscriptionsPerPrinter %d for %s (count=%d)", MaxSubscriptionsPerPrinter, dest->name, count); return (NULL); } } /* * Allocate memory for this subscription... */ if ((temp = calloc(1, sizeof(cupsd_subscription_t))) == NULL) { cupsdLogMessage(CUPSD_LOG_CRIT, "Unable to allocate memory for subscription object - %s", strerror(errno)); return (NULL); } /* * Fill in common data... */ if (sub_id) { temp->id = sub_id; if (sub_id >= NextSubscriptionId) NextSubscriptionId = sub_id + 1; } else { temp->id = NextSubscriptionId; NextSubscriptionId ++; } temp->mask = mask; temp->dest = dest; temp->job = job; temp->pipe = -1; temp->first_event_id = 1; temp->next_event_id = 1; cupsdSetString(&(temp->recipient), uri); /* * Add the subscription to the array... */ cupsArrayAdd(Subscriptions, temp); /* * For RSS subscriptions, run the notifier immediately... */ if (uri && !strncmp(uri, "rss:", 4)) cupsd_start_notifier(temp); return (temp); }
int /* O - Exit status */ main(int argc, /* I - Number of command-line args */ char *argv[] /* I - Command-line arguments */ #ifdef PRINT_AUXV , char* envp[] #endif ) { int i; /* Looping var */ char *opt; /* Option character */ int fg; /* Run in the foreground */ int fds; /* Number of ready descriptors */ cupsd_client_t *con; /* Current client */ cupsd_job_t *job; /* Current job */ cupsd_listener_t *lis; /* Current listener */ time_t current_time, /* Current time */ activity, /* Client activity timer */ browse_time, /* Next browse send time */ senddoc_time, /* Send-Document time */ expire_time, /* Subscription expire time */ report_time, /* Malloc/client/job report time */ event_time; /* Last time an event notification was done */ long timeout; /* Timeout for cupsdDoSelect() */ struct rlimit limit; /* Runtime limit */ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* Actions for POSIX signals */ #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ #ifdef __sgi cups_file_t *fp; /* Fake lpsched lock file */ struct stat statbuf; /* Needed for checking lpsched FIFO */ #endif /* __sgi */ #ifdef __APPLE__ int run_as_child = 0; /* Needed for Mac OS X fork/exec */ #else time_t netif_time = 0; /* Time since last network update */ #endif /* __APPLE__ */ #if HAVE_LAUNCHD int launchd_idle_exit; /* Idle exit on select timeout? */ #endif /* HAVE_LAUNCHD */ #ifdef PRINT_AUXV Elf32_auxv_t *auxv; while(*envp++ != NULL); for (auxv = (Elf32_auxv_t *)envp; auxv->a_type != AT_NULL; auxv++) { if (auxv->a_type == AT_SYSINFO) { printf("AT_RANDOM is 0x%x\n", auxv->a_un.a_val); } } #endif #if UNDERSTAND_CUPS printf("[main.c::main()] cups has just started in main()\n"); #endif int count = 0; int loops_desired = -1; // loop forever unless this is set #ifdef HAVE_GETEUID /* * Check for setuid invocation, which we do not support! */ if (getuid() != geteuid()) { fputs("cupsd: Cannot run as a setuid program!\n", stderr); return (1); } #if UNDERSTAND_CUPS printf("[main.c::main()] uid = %d, euid = %d\n", getuid(), geteuid()); #endif #endif /* HAVE_GETEUID */ /* * Check for command-line arguments... */ #if UNDERSTAND_CUPS printf("[main.c::main()] parsing command line arguments \n"); #endif fg = 0; #ifdef HAVE_LAUNCHD if (getenv("CUPSD_LAUNCHD")) { Launchd = 1; fg = 1; } #endif /* HAVE_LAUNCHD */ for (i = 1; i < argc; i ++) if (argv[i][0] == '-') for (opt = argv[i] + 1; *opt != '\0'; opt ++) switch (*opt) { #ifdef __APPLE__ case 'C' : /* Run as child with config file */ run_as_child = 1; fg = -1; #endif /* __APPLE__ */ case 'c' : /* Configuration file */ i ++; if (i >= argc) { _cupsLangPuts(stderr, _("cupsd: Expected config filename " "after \"-c\" option!\n")); usage(1); } if (argv[i][0] == '/') { /* * Absolute directory... */ cupsdSetString(&ConfigurationFile, argv[i]); } else { /* * Relative directory... */ char *current; /* Current directory */ /* * Allocate a buffer for the current working directory to * reduce run-time stack usage; this approximates the * behavior of some implementations of getcwd() when they * are passed a NULL pointer. */ if ((current = malloc(1024)) == NULL) { _cupsLangPuts(stderr, _("cupsd: Unable to get current directory!\n")); return (1); } if (!getcwd(current, 1024)) { _cupsLangPuts(stderr, _("cupsd: Unable to get current directory!\n")); free(current); return (1); } cupsdSetStringf(&ConfigurationFile, "%s/%s", current, argv[i]); free(current); } break; case 'f' : /* Run in foreground... */ fg = 1; break; case 'F' : /* Run in foreground, but disconnect from terminal... */ fg = -1; break; case 'h' : /* Show usage/help */ usage(0); break; case 'l' : /* Started by launchd... */ #ifdef HAVE_LAUNCHD Launchd = 1; fg = 1; #else _cupsLangPuts(stderr, _("cupsd: launchd(8) support not compiled " "in, running in normal mode.\n")); fg = 0; #endif /* HAVE_LAUNCHD */ break; case 'p' : /* Stop immediately for profiling */ puts("Warning: -p option is for internal testing use only!"); stop_scheduler = 1; fg = 1; break; /// XXX RAZA case 'x' : /* Number of Iterations to Perform */ i ++; loops_desired = atoi(argv[i]); break; default : /* Unknown option */ _cupsLangPrintf(stderr, _("cupsd: Unknown option \"%c\" - " "aborting!\n"), *opt); usage(1); break; } else { _cupsLangPrintf(stderr, _("cupsd: Unknown argument \"%s\" - aborting!\n"), argv[i]); usage(1); } #if UNDERSTAND_CUPS printf("[main.c::main()] done parsing command line arguments \n"); printf("[main.c::main()] num_loops = %d, foreground = %d\n", loops_desired, fg); #endif if (!ConfigurationFile) cupsdSetString(&ConfigurationFile, CUPS_SERVERROOT "/cupsd.conf"); printf("[main.c::main()] configuration file = %s\n", ConfigurationFile); /* * If the user hasn't specified "-f", run in the background... */ if (!fg) { /* * Setup signal handlers for the parent... */ #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ sigset(SIGUSR1, parent_handler); sigset(SIGCHLD, parent_handler); sigset(SIGHUP, SIG_IGN); #elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGUSR1); action.sa_handler = parent_handler; sigaction(SIGUSR1, &action, NULL); sigaction(SIGCHLD, &action, NULL); sigemptyset(&action.sa_mask); action.sa_handler = SIG_IGN; sigaction(SIGHUP, &action, NULL); #else signal(SIGUSR1, parent_handler); signal(SIGCLD, parent_handler); signal(SIGHUP, SIG_IGN); #endif /* HAVE_SIGSET */ if (fork() > 0) { /* * OK, wait for the child to startup and send us SIGUSR1 or to crash * and the OS send us SIGCHLD... We also need to ignore SIGHUP which * might be sent by the init script to restart the scheduler... */ for (; parent_signal == 0;) sleep(1); if (parent_signal == SIGUSR1) return (0); if (wait(&i) < 0) { perror("cupsd"); return (1); } else if (WIFEXITED(i)) { fprintf(stderr, "cupsd: Child exited with status %d!\n", WEXITSTATUS(i)); return (2); } else { fprintf(stderr, "cupsd: Child exited on signal %d!\n", WTERMSIG(i)); return (3); } } #ifdef __APPLE__ /* * Since CoreFoundation has an overly-agressive check for whether a * process has forked but not exec'd (whether CF has been called or * not...), we now have to exec ourselves with the "-f" option to * eliminate their bogus warning messages. */ execlp(argv[0], argv[0], "-C", ConfigurationFile, (char *)0); exit(errno); #endif /* __APPLE__ */ } if (fg < 1) { /* * Make sure we aren't tying up any filesystems... */ chdir("/"); #ifndef DEBUG /* * Disable core dumps... */ getrlimit(RLIMIT_CORE, &limit); limit.rlim_cur = 0; setrlimit(RLIMIT_CORE, &limit); /* * Disconnect from the controlling terminal... */ setsid(); /* * Close all open files... */ getrlimit(RLIMIT_NOFILE, &limit); for (i = 0; i < limit.rlim_cur && i < 1024; i ++) close(i); #endif /* DEBUG */ } /* * Set the timezone info... */ #if UNDERSTAND_CUPS printf("[main.c::main()] setting timezone info, tzset()\n"); #endif tzset(); #ifdef LC_TIME setlocale(LC_TIME, ""); #if UNDERSTAND_CUPS printf("[main.c::main()] setting locale info, setlocale()\n"); #endif #endif /* LC_TIME */ /* * Set the maximum number of files... */ getrlimit(RLIMIT_NOFILE, &limit); #if !defined(HAVE_POLL) && !defined(HAVE_EPOLL) && !defined(HAVE_KQUEUE) if (limit.rlim_max > FD_SETSIZE) MaxFDs = FD_SETSIZE; else #endif /* !HAVE_POLL && !HAVE_EPOLL && !HAVE_KQUEUE */ #ifdef RLIM_INFINITY if (limit.rlim_max == RLIM_INFINITY) MaxFDs = 16384; else #endif /* RLIM_INFINITY */ MaxFDs = limit.rlim_max; limit.rlim_cur = MaxFDs; setrlimit(RLIMIT_NOFILE, &limit); #if UNDERSTAND_CUPS printf("[main.c::main()] set the maximum nuber of files = %d\n", MaxFDs); #endif cupsdStartSelect(); #if UNDERSTAND_CUPS printf("[main.c::main()] done with cupsdStartSelect() i.e. polling engine\n"); #endif /* * Read configuration... */ if (!cupsdReadConfiguration()) { syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!", ConfigurationFile); return (1); } printf("[main.c::main()] done with cupsdReadConfiguration\n"); if (!strncmp(TempDir, RequestRoot, strlen(RequestRoot))) { /* * Clean out the temporary directory... */ cups_dir_t *dir; /* Temporary directory */ cups_dentry_t *dent; /* Directory entry */ char tempfile[1024]; /* Temporary filename */ printf("[main.c::main()] cleaning temp dir = %s\n", TempDir); if ((dir = cupsDirOpen(TempDir)) != NULL) { cupsdLogMessage(CUPSD_LOG_INFO, "Cleaning out old temporary files in \"%s\"...", TempDir); while ((dent = cupsDirRead(dir)) != NULL) { snprintf(tempfile, sizeof(tempfile), "%s/%s", TempDir, dent->filename); printf("[main.c::main()] deleting temp file = %s\n", tempfile); if (cupsdRemoveFile(tempfile)) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove temporary file \"%s\" - %s", tempfile, strerror(errno)); printf("[main.c::main()] failed in deleting temp file = %s\n", tempfile); } else { cupsdLogMessage(CUPSD_LOG_DEBUG, "Removed temporary file \"%s\"...", tempfile); printf("[main.c::main()] deleted temp file = %s\n", tempfile); } } cupsDirClose(dir); } else { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open temporary directory \"%s\" - %s", TempDir, strerror(errno)); printf("[main.c::main()] couldn't open temp dir = %s\n", TempDir); } printf("[main.c::main()] DONE cleaning temp dir = %s\n", TempDir); } #if HAVE_LAUNCHD if (Launchd) { /* * If we were started by launchd get the listen sockets file descriptors... */ printf("[main.c::main()] launchd check in \n"); launchd_checkin(); } #endif /* HAVE_LAUNCHD */ #if defined(__APPLE__) && defined(HAVE_DLFCN_H) /* * Load Print Service quota enforcement library (X Server only) */ PSQLibRef = dlopen(PSQLibPath, RTLD_LAZY); if (PSQLibRef) PSQUpdateQuotaProc = dlsym(PSQLibRef, PSQLibFuncName); #endif /* __APPLE__ && HAVE_DLFCN_H */ #ifdef HAVE_GSSAPI # ifdef __APPLE__ /* * If the weak-linked GSSAPI/Kerberos library is not present, don't try * to use it... */ if (krb5_init_context != NULL) # endif /* __APPLE__ */ /* * Setup a Kerberos context for the scheduler to use... */ if (krb5_init_context(&KerberosContext)) { KerberosContext = NULL; printf("[main.c::main()] unable to use krb5_init_context() \n"); cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize Kerberos context"); } else { printf("[main.c::main()] used krb5_init_context() \n"); } #endif /* HAVE_GSSAPI */ /* * Startup the server... */ cupsdStartServer(); /* * Catch hangup and child signals and ignore broken pipes... */ #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ sigset(SIGCHLD, sigchld_handler); sigset(SIGHUP, sighup_handler); sigset(SIGPIPE, SIG_IGN); sigset(SIGTERM, sigterm_handler); printf("[main.c::main()] used system V signals over POSIX\n"); #elif defined(HAVE_SIGACTION) printf("[main.c::main()] used HAVE_SIGACTION\n"); memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGTERM); sigaddset(&action.sa_mask, SIGCHLD); action.sa_handler = sigchld_handler; sigaction(SIGCHLD, &action, NULL); sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGHUP); action.sa_handler = sighup_handler; sigaction(SIGHUP, &action, NULL); sigemptyset(&action.sa_mask); action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &action, NULL); sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGTERM); sigaddset(&action.sa_mask, SIGCHLD); action.sa_handler = sigterm_handler; sigaction(SIGTERM, &action, NULL); #else printf("[main.c::main()]last signal options\n"); signal(SIGCLD, sigchld_handler); /* No, SIGCLD isn't a typo... */ signal(SIGHUP, sighup_handler); signal(SIGPIPE, SIG_IGN); signal(SIGTERM, sigterm_handler); #endif /* HAVE_SIGSET */ #ifdef __sgi /* * Try to create a fake lpsched lock file if one is not already there. * Some Adobe applications need it under IRIX in order to enable * printing... */ printf("[main.c::main()] creating fake lpsched lock file \n"); if ((fp = cupsFileOpen("/var/spool/lp/SCHEDLOCK", "w")) == NULL) { syslog(LOG_LPR, "Unable to create fake lpsched lock file " "\"/var/spool/lp/SCHEDLOCK\"\' - %s!", strerror(errno)); } else { fchmod(cupsFileNumber(fp), 0644); fchown(cupsFileNumber(fp), User, Group); cupsFileClose(fp); } #endif /* __sgi */ /* * Initialize authentication certificates... */ cupsdInitCerts(); /* * If we are running in the background, signal the parent process that * we are up and running... */ #ifdef __APPLE__ if (!fg || run_as_child) #else if (!fg) #endif /* __APPLE__ */ { /* * Send a signal to the parent process, but only if the parent is * not PID 1 (init). This avoids accidentally shutting down the * system on OpenBSD if you CTRL-C the server before it is up... */ i = getppid(); /* Save parent PID to avoid race condition */ if (i != 1) kill(i, SIGUSR1); } #ifdef __APPLE__ /* * Start power management framework... */ printf("[main.c::main()] starting system monitor (power management) \n"); cupsdStartSystemMonitor(); #endif /* __APPLE__ */ /* * Start any pending print jobs... */ printf("[main.c::main()] starting any pending print jobs \n"); cupsdCheckJobs(); /* * Loop forever... */ current_time = time(NULL); browse_time = current_time; event_time = current_time; expire_time = current_time; fds = 1; report_time = 0; senddoc_time = current_time; while (!stop_scheduler) { // XXX RAZA printf("[main.c::main()] scheduler loop # %d\n", count); if (count++ == loops_desired) { printf("[main.c::main()] stopping scheduler loop\n"); stop_scheduler = 1; continue; } printf("[main.c::main()] Top of loop, dead_children=%d, NeedReload=%d\n", dead_children, NeedReload); #ifdef DEBUG cupsdLogMessage(CUPSD_LOG_DEBUG2, "main: Top of loop, dead_children=%d, NeedReload=%d", dead_children, NeedReload); #endif /* DEBUG */ /* * Check if there are dead children to handle... */ if (dead_children) process_children(); /* * Check if we need to load the server configuration file... */ if (NeedReload) { /* * Close any idle clients... */ if (cupsArrayCount(Clients) > 0) { for (con = (cupsd_client_t *)cupsArrayFirst(Clients); con; con = (cupsd_client_t *)cupsArrayNext(Clients)) if (con->http.state == HTTP_WAITING) cupsdCloseClient(con); else con->http.keep_alive = HTTP_KEEPALIVE_OFF; cupsdPauseListening(); } /* * Check for any active jobs... */ for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); job; job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) if (job->state_value == IPP_JOB_PROCESSING) break; /* * Restart if all clients are closed and all jobs finished, or * if the reload timeout has elapsed... */ if ((cupsArrayCount(Clients) == 0 && (!job || NeedReload != RELOAD_ALL)) || (time(NULL) - ReloadTime) >= ReloadTimeout) { /* * Shutdown the server... */ cupsdStopServer(); /* * Read configuration... */ if (!cupsdReadConfiguration()) { syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!", ConfigurationFile); break; } #if HAVE_LAUNCHD if (Launchd) { /* * If we were started by launchd get the listen sockets file descriptors... */ launchd_checkin(); } #endif /* HAVE_LAUNCHD */ /* * Startup the server... */ cupsdStartServer(); } } /* * Check for available input or ready output. If cupsdDoSelect() * returns 0 or -1, something bad happened and we should exit * immediately. * * Note that we at least have one listening socket open at all * times. */ if ((timeout = select_timeout(fds)) > 1 && LastEvent) timeout = 1; #if HAVE_LAUNCHD /* * If no other work is scheduled and we're being controlled by * launchd then timeout after 'LaunchdTimeout' seconds of * inactivity... */ if (timeout == 86400 && Launchd && LaunchdTimeout && !NumPolled && !cupsArrayCount(ActiveJobs) && (!Browsing || (!BrowseRemoteProtocols && (!NumBrowsers || !BrowseLocalProtocols || cupsArrayCount(Printers) == 0)))) { timeout = LaunchdTimeout; launchd_idle_exit = 1; } else launchd_idle_exit = 0; #endif /* HAVE_LAUNCHD */ if ((fds = cupsdDoSelect(timeout)) < 0) { /* * Got an error from select! */ #ifdef HAVE_DNSSD cupsd_printer_t *p; /* Current printer */ #endif /* HAVE_DNSSD */ if (errno == EINTR) /* Just interrupted by a signal */ continue; /* * Log all sorts of debug info to help track down the problem. */ cupsdLogMessage(CUPSD_LOG_EMERG, "cupsdDoSelect() failed - %s!", strerror(errno)); for (i = 0, con = (cupsd_client_t *)cupsArrayFirst(Clients); con; i ++, con = (cupsd_client_t *)cupsArrayNext(Clients)) cupsdLogMessage(CUPSD_LOG_EMERG, "Clients[%d] = %d, file = %d, state = %d", i, con->http.fd, con->file, con->http.state); for (i = 0, lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); lis; i ++, lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) cupsdLogMessage(CUPSD_LOG_EMERG, "Listeners[%d] = %d", i, lis->fd); cupsdLogMessage(CUPSD_LOG_EMERG, "BrowseSocket = %d", BrowseSocket); cupsdLogMessage(CUPSD_LOG_EMERG, "CGIPipes[0] = %d", CGIPipes[0]); #ifdef __APPLE__ cupsdLogMessage(CUPSD_LOG_EMERG, "SysEventPipes[0] = %d", SysEventPipes[0]); #endif /* __APPLE__ */ for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); job; job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) cupsdLogMessage(CUPSD_LOG_EMERG, "Jobs[%d] = %d < [%d %d] > [%d %d]", job->id, job->status_buffer ? job->status_buffer->fd : -1, job->print_pipes[0], job->print_pipes[1], job->back_pipes[0], job->back_pipes[1]); #ifdef HAVE_DNSSD for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); p; p = (cupsd_printer_t *)cupsArrayNext(Printers)) cupsdLogMessage(CUPSD_LOG_EMERG, "printer[%s] %d", p->name, p->dnssd_ipp_fd); #endif /* HAVE_DNSSD */ break; } current_time = time(NULL); #ifndef __APPLE__ /* * Update the network interfaces once a minute... */ if ((current_time - netif_time) >= 60) { netif_time = current_time; NetIFUpdate = 1; } #endif /* !__APPLE__ */ #if HAVE_LAUNCHD /* * If no other work was scheduled and we're being controlled by launchd * then timeout after 'LaunchdTimeout' seconds of inactivity... */ if (!fds && launchd_idle_exit) { cupsdLogMessage(CUPSD_LOG_INFO, "Printer sharing is off and there are no jobs pending, " "will restart on demand."); stop_scheduler = 1; break; } #endif /* HAVE_LAUNCHD */ /* * Resume listening for new connections as needed... */ if (ListeningPaused && ListeningPaused <= current_time && cupsArrayCount(Clients) < MaxClients) cupsdResumeListening(); /* * Expire subscriptions and unload completed jobs as needed... */ if (current_time > expire_time) { if (cupsArrayCount(Subscriptions) > 0) cupsdExpireSubscriptions(NULL, NULL); cupsdUnloadCompletedJobs(); expire_time = current_time; } /* * Update the browse list as needed... */ if (Browsing) { #ifdef HAVE_LIBSLP if ((BrowseRemoteProtocols & BROWSE_SLP) && BrowseSLPRefresh <= current_time) cupsdUpdateSLPBrowse(); #endif /* HAVE_LIBSLP */ #ifdef HAVE_LDAP if ((BrowseRemoteProtocols & BROWSE_LDAP) && BrowseLDAPRefresh <= current_time) cupsdUpdateLDAPBrowse(); #endif /* HAVE_LDAP */ } if (Browsing && current_time > browse_time) { cupsdSendBrowseList(); browse_time = current_time; } /* * Update the root certificate once every 5 minutes if we have client * connections... */ if ((current_time - RootCertTime) >= RootCertDuration && RootCertDuration && !RunUser && cupsArrayCount(Clients)) { /* * Update the root certificate... */ cupsdDeleteCert(0); cupsdAddCert(0, "root", NULL); } /* * Check for new data on the client sockets... */ for (con = (cupsd_client_t *)cupsArrayFirst(Clients); con; con = (cupsd_client_t *)cupsArrayNext(Clients)) { /* * Process pending data in the input buffer... */ if (con->http.used) { cupsdReadClient(con); continue; } /* * Check the activity and close old clients... */ activity = current_time - Timeout; if (con->http.activity < activity && !con->pipe_pid) { cupsdLogMessage(CUPSD_LOG_DEBUG, "Closing client %d after %d seconds of inactivity...", con->http.fd, Timeout); cupsdCloseClient(con); continue; } } /* * Update any pending multi-file documents... */ if ((current_time - senddoc_time) >= 10) { cupsdCheckJobs(); senddoc_time = current_time; } /* * Log statistics at most once a minute when in debug mode... */ if ((current_time - report_time) >= 60 && LogLevel >= CUPSD_LOG_DEBUG) { size_t string_count, /* String count */ alloc_bytes, /* Allocated string bytes */ total_bytes; /* Total string bytes */ #ifdef HAVE_MALLINFO struct mallinfo mem; /* Malloc information */ mem = mallinfo(); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: malloc-arena=%lu", mem.arena); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: malloc-used=%lu", mem.usmblks + mem.uordblks); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: malloc-free=%lu", mem.fsmblks + mem.fordblks); #endif /* HAVE_MALLINFO */ cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: clients=%d", cupsArrayCount(Clients)); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: jobs=%d", cupsArrayCount(Jobs)); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: jobs-active=%d", cupsArrayCount(ActiveJobs)); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: printers=%d", cupsArrayCount(Printers)); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: printers-implicit=%d", cupsArrayCount(ImplicitPrinters)); string_count = _cupsStrStatistics(&alloc_bytes, &total_bytes); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: stringpool-string-count=" CUPS_LLFMT, CUPS_LLCAST string_count); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: stringpool-alloc-bytes=" CUPS_LLFMT, CUPS_LLCAST alloc_bytes); cupsdLogMessage(CUPSD_LOG_DEBUG, "Report: stringpool-total-bytes=" CUPS_LLFMT, CUPS_LLCAST total_bytes); report_time = current_time; } /* * Handle OS-specific event notification for any events that have * accumulated. Don't send these more than once a second... */ if (LastEvent && (current_time - event_time) >= 1) { #ifdef HAVE_NOTIFY_POST if (LastEvent & (CUPSD_EVENT_PRINTER_ADDED | CUPSD_EVENT_PRINTER_DELETED | CUPSD_EVENT_PRINTER_MODIFIED)) { cupsdLogMessage(CUPSD_LOG_DEBUG2, "notify_post(\"com.apple.printerListChange\")"); notify_post("com.apple.printerListChange"); } if (LastEvent & CUPSD_EVENT_PRINTER_STATE_CHANGED) { cupsdLogMessage(CUPSD_LOG_DEBUG2, "notify_post(\"com.apple.printerHistoryChange\")"); notify_post("com.apple.printerHistoryChange"); } if (LastEvent & (CUPSD_EVENT_JOB_STATE_CHANGED | CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_PROGRESS)) { cupsdLogMessage(CUPSD_LOG_DEBUG2, "notify_post(\"com.apple.jobChange\")"); notify_post("com.apple.jobChange"); } #endif /* HAVE_NOTIFY_POST */ /* * Reset the accumulated events... */ LastEvent = CUPSD_EVENT_NONE; event_time = current_time; } } /* * Log a message based on what happened... */ if (stop_scheduler) cupsdLogMessage(CUPSD_LOG_INFO, "Scheduler shutting down normally."); else cupsdLogMessage(CUPSD_LOG_ERROR, "Scheduler shutting down due to program error."); /* * Close all network clients... */ cupsdStopServer(); #ifdef HAVE_LAUNCHD /* * Update the launchd KeepAlive file as needed... */ if (Launchd) launchd_checkout(); #endif /* HAVE_LAUNCHD */ /* * Stop all jobs... */ cupsdFreeAllJobs(); #ifdef __APPLE__ /* * Stop monitoring system event monitoring... */ cupsdStopSystemMonitor(); #endif /* __APPLE__ */ #ifdef HAVE_GSSAPI /* * Free the scheduler's Kerberos context... */ # ifdef __APPLE__ /* * If the weak-linked GSSAPI/Kerberos library is not present, don't try * to use it... */ if (krb5_init_context != NULL) # endif /* __APPLE__ */ if (KerberosContext) krb5_free_context(KerberosContext); #endif /* HAVE_GSSAPI */ #ifdef __APPLE__ #ifdef HAVE_DLFCN_H /* * Unload Print Service quota enforcement library (X Server only) */ PSQUpdateQuotaProc = NULL; if (PSQLibRef) { dlclose(PSQLibRef); PSQLibRef = NULL; } #endif /* HAVE_DLFCN_H */ #endif /* __APPLE__ */ #ifdef __sgi /* * Remove the fake IRIX lpsched lock file, but only if the existing * file is not a FIFO which indicates that the real IRIX lpsched is * running... */ if (!stat("/var/spool/lp/FIFO", &statbuf)) if (!S_ISFIFO(statbuf.st_mode)) unlink("/var/spool/lp/SCHEDLOCK"); #endif /* __sgi */ cupsdStopSelect(); return (!stop_scheduler); }
void cupsdLoadAllClasses(void) { int i; /* Looping var */ cups_file_t *fp; /* classes.conf file */ int linenum; /* Current line number */ char line[4096], /* Line from file */ *value, /* Pointer to value */ *valueptr; /* Pointer into value */ cupsd_printer_t *p, /* Current printer class */ *temp; /* Temporary pointer to printer */ /* * Open the classes.conf file... */ snprintf(line, sizeof(line), "%s/classes.conf", ServerRoot); if ((fp = cupsFileOpen(line, "r")) == NULL) { if (errno != ENOENT) cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open %s - %s", line, strerror(errno)); return; } /* * Read class configurations until we hit EOF... */ linenum = 0; p = NULL; while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) { /* * Decode the directive... */ if (!strcasecmp(line, "<Class") || !strcasecmp(line, "<DefaultClass")) { /* * <Class name> or <DefaultClass name> */ if (p == NULL && value) { cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading class %s...", value); /* * Since prior classes may have implicitly defined this class, * see if it already exists... */ if ((p = cupsdFindDest(value)) != NULL) { p->type = CUPS_PRINTER_CLASS; cupsdSetStringf(&p->uri, "ipp://%s:%d/classes/%s", ServerName, LocalPort, value); cupsdSetString(&p->error_policy, "retry-job"); } else p = cupsdAddClass(value); p->accepting = 1; p->state = IPP_PRINTER_IDLE; if (!strcasecmp(line, "<DefaultClass")) DefaultPrinter = p; } else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "</Class>")) { if (p != NULL) { cupsdSetPrinterAttrs(p); p = NULL; } else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!p) { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "AuthInfoRequired")) { if (!cupsdSetAuthInfoRequired(p, value, NULL)) cupsdLogMessage(CUPSD_LOG_ERROR, "Bad AuthInfoRequired on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "Info")) { if (value) cupsdSetString(&p->info, value); } else if (!strcasecmp(line, "Location")) { if (value) cupsdSetString(&p->location, value); } else if (!strcasecmp(line, "Option") && value) { /* * Option name value */ for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++); if (!*valueptr) cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); else { for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0'); p->num_options = cupsAddOption(value, valueptr, p->num_options, &(p->options)); } } else if (!strcasecmp(line, "Printer")) { if (!value) { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); continue; } else if ((temp = cupsdFindPrinter(value)) == NULL) { cupsdLogMessage(CUPSD_LOG_WARN, "Unknown printer %s on line %d of classes.conf.", value, linenum); /* * Add the missing remote printer... */ if ((temp = cupsdAddPrinter(value)) != NULL) { cupsdSetString(&temp->make_model, "Remote Printer on unknown"); temp->state = IPP_PRINTER_STOPPED; temp->type |= CUPS_PRINTER_REMOTE; temp->browse_time = 2147483647; cupsdSetString(&temp->location, "Location Unknown"); cupsdSetString(&temp->info, "No Information Available"); temp->hostname[0] = '\0'; cupsdSetPrinterAttrs(temp); } } if (temp) cupsdAddPrinterToClass(p, temp); } else if (!strcasecmp(line, "State")) { /* * Set the initial queue state... */ if (!strcasecmp(value, "idle")) p->state = IPP_PRINTER_IDLE; else if (!strcasecmp(value, "stopped")) { p->state = IPP_PRINTER_STOPPED; for (i = 0 ; i < p->num_reasons; i ++) if (!strcmp("paused", p->reasons[i])) break; if (i >= p->num_reasons && p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0]))) { p->reasons[p->num_reasons] = _cupsStrAlloc("paused"); p->num_reasons ++; } } else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "StateMessage")) { /* * Set the initial queue state message... */ if (value) strlcpy(p->state_message, value, sizeof(p->state_message)); } else if (!strcasecmp(line, "StateTime")) { /* * Set the state time... */ if (value) p->state_time = atoi(value); } else if (!strcasecmp(line, "Accepting")) { /* * Set the initial accepting state... */ if (value && (!strcasecmp(value, "yes") || !strcasecmp(value, "on") || !strcasecmp(value, "true"))) p->accepting = 1; else if (value && (!strcasecmp(value, "no") || !strcasecmp(value, "off") || !strcasecmp(value, "false"))) p->accepting = 0; else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "Shared")) { /* * Set the initial shared state... */ if (value && (!strcasecmp(value, "yes") || !strcasecmp(value, "on") || !strcasecmp(value, "true"))) p->shared = 1; else if (value && (!strcasecmp(value, "no") || !strcasecmp(value, "off") || !strcasecmp(value, "false"))) p->shared = 0; else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "JobSheets")) { /* * Set the initial job sheets... */ if (value) { for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++); if (*valueptr) *valueptr++ = '\0'; cupsdSetString(&p->job_sheets[0], value); while (isspace(*valueptr & 255)) valueptr ++; if (*valueptr) { for (value = valueptr; *valueptr && !isspace(*valueptr & 255); valueptr ++); if (*valueptr) *valueptr = '\0'; cupsdSetString(&p->job_sheets[1], value); } } else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "AllowUser")) { if (value) { p->deny_users = 0; cupsdAddPrinterUser(p, value); } else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "DenyUser")) { if (value) { p->deny_users = 1; cupsdAddPrinterUser(p, value); } else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "QuotaPeriod")) { if (value) p->quota_period = atoi(value); else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "PageLimit")) { if (value) p->page_limit = atoi(value); else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "KLimit")) { if (value) p->k_limit = atoi(value); else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "OpPolicy")) { if (value) { cupsd_policy_t *pol; /* Policy */ if ((pol = cupsdFindPolicy(value)) != NULL) { cupsdSetString(&p->op_policy, value); p->op_policy_ptr = pol; } else cupsdLogMessage(CUPSD_LOG_ERROR, "Bad policy \"%s\" on line %d of classes.conf", value, linenum); } else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else if (!strcasecmp(line, "ErrorPolicy")) { if (value) { if (strcmp(value, "retry-current-job") && strcmp(value, "retry-job")) cupsdLogMessage(CUPSD_LOG_WARN, "ErrorPolicy %s ignored on line %d of classes.conf", value, linenum); } else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of classes.conf.", linenum); } else { /* * Something else we don't understand... */ cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown configuration directive %s on line %d of classes.conf.", line, linenum); } } cupsFileClose(fp); }