X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=scheduler%2Fmain.c;h=81e04ce4990b84241b53e5d3620192b65fbbe305;hb=26c14fa6e1b9639d47aca3032337d9fe728ee2dc;hp=613b593fb276369dc6cbc0269aad89c318931c64;hpb=ac884b6a1c1c3dfe73ef7e770d91ea225002a2c3;p=thirdparty%2Fcups.git diff --git a/scheduler/main.c b/scheduler/main.c index 613b593fb..81e04ce49 100644 --- a/scheduler/main.c +++ b/scheduler/main.c @@ -1,38 +1,14 @@ /* - * "$Id: main.c 6914 2007-09-05 21:05:04Z mike $" + * Main loop for the CUPS scheduler. * - * Scheduler main loop for the Common UNIX Printing System (CUPS). + * Copyright 2007-2016 by Apple Inc. + * Copyright 1997-2007 by Easy Software Products, all rights reserved. * - * Copyright 2007-2008 by Apple Inc. - * Copyright 1997-2007 by Easy Software Products, all rights reserved. - * - * These coded instructions, statements, and computer programs are the - * property of Apple Inc. and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "LICENSE.txt" - * "LICENSE" which should have been included with this file. If this - * file is missing or damaged, see the license at "http://www.cups.org/". - * - * Contents: - * - * main() - Main entry for the CUPS scheduler. - * cupsdClosePipe() - Close a pipe as necessary. - * cupsdOpenPipe() - Create a pipe which is closed on exec. - * cupsdHoldSignals() - Hold child and termination signals. - * cupsdReleaseSignals() - Release signals for delivery. - * cupsdSetString() - Set a string value. - * cupsdSetStringf() - Set a formatted string value. - * launchd_checkin() - Check-in with launchd and collect the - * listening fds. - * launchd_checkout() - Check-out with launchd. - * parent_handler() - Catch USR1/CHLD signals... - * process_children() - Process all dead children... - * sigchld_handler() - Handle 'child' signals from old processes. - * sighup_handler() - Handle 'hangup' signals to reconfigure the - * scheduler. - * sigterm_handler() - Handle 'terminate' signals that stop the - * scheduler. - * select_timeout() - Calculate the select timeout value. - * usage() - Show scheduler usage. + * These coded instructions, statements, and computer programs are the + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * "LICENSE" which should have been included with this file. If this + * file is missing or damaged, see the license at "http://www.cups.org/". */ /* @@ -42,53 +18,64 @@ #define _MAIN_C_ #include "cupsd.h" #include +#ifdef HAVE_ASL_H +# include +#elif defined(HAVE_SYSTEMD_SD_JOURNAL_H) +# define SD_JOURNAL_SUPPRESS_LOCATION +# include +#endif /* HAVE_ASL_H */ #include #include -#include #ifdef HAVE_LAUNCH_H # include # include -# define CUPS_KEEPALIVE CUPS_CACHEDIR "/org.cups.cupsd" +# define CUPS_KEEPALIVE CUPS_CACHEDIR "/org.cups.cupsd" /* Name of the launchd KeepAlive file */ -# ifndef LAUNCH_JOBKEY_KEEPALIVE -# define LAUNCH_JOBKEY_KEEPALIVE "KeepAlive" -# endif /* !LAUNCH_JOBKEY_KEEPALIVE */ -# ifndef LAUNCH_JOBKEY_PATHSTATE -# define LAUNCH_JOBKEY_PATHSTATE "PathState" -# endif /* !LAUNCH_JOBKEY_PATHSTATE */ -# ifndef LAUNCH_JOBKEY_SERVICEIPC -# define LAUNCH_JOBKEY_SERVICEIPC "ServiceIPC" -# endif /* !LAUNCH_JOBKEY_SERVICEIPC */ +# ifdef HAVE_LAUNCH_ACTIVATE_SOCKET +/* Update when we have a public header we can include */ +extern int launch_activate_socket(const char *name, int **fds, size_t *cnt); +# endif /* HAVE_LAUNCH_ACTIVATE_SOCKET */ #endif /* HAVE_LAUNCH_H */ +#ifdef HAVE_SYSTEMD +# include +# define CUPS_KEEPALIVE CUPS_CACHEDIR "/org.cups.cupsd" + /* Name of the systemd path file */ +#endif /* HAVE_SYSTEMD */ + #if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO) # include #endif /* HAVE_MALLOC_H && HAVE_MALLINFO */ + #ifdef HAVE_NOTIFY_H # include #endif /* HAVE_NOTIFY_H */ -#if defined(__APPLE__) && defined(HAVE_DLFCN_H) -# include -#endif /* __APPLE__ && HAVE_DLFCN_H */ +#ifdef HAVE_DBUS +# include +#endif /* HAVE_DBUS */ + +#ifdef HAVE_SYS_PARAM_H +# include +#endif /* HAVE_SYS_PARAM_H */ /* * Local functions... */ -#ifdef HAVE_LAUNCHD -static void launchd_checkin(void); -static void launchd_checkout(void); -#endif /* HAVE_LAUNCHD */ static void parent_handler(int sig); static void process_children(void); static void sigchld_handler(int sig); static void sighup_handler(int sig); static void sigterm_handler(int sig); static long select_timeout(int fds); -static void usage(int status); +#if defined(HAVE_ONDEMAND) +static void service_checkin(void); +static void service_checkout(void); +#endif /* HAVE_ONDEMAND */ +static void usage(int status) __attribute__((noreturn)); /* @@ -106,12 +93,6 @@ static int dead_children = 0; static int stop_scheduler = 0; /* Should the scheduler stop? */ -#if defined(__APPLE__) && defined(HAVE_DLFCN_H) -static const char *PSQLibPath = "/usr/lib/libPrintServiceQuota.dylib"; -static const char *PSQLibFuncName = "PSQUpdateQuota"; -static void *PSQLibRef; /* libPrintServiceQuota.dylib */ -#endif /* HAVE_DLFCN_H */ - /* * 'main()' - Main entry for the CUPS scheduler. @@ -123,36 +104,37 @@ main(int argc, /* I - Number of command-line args */ { int i; /* Looping var */ char *opt; /* Option character */ - int fg; /* Run in the foreground */ + int close_all = 1, /* Close all file descriptors? */ + disconnect = 1, /* Disconnect from controlling terminal? */ + fg = 0, /* Run in foreground? */ + run_as_child = 0, + /* Running as child process? */ + print_profile = 0; + /* Print the sandbox profile to stdout? */ 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 */ + 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 */ #ifdef __APPLE__ - int run_as_child = 0; - /* Needed for Mac OS X fork/exec */ + int use_sysman = 1; /* Use system management functions? */ #else time_t netif_time = 0; /* Time since last network update */ #endif /* __APPLE__ */ -#if HAVE_LAUNCHD - int launchd_idle_exit; +#if defined(HAVE_ONDEMAND) + int service_idle_exit; /* Idle exit on select timeout? */ -#endif /* HAVE_LAUNCHD */ +#endif /* HAVE_ONDEMAND */ #ifdef HAVE_GETEUID @@ -162,7 +144,7 @@ main(int argc, /* I - Number of command-line args */ if (getuid() != geteuid()) { - fputs("cupsd: Cannot run as a setuid program!\n", stderr); + fputs("cupsd: Cannot run as a setuid program.\n", stderr); return (1); } #endif /* HAVE_GETEUID */ @@ -176,8 +158,10 @@ main(int argc, /* I - Number of command-line args */ #ifdef HAVE_LAUNCHD if (getenv("CUPSD_LAUNCHD")) { - Launchd = 1; - fg = 1; + OnDemand = 1; + fg = 1; + close_all = 0; + disconnect = 0; } #endif /* HAVE_LAUNCHD */ @@ -186,18 +170,17 @@ main(int argc, /* I - Number of command-line args */ 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__ */ + fg = 1; + close_all = 0; case 'c' : /* Configuration file */ i ++; if (i >= argc) { _cupsLangPuts(stderr, _("cupsd: Expected config filename " - "after \"-c\" option!\n")); + "after \"-c\" option.")); usage(1); } @@ -217,7 +200,6 @@ main(int argc, /* I - Number of command-line args */ char *current; /* Current directory */ - /* * Allocate a buffer for the current working directory to * reduce run-time stack usage; this approximates the @@ -228,14 +210,14 @@ main(int argc, /* I - Number of command-line args */ if ((current = malloc(1024)) == NULL) { _cupsLangPuts(stderr, - _("cupsd: Unable to get current directory!\n")); + _("cupsd: Unable to get current directory.")); return (1); } if (!getcwd(current, 1024)) { _cupsLangPuts(stderr, - _("cupsd: Unable to get current directory!\n")); + _("cupsd: Unable to get current directory.")); free(current); return (1); } @@ -246,48 +228,103 @@ main(int argc, /* I - Number of command-line args */ break; case 'f' : /* Run in foreground... */ - fg = 1; + fg = 1; + disconnect = 0; + close_all = 0; break; case 'F' : /* Run in foreground, but disconnect from terminal... */ - fg = -1; + fg = 1; + close_all = 0; break; case 'h' : /* Show usage/help */ usage(0); break; - case 'l' : /* Started by launchd... */ -#ifdef HAVE_LAUNCHD - Launchd = 1; - fg = 1; + case 'l' : /* Started by launchd/systemd... */ +#if defined(HAVE_ONDEMAND) + OnDemand = 1; + fg = 1; + close_all = 0; + disconnect = 0; #else - _cupsLangPuts(stderr, _("cupsd: launchd(8) support not compiled " - "in, running in normal mode.\n")); - fg = 0; -#endif /* HAVE_LAUNCHD */ + _cupsLangPuts(stderr, _("cupsd: On-demand support not compiled " + "in, running in normal mode.")); + fg = 0; + disconnect = 1; + close_all = 1; +#endif /* HAVE_ONDEMAND */ break; case 'p' : /* Stop immediately for profiling */ - puts("Warning: -p option is for internal testing use only!"); + fputs("cupsd: -p (startup profiling) is for internal testing " + "use only!\n", stderr); stop_scheduler = 1; fg = 1; + disconnect = 0; + close_all = 0; + break; + + case 'P' : /* Disable security profiles */ + fputs("cupsd: -P (disable sandboxing) is for internal testing use only.\n", stderr); + UseSandboxing = 0; + break; + + case 's' : /* Set cups-files.conf location */ + i ++; + if (i >= argc) + { + _cupsLangPuts(stderr, _("cupsd: Expected cups-files.conf " + "filename after \"-s\" option.")); + usage(1); + } + + if (argv[i][0] != '/') + { + /* + * Relative filename not allowed... + */ + + _cupsLangPuts(stderr, _("cupsd: Relative cups-files.conf " + "filename not allowed.")); + usage(1); + } + + cupsdSetString(&CupsFilesFile, argv[i]); + 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; + disconnect = 0; + close_all = 0; break; + case 'T' : /* Print security profile */ + print_profile = 1; + fg = 1; + disconnect = 0; + close_all = 0; + break; + default : /* Unknown option */ _cupsLangPrintf(stderr, _("cupsd: Unknown option \"%c\" - " - "aborting!\n"), *opt); + "aborting."), *opt); usage(1); break; } else { - _cupsLangPrintf(stderr, _("cupsd: Unknown argument \"%s\" - aborting!\n"), + _cupsLangPrintf(stderr, _("cupsd: Unknown argument \"%s\" - aborting."), argv[i]); usage(1); } @@ -295,8 +332,88 @@ main(int argc, /* I - Number of command-line args */ if (!ConfigurationFile) cupsdSetString(&ConfigurationFile, CUPS_SERVERROOT "/cupsd.conf"); + if (!CupsFilesFile) + { + char *filename, /* Copy of cupsd.conf filename */ + *slash; /* Final slash in cupsd.conf filename */ + size_t len; /* Size of buffer */ + + len = strlen(ConfigurationFile) + 15; + if ((filename = malloc(len)) == NULL) + { + _cupsLangPrintf(stderr, + _("cupsd: Unable to get path to " + "cups-files.conf file.")); + return (1); + } + + strlcpy(filename, ConfigurationFile, len); + if ((slash = strrchr(filename, '/')) == NULL) + { + _cupsLangPrintf(stderr, + _("cupsd: Unable to get path to " + "cups-files.conf file.")); + return (1); + } + + strlcpy(slash, "/cups-files.conf", len - (size_t)(slash - filename)); + cupsdSetString(&CupsFilesFile, filename); + free(filename); + } + + if (disconnect) + { + /* + * Make sure we aren't tying up any filesystems... + */ + + chdir("/"); + + /* + * Disconnect from the controlling terminal... + */ + + setsid(); + } + + if (close_all) + { + /* + * Close all open files... + */ + + getrlimit(RLIMIT_NOFILE, &limit); + + for (i = 0; i < (int)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); + } + } + else + LogStderr = cupsFileStderr(); + /* - * If the user hasn't specified "-f", run in the background... + * Run in the background as needed... */ if (!fg) @@ -349,70 +466,37 @@ main(int argc, /* I - Number of command-line args */ } else if (WIFEXITED(i)) { - fprintf(stderr, "cupsd: Child exited with status %d!\n", + 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)); + 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(); - +#if defined(__OpenBSD__) && OpenBSD < 201211 /* - * Close all open files... + * 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 older versions of OpenBSD. */ - getrlimit(RLIMIT_NOFILE, &limit); - - for (i = 0; i < limit.rlim_cur && i < 1024; i ++) - close(i); + _thread_sys_closefrom(0); +#endif /* __OpenBSD__ && OpenBSD < 201211 */ /* - * Redirect stdin/out/err to /dev/null... + * Since many system libraries create fork-unsafe data on execution of a + * program, we need to re-execute the background cupsd with the "-C" and "-s" + * options 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... */ - open("/dev/null", O_RDONLY); - open("/dev/null", O_WRONLY); - open("/dev/null", O_WRONLY); -#endif /* DEBUG */ + execlp(argv[0], argv[0], "-C", ConfigurationFile, "-s", CupsFilesFile, (char *)0); + exit(errno); } /* @@ -425,6 +509,14 @@ main(int argc, /* I - Number of command-line args */ setlocale(LC_TIME, ""); #endif /* LC_TIME */ +#ifdef HAVE_DBUS_THREADS_INIT + /* + * Enable threading support for D-BUS... + */ + + dbus_threads_init_default(); +#endif /* HAVE_DBUS_THREADS_INIT */ + /* * Set the maximum number of files... */ @@ -443,7 +535,7 @@ main(int argc, /* I - Number of command-line args */ #endif /* RLIM_INFINITY */ MaxFDs = limit.rlim_max; - limit.rlim_cur = MaxFDs; + limit.rlim_cur = (rlim_t)MaxFDs; setrlimit(RLIMIT_NOFILE, &limit); @@ -454,105 +546,63 @@ main(int argc, /* I - Number of command-line args */ */ 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); + printf("\"%s\" is OK.\n", CupsFilesFile); + printf("\"%s\" is OK.\n", ConfigurationFile); return (0); } - - if (!strncmp(TempDir, RequestRoot, strlen(RequestRoot))) + else if (print_profile) { - /* - * Clean out the temporary directory... - */ + cups_file_t *fp; /* File pointer */ + const char *profile = cupsdCreateProfile(42, 0); + /* Profile */ + char line[1024]; /* Line from file */ - cups_dir_t *dir; /* Temporary directory */ - cups_dentry_t *dent; /* Directory entry */ - char tempfile[1024]; /* Temporary filename */ - - if ((dir = cupsDirOpen(TempDir)) != NULL) + if ((fp = cupsFileOpen(profile, "r")) == 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); - - if (cupsdRemoveFile(tempfile)) - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to remove temporary file \"%s\" - %s", - tempfile, strerror(errno)); - else - cupsdLogMessage(CUPSD_LOG_DEBUG, "Removed temporary file \"%s\"...", - tempfile); - } - - cupsDirClose(dir); + printf("Unable to open profile file \"%s\": %s\n", profile ? profile : "(null)", strerror(errno)); + return (1); } - else - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to open temporary directory \"%s\" - %s", - TempDir, strerror(errno)); - } -#if HAVE_LAUNCHD - if (Launchd) - { - /* - * If we were started by launchd get the listen sockets file descriptors... - */ - - launchd_checkin(); - } -#endif /* HAVE_LAUNCHD */ - -#if defined(__APPLE__) && defined(HAVE_DLFCN_H) - /* - * Load Print Service quota enforcement library (X Server only) - */ + while (cupsFileGets(fp, line, sizeof(line))) + puts(line); - PSQLibRef = dlopen(PSQLibPath, RTLD_LAZY); + cupsFileClose(fp); - if (PSQLibRef) - PSQUpdateQuotaProc = dlsym(PSQLibRef, PSQLibFuncName); -#endif /* __APPLE__ && HAVE_DLFCN_H */ + return (0); + } -#ifdef HAVE_GSSAPI -# ifdef __APPLE__ /* - * If the weak-linked GSSAPI/Kerberos library is not present, don't try - * to use it... + * Clean out old temp files and printer cache data. */ - if (krb5_init_context != NULL) -# endif /* __APPLE__ */ + if (!strncmp(TempDir, RequestRoot, strlen(RequestRoot))) + cupsdCleanFiles(TempDir, NULL); - /* - * Setup a Kerberos context for the scheduler to use... - */ + cupsdCleanFiles(CacheDir, "*.ipp"); - if (krb5_init_context(&KerberosContext)) +#if defined(HAVE_ONDEMAND) + if (OnDemand) { - KerberosContext = NULL; + /* + * If we were started on demand by launchd or systemd get the listen sockets + * file descriptors... + */ - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize Kerberos context"); + service_checkin(); + service_checkout(); } -#endif /* HAVE_GSSAPI */ +#endif /* HAVE_ONDEMAND */ /* * Startup the server... */ + httpInitialize(); + cupsdStartServer(); /* @@ -594,28 +644,6 @@ main(int argc, /* I - Number of command-line args */ 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... */ @@ -627,11 +655,7 @@ main(int argc, /* I - Number of command-line args */ * 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 @@ -650,9 +674,24 @@ main(int argc, /* I - Number of command-line args */ * Start power management framework... */ - cupsdStartSystemMonitor(); + if (use_sysman) + cupsdStartSystemMonitor(); #endif /* __APPLE__ */ + /* + * Send server-started event... + */ + +#if defined(HAVE_ONDEMAND) + if (OnDemand) + cupsdAddEvent(CUPSD_EVENT_SERVER_STARTED, NULL, NULL, "Scheduler started on demand."); + else +#endif /* HAVE_ONDEMAND */ + 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... */ @@ -663,20 +702,15 @@ main(int argc, /* I - Number of command-line args */ * Loop forever... */ - browse_time = time(NULL); - expire_time = time(NULL); + current_time = time(NULL); + event_time = current_time; + expire_time = current_time; fds = 1; report_time = 0; - senddoc_time = time(NULL); + senddoc_time = current_time; while (!stop_scheduler) { -#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... */ @@ -699,37 +733,29 @@ main(int argc, /* I - Number of command-line args */ for (con = (cupsd_client_t *)cupsArrayFirst(Clients); con; con = (cupsd_client_t *)cupsArrayNext(Clients)) - if (con->http.state == HTTP_WAITING) + if (httpGetState(con->http) == HTTP_WAITING) cupsdCloseClient(con); else - con->http.keep_alive = HTTP_KEEPALIVE_OFF; + 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)) || + (cupsArrayCount(PrintingJobs) == 0 || NeedReload != RELOAD_ALL)) || (time(NULL) - ReloadTime) >= ReloadTimeout) { /* * Shutdown the server... */ + DoingShutdown = 1; + cupsdStopServer(); /* @@ -738,27 +764,36 @@ main(int argc, /* I - Number of command-line args */ 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... - */ +#ifdef HAVE_ASL_H + asl_object_t m; /* Log message */ + + m = asl_new(ASL_TYPE_MSG); + asl_set(m, ASL_KEY_FACILITY, "org.cups.cupsd"); + asl_log(NULL, m, ASL_LEVEL_ERR, "Unable to read configuration file \"%s\" - exiting.", ConfigurationFile); + asl_release(m); +#elif defined(HAVE_SYSTEMD_SD_JOURNAL_H) + sd_journal_print(LOG_ERR, "Unable to read configuration file \"%s\" - exiting.", ConfigurationFile); +#else + syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting.", ConfigurationFile); +#endif /* HAVE_ASL_H */ - launchd_checkin(); + break; } -#endif /* HAVE_LAUNCHD */ /* * Startup the server... */ + DoingShutdown = 0; + cupsdStartServer(); + + /* + * Send a server-restarted event... + */ + + cupsdAddEvent(CUPSD_EVENT_SERVER_RESTARTED, NULL, NULL, + "Scheduler restarted."); } } @@ -771,28 +806,29 @@ main(int argc, /* I - Number of command-line args */ * times. */ - timeout = select_timeout(fds); + if ((timeout = select_timeout(fds)) > 1 && LastEvent) + timeout = 1; -#if HAVE_LAUNCHD +#if defined(HAVE_ONDEMAND) /* * 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 && + if (timeout == 86400 && OnDemand && IdleExitTimeout && !cupsArrayCount(ActiveJobs) && - (!Browsing || - (!BrowseRemoteProtocols && - (!NumBrowsers || !BrowseLocalProtocols || - cupsArrayCount(Printers) == 0)))) +# ifdef HAVE_SYSTEMD + !WebInterface && +# endif /* HAVE_SYSTEMD */ + (!Browsing || !BrowseLocalProtocols || !cupsArrayCount(Printers))) { - timeout = LaunchdTimeout; - launchd_idle_exit = 1; + timeout = IdleExitTimeout; + service_idle_exit = 1; } else - launchd_idle_exit = 0; -#endif /* HAVE_LAUNCHD */ + service_idle_exit = 0; +#endif /* HAVE_ONDEMAND */ if ((fds = cupsdDoSelect(timeout)) < 0) { @@ -800,9 +836,9 @@ main(int argc, /* I - Number of command-line args */ * Got an error from select! */ -#ifdef HAVE_DNSSD +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) cupsd_printer_t *p; /* Current printer */ -#endif /* HAVE_DNSSD */ +#endif /* HAVE_DNSSD || HAVE_AVAHI */ if (errno == EINTR) /* Just interrupted by a signal */ @@ -820,15 +856,13 @@ main(int argc, /* I - Number of command-line args */ 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); + i, con->number, con->file, httpGetState(con->http)); 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__ @@ -845,19 +879,40 @@ main(int argc, /* I - Number of command-line args */ job->print_pipes[0], job->print_pipes[1], job->back_pipes[0], job->back_pipes[1]); -#ifdef HAVE_DNSSD +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) 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 */ + cupsdLogMessage(CUPSD_LOG_EMERG, "printer[%s] reg_name=\"%s\"", p->name, + p->reg_name ? p->reg_name : "(null)"); +#endif /* HAVE_DNSSD || HAVE_AVAHI */ 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... @@ -870,13 +925,13 @@ main(int argc, /* I - Number of command-line args */ } #endif /* !__APPLE__ */ -#if HAVE_LAUNCHD +#if defined(HAVE_ONDEMAND) /* * 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) + if (!fds && service_idle_exit) { cupsdLogMessage(CUPSD_LOG_INFO, "Printer sharing is off and there are no jobs pending, " @@ -884,7 +939,7 @@ main(int argc, /* I - Number of command-line args */ stop_scheduler = 1; break; } -#endif /* HAVE_LAUNCHD */ +#endif /* HAVE_ONDEMAND */ /* * Resume listening for new connections as needed... @@ -908,31 +963,7 @@ main(int argc, /* I - Number of command-line args */ 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 && BrowseLocalProtocols && current_time > browse_time) - { - cupsdSendBrowseList(); - browse_time = current_time; - } - +#ifndef HAVE_AUTHORIZATION_H /* * Update the root certificate once every 5 minutes if we have client * connections... @@ -946,8 +977,9 @@ main(int argc, /* I - Number of command-line args */ */ cupsdDeleteCert(0); - cupsdAddCert(0, "root", NULL); + cupsdAddCert(0, "root", cupsdDefaultAuthType()); } +#endif /* !HAVE_AUTHORIZATION_H */ /* * Check for new data on the client sockets... @@ -961,7 +993,7 @@ main(int argc, /* I - Number of command-line args */ * Process pending data in the input buffer... */ - if (con->http.used) + if (httpGetReady(con->http)) { cupsdReadClient(con); continue; @@ -972,11 +1004,9 @@ main(int argc, /* I - Number of command-line args */ */ activity = current_time - Timeout; - if (con->http.activity < activity && !con->pipe_pid) + if (httpGetActivity(con->http) < activity && !con->pipe_pid) { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Closing client %d after %d seconds of inactivity...", - con->http.fd, Timeout); + cupsdLogMessage(CUPSD_LOG_DEBUG, "Closing client %d after %d seconds of inactivity.", con->number, Timeout); cupsdCloseClient(con); continue; @@ -993,6 +1023,13 @@ main(int argc, /* I - Number of command-line args */ senddoc_time = current_time; } + /* + * Clean job history... + */ + + if (JobHistoryUpdate && current_time >= JobHistoryUpdate) + cupsdCleanJobs(); + /* * Log statistics at most once a minute when in debug mode... */ @@ -1022,8 +1059,6 @@ main(int argc, /* I - Number of command-line args */ 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, @@ -1044,7 +1079,7 @@ main(int argc, /* I - Number of command-line args */ * accumulated. Don't send these more than once a second... */ - if (LastEvent) + if (LastEvent && (current_time - event_time) >= 1) { #ifdef HAVE_NOTIFY_POST if (LastEvent & (CUPSD_EVENT_PRINTER_ADDED | @@ -1077,7 +1112,8 @@ main(int argc, /* I - Number of command-line args */ * Reset the accumulated events... */ - LastEvent = CUPSD_EVENT_NONE; + LastEvent = CUPSD_EVENT_NONE; + event_time = current_time; } } @@ -1086,25 +1122,35 @@ main(int argc, /* I - Number of command-line args */ */ 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 +#if defined(HAVE_ONDEMAND) /* - * Update the launchd KeepAlive file as needed... + * Update the keep-alive file as needed... */ - if (Launchd) - launchd_checkout(); -#endif /* HAVE_LAUNCHD */ + if (OnDemand) + service_checkout(); +#endif /* HAVE_ONDEMAND */ /* * Stop all jobs... @@ -1117,148 +1163,75 @@ main(int argc, /* I - Number of command-line args */ * Stop monitoring system event monitoring... */ - cupsdStopSystemMonitor(); + 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 __APPLE__ -#ifdef HAVE_DLFCN_H - /* - * Unload Print Service quota enforcement library (X Server only) - */ + cupsdStopSelect(); - PSQUpdateQuotaProc = NULL; - if (PSQLibRef) - { - dlclose(PSQLibRef); - PSQLibRef = NULL; - } -#endif /* HAVE_DLFCN_H */ -#endif /* __APPLE__ */ + return (!stop_scheduler); +} -#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 */ +/* + * 'cupsdAddString()' - Copy and add a string to an array. + */ - cupsdStopSelect(); +int /* O - 1 on success, 0 on failure */ +cupsdAddString(cups_array_t **a, /* IO - String array */ + const char *s) /* I - String to copy and add */ +{ + if (!*a) + *a = cupsArrayNew3((cups_array_func_t)strcmp, NULL, + (cups_ahash_func_t)NULL, 0, + (cups_acopy_func_t)strdup, + (cups_afree_func_t)free); - return (!stop_scheduler); + return (cupsArrayAdd(*a, (char *)s)); } /* - * 'cupsdClosePipe()' - Close a pipe as necessary. + * 'cupsdCheckProcess()' - Tell the main loop to check for dead children. */ void -cupsdClosePipe(int *fds) /* I - Pipe file descriptors (2) */ +cupsdCheckProcess(void) { /* - * Close file descriptors as needed... + * Flag that we have dead children... */ - if (fds[0] >= 0) - { - close(fds[0]); - fds[0] = -1; - } - - if (fds[1] >= 0) - { - close(fds[1]); - fds[1] = -1; - } + dead_children = 1; } /* - * 'cupsdOpenPipe()' - Create a pipe which is closed on exec. + * 'cupsdClearString()' - Clear a string. */ -int /* O - 0 on success, -1 on error */ -cupsdOpenPipe(int *fds) /* O - Pipe file descriptors (2) */ +void +cupsdClearString(char **s) /* O - String value */ { - /* - * Create the pipe... - */ - - if (pipe(fds)) - { - fds[0] = -1; - fds[1] = -1; - - return (-1); - } - - /* - * Set the "close on exec" flag on each end of the pipe... - */ - - if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC)) - { - close(fds[0]); - close(fds[1]); - - fds[0] = -1; - fds[1] = -1; - - return (-1); - } - - if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC)) + if (s && *s) { - close(fds[0]); - close(fds[1]); - - fds[0] = -1; - fds[1] = -1; - - return (-1); + free(*s); + *s = NULL; } - - /* - * Return 0 indicating success... - */ - - return (0); } /* - * 'cupsdClearString()' - Clear a string. + * 'cupsdFreeStrings()' - Free an array of strings. */ void -cupsdClearString(char **s) /* O - String value */ +cupsdFreeStrings(cups_array_t **a) /* IO - String array */ { - if (s && *s) + if (*a) { - _cupsStrFree(*s); - *s = NULL; + cupsArrayDelete(*a); + *a = NULL; } } @@ -1323,10 +1296,10 @@ cupsdSetString(char **s, /* O - New string */ return; if (*s) - _cupsStrFree(*s); + free(*s); if (v) - *s = _cupsStrAlloc(v); + *s = strdup(v); else *s = NULL; } @@ -1341,7 +1314,7 @@ cupsdSetStringf(char **s, /* O - New string */ const char *f, /* I - Printf-style format string */ ...) /* I - Additional args as needed */ { - char v[4096]; /* Formatting string value */ + char v[65536 + 64]; /* Formatting string value */ va_list ap; /* Argument pointer */ char *olds; /* Old string */ @@ -1357,209 +1330,14 @@ cupsdSetStringf(char **s, /* O - New string */ vsnprintf(v, sizeof(v), f, ap); va_end(ap); - *s = _cupsStrAlloc(v); + *s = strdup(v); } else *s = NULL; if (olds) - _cupsStrFree(olds); -} - - -#ifdef HAVE_LAUNCHD -/* - * 'launchd_checkin()' - Check-in with launchd and collect the listening fds. - */ - -static void -launchd_checkin(void) -{ - int i, /* Looping var */ - count, /* Numebr of listeners */ - portnum; /* Port number */ - launch_data_t ld_msg, /* Launch data message */ - ld_resp, /* Launch data response */ - ld_array, /* Launch data array */ - ld_sockets, /* Launch data sockets dictionary */ - tmp; /* Launch data */ - cupsd_listener_t *lis; /* Listeners array */ - http_addr_t addr; /* Address variable */ - socklen_t addrlen; /* Length of address */ - int fd; /* File descriptor */ - char s[256]; /* String addresss */ - - - cupsdLogMessage(CUPSD_LOG_DEBUG, "launchd_checkin: pid=%d", (int)getpid()); - - /* - * Check-in with launchd... - */ - - ld_msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); - if ((ld_resp = launch_msg(ld_msg)) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "launchd_checkin: launch_msg(\"" LAUNCH_KEY_CHECKIN - "\") IPC failure"); - exit(EXIT_FAILURE); - } - - if (launch_data_get_type(ld_resp) == LAUNCH_DATA_ERRNO) - { - errno = launch_data_get_errno(ld_resp); - cupsdLogMessage(CUPSD_LOG_ERROR, "launchd_checkin: Check-in failed: %s", - strerror(errno)); - exit(EXIT_FAILURE); - } - - /* - * Get the sockets dictionary... - */ - - if (!(ld_sockets = launch_data_dict_lookup(ld_resp, LAUNCH_JOBKEY_SOCKETS))) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "launchd_checkin: No sockets found to answer requests on!"); - exit(EXIT_FAILURE); - } - - /* - * Get the array of listener sockets... - */ - - if (!(ld_array = launch_data_dict_lookup(ld_sockets, "Listeners"))) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "launchd_checkin: No sockets found to answer requests on!"); - exit(EXIT_FAILURE); - } - - /* - * Add listening fd(s) to the Listener array... - */ - - if (launch_data_get_type(ld_array) == LAUNCH_DATA_ARRAY) - { - count = launch_data_array_get_count(ld_array); - - for (i = 0; i < count; i ++) - { - /* - * Get the launchd file descriptor and address... - */ - - tmp = launch_data_array_get_index(ld_array, i); - fd = launch_data_get_fd(tmp); - addrlen = sizeof(addr); - - if (getsockname(fd, (struct sockaddr *)&addr, &addrlen)) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "launchd_checkin: Unable to get local address - %s", - strerror(errno)); - continue; - } - - /* - * Try to match the launchd socket address to one of the listeners... - */ - - for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); - lis; - lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) - if (httpAddrEqual(&lis->address, &addr)) - break; - - /* - * Add a new listener If there's no match... - */ - - if (lis) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "launchd_checkin: Matched existing listener %s with fd %d...", - httpAddrString(&(lis->address), s, sizeof(s)), fd); - } - else - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "launchd_checkin: Adding new listener %s with fd %d...", - httpAddrString(&addr, s, sizeof(s)), fd); - - if ((lis = calloc(1, sizeof(cupsd_listener_t))) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "launchd_checkin: Unable to allocate listener - %s.", - strerror(errno)); - exit(EXIT_FAILURE); - } - - cupsArrayAdd(Listeners, lis); - - memcpy(&lis->address, &addr, sizeof(lis->address)); - } - - lis->fd = fd; - -# ifdef HAVE_SSL - portnum = 0; - -# ifdef AF_INET6 - if (lis->address.addr.sa_family == AF_INET6) - portnum = ntohs(lis->address.ipv6.sin6_port); - else -# endif /* AF_INET6 */ - if (lis->address.addr.sa_family == AF_INET) - portnum = ntohs(lis->address.ipv4.sin_port); - - if (portnum == 443) - lis->encryption = HTTP_ENCRYPT_ALWAYS; -# endif /* HAVE_SSL */ - } - } - - launch_data_free(ld_msg); - launch_data_free(ld_resp); -} - - -/* - * 'launchd_checkout()' - Update the launchd KeepAlive file as needed. - */ - -static void -launchd_checkout(void) -{ - int fd; /* File descriptor */ - - - /* - * Create or remove the launchd KeepAlive file based on whether - * there are active jobs, polling, browsing for remote printers or - * shared printers to advertise... - */ - - if ((cupsArrayCount(ActiveJobs) || NumPolled || - (Browsing && - (BrowseRemoteProtocols || - (BrowseLocalProtocols && NumBrowsers && cupsArrayCount(Printers)))))) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Creating launchd keepalive file \"" CUPS_KEEPALIVE "\"..."); - - if ((fd = open(CUPS_KEEPALIVE, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR)) >= 0) - close(fd); - } - else - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Removing launchd keepalive file \"" CUPS_KEEPALIVE "\"..."); - - unlink(CUPS_KEEPALIVE); - } + free(olds); } -#endif /* HAVE_LAUNCHD */ /* @@ -1585,10 +1363,12 @@ static void process_children(void) { int status; /* Exit status of child */ - int pid; /* Process ID 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()"); @@ -1612,31 +1392,10 @@ process_children(void) #endif /* HAVE_WAITPID */ { /* - * Ignore SIGTERM errors - that comes when a job is canceled... + * Collect the name of the process that finished... */ - cupsdFinishProcess(pid, name, sizeof(name)); - - if (status == SIGTERM) - status = 0; - - if (status) - { - if (WIFEXITED(status)) - cupsdLogMessage(CUPSD_LOG_ERROR, "PID %d (%s) stopped with status %d!", - pid, name, WEXITSTATUS(status)); - else - cupsdLogMessage(CUPSD_LOG_ERROR, "PID %d (%s) crashed on signal %d!", - pid, name, WTERMSIG(status)); - - if (LogLevel < CUPSD_LOG_DEBUG) - cupsdLogMessage(CUPSD_LOG_INFO, - "Hint: Try setting the LogLevel to \"debug\" to find " - "out more."); - } - else - cupsdLogMessage(CUPSD_LOG_DEBUG, "PID %d (%s) exited with no errors.", - pid, name); + cupsdFinishProcess(pid, name, sizeof(name), &job_id); /* * Delete certificates for CGI processes... @@ -1646,143 +1405,193 @@ process_children(void) cupsdDeleteCert(pid); /* - * Lookup the PID in the jobs list... + * Handle completed job filters... */ - for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); - job; - job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) - if (job->state_value == IPP_JOB_PROCESSING) + 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) { - for (i = 0; job->filters[i]; i ++) - if (job->filters[i] == pid) - break; + /* + * 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 (job->filters[i] || job->backend == pid) + if (status && status != SIGTERM && status != SIGKILL && + status != SIGPIPE) { /* - * OK, this process has gone away; what's left? + * 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. */ - if (job->filters[i]) - job->filters[i] = -pid; - else - job->backend = -pid; + int old_status = abs(job->status); - if (status && job->status >= 0) + 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))) { - /* - * 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. - */ + char message[1024]; /* New printer-state-message */ - if (job->filters[i]) - job->status = status; /* Filter failed */ - else - job->status = -status; /* Backend failed */ - if (job->printer && !(job->printer->type & CUPS_PRINTER_FAX) && - job->status_level > CUPSD_LOG_ERROR) + job->status_level = CUPSD_LOG_ERROR; + + snprintf(message, sizeof(message), "%s failed", type); + + if (job->printer) { - job->status_level = CUPSD_LOG_ERROR; + strlcpy(job->printer->state_message, message, + sizeof(job->printer->state_message)); + } - snprintf(job->printer->state_message, - sizeof(job->printer->state_message), "%s failed", name); - cupsdAddPrinterHistory(job->printer); + 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) + ippSetString(job->attrs, &job->printer_message, 0, 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->state_value >= IPP_JOB_CANCELED) + { /* - * 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. + * Remove the job from the active list if there are no processes still + * running for it... */ - if (job->current_file < job->num_files) - { - for (i = 0; job->filters[i] < 0; i ++); + for (i = 0; job->filters[i] < 0; i++); - if (!job->filters[i]) - { - /* - * Process the next file... - */ + if (!job->filters[i] && job->backend <= 0) + cupsArrayRemove(ActiveJobs, job); + } + else if (job->current_file < job->num_files && job->printer) + { + for (i = 0; job->filters[i] < 0; i ++); - cupsdFinishJob(job); - } + if (!job->filters[i] && + (!job->printer->pc || !job->printer->pc->single_file || + job->backend <= 0)) + { + /* + * Process the next file... + */ + + cupsdContinueJob(job); } - break; } } - } -} - - -/* - * 'sigchld_handler()' - Handle 'child' signals from old processes. - */ + } -static void -sigchld_handler(int sig) /* I - Signal number */ -{ - (void)sig; + /* + * Show the exit status as needed, ignoring SIGTERM and SIGKILL errors + * since they come when we kill/end a process... + */ - /* - * Flag that we have dead children... - */ + 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)); - dead_children = 1; + 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); + } /* - * Reset the signal handler as needed... + * If wait*() is interrupted by a signal, tell main() to call us again... */ -#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION) - signal(SIGCLD, sigchld_handler); -#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */ + if (pid < 0 && errno == EINTR) + dead_children = 1; } /* - * 'sighup_handler()' - Handle 'hangup' signals to reconfigure the scheduler. - */ - -static void -sighup_handler(int sig) /* I - Signal number */ -{ - (void)sig; - - NeedReload = RELOAD_ALL; - ReloadTime = time(NULL); - -#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION) - signal(SIGHUP, sighup_handler); -#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */ -} - - -/* - * 'sigterm_handler()' - Handle 'terminate' signals that stop the scheduler. - */ - -static void -sigterm_handler(int sig) /* I - Signal */ -{ - (void)sig; /* remove compiler warnings... */ - - /* - * Flag that we should stop and return... - */ - - stop_scheduler = 1; -} - - -/* - * 'select_timeout()' - Calculate the select timeout value. - * + * 'select_timeout()' - Calculate the select timeout value. + * */ static long /* O - Number of seconds */ @@ -1791,12 +1600,13 @@ select_timeout(int fds) /* I - Number of descriptors returned */ long timeout; /* Timeout for select */ time_t now; /* Current time */ cupsd_client_t *con; /* Client information */ - cupsd_printer_t *p; /* Printer information */ cupsd_job_t *job; /* Job information */ - cupsd_subscription_t *sub; /* Subscription information */ const char *why; /* Debugging aid */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "select_timeout: JobHistoryUpdate=%ld", + (long)JobHistoryUpdate); + /* * Check to see if any of the clients have pending data to be * processed; if so, the timeout should be 0... @@ -1805,7 +1615,7 @@ select_timeout(int fds) /* I - Number of descriptors returned */ for (con = (cupsd_client_t *)cupsArrayFirst(Clients); con; con = (cupsd_client_t *)cupsArrayNext(Clients)) - if (con->http.used > 0) + if (httpGetReady(con->http)) return (0); /* @@ -1825,6 +1635,18 @@ select_timeout(int fds) /* I - Number of descriptors returned */ timeout = now + 86400; /* 86400 == 1 day */ why = "do nothing"; +#ifdef __APPLE__ + /* + * When going to sleep, wake up to cancel jobs that don't complete in time. + */ + + if (SleepJobs > 0 && SleepJobs < timeout) + { + timeout = SleepJobs; + why = "cancel jobs before sleeping"; + } +#endif /* __APPLE__ */ + /* * Check whether we are accepting new connections... */ @@ -1847,75 +1669,60 @@ select_timeout(int fds) /* I - Number of descriptors returned */ for (con = (cupsd_client_t *)cupsArrayFirst(Clients); con; con = (cupsd_client_t *)cupsArrayNext(Clients)) - if ((con->http.activity + Timeout) < timeout) + if ((httpGetActivity(con->http) + Timeout) < timeout) { - timeout = con->http.activity + Timeout; + timeout = httpGetActivity(con->http) + Timeout; why = "timeout a client connection"; } /* - * Update the browse list as needed... + * Write out changes to configuration and state files... + */ + + if (DirtyCleanTime && timeout > DirtyCleanTime) + { + timeout = DirtyCleanTime; + why = "write dirty config/state files"; + } + + /* + * Check for any job activity... */ - if (Browsing && BrowseLocalProtocols) + if (JobHistoryUpdate && timeout > JobHistoryUpdate) + { + timeout = JobHistoryUpdate; + why = "update job history"; + } + + for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); + job; + job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) { -#ifdef HAVE_LIBSLP - if ((BrowseLocalProtocols & BROWSE_SLP) && (BrowseSLPRefresh < timeout)) + if (job->cancel_time && job->cancel_time < timeout) { - timeout = BrowseSLPRefresh; - why = "update SLP browsing"; + timeout = job->cancel_time; + why = "cancel stuck jobs"; } -#endif /* HAVE_LIBSLP */ -#ifdef HAVE_LDAP - if ((BrowseLocalProtocols & BROWSE_LDAP) && (BrowseLDAPRefresh < timeout)) + if (job->kill_time && job->kill_time < timeout) { - timeout = BrowseLDAPRefresh; - why = "update LDAP browsing"; + timeout = job->kill_time; + why = "kill unresponsive jobs"; } -#endif /* HAVE_LDAP */ - if ((BrowseLocalProtocols & BROWSE_CUPS) && NumBrowsers) + if (job->state_value == IPP_JOB_HELD && job->hold_until < timeout) { - for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); - p; - p = (cupsd_printer_t *)cupsArrayNext(Printers)) - { - if (p->type & CUPS_PRINTER_REMOTE) - { - if ((p->browse_time + BrowseTimeout) < timeout) - { - timeout = p->browse_time + BrowseTimeout; - why = "browse timeout a printer"; - } - } - else if (p->shared && !(p->type & CUPS_PRINTER_IMPLICIT)) - { - if (BrowseInterval && (p->browse_time + BrowseInterval) < timeout) - { - timeout = p->browse_time + BrowseInterval; - why = "send browse update"; - } - } - } + timeout = job->hold_until; + why = "release held jobs"; } - } - - /* - * Check for any active jobs... - */ - if (timeout > (now + 10) && ActiveJobs) - { - for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); - job; - job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) - if (job->state_value <= IPP_JOB_PROCESSING) - { - timeout = now + 10; - why = "process active jobs"; - break; - } + if (job->state_value == IPP_JOB_PENDING && timeout > (now + 10)) + { + timeout = now + 10; + why = "start pending jobs"; + break; + } } #ifdef HAVE_MALLINFO @@ -1931,24 +1738,9 @@ select_timeout(int fds) /* I - Number of descriptors returned */ #endif /* HAVE_MALLINFO */ /* - * Expire subscriptions as needed... - */ - - for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions); - sub; - sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions)) - if (!sub->job && sub->expire && sub->expire < timeout) - { - timeout = sub->expire; - why = "expire subscription"; - } - - /* - * Adjust from absolute to relative time. If p->browse_time above - * was 0 then we can end up with a negative value here, so check. - * We add 1 second to the timeout since events occur after the - * timeout expires, and limit the timeout to 86400 seconds (1 day) - * to avoid select() timeout limits present on some operating + * Adjust from absolute to relative time. We add 1 second to the timeout since + * events occur after the timeout expires, and limit the timeout to 86400 + * seconds (1 day) to avoid select() timeout limits present on some operating * systems... */ @@ -1971,24 +1763,355 @@ select_timeout(int fds) /* I - Number of descriptors returned */ /* - * 'usage()' - Show scheduler usage. + * 'sigchld_handler()' - Handle 'child' signals from old processes. */ static void -usage(int status) /* O - Exit status */ +sigchld_handler(int sig) /* I - Signal number */ { - _cupsLangPuts(status ? stderr : stdout, - _("Usage: cupsd [-c config-file] [-f] [-F] [-h] [-l]\n" - "\n" - "-c config-file Load alternate configuration file\n" - "-f Run in the foreground\n" - "-F Run in the foreground but detach\n" - "-h Show this usage message\n" - "-l Run cupsd from launchd(8)\n")); - exit(status); + (void)sig; + + /* + * Flag that we have dead children... + */ + + dead_children = 1; + + /* + * Reset the signal handler as needed... + */ + +#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION) + signal(SIGCLD, sigchld_handler); +#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */ +} + + +/* + * 'sighup_handler()' - Handle 'hangup' signals to reconfigure the scheduler. + */ + +static void +sighup_handler(int sig) /* I - Signal number */ +{ + (void)sig; + + NeedReload = RELOAD_ALL; + ReloadTime = time(NULL); + +#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION) + signal(SIGHUP, sighup_handler); +#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */ +} + + +/* + * 'sigterm_handler()' - Handle 'terminate' signals that stop the scheduler. + */ + +static void +sigterm_handler(int sig) /* I - Signal number */ +{ + (void)sig; /* remove compiler warnings... */ + + /* + * Flag that we should stop and return... + */ + + stop_scheduler = 1; +} + + +#if defined(HAVE_ONDEMAND) + +/* + * 'add_ondemand_listener()' - Bind an open fd as a Listener. + */ + +static void +add_ondemand_listener(int fd, /* I - Socket file descriptor */ + int idx) /* I - Listener number, for logging */ +{ + cupsd_listener_t *lis; /* Listeners array */ + http_addr_t addr; /* Address variable */ + socklen_t addrlen; /* Length of address */ + char s[256]; /* String addresss */ + + addrlen = sizeof(addr); + + if (getsockname(fd, (struct sockaddr *)&addr, &addrlen)) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "service_checkin: Unable to get local address for listener #%d: %s", + idx + 1, strerror(errno)); + return; + } + + cupsdLogMessage(CUPSD_LOG_DEBUG, + "service_checkin: Listener #%d at fd %d, \"%s\".", + idx + 1, fd, httpAddrString(&addr, s, sizeof(s))); + + /* + * Try to match the on-demand socket address to one of the listeners... + */ + + for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); + lis; + lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) + if (httpAddrEqual(&lis->address, &addr)) + break; + + /* + * Add a new listener If there's no match... + */ + + if (lis) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, + "service_checkin: Matched existing listener #%d to %s.", + idx + 1, httpAddrString(&(lis->address), s, sizeof(s))); + } + else + { + cupsdLogMessage(CUPSD_LOG_DEBUG, + "service_checkin: Adding new listener #%d for %s.", + idx + 1, httpAddrString(&addr, s, sizeof(s))); + + if ((lis = calloc(1, sizeof(cupsd_listener_t))) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "service_checkin: Unable to allocate listener: %s.", strerror(errno)); + exit(EXIT_FAILURE); + return; + } + + cupsArrayAdd(Listeners, lis); + + memcpy(&lis->address, &addr, sizeof(lis->address)); + } + + lis->fd = fd; + lis->on_demand = 1; + +# ifdef HAVE_SSL + if (httpAddrPort(&(lis->address)) == 443) + lis->encryption = HTTP_ENCRYPT_ALWAYS; +# endif /* HAVE_SSL */ +} + +/* + * 'service_checkin()' - Check-in with launchd and collect the listening fds. + */ + +static void +service_checkin(void) +{ +# ifdef HAVE_LAUNCH_ACTIVATE_SOCKET + int error; /* Check-in error, if any */ + size_t i, /* Looping var */ + count; /* Number of listeners */ + int *ld_sockets; /* Listener sockets */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG, "service_checkin: pid=%d", (int)getpid()); + + /* + * Check-in with launchd... + */ + + if ((error = launch_activate_socket("Listeners", &ld_sockets, &count)) != 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "service_checkin: Unable to get listener sockets: %s", strerror(error)); + exit(EXIT_FAILURE); + return; /* anti-compiler-warning */ + } + + /* + * Try to match the launchd sockets to the cupsd listeners... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG, "service_checkin: %d listeners.", (int)count); + + for (i = 0; i < count; i ++) + { + add_ondemand_listener(ld_sockets[i], i); + } + + free(ld_sockets); + +# elif defined(HAVE_LAUNCHD) + size_t i, /* Looping var */ + count; /* Number of listeners */ + launch_data_t ld_msg, /* Launch data message */ + ld_resp, /* Launch data response */ + ld_array, /* Launch data array */ + ld_sockets, /* Launch data sockets dictionary */ + tmp; /* Launch data */ + int fd; /* File descriptor */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG, "service_checkin: pid=%d", (int)getpid()); + + /* + * Check-in with launchd... + */ + + ld_msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); + if ((ld_resp = launch_msg(ld_msg)) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "service_checkin: launch_msg(\"" LAUNCH_KEY_CHECKIN + "\") IPC failure"); + exit(EXIT_FAILURE); + return; /* anti-compiler-warning */ + } + + if (launch_data_get_type(ld_resp) == LAUNCH_DATA_ERRNO) + { + errno = launch_data_get_errno(ld_resp); + cupsdLogMessage(CUPSD_LOG_ERROR, "service_checkin: Check-in failed: %s", + strerror(errno)); + exit(EXIT_FAILURE); + return; /* anti-compiler-warning */ + } + + /* + * Get the sockets dictionary... + */ + + if ((ld_sockets = launch_data_dict_lookup(ld_resp, LAUNCH_JOBKEY_SOCKETS)) + == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "service_checkin: No sockets found to answer requests on."); + exit(EXIT_FAILURE); + return; /* anti-compiler-warning */ + } + + /* + * Get the array of listener sockets... + */ + + if ((ld_array = launch_data_dict_lookup(ld_sockets, "Listeners")) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "service_checkin: No sockets found to answer requests on."); + exit(EXIT_FAILURE); + return; /* anti-compiler-warning */ + } + + /* + * Add listening fd(s) to the Listener array... + */ + + if (launch_data_get_type(ld_array) == LAUNCH_DATA_ARRAY) + { + count = launch_data_array_get_count(ld_array); + + cupsdLogMessage(CUPSD_LOG_DEBUG, "service_checkin: %d listeners.", (int)count); + + for (i = 0; i < count; i ++) + { + /* + * Get the launchd file descriptor and address... + */ + + if ((tmp = launch_data_array_get_index(ld_array, i)) != NULL) + { + fd = launch_data_get_fd(tmp); + add_ondemand_listener(fd, i); + } + } + } + + launch_data_free(ld_msg); + launch_data_free(ld_resp); + +# else /* HAVE_SYSTEMD */ + int i, /* Looping var */ + count; /* Number of listeners */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG, "service_checkin: pid=%d", (int)getpid()); + + /* + * Check-in with systemd... + */ + + if ((count = sd_listen_fds(0)) < 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "service_checkin: Unable to get listener sockets: %s", strerror(-count)); + exit(EXIT_FAILURE); + return; /* anti-compiler-warning */ + } + + /* + * Try to match the systemd sockets to the cupsd listeners... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG, "service_checkin: %d listeners.", count); + + for (i = 0; i < count; i ++) + { + add_ondemand_listener(SD_LISTEN_FDS_START + i, i); + } +# endif /* HAVE_LAUNCH_ACTIVATE_SOCKET */ } /* - * End of "$Id: main.c 6914 2007-09-05 21:05:04Z mike $". + * 'service_checkout()' - Update the CUPS_KEEPALIVE file as needed. */ + +static void +service_checkout(void) +{ + int fd; /* File descriptor */ + + + /* + * Create or remove the "keep-alive" file based on whether there are active + * jobs or shared printers to advertise... + */ + + if (cupsArrayCount(ActiveJobs) || /* Active jobs */ + WebInterface || /* Web interface enabled */ + (Browsing && BrowseLocalProtocols && cupsArrayCount(Printers))) + /* Printers being shared */ + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "Creating keep-alive file \"" CUPS_KEEPALIVE "\"."); + + if ((fd = open(CUPS_KEEPALIVE, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR)) >= 0) + close(fd); + } + else + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "Removing keep-alive file \"" CUPS_KEEPALIVE "\"."); + + unlink(CUPS_KEEPALIVE); + } +} +#endif /* HAVE_ONDEMAND */ + + +/* + * 'usage()' - Show scheduler usage. + */ + +static void +usage(int status) /* O - Exit status */ +{ + FILE *fp = status ? stderr : stdout; /* Output file */ + + + _cupsLangPuts(fp, _("Usage: cupsd [options]")); + _cupsLangPuts(fp, _("Options:")); + _cupsLangPuts(fp, _(" -c cupsd.conf Set cupsd.conf file to use.")); + _cupsLangPuts(fp, _(" -f Run in the foreground.")); + _cupsLangPuts(fp, _(" -F Run in the foreground but detach from console.")); + _cupsLangPuts(fp, _(" -h Show this usage message.")); + _cupsLangPuts(fp, _(" -l Run cupsd on demand.")); + _cupsLangPuts(fp, _(" -s cups-files.conf Set cups-files.conf file to use.")); + _cupsLangPuts(fp, _(" -t Test the configuration file.")); + + exit(status); +}