From 5db6985b96fb77a1088f7ebf86aa27788ae5ebdd Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 28 Jan 2006 13:36:15 +0000 Subject: [PATCH] Merge launchd branch into trunk. git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@5020 7a7537e8-13f0-0310-91df-b6672ffda945 --- Makedefs.in | 3 + Makefile | 6 + cgi-bin/admin.c | 19 + config-scripts/cups-directories.m4 | 4 +- config-scripts/cups-launchd.m4 | 53 +++ config.h.in | 9 + configure.in | 1 + doc/Makefile | 1 + init/cups.osx | 0 init/cupsd-launchd.plist | 39 ++ init/cupsd-launchd.sh | 7 + scheduler/conf.c | 25 +- scheduler/conf.h | 6 + scheduler/dirsvc.h | 3 +- scheduler/listen.c | 209 ++++----- scheduler/main.c | 692 ++++++++++++++++++++++++++++- tools/testosx | 4 +- 17 files changed, 965 insertions(+), 116 deletions(-) create mode 100644 config-scripts/cups-launchd.m4 mode change 100644 => 100755 init/cups.osx create mode 100644 init/cupsd-launchd.plist create mode 100755 init/cupsd-launchd.sh diff --git a/Makedefs.in b/Makedefs.in index 0deab99d8..79a92aeb3 100644 --- a/Makedefs.in +++ b/Makedefs.in @@ -118,6 +118,7 @@ OPTIONS = PAMLIBS = @PAMLIBS@ SSLFLAGS = @SSLFLAGS@ SSLLIBS = @SSLLIBS@ +LAUNCHDLIBS = @LAUNCHDLIBS@ # # Directories... @@ -177,6 +178,8 @@ MAN8DIR = @MAN8DIR@ PAMDIR = $(BUILDROOT)@PAMDIR@ PAMFILE = @PAMFILE@ +DEFAULT_LAUNCHD_CONF = @DEFAULT_LAUNCHD_CONF@ + # # Rules... diff --git a/Makefile b/Makefile index 8bd4cfa9c..c82d63417 100644 --- a/Makefile +++ b/Makefile @@ -107,6 +107,12 @@ install: installhdrs $(INSTALL_DATA) init/cups.plist $(BUILDROOT)$(INITDDIR)/StartupParameters.plist; \ $(INSTALL_DIR) $(BUILDROOT)$(INITDDIR)/Resources/English.lproj; \ $(INSTALL_DATA) init/cups.strings $(BUILDROOT)$(INITDDIR)/Resources/English.lproj/Localizable.strings; \ + elif test "$(INITDDIR)" = "/System/Library/LaunchDaemons"; then \ + $(INSTALL_DATA) init/cupsd-launchd.plist $(BUILDROOT)$(DEFAULT_LAUNCHD_CONF); \ + $(INSTALL_SCRIPT) init/cupsd-launchd.sh $(BUILDROOT)/System/Library/StartupItems/PrintingServices/PrintingServices; \ + $(INSTALL_DATA) init/cups.plist $(BUILDROOT)/System/Library/StartupItems/PrintingServices/StartupParameters.plist; \ + $(INSTALL_DIR) $(BUILDROOT)/System/Library/StartupItems/PrintingServices/Resources/English.lproj; \ + $(INSTALL_DATA) init/cups.strings $(BUILDROOT)/System/Library/StartupItems/PrintingServices/Resources/English.lproj/Localizable.strings; \ else \ $(INSTALL_SCRIPT) init/cups.sh $(BUILDROOT)$(INITDDIR)/cups; \ fi \ diff --git a/cgi-bin/admin.c b/cgi-bin/admin.c index 2c027e869..0bad845fd 100644 --- a/cgi-bin/admin.c +++ b/cgi-bin/admin.c @@ -87,6 +87,15 @@ main(int argc, /* I - Number of command-line arguments */ http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption()); + if (!http) + { + perror("ERROR: Unable to connect to cupsd"); + fprintf(stderr, "DEBUG: cupsServer()=\"%s\"\n", cupsServer()); + fprintf(stderr, "DEBUG: ippPort()=%d\n", ippPort()); + fprintf(stderr, "DEBUG: cupsEncryption()=%d\n", cupsEncryption()); + exit(1); + } + /* * Set the web interface section... */ @@ -611,6 +620,8 @@ do_am_printer(http_t *http, /* I - HTTP connection */ * printer-uri */ + fputs("DEBUG: Getting list of devices...\n", stderr); + request = ippNewRequest(CUPS_GET_DEVICES); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", @@ -620,11 +631,19 @@ do_am_printer(http_t *http, /* I - HTTP connection */ * Do the request and get back a response... */ + fprintf(stderr, "DEBUG: http=%p (%s)\n", http, http->hostname); + if ((response = cupsDoRequest(http, request, "/")) != NULL) { + fputs("DEBUG: Got device list!\n", stderr); + cgiSetIPPVars(response, NULL, NULL, NULL, 0); ippDelete(response); } + else + fprintf(stderr, + "ERROR: CUPS-Get-Devices request failed with status %x: %s\n", + cupsLastError(), cupsLastErrorString()); /* * Let the user choose... diff --git a/config-scripts/cups-directories.m4 b/config-scripts/cups-directories.m4 index 5d6c4f8ab..cd1c15581 100644 --- a/config-scripts/cups-directories.m4 +++ b/config-scripts/cups-directories.m4 @@ -126,7 +126,9 @@ if test x$rcdir = x; then Darwin*) # Darwin and MacOS X... INITDIR="" - INITDDIR="/System/Library/StartupItems/PrintingServices" + AC_CHECK_PROG(INITDDIR, launchd, + "/System/Library/LaunchDaemons", + "/System/Library/StartupItems/PrintingServices") ;; Linux | GNU) diff --git a/config-scripts/cups-launchd.m4 b/config-scripts/cups-launchd.m4 new file mode 100644 index 000000000..8e075e9bf --- /dev/null +++ b/config-scripts/cups-launchd.m4 @@ -0,0 +1,53 @@ +dnl +dnl "$Id$" +dnl +dnl launchd stuff for the Common UNIX Printing System (CUPS). +dnl +dnl Copyright 1997-2005 by Easy Software Products, all rights reserved. +dnl +dnl These coded instructions, statements, and computer programs are the +dnl property of Easy Software Products and are protected by Federal +dnl copyright law. Distribution and use rights are outlined in the file +dnl "LICENSE.txt" which should have been included with this file. If this +dnl file is missing or damaged please contact Easy Software Products +dnl at: +dnl +dnl Attn: CUPS Licensing Information +dnl Easy Software Products +dnl 44141 Airport View Drive, Suite 204 +dnl Hollywood, Maryland 20636 USA +dnl +dnl Voice: (301) 373-9600 +dnl EMail: cups-info@cups.org +dnl WWW: http://www.cups.org +dnl + + +AC_ARG_ENABLE(launchd, [ --enable-launchd turn on launchd support, default=yes]) + +DEFAULT_LAUNCHD_CONF="" +LAUNCHDLIBS="" + +if test x$enable_launchd != xno; then + AC_CHECK_FUNC(launch_msg, AC_DEFINE(HAVE_LAUNCHD)) + AC_CHECK_HEADER(launch.h, AC_DEFINE(HAVE_LAUNCH_H)) + + case "$uname" in + Darwin*) + # Darwin, MacOS X + DEFAULT_LAUNCHD_CONF="/System/Library/LaunchDaemons/org.cups.cupsd.plist" + # liblaunch is already part of libSystem + ;; + *) + # All others; this test will need to be updated + ;; + esac +fi + +AC_DEFINE_UNQUOTED(CUPS_DEFAULT_LAUNCHD_CONF, "$DEFAULT_LAUNCHD_CONF") +AC_SUBST(DEFAULT_LAUNCHD_CONF) +AC_SUBST(LAUNCHDLIBS) + +dnl +dnl End of "$Id$". +dnl diff --git a/config.h.in b/config.h.in index e75436acb..11982790a 100644 --- a/config.h.in +++ b/config.h.in @@ -352,6 +352,15 @@ #undef HAVE_PTHREAD_H +/* + * Do we have launchd support? + */ + +#undef HAVE_LAUNCH_H +#undef HAVE_LAUNCHD +#define CUPS_DEFAULT_LAUNCHD_CONF "" + + /* * Various scripting languages... */ diff --git a/configure.in b/configure.in index 264e678ef..a481c8bbf 100644 --- a/configure.in +++ b/configure.in @@ -41,6 +41,7 @@ sinclude(config-scripts/cups-ssl.m4) sinclude(config-scripts/cups-pam.m4) sinclude(config-scripts/cups-threads.m4) sinclude(config-scripts/cups-largefile.m4) +sinclude(config-scripts/cups-launchd.m4) MAKEDEFS="../Makedefs" AC_SUBST(MAKEDEFS) diff --git a/doc/Makefile b/doc/Makefile index 25ee17422..9b21b8fdf 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -30,6 +30,7 @@ include ../Makedefs WEBPAGES = cups.css cupsdoc.css index.html robots.txt WEBIMAGES = \ + favicon.ico \ images/accept-jobs.gif \ images/add-class.gif \ images/add-printer.gif \ diff --git a/init/cups.osx b/init/cups.osx old mode 100644 new mode 100755 diff --git a/init/cupsd-launchd.plist b/init/cupsd-launchd.plist new file mode 100644 index 000000000..67f19cbd2 --- /dev/null +++ b/init/cupsd-launchd.plist @@ -0,0 +1,39 @@ + + + + + Enabled + + Label + org.cups.cupsd + OnDemand + + ProgramArguments + + /usr/sbin/cupsd + -l + + RunAtLoad + + ServiceIPC + + Sockets + + Listeners + + + SockNodeName + localhost + SockServiceName + ipp + + + SockPathMode + 49663 + SockPathName + /private/var/run/cupsd + + + + + diff --git a/init/cupsd-launchd.sh b/init/cupsd-launchd.sh new file mode 100755 index 000000000..c95622054 --- /dev/null +++ b/init/cupsd-launchd.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# This is a dummy script to ensure that the CUPS domain socket is world- +# writable. This works around a problem in launchd... + +if test -e /private/var/run/cupsd; then + chmod g+w,o+w /private/var/run/cupsd +fi diff --git a/scheduler/conf.c b/scheduler/conf.c index 89124846f..d93a94749 100644 --- a/scheduler/conf.c +++ b/scheduler/conf.c @@ -154,6 +154,10 @@ static cupsd_var_t variables[] = { "ServerKey", &ServerKey, CUPSD_VARTYPE_STRING }, # endif /* HAVE_LIBSSL || HAVE_GNUTLS */ #endif /* HAVE_SSL */ +#ifdef HAVE_LAUNCHD + { "LaunchdTimeout", &LaunchdTimeout, CUPSD_VARTYPE_INTEGER }, + { "LaunchdConf", &LaunchdConf, CUPSD_VARTYPE_STRING }, +#endif /* HAVE_LAUNCHD */ { "ServerName", &ServerName, CUPSD_VARTYPE_STRING }, { "ServerRoot", &ServerRoot, CUPSD_VARTYPE_STRING }, { "StateDir", &StateDir, CUPSD_VARTYPE_STRING }, @@ -208,12 +212,6 @@ cupsdReadConfiguration(void) char *old_serverroot, /* Old ServerRoot */ *old_requestroot; /* Old RequestRoot */ - /* - * Shutdown the server... - */ - - cupsdStopServer(); - /* * Save the old root paths... */ @@ -450,6 +448,11 @@ cupsdReadConfiguration(void) DefaultLeaseDuration = 86400; MaxLeaseDuration = 0; +#ifdef HAVE_LAUNCHD + LaunchdTimeout = DEFAULT_TIMEOUT + 10; + cupsdSetString(&LaunchdConf, CUPS_DEFAULT_LAUNCHD_CONF); +#endif /* HAVE_LAUNCHD */ + /* * Read the configuration file... */ @@ -1010,12 +1013,6 @@ cupsdReadConfiguration(void) cupsdClearString(&old_serverroot); cupsdClearString(&old_requestroot); - /* - * Startup the server and return... - */ - - cupsdStartServer(); - return (1); } @@ -1988,12 +1985,16 @@ read_configuration(cups_file_t *fp) /* I - File to read from */ memset(lis, 0, sizeof(cupsd_listener_t)); memcpy(&(lis->address), &(addr->addr), sizeof(lis->address)); + lis->fd = -1; #ifdef HAVE_SSL if (!strcasecmp(line, "SSLPort") || !strcasecmp(line, "SSLListen")) lis->encryption = HTTP_ENCRYPT_ALWAYS; #endif /* HAVE_SSL */ + + httpAddrString(&lis->address, temp, sizeof(temp)); + #ifdef AF_INET6 if (lis->address.addr.sa_family == AF_INET6) cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s:%d (IPv6)", temp, diff --git a/scheduler/conf.h b/scheduler/conf.h index f413bcc73..bbc6c974b 100644 --- a/scheduler/conf.h +++ b/scheduler/conf.h @@ -192,6 +192,12 @@ VAR CFArrayRef ServerCertificatesArray VALUE(NULL); # endif /* HAVE_LIBSSL || HAVE_GNUTLS */ #endif /* HAVE_SSL */ +#ifdef HAVE_LAUNCHD +VAR int LaunchdTimeout VALUE(DEFAULT_TIMEOUT); + /* Time after which an idle cupsd will exit */ +VAR char *LaunchdConf VALUE(NULL); + /* launchd(8) configuration file */ +#endif /* HAVE_LAUNCHD */ /* * Prototypes... diff --git a/scheduler/dirsvc.h b/scheduler/dirsvc.h index acbecf80d..ea7dd6eff 100644 --- a/scheduler/dirsvc.h +++ b/scheduler/dirsvc.h @@ -39,7 +39,8 @@ #define BROWSE_CUPS 1 /* CUPS */ #define BROWSE_SLP 2 /* SLPv2 */ #define BROWSE_LDAP 4 /* LDAP (not supported yet) */ -#define BROWSE_ALL 7 /* All protocols */ +#define BROWSE_DNSSD 8 /* DNS Service Discovery aka Bonjour */ +#define BROWSE_ALL 15 /* All protocols */ /* diff --git a/scheduler/listen.c b/scheduler/listen.c index 5b6999fb0..b3107a7b0 100644 --- a/scheduler/listen.c +++ b/scheduler/listen.c @@ -169,116 +169,124 @@ cupsdStartListening(void) p = ntohs(lis->address.ipv4.sin_port); /* - * Create a socket for listening... + * If needed, create a socket for listening... */ - lis->fd = socket(lis->address.addr.sa_family, SOCK_STREAM, 0); - if (lis->fd == -1) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdStartListening: Unable to open listen socket for address %s:%d - %s.", - s, p, strerror(errno)); - continue; - } - - fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC); - - /* - * Set things up to reuse the local address for this port. - */ - - val = 1; -#ifdef __sun - setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); -#else - setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); -#endif /* __sun */ - - /* - * Bind to the port we found... - */ - -#ifdef AF_INET6 - if (lis->address.addr.sa_family == AF_INET6) - { -# ifdef IPV6_V6ONLY - /* - * Accept only IPv6 connections on this socket, to avoid - * potential security issues and to make all platforms behave - * the same. - */ - - val = 1; -# ifdef __sun - setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val)); -# else - setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); -# endif /* __sun */ -# endif /* IPV6_V6ONLY */ - - status = bind(lis->fd, (struct sockaddr *)&(lis->address), - httpAddrLength(&(lis->address))); - } - else -#endif /* AF_INET6 */ -#ifdef AF_LOCAL - if (lis->address.addr.sa_family == AF_LOCAL) - { - mode_t mask; /* Umask setting */ - - /* - * Remove any existing domain socket file... + * Create a socket for listening... */ - - unlink(lis->address.un.sun_path); - + + lis->fd = socket(lis->address.addr.sa_family, SOCK_STREAM, 0); + + if (lis->fd == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "cupsdStartListening: Unable to open listen socket for address %s:%d - %s.", + s, p, strerror(errno)); + continue; + } + /* - * Save the curent umask and set it to 0... + * Set things up to reuse the local address for this port. */ - - mask = umask(0); - + + val = 1; + #ifdef __sun + setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); + #else + setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + #endif /* __sun */ + /* - * Bind the domain socket... + * Bind to the port we found... */ - + + #ifdef AF_INET6 + if (lis->address.addr.sa_family == AF_INET6) + { + # ifdef IPV6_V6ONLY + /* + * Accept only IPv6 connections on this socket, to avoid + * potential security issues and to make all platforms behave + * the same. + */ + + val = 1; + # ifdef __sun + setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val)); + # else + setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); + # endif /* __sun */ + # endif /* IPV6_V6ONLY */ + + status = bind(lis->fd, (struct sockaddr *)&(lis->address), + httpAddrLength(&(lis->address))); + } + else + #endif /* AF_INET6 */ + #ifdef AF_LOCAL + if (lis->address.addr.sa_family == AF_LOCAL) + { + mode_t mask; /* Umask setting */ + + + /* + * Remove any existing domain socket file... + */ + + unlink(lis->address.un.sun_path); + + /* + * Save the curent umask and set it to 0... + */ + + mask = umask(0); + + /* + * Bind the domain socket... + */ + + status = bind(lis->fd, (struct sockaddr *)&(lis->address), + httpAddrLength(&(lis->address))); + + /* + * Restore the umask... + */ + + umask(mask); + } + else + #endif /* AF_LOCAL */ status = bind(lis->fd, (struct sockaddr *)&(lis->address), - httpAddrLength(&(lis->address))); - + sizeof(lis->address.ipv4)); + + if (status < 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "cupsdStartListening: Unable to bind socket for address %s:%d - %s.", + s, p, strerror(errno)); + close(lis->fd); + lis->fd = -1; + continue; + } + /* - * Restore the umask... + * Listen for new clients. */ - - umask(mask); + + if (listen(lis->fd, ListenBackLog) < 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "cupsdStartListening: Unable to listen for clients on address %s:%d - %s.", + s, p, strerror(errno)); + exit(errno); + } } - else -#endif /* AF_LOCAL */ - status = bind(lis->fd, (struct sockaddr *)&(lis->address), - sizeof(lis->address.ipv4)); - if (status < 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdStartListening: Unable to bind socket for address %s:%d - %s.", - s, p, strerror(errno)); - close(lis->fd); - lis->fd = -1; - continue; - } - - /* - * Listen for new clients. - */ - - if (listen(lis->fd, ListenBackLog) < 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdStartListening: Unable to listen for clients on address %s:%d - %s.", - s, p, strerror(errno)); - exit(errno); - } + fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC); + if (p) cupsdLogMessage(CUPSD_LOG_INFO, @@ -375,10 +383,12 @@ cupsdStopListening(void) for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++) { + if (lis->fd != -1) + { #ifdef WIN32 - closesocket(lis->fd); + closesocket(lis->fd); #else - close(lis->fd); + close(lis->fd); #endif /* WIN32 */ #ifdef AF_LOCAL @@ -386,9 +396,10 @@ cupsdStopListening(void) * Remove domain sockets... */ - if (lis->address.addr.sa_family == AF_LOCAL) - unlink(lis->address.un.sun_path); + if (lis->address.addr.sa_family == AF_LOCAL) + unlink(lis->address.un.sun_path); #endif /* AF_LOCAL */ + } } } diff --git a/scheduler/main.c b/scheduler/main.c index 75fcaee65..d4e1a9292 100644 --- a/scheduler/main.c +++ b/scheduler/main.c @@ -32,6 +32,12 @@ * 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_reload() - Tell launchd to reload the configuration + * file to pick up the new listening directives. + * launchd_sync_conf() - Re-write the launchd(8) config file + * com.easysw.cupsd.plist based on cupsd.conf. * parent_handler() - Catch USR1/CHLD signals... * process_children() - Process all dead children... * sigchld_handler() - Handle 'child' signals from old processes. @@ -53,6 +59,11 @@ #include #include +#ifdef HAVE_LAUNCH_H +# include +# include +#endif /* HAVE_LAUNCH_H */ + #if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO) # include #endif /* HAVE_MALLOC_H && HAVE_MALLINFO */ @@ -65,6 +76,12 @@ * Local functions... */ +#ifdef HAVE_LAUNCHD +static void launchd_checkin(void); +static void launchd_reload(void); +static int launchd_sync_conf(void); +#endif /* HAVE_LAUNCHD */ + static void parent_handler(int sig); static void process_children(void); static void sigchld_handler(int sig); @@ -105,7 +122,7 @@ main(int argc, /* I - Number of command-line arguments */ cupsd_job_t *job; /* Current job */ cupsd_listener_t *lis; /* Current listener */ time_t current_time, /* Current time */ - activity, /* Activity timer */ + activity, /* Client activity timer */ browse_time, /* Next browse send time */ senddoc_time, /* Send-Document time */ expire_time; /* Subscription expire time */ @@ -121,13 +138,21 @@ main(int argc, /* I - Number of command-line arguments */ cups_file_t *fp; /* Fake lpsched lock file */ struct stat statbuf; /* Needed for checking lpsched FIFO */ #endif /* __sgi */ +#if HAVE_LAUNCHD + int launchd, /* Started with the -l option? */ + launchd_idle_exit; + /* Idle exit on select timeout? */ +#endif /* HAVE_LAUNCHD */ /* * Check for command-line arguments... */ - fg = 0; + fg = 0; +#if HAVE_LAUNCHD + launchd = 0; +#endif /* HAVE_LAUNCHD */ for (i = 1; i < argc; i ++) if (argv[i][0] == '-') @@ -180,6 +205,13 @@ main(int argc, /* I - Number of command-line arguments */ fg = -1; break; +#ifdef HAVE_LAUNCHD + case 'l' : /* Started by launchd... */ + launchd = 1; + fg = 1; + break; +#endif /* HAVE_LAUNCHD */ + default : /* Unknown option */ fprintf(stderr, "cupsd: Unknown option \'%c\' - aborting!\n", *opt); @@ -349,6 +381,37 @@ main(int argc, /* I - Number of command-line arguments */ return (1); } +#if HAVE_LAUNCHD + if (launchd) + { + /* + * If we were started by launchd make sure the cupsd plist file contains the + * same listeners as cupsd.conf; If it didn't then reload it before getting + * the list of listening file descriptors... + */ + + if (launchd_sync_conf()) + { + launchd_reload(); + + /* + * Until rdar://3854821 is fixed we have to exit after the reload... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "Exiting on launchd_reload"); + exit(0); + } + + launchd_checkin(); + } +#endif /* HAVE_LAUNCHD */ + + /* + * Startup the server... + */ + + cupsdStartServer(); + /* * Catch hangup and child signals and ignore broken pipes... */ @@ -539,12 +602,48 @@ main(int argc, /* I - Number of command-line arguments */ if ((NumClients == 0 && (!job || NeedReload != RELOAD_ALL)) || (time(NULL) - ReloadTime) >= ReloadTimeout) { + /* + * Shutdown the server... + */ + + cupsdStopServer(); + + /* + * Read configuration... + */ + if (!cupsdReadConfiguration()) { syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!", ConfigurationFile); break; } + +#if HAVE_LAUNCHD + if (launchd) + { + if (launchd_sync_conf()) + { + launchd_reload(); + + /* + * Until rdar://3854821 is fixed we have to exit after the reload... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "Exiting on launchd_reload"); + stop_scheduler = 1; + break; + } + + launchd_checkin(); + } +#endif /* HAVE_LAUNCHD */ + + /* + * Startup the server... + */ + + cupsdStartServer(); } } @@ -562,6 +661,24 @@ main(int argc, /* I - Number of command-line arguments */ timeout.tv_sec = select_timeout(fds); timeout.tv_usec = 0; +#if HAVE_LAUNCHD + /* + * If no other work is scheduled and we're being controlled by + * launchd(8) then timeout after 'LaunchdTimeout' seconds of + * inactivity... + */ + + if (timeout.tv_sec == 86400 && launchd && LaunchdTimeout && + (!Browsing || !(BrowseLocalProtocols & BROWSE_DNSSD) || + cupsArrayCount(Printers) == 0)) + { + timeout.tv_sec = LaunchdTimeout; + launchd_idle_exit = 1; + } + else + launchd_idle_exit = 0; +#endif /* HAVE_LAUNCHD */ + if (timeout.tv_sec < 86400) /* Only use timeout for < 1 day */ fds = select(MaxFDs, input, output, NULL, &timeout); else @@ -646,6 +763,22 @@ main(int argc, /* I - Number of command-line arguments */ current_time = time(NULL); +#if HAVE_LAUNCHD + /* + * If no other work was scheduled and we're being controlled by launchd(8) + * then timeout after 'LaunchdTimeout' seconds of inactivity... + */ + + if (!fds && launchd_idle_exit) + { + cupsdLogMessage(CUPSD_LOG_INFO, + "Printer sharing is off and there are no jobs pending, " + "will restart on demand."); + stop_scheduler = 1; + break; + } +#endif /* HAVE_LAUNCHD */ + /* * Check for status info from job filters... */ @@ -926,6 +1059,14 @@ main(int argc, /* I - Number of command-line arguments */ cupsdStopSystemMonitor(); +#ifdef HAVE_LAUNCHD + /* + * Update the launchd config file as needed... + */ + + launchd_sync_conf(); +#endif /* HAVE_LAUNCHD */ + #ifdef __sgi /* * Remove the fake IRIX lpsched lock file, but only if the existing @@ -1193,6 +1334,553 @@ cupsdSetStringf(char **s, /* O - New string */ } +#ifdef HAVE_LAUNCHD +/* + * 'launchd_checkin()' - Check-in with launchd and collect the listening fds. + */ + +static void +launchd_checkin(void) +{ + int i, /* Looping var */ + 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 */ + ld_runatload, /* Run-at-load setting */ + tmp; /* Launch data */ + cupsd_listener_t *lis; /* Listeners array */ + http_addr_t addr; /* Address variable */ + socklen_t addrlen; /* Length of address */ + bool runatload; /* Run-at-load setting value */ + + + 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 "run-at-load" setting... + */ + + if ((ld_runatload = launch_data_dict_lookup(ld_resp, + LAUNCH_JOBKEY_RUNATLOAD)) != NULL && + launch_data_get_type(ld_runatload) == LAUNCH_DATA_BOOL) + runatload = launch_data_get_bool(ld_runatload); + else + { + errno = launch_data_get_errno(ld_resp); + cupsdLogMessage(CUPSD_LOG_ERROR, + "launchd_checkin: Unable to find Run-at-load setting: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } + + cupsdLogMessage(CUPSD_LOG_DEBUG, "launchd_checkin: Run-at-load=%s", + runatload ? "true" : "false"); + + /* + * 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) + { + /* + * Free the listeners array built from cupsd.conf... + */ + + if (NumListeners > 0) + free(Listeners); + + NumListeners = launch_data_array_get_count(ld_array); + Listeners = calloc(NumListeners, sizeof(cupsd_listener_t)); + + if (!Listeners) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "launchd_checkin: Unable to allocate new Listeners - %s.", + strerror(errno)); + exit(EXIT_FAILURE); + } + + /* + * Note: launchd wants us to access the array in ascending order, + * thus "i" counts up and not down as we normally do elsewhere... + */ + + for (i = 0, lis = Listeners; i < NumListeners; i ++, lis ++) + { + /* + * Copy the current address and log it... + */ + + tmp = launch_data_array_get_index(ld_array, i); + lis->fd = launch_data_get_fd(tmp); + addrlen = sizeof(lis->address); + + if (getsockname(lis->fd, (struct sockaddr *)&(lis->address), &addrlen)) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "launchd_checkin: Unable to get local address - %s", + strerror(errno)); + } + +# ifdef HAVE_SSL + portnum = 0; + +# ifdef AF_INET6 + if (addr.addr.sa_family == AF_INET6) + portnum = ntohs(addr.ipv6.sin6_port); + else +# endif /* AF_INET6 */ +# ifdef AF_LOCAL + if (addr.addr.sa_family == AF_LOCAL) + { + /* + * Make sure the domain socket is accessible to all... + */ + + fchmod(lis->fd, 0140777); + } + else +# endif /* AF_LOCAL */ + if (addr.addr.sa_family == AF_INET) + portnum = ntohs(addr.ipv4.sin_port); + + if (portnum == 443) + lis->encryption = HTTP_ENCRYPT_ALWAYS; +# endif /* HAVE_SSL */ + } + } + + /* + * Collect the browse socket (if there is one)... + */ + + if ((ld_array = launch_data_dict_lookup(ld_sockets, "BrowseSockets"))) + { + if (launch_data_get_type(ld_array) == LAUNCH_DATA_ARRAY) + { + tmp = launch_data_array_get_index(ld_array, 0); + + if (launch_data_get_type(tmp) == LAUNCH_DATA_FD) + { + if (BrowseSocket != -1) + close(BrowseSocket); + + BrowseSocket = launch_data_get_fd(tmp); + } + else + cupsdLogMessage(CUPSD_LOG_WARN, + "launchd_checkin: BrowseSocket not a fd!"); + } + else + cupsdLogMessage(CUPSD_LOG_WARN, + "launchd_checkin: BrowseSockets is not an array!"); + } + else + cupsdLogMessage(CUPSD_LOG_DEBUG, "launchd_checkin: No BrowseSockets"); + + launch_data_free(ld_msg); + launch_data_free(ld_resp); +} + + +/* + * 'launchd_reload()' - Tell launchd to reload the configuration file to pick + * up the new listening directives. + */ + +static void +launchd_reload(void) +{ + int child_status; /* Exit status of child process */ + pid_t child_pid, /* Child PID */ + waitpid_status; /* Child process exit status */ + char *argv[4]; /* Argument strings */ + + + /* + * The current launchd doesn't support a reload option (rdar://3854821). + * Until this is fixed we need to reload the config file by execing launchctl + * twice (to unload then load). NOTE: This will cause us to exit on SIGTERM + * which will cancel all client & job activity. + * + * After this is fixed we'll be able to tell launchd to reload the file + * and pick up the new listening descriptors without disrupting current + * activity. + */ + + /* + * Unloading the current configuration will cause launchd to send us a SIGTERM; + * block it for now so we can get our work done... + */ + + cupsdHoldSignals(); + + /* + * Set up the unload arguments to launchctl... + */ + + argv[0] = "/bin/launchctl"; + argv[1] = "unload"; + argv[2] = LaunchdConf; + argv[3] = NULL; + + if (cupsdStartProcess(argv[0], argv, NULL, -1, -1, -1, -1, 1, &child_pid) < 0) + cupsdLogMessage(CUPSD_LOG_ERROR, + "launchd_reload: Unable to execute %s - %s", argv[0], + strerror(errno)); + else + { + do + { + waitpid_status = waitpid(child_pid, &child_status, 0); + } + while (waitpid_status == (pid_t)-1 && errno == EINTR); + + if (WIFSIGNALED(child_status)) + cupsdLogMessage(CUPSD_LOG_DEBUG, + "launchd_reload: %s pid %d crashed on signal %d!", + basename(argv[0]), child_pid, WTERMSIG(child_status)); + else + cupsdLogMessage(CUPSD_LOG_DEBUG, + "launchd_reload: %s pid %d stopped with status %d!", + basename(argv[0]), child_pid, WEXITSTATUS(child_status)); + + /* + * Do it again with the load command... + */ + + argv[1] = "load"; + + if (cupsdStartProcess(argv[0], argv, NULL, -1, -1, -1, -1, 1, + &child_pid) < 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "launchd_reload: Unable to fork for %s - %s", argv[0], + strerror(errno)); + } + else + { + do + { + waitpid_status = waitpid(child_pid, &child_status, 0); + } while (waitpid_status == (pid_t)-1 && errno == EINTR); + + if (WIFSIGNALED(child_status)) + cupsdLogMessage(CUPSD_LOG_DEBUG, + "launchd_reload: %s pid %d crashed on signal %d!", + basename(argv[0]), child_pid, WTERMSIG(child_status)); + else + cupsdLogMessage(CUPSD_LOG_DEBUG, + "launchd_reload: %s pid %d stopped with status %d", + basename(argv[0]), child_pid, + WEXITSTATUS(child_status)); + } + } + + /* + * Leave signals blocked since exit() will be called momentarily anyways... + */ +} + + +/* + * 'launchd_sync_conf()' - Re-write the launchd(8) config file + * org.cups.cupsd.plist based on cupsd.conf. + */ + +static int /* O - 1 if the file was updated */ +launchd_sync_conf(void) +{ + int i, /* Looping var */ + portnum; /* Port number */ + CFMutableDictionaryRef cupsd_dict, /* com.easysw.cupsd.plist dictionary */ + sockets, /* Sockets dictionary */ + listener; /* Listener dictionary */ + CFDataRef resourceData; /* XML representation of the property list */ + CFMutableArrayRef array; /* Array */ + CFNumberRef socket_mode; /* Domain socket mode bits */ + CFStringRef socket_path; /* Domain socket path */ + CFTypeRef value; /* CF value */ + CFURLRef fileURL; /* File URL */ + SInt32 errorCode; /* Error code */ + cupsd_listener_t *lis; /* Current listening socket */ + struct servent *service; /* Services data base entry */ + char temp[1024]; /* Temporary buffer for value */ + struct stat cupsd_sb, /* File info for cupsd.conf */ + launchd_sb; /* File info for com.easysw.cupsd.plist */ + + + /* + * If the launchd conf file modification time is newer than the cupsd.conf + * time then there's nothing to do... + */ + + if (!stat(ConfigurationFile, &cupsd_sb) && + !stat(LaunchdConf, &launchd_sb) && + launchd_sb.st_mtimespec.tv_sec >= cupsd_sb.st_mtimespec.tv_sec) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, + "launchd_sync_conf: Nothing to do, pid=%d.", + (int)getpid()); + return (0); + } + + /* + * Time to write a new 'com.easysw.cupsd.plist' file. + * Create the new dictionary and populate it with values... + */ + + if ((cupsd_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)) != NULL) + { + CFDictionaryAddValue(cupsd_dict, CFSTR("Label"), CFSTR("org.cups.cupsd")); + CFDictionaryAddValue(cupsd_dict, CFSTR("Enabled"), kCFBooleanTrue); + CFDictionaryAddValue(cupsd_dict, CFSTR("OnDemand"), kCFBooleanTrue); + + if ((Browsing && BrowseLocalProtocols && cupsArrayCount(Printers)) || + cupsArrayCount(ActiveJobs)) + CFDictionaryAddValue(cupsd_dict, CFSTR("RunAtLoad"), kCFBooleanTrue); + else + CFDictionaryAddValue(cupsd_dict, CFSTR("RunAtLoad"), kCFBooleanFalse); + + CFDictionaryAddValue(cupsd_dict, CFSTR("ServiceIPC"), kCFBooleanTrue); + + if ((array = CFArrayCreateMutable(kCFAllocatorDefault, 2, + &kCFTypeArrayCallBacks)) != NULL) + { + CFDictionaryAddValue(cupsd_dict, CFSTR("ProgramArguments"), array); + CFArrayAppendValue(array, CFSTR("/usr/sbin/cupsd")); + CFArrayAppendValue(array, CFSTR("-l")); + CFRelease(array); + } + + /* + * Add a sockets dictionary... + */ + + if ((sockets = (CFMutableDictionaryRef)CFDictionaryCreateMutable( + kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)) != NULL) + { + CFDictionaryAddValue(cupsd_dict, CFSTR("Sockets"), sockets); + + /* + * Add a Listeners array to the sockets dictionary... + */ + + if ((array = CFArrayCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeArrayCallBacks)) != NULL) + { + CFDictionaryAddValue(sockets, CFSTR("Listeners"), array); + + /* + * For each listener add a dictionary to the listeners array... + */ + + for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++) + { + if ((listener = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)) != NULL) + { + CFArrayAppendValue(array, listener); + +# ifdef AF_LOCAL + if (lis->address.addr.sa_family == AF_LOCAL) + { + if ((socket_path = CFStringCreateWithCString(kCFAllocatorDefault, + lis->address.un.sun_path, + kCFStringEncodingUTF8))) + { + CFDictionaryAddValue(listener, CFSTR("SockPathName"), + socket_path); + CFRelease(socket_path); + } + portnum = 0140777; /* (S_IFSOCK|S_IRWXU|S_IRWXG|S_IRWXO) or * + * 49663d decimal */ + if ((socket_mode = CFNumberCreate(kCFAllocatorDefault, + kCFNumberIntType, &portnum))) + { + CFDictionaryAddValue(listener, CFSTR("SockPathMode"), + socket_mode); + CFRelease(socket_mode); + } + } + else +# endif /* AF_LOCAL */ + { +# ifdef AF_INET6 + if (lis->address.addr.sa_family == AF_INET6) + { + CFDictionaryAddValue(listener, CFSTR("SockFamily"), + CFSTR("IPv6")); + portnum = lis->address.ipv6.sin6_port; + } + else +# endif /* AF_INET6 */ + { + CFDictionaryAddValue(listener, CFSTR("SockFamily"), + CFSTR("IPv4")); + portnum = lis->address.ipv4.sin_port; + } + + if ((service = getservbyport(portnum, NULL))) + value = CFStringCreateWithCString(kCFAllocatorDefault, + service->s_name, + kCFStringEncodingUTF8); + else + value = CFNumberCreate(kCFAllocatorDefault, + kCFNumberIntType, &portnum); + + if (value) + { + CFDictionaryAddValue(listener, CFSTR("SockServiceName"), value); + CFRelease(value); + } + + httpAddrString(&lis->address, temp, sizeof(temp)); + if ((value = CFStringCreateWithCString(kCFAllocatorDefault, temp, + kCFStringEncodingUTF8))) + { + CFDictionaryAddValue(listener, CFSTR("SockNodeName"), value); + CFRelease(value); + } + } + + CFRelease(listener); + } + } + + CFRelease(array); + } + + /* + * Add the BrowseSocket to the sockets dictionary... + */ + + if (Browsing && (BrowseRemoteProtocols & BROWSE_CUPS)) + { + if ((array = CFArrayCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeArrayCallBacks)) != NULL) + { + CFDictionaryAddValue(sockets, CFSTR("BrowseSockets"), array); + + if ((listener = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)) != NULL) + { + CFArrayAppendValue(array, listener); + + CFDictionaryAddValue(listener, CFSTR("SockFamily"), CFSTR("IPv4")); + CFDictionaryAddValue(listener, CFSTR("SockType"), CFSTR("dgram")); + + if ((service = getservbyport(BrowsePort, NULL))) + value = CFStringCreateWithCString(kCFAllocatorDefault, + service->s_name, + kCFStringEncodingUTF8); + else + value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, + &BrowsePort); + + CFDictionaryAddValue(listener, CFSTR("SockServiceName"), value); + CFRelease(value); + + CFRelease(listener); + } + + CFRelease(array); + } + } + + CFRelease(sockets); + } + + cupsdLogMessage(CUPSD_LOG_DEBUG, + "launchd_sync_conf: Updating \"%s\", pid=%d\n", + LaunchdConf, (int)getpid()); + + if ((fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, + (const unsigned char *)LaunchdConf, + strlen(LaunchdConf), false))) + { + if ((resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault, + cupsd_dict))) + { + if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData, + NULL, &errorCode)) + { + cupsdLogMessage(CUPSD_LOG_WARN, + "launchd_sync_conf: " + "CFURLWriteDataAndPropertiesToResource(\"%s\") " + "failed: %d\n", + LaunchdConf, (int)errorCode); + } + + CFRelease(resourceData); + } + + CFRelease(fileURL); + } + + CFRelease(cupsd_dict); + } + + /* + * Let the caller know we updated the file... + */ + + return (1); +} +#endif /* HAVE_LAUNCHD */ + + /* * 'parent_handler()' - Catch USR1/CHLD signals... */ diff --git a/tools/testosx b/tools/testosx index 8729d53cf..e28d2fa5c 100755 --- a/tools/testosx +++ b/tools/testosx @@ -33,13 +33,15 @@ cp packaging/installer.tif $pkgdir/Resources/background.tif cat >$pkgdir/Resources/preflight <$pkgdir/Resources/postflight <