X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=scheduler%2Fmain.c;h=81e04ce4990b84241b53e5d3620192b65fbbe305;hb=26c14fa6e1b9639d47aca3032337d9fe728ee2dc;hp=bdc46bc59165d2e07b4ec6461de83e9b458340da;hpb=0af14961a854522d785f6716459b8fb951632963;p=thirdparty%2Fcups.git diff --git a/scheduler/main.c b/scheduler/main.c index bdc46bc59..81e04ce49 100644 --- a/scheduler/main.c +++ b/scheduler/main.c @@ -1,38 +1,14 @@ /* - * "$Id: main.c 7925 2008-09-10 17:47:26Z 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,37 +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 */ - event_time; /* Last time an event notification was done */ + 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 @@ -163,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 */ @@ -177,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 */ @@ -187,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); } @@ -218,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 @@ -229,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); } @@ -247,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); } @@ -296,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) @@ -350,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); } /* @@ -426,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... */ @@ -444,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); @@ -455,83 +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 (unlink(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... - */ + while (cupsFileGets(fp, line, sizeof(line))) + puts(line); + + cupsFileClose(fp); - launchd_checkin(); + return (0); } -#endif /* HAVE_LAUNCHD */ -#if defined(__APPLE__) && defined(HAVE_DLFCN_H) /* - * Load Print Service quota enforcement library (X Server only) + * Clean out old temp files and printer cache data. */ - PSQLibRef = dlopen(PSQLibPath, RTLD_LAZY); + if (!strncmp(TempDir, RequestRoot, strlen(RequestRoot))) + cupsdCleanFiles(TempDir, NULL); + + cupsdCleanFiles(CacheDir, "*.ipp"); + +#if defined(HAVE_ONDEMAND) + if (OnDemand) + { + /* + * If we were started on demand by launchd or systemd get the listen sockets + * file descriptors... + */ - if (PSQLibRef) - PSQUpdateQuotaProc = dlsym(PSQLibRef, PSQLibFuncName); -#endif /* __APPLE__ && HAVE_DLFCN_H */ + service_checkin(); + service_checkout(); + } +#endif /* HAVE_ONDEMAND */ /* * Startup the server... */ + httpInitialize(); + cupsdStartServer(); /* @@ -573,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... */ @@ -606,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 @@ -629,25 +674,23 @@ main(int argc, /* I - Number of command-line args */ * Start power management framework... */ - cupsdStartSystemMonitor(); + 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."); +#if defined(HAVE_ONDEMAND) + if (OnDemand) + cupsdAddEvent(CUPSD_EVENT_SERVER_STARTED, NULL, NULL, "Scheduler started on demand."); else -#endif /* HAVE_LAUNCHD */ +#endif /* HAVE_ONDEMAND */ if (fg) - cupsdAddEvent(CUPSD_EVENT_SERVER_STARTED, NULL, NULL, - "Scheduler started in foreground."); + cupsdAddEvent(CUPSD_EVENT_SERVER_STARTED, NULL, NULL, "Scheduler started in foreground."); else - cupsdAddEvent(CUPSD_EVENT_SERVER_STARTED, NULL, NULL, - "Scheduler started in background."); + cupsdAddEvent(CUPSD_EVENT_SERVER_STARTED, NULL, NULL, "Scheduler started in background."); /* * Start any pending print jobs... @@ -660,7 +703,6 @@ main(int argc, /* I - Number of command-line args */ */ current_time = time(NULL); - browse_time = current_time; event_time = current_time; expire_time = current_time; fds = 1; @@ -669,12 +711,6 @@ main(int argc, /* I - Number of command-line args */ 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... */ @@ -697,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(); /* @@ -736,26 +764,28 @@ 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(); /* @@ -779,26 +809,26 @@ main(int argc, /* I - Number of command-line args */ 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) { @@ -806,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 */ @@ -826,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__ @@ -851,13 +879,13 @@ 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] reg_name=\"%s\"", p->name, p->reg_name ? p->reg_name : "(null)"); -#endif /* HAVE_DNSSD */ +#endif /* HAVE_DNSSD || HAVE_AVAHI */ break; } @@ -871,6 +899,20 @@ main(int argc, /* I - Number of command-line args */ 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... @@ -883,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, " @@ -897,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... @@ -921,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 && 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... @@ -959,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... @@ -974,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; @@ -985,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; @@ -1006,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... */ @@ -1035,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, @@ -1117,16 +1139,18 @@ main(int argc, /* I - Number of command-line args */ * 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... @@ -1139,150 +1163,77 @@ 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; - } + dead_children = 1; +} - if (fds[1] >= 0) + +/* + * 'cupsdClearString()' - Clear a string. + */ + +void +cupsdClearString(char **s) /* O - String value */ +{ + if (s && *s) { - close(fds[1]); - fds[1] = -1; + free(*s); + *s = NULL; } } /* - * 'cupsdOpenPipe()' - Create a pipe which is closed on exec. + * 'cupsdFreeStrings()' - Free an array of strings. */ -int /* O - 0 on success, -1 on error */ -cupsdOpenPipe(int *fds) /* O - Pipe file descriptors (2) */ +void +cupsdFreeStrings(cups_array_t **a) /* IO - String array */ { - /* - * Create the pipe... - */ - - if (pipe(fds)) + if (*a) { - fds[0] = -1; - fds[1] = -1; - - return (-1); + cupsArrayDelete(*a); + *a = NULL; } - - /* - * 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)) - { - close(fds[0]); - close(fds[1]); - - fds[0] = -1; - fds[1] = -1; - - return (-1); - } - - /* - * Return 0 indicating success... - */ - - return (0); -} - - -/* - * 'cupsdClearString()' - Clear a string. - */ - -void -cupsdClearString(char **s) /* O - String value */ -{ - if (s && *s) - { - _cupsStrFree(*s); - *s = NULL; - } -} +} /* @@ -1345,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; } @@ -1363,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 */ @@ -1379,216 +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) -{ - size_t i, /* Looping var */ - count; /* Numebr of listeners */ - int 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); - 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, "launchd_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, - "launchd_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, - "launchd_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); - - 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); - 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 */ /* @@ -1614,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()"); @@ -1641,10 +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)); + cupsdFinishProcess(pid, name, sizeof(name), &job_id); /* * Delete certificates for CGI processes... @@ -1654,129 +1405,387 @@ 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_HELD && (job->filters[0] || job->backend)) + 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->backend == pid) + if (job->filters[i]) + { + job->filters[i] = -pid; + type = "Filter"; + } + else + { + job->backend = -pid; + type = "Backend"; + } + + 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 (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 (status && job->status >= 0) + 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)); + } + + if (!job->attrs) + cupsdLoadJob(job); - snprintf(job->printer->state_message, - sizeof(job->printer->state_message), "%s failed", name); - cupsdAddPrinterHistory(job->printer); + 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; } } + } /* - * Show the exit status as needed... + * Show the exit status as needed, ignoring SIGTERM and SIGKILL errors + * since they come when we kill/end a process... */ - if (status == SIGTERM) - status = 0; - - if (status) + 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)) - cupsdLogMessage(CUPSD_LOG_DEBUG, "PID %d (%s) stopped with status %d!", - pid, name, WEXITSTATUS(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 - cupsdLogMessage(CUPSD_LOG_ERROR, "PID %d (%s) crashed on signal %d!", - pid, name, WTERMSIG(status)); + cupsdLogJob(job, CUPSD_LOG_DEBUG, "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."); + cupsdLogJob(job, 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); + 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; } /* - * 'sigchld_handler()' - Handle 'child' signals from old processes. + * 'select_timeout()' - Calculate the select timeout value. + * */ -static void -sigchld_handler(int sig) /* I - Signal number */ +static long /* O - Number of seconds */ +select_timeout(int fds) /* I - Number of descriptors returned */ { - (void)sig; + long timeout; /* Timeout for select */ + time_t now; /* Current time */ + cupsd_client_t *con; /* Client information */ + cupsd_job_t *job; /* Job information */ + const char *why; /* Debugging aid */ - /* - * Flag that we have dead children... - */ - dead_children = 1; + cupsdLogMessage(CUPSD_LOG_DEBUG2, "select_timeout: JobHistoryUpdate=%ld", + (long)JobHistoryUpdate); /* - * Reset the signal handler as needed... + * Check to see if any of the clients have pending data to be + * processed; if so, the timeout should be 0... */ -#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION) - signal(SIGCLD, sigchld_handler); -#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */ -} - + for (con = (cupsd_client_t *)cupsArrayFirst(Clients); + con; + con = (cupsd_client_t *)cupsArrayNext(Clients)) + if (httpGetReady(con->http)) + return (0); + + /* + * If select has been active in the last second (fds > 0) or we have + * many resources in use then don't bother trying to optimize the + * timeout, just make it 1 second. + */ + + if (fds > 0 || cupsArrayCount(Clients) > 50) + return (1); + + /* + * Otherwise, check all of the possible events that we need to wake for... + */ + + now = time(NULL); + 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... + */ + + if (ListeningPaused > 0 && cupsArrayCount(Clients) < MaxClients && + ListeningPaused < timeout) + { + if (ListeningPaused <= now) + timeout = now; + else + timeout = ListeningPaused; + + why = "resume listening"; + } + + /* + * Check the activity and close old clients... + */ + + for (con = (cupsd_client_t *)cupsArrayFirst(Clients); + con; + con = (cupsd_client_t *)cupsArrayNext(Clients)) + if ((httpGetActivity(con->http) + Timeout) < timeout) + { + timeout = httpGetActivity(con->http) + Timeout; + why = "timeout a client connection"; + } + + /* + * 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 (JobHistoryUpdate && timeout > JobHistoryUpdate) + { + timeout = JobHistoryUpdate; + why = "update job history"; + } + + for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); + job; + job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) + { + if (job->cancel_time && job->cancel_time < timeout) + { + timeout = job->cancel_time; + why = "cancel stuck jobs"; + } + + if (job->kill_time && job->kill_time < timeout) + { + timeout = job->kill_time; + why = "kill unresponsive jobs"; + } + + if (job->state_value == IPP_JOB_HELD && job->hold_until < timeout) + { + timeout = job->hold_until; + why = "release held jobs"; + } + + if (job->state_value == IPP_JOB_PENDING && timeout > (now + 10)) + { + timeout = now + 10; + why = "start pending jobs"; + break; + } + } + +#ifdef HAVE_MALLINFO + /* + * Log memory usage every minute... + */ + + if (LogLevel >= CUPSD_LOG_DEBUG && (mallinfo_time + 60) < timeout) + { + timeout = mallinfo_time + 60; + why = "display memory usage"; + } +#endif /* HAVE_MALLINFO */ + + /* + * 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... + */ + + timeout = timeout - now + 1; + + if (timeout < 1) + timeout = 1; + else if (timeout > 86400) + timeout = 86400; + + /* + * Log and return the timeout value... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "select_timeout(%d): %ld seconds to %s", + fds, timeout, why); + + return (timeout); +} + + +/* + * 'sigchld_handler()' - Handle 'child' signals from old processes. + */ + +static void +sigchld_handler(int sig) /* I - Signal number */ +{ + (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. @@ -1813,204 +1822,275 @@ sigterm_handler(int sig) /* I - Signal number */ } +#if defined(HAVE_ONDEMAND) + /* - * 'select_timeout()' - Calculate the select timeout value. - * + * 'add_ondemand_listener()' - Bind an open fd as a Listener. */ -static long /* O - Number of seconds */ -select_timeout(int fds) /* I - Number of descriptors returned */ +static void +add_ondemand_listener(int fd, /* I - Socket file descriptor */ + int idx) /* I - Listener number, for logging */ { - 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 */ + 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 to see if any of the clients have pending data to be - * processed; if so, the timeout should be 0... + * Check-in with launchd... */ - for (con = (cupsd_client_t *)cupsArrayFirst(Clients); - con; - con = (cupsd_client_t *)cupsArrayNext(Clients)) - if (con->http.used > 0) - return (0); + 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 */ + } /* - * If select has been active in the last second (fds > 0) or we have - * many resources in use then don't bother trying to optimize the - * timeout, just make it 1 second. + * Try to match the launchd sockets to the cupsd listeners... */ - if (fds > 0 || cupsArrayCount(Clients) > 50) - return (1); + 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()); /* - * Otherwise, check all of the possible events that we need to wake for... + * Check-in with launchd... */ - now = time(NULL); - timeout = now + 86400; /* 86400 == 1 day */ - why = "do nothing"; + 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 */ + } /* - * Check whether we are accepting new connections... + * Get the sockets dictionary... */ - if (ListeningPaused > 0 && cupsArrayCount(Clients) < MaxClients && - ListeningPaused < timeout) + if ((ld_sockets = launch_data_dict_lookup(ld_resp, LAUNCH_JOBKEY_SOCKETS)) + == NULL) { - if (ListeningPaused <= now) - timeout = now; - else - timeout = ListeningPaused; - - why = "resume listening"; + cupsdLogMessage(CUPSD_LOG_ERROR, + "service_checkin: No sockets found to answer requests on."); + exit(EXIT_FAILURE); + return; /* anti-compiler-warning */ } /* - * Check the activity and close old clients... + * Get the array of listener sockets... */ - for (con = (cupsd_client_t *)cupsArrayFirst(Clients); - con; - con = (cupsd_client_t *)cupsArrayNext(Clients)) - if ((con->http.activity + Timeout) < timeout) - { - timeout = con->http.activity + Timeout; - why = "timeout a client connection"; - } + 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 */ + } /* - * Update the browse list as needed... + * Add listening fd(s) to the Listener array... */ - if (Browsing && BrowseLocalProtocols) + if (launch_data_get_type(ld_array) == LAUNCH_DATA_ARRAY) { -#ifdef HAVE_LIBSLP - if ((BrowseLocalProtocols & BROWSE_SLP) && (BrowseSLPRefresh < timeout)) - { - timeout = BrowseSLPRefresh; - why = "update SLP browsing"; - } -#endif /* HAVE_LIBSLP */ + count = launch_data_array_get_count(ld_array); -#ifdef HAVE_LDAP - if ((BrowseLocalProtocols & BROWSE_LDAP) && (BrowseLDAPRefresh < timeout)) - { - timeout = BrowseLDAPRefresh; - why = "update LDAP browsing"; - } -#endif /* HAVE_LDAP */ + cupsdLogMessage(CUPSD_LOG_DEBUG, "service_checkin: %d listeners.", (int)count); - if ((BrowseLocalProtocols & BROWSE_CUPS) && NumBrowsers) + for (i = 0; i < count; i ++) { - for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); - p; - p = (cupsd_printer_t *)cupsArrayNext(Printers)) + /* + * Get the launchd file descriptor and address... + */ + + if ((tmp = launch_data_array_get_index(ld_array, i)) != NULL) { - 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"; - } - } + fd = launch_data_get_fd(tmp); + add_ondemand_listener(fd, i); } } } - /* - * Check for any active jobs... - */ + launch_data_free(ld_msg); + launch_data_free(ld_resp); - if (DirtyCleanTime && timeout > DirtyCleanTime) - { - timeout = DirtyCleanTime; - why = "write dirty config/state files"; - } +# else /* HAVE_SYSTEMD */ + int i, /* Looping var */ + count; /* Number of listeners */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG, "service_checkin: pid=%d", (int)getpid()); /* - * Check for any active jobs... + * Check-in with systemd... */ - if (timeout > (now + 10) && ActiveJobs) + if ((count = sd_listen_fds(0)) < 0) { - 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; - } + cupsdLogMessage(CUPSD_LOG_ERROR, "service_checkin: Unable to get listener sockets: %s", strerror(-count)); + exit(EXIT_FAILURE); + return; /* anti-compiler-warning */ } -#ifdef HAVE_MALLINFO /* - * Log memory usage every minute... + * Try to match the systemd sockets to the cupsd listeners... */ - if (LogLevel >= CUPSD_LOG_DEBUG && (mallinfo_time + 60) < timeout) + cupsdLogMessage(CUPSD_LOG_DEBUG, "service_checkin: %d listeners.", count); + + for (i = 0; i < count; i ++) { - timeout = mallinfo_time + 60; - why = "display memory usage"; + add_ondemand_listener(SD_LISTEN_FDS_START + i, i); } -#endif /* HAVE_MALLINFO */ +# endif /* HAVE_LAUNCH_ACTIVATE_SOCKET */ +} - /* - * 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"; - } +/* + * 'service_checkout()' - Update the CUPS_KEEPALIVE file as needed. + */ - /* - * 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 - * systems... - */ +static void +service_checkout(void) +{ + int fd; /* File descriptor */ - timeout = timeout - now + 1; - - if (timeout < 1) - timeout = 1; - else if (timeout > 86400) - timeout = 86400; /* - * Log and return the timeout value... + * Create or remove the "keep-alive" file based on whether there are active + * jobs or shared printers to advertise... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "select_timeout(%d): %ld seconds to %s", - fds, timeout, why); + 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 "\"."); - return (timeout); + 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 */ /* @@ -2020,18 +2100,18 @@ select_timeout(int fds) /* I - Number of descriptors returned */ static void usage(int status) /* O - Exit status */ { - _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); -} + FILE *fp = status ? stderr : stdout; /* Output file */ -/* - * End of "$Id: main.c 7925 2008-09-10 17:47:26Z mike $". - */ + _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); +}