| /* |
| * Server listening routines for the CUPS scheduler. |
| * |
| * Copyright 2007-2016 by Apple Inc. |
| * Copyright 1997-2006 by Easy Software Products, all rights reserved. |
| * |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more information. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include "cupsd.h" |
| |
| |
| /* |
| * Make sure the IPV6_V6ONLY is defined on Linux - older versions of |
| * glibc don't define it even if the kernel supports it... |
| */ |
| |
| #if defined(__linux) && !defined(IPV6_V6ONLY) |
| # define IPV6_V6ONLY 26 |
| #endif /* __linux && !IPV6_V6ONLY */ |
| |
| |
| /* |
| * 'cupsdDeleteAllListeners()' - Delete all listeners. |
| */ |
| |
| void |
| cupsdDeleteAllListeners(void) |
| { |
| cupsd_listener_t *lis; /* Current listening socket */ |
| |
| |
| for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); |
| lis; |
| lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) |
| #ifdef HAVE_ONDEMAND |
| if (!lis->on_demand) |
| #endif /* HAVE_ONDEMAND */ |
| { |
| cupsArrayRemove(Listeners, lis); |
| free(lis); |
| } |
| |
| if (cupsArrayCount(Listeners) == 0) |
| { |
| cupsArrayDelete(Listeners); |
| Listeners = NULL; |
| } |
| } |
| |
| |
| /* |
| * 'cupsdPauseListening()' - Clear input polling on all listening sockets... |
| */ |
| |
| void |
| cupsdPauseListening(void) |
| { |
| cupsd_listener_t *lis; /* Current listening socket */ |
| |
| |
| if (cupsArrayCount(Listeners) < 1) |
| return; |
| |
| if (cupsArrayCount(Clients) == MaxClients) |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "Max clients reached, holding new connections..."); |
| else if (errno == ENFILE || errno == EMFILE) |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "Too many open files, holding new connections for " |
| "30 seconds..."); |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdPauseListening: Clearing input bits..."); |
| |
| for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); |
| lis; |
| lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) |
| cupsdRemoveSelect(lis->fd); |
| |
| ListeningPaused = time(NULL) + 30; |
| } |
| |
| |
| /* |
| * 'cupsdResumeListening()' - Set input polling on all listening sockets... |
| */ |
| |
| void |
| cupsdResumeListening(void) |
| { |
| cupsd_listener_t *lis; /* Current listening socket */ |
| |
| |
| if (cupsArrayCount(Listeners) < 1) |
| return; |
| |
| cupsdLogMessage(CUPSD_LOG_INFO, "Resuming new connection processing..."); |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "cupsdResumeListening: Setting input bits..."); |
| |
| for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); |
| lis; |
| lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) |
| cupsdAddSelect(lis->fd, (cupsd_selfunc_t)cupsdAcceptClient, NULL, lis); |
| |
| ListeningPaused = 0; |
| } |
| |
| |
| /* |
| * 'cupsdStartListening()' - Create all listening sockets... |
| */ |
| |
| void |
| cupsdStartListening(void) |
| { |
| int p; /* Port number */ |
| cupsd_listener_t *lis; /* Current listening socket */ |
| char s[256]; /* String addresss */ |
| const char *have_domain; /* Have a domain socket? */ |
| static const char * const encryptions[] = |
| { /* Encryption values */ |
| "IfRequested", |
| "Never", |
| "Required", |
| "Always" |
| }; |
| |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartListening: %d Listeners", |
| cupsArrayCount(Listeners)); |
| |
| /* |
| * Setup socket listeners... |
| */ |
| |
| for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners), LocalPort = 0, |
| have_domain = NULL; |
| lis; |
| lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) |
| { |
| httpAddrString(&(lis->address), s, sizeof(s)); |
| p = httpAddrPort(&(lis->address)); |
| |
| /* |
| * If needed, create a socket for listening... |
| */ |
| |
| if (lis->fd == -1) |
| { |
| /* |
| * Create a socket for listening... |
| */ |
| |
| lis->fd = httpAddrListen(&(lis->address), p); |
| |
| if (lis->fd == -1) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Unable to open listen socket for address %s:%d - %s.", |
| s, p, strerror(errno)); |
| |
| #ifdef AF_INET6 |
| /* |
| * IPv6 is often disabled while DNS returns IPv6 addresses... |
| */ |
| |
| if (lis->address.addr.sa_family != AF_INET6 && |
| (FatalErrors & CUPSD_FATAL_LISTEN)) |
| cupsdEndProcess(getpid(), 0); |
| #else |
| if (FatalErrors & CUPSD_FATAL_LISTEN) |
| cupsdEndProcess(getpid(), 0); |
| #endif /* AF_INET6 */ |
| |
| continue; |
| } |
| } |
| |
| if (p) |
| cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s:%d on fd %d...", |
| s, p, lis->fd); |
| else |
| cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s on fd %d...", |
| s, lis->fd); |
| |
| /* |
| * Save the first port that is bound to the local loopback or |
| * "any" address... |
| */ |
| |
| if ((!LocalPort || LocalEncryption == HTTP_ENCRYPT_ALWAYS) && p > 0 && |
| (httpAddrLocalhost(&(lis->address)) || |
| httpAddrAny(&(lis->address)))) |
| { |
| LocalPort = p; |
| LocalEncryption = lis->encryption; |
| } |
| |
| #ifdef AF_LOCAL |
| if (lis->address.addr.sa_family == AF_LOCAL && !have_domain) |
| have_domain = lis->address.un.sun_path; |
| #endif /* AF_LOCAL */ |
| } |
| |
| /* |
| * Make sure that we are listening on localhost! |
| */ |
| |
| if (!LocalPort && !have_domain) |
| { |
| cupsdLogMessage(CUPSD_LOG_EMERG, |
| "No Listen or Port lines were found to allow access via " |
| "localhost."); |
| |
| if (FatalErrors & (CUPSD_FATAL_CONFIG | CUPSD_FATAL_LISTEN)) |
| cupsdEndProcess(getpid(), 0); |
| } |
| |
| /* |
| * Set the CUPS_SERVER, IPP_PORT, and CUPS_ENCRYPTION variables based on |
| * the listeners... |
| */ |
| |
| if (have_domain) |
| { |
| /* |
| * Use domain sockets for the local connection... |
| */ |
| |
| cupsdSetEnv("CUPS_SERVER", have_domain); |
| |
| LocalEncryption = HTTP_ENCRYPT_IF_REQUESTED; |
| } |
| else |
| { |
| /* |
| * Use the default local loopback address for the server... |
| */ |
| |
| cupsdSetEnv("CUPS_SERVER", "localhost"); |
| } |
| |
| cupsdSetEnv("CUPS_ENCRYPTION", encryptions[LocalEncryption]); |
| |
| if (LocalPort) |
| cupsdSetEnvf("IPP_PORT", "%d", LocalPort); |
| |
| /* |
| * Resume listening for connections... |
| */ |
| |
| cupsdResumeListening(); |
| } |
| |
| |
| /* |
| * 'cupsdStopListening()' - Close all listening sockets... |
| */ |
| |
| void |
| cupsdStopListening(void) |
| { |
| cupsd_listener_t *lis; /* Current listening socket */ |
| |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "cupsdStopListening: closing all listen sockets."); |
| |
| cupsdPauseListening(); |
| |
| for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); |
| lis; |
| lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) |
| { |
| #ifdef HAVE_ONDEMAND |
| if (!lis->on_demand && lis->fd != -1) |
| { |
| httpAddrClose(&(lis->address), lis->fd); |
| lis->fd = -1; |
| } |
| |
| #else |
| if (lis->fd != -1) |
| { |
| httpAddrClose(&(lis->address), lis->fd); |
| lis->fd = -1; |
| } |
| #endif /* HAVE_ONDEMAND */ |
| } |
| } |