void cupsdStopAllNotifiers(void) { cupsd_subscription_t *sub; /* Current subscription */ /* * See if we have started any notifiers... */ if (!NotifierStatusBuffer) return; /* * Yes, kill any processes that are left... */ for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions); sub; sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions)) if (sub->pid) { cupsdEndProcess(sub->pid, 0); close(sub->pipe); sub->pipe = -1; } /* * Close the status pipes... */ if (NotifierPipes[0] >= 0) { cupsdRemoveSelect(NotifierPipes[0]); cupsdStatBufDelete(NotifierStatusBuffer); close(NotifierPipes[0]); close(NotifierPipes[1]); NotifierPipes[0] = -1; NotifierPipes[1] = -1; NotifierStatusBuffer = NULL; } }
static void dnssdClientCallback( AvahiClient *c, /* I - Client */ AvahiClientState state, /* I - Current state */ void *userdata) /* I - User data (unused) */ { int error; /* Error code, if any */ (void)userdata; if (!c) return; /* * Make sure DNSSDClient is already set also if this callback function is * already running before avahi_client_new() in dnssdStartBrowsing() * finishes. */ if (!DNSSDClient) DNSSDClient = c; switch (state) { case AVAHI_CLIENT_S_REGISTERING: case AVAHI_CLIENT_S_RUNNING: case AVAHI_CLIENT_S_COLLISION: cupsdLogMessage(CUPSD_LOG_DEBUG, "Avahi server connection now available, registering printers for Bonjour broadcasting."); /* * Mark that Avahi server is running... */ avahi_running = 1; /* * Set the computer name and register the web interface... */ DNSSDPort = 0; dnssdUpdateDNSSDName(1); /* * Register the individual printers */ dnssdRegisterAllPrinters(1); break; case AVAHI_CLIENT_FAILURE: if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) { cupsdLogMessage(CUPSD_LOG_DEBUG, "Avahi server disappeared, unregistering printers for Bonjour broadcasting."); /* * Unregister everything and close the client... */ dnssdDeregisterAllPrinters(1); dnssdDeregisterInstance(&WebIFSrv, 1); avahi_client_free(DNSSDClient); DNSSDClient = NULL; /* * Mark that Avahi server is not running... */ avahi_running = 0; /* * Renew Avahi client... */ DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssdClientCallback, NULL, &error); if (!DNSSDClient) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to communicate with avahi-daemon: %s", dnssdErrorString(error)); if (FatalErrors & CUPSD_FATAL_BROWSE) cupsdEndProcess(getpid(), 0); } } else { cupsdLogMessage(CUPSD_LOG_ERROR, "Communication with avahi-daemon has failed: %s", avahi_strerror(avahi_client_errno(c))); if (FatalErrors & CUPSD_FATAL_BROWSE) cupsdEndProcess(getpid(), 0); } break; default: break; } }
void cupsdStartBrowsing(void) { if (!Browsing || !BrowseLocalProtocols) return; #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) if (BrowseLocalProtocols & BROWSE_DNSSD) { # ifdef HAVE_DNSSD DNSServiceErrorType error; /* Error from service creation */ /* * First create a "master" connection for all registrations... */ if ((error = DNSServiceCreateConnection(&DNSSDMaster)) != kDNSServiceErr_NoError) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create master DNS-SD reference: %d", error); if (FatalErrors & CUPSD_FATAL_BROWSE) cupsdEndProcess(getpid(), 0); } else { /* * Add the master connection to the select list... */ int fd = DNSServiceRefSockFD(DNSSDMaster); fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); cupsdAddSelect(fd, (cupsd_selfunc_t)dnssdUpdate, NULL, NULL); } /* * Set the computer name and register the web interface... */ DNSSDPort = 0; cupsdUpdateDNSSDName(); # else /* HAVE_AVAHI */ if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create DNS-SD thread."); if (FatalErrors & CUPSD_FATAL_BROWSE) cupsdEndProcess(getpid(), 0); } else { int error; /* Error code, if any */ DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssdClientCallback, NULL, &error); if (DNSSDClient == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to communicate with avahi-daemon: %s", dnssdErrorString(error)); if (FatalErrors & CUPSD_FATAL_BROWSE) cupsdEndProcess(getpid(), 0); avahi_threaded_poll_free(DNSSDMaster); DNSSDMaster = NULL; } else avahi_threaded_poll_start(DNSSDMaster); } # endif /* HAVE_DNSSD */ } #endif /* HAVE_DNSSD || HAVE_AVAHI */ /* * Enable LPD and SMB printer sharing as needed through external programs... */ if (BrowseLocalProtocols & BROWSE_LPD) update_lpd(1); if (BrowseLocalProtocols & BROWSE_SMB) update_smb(1); #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) /* * Register the individual printers */ dnssdRegisterAllPrinters(0); #endif /* HAVE_DNSSD || HAVE_AVAHI */ }
static void cupsd_send_notification( cupsd_subscription_t *sub, /* I - Subscription object */ cupsd_event_t *event) /* I - Event to send */ { ipp_state_t state; /* IPP event state */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsd_send_notification(sub=%p(%d), event=%p(%s))", sub, sub->id, event, cupsdEventName(event->event)); /* * Allocate the events array as needed... */ if (!sub->events) { sub->events = cupsArrayNew3((cups_array_func_t)NULL, NULL, (cups_ahash_func_t)NULL, 0, (cups_acopy_func_t)NULL, (cups_afree_func_t)cupsd_delete_event); if (!sub->events) { cupsdLogMessage(CUPSD_LOG_CRIT, "Unable to allocate memory for subscription #%d!", sub->id); return; } } /* * Purge an old event as needed... */ if (cupsArrayCount(sub->events) >= MaxEvents) { /* * Purge the oldest event in the cache... */ cupsArrayRemove(sub->events, cupsArrayFirst(sub->events)); sub->first_event_id ++; } /* * Add the event to the subscription. Since the events array is * always MaxEvents in length, and since we will have already * removed an event from the subscription cache if we hit the * event cache limit, we don't need to check for overflow here... */ cupsArrayAdd(sub->events, event); /* * Deliver the event... */ if (sub->recipient) { for (;;) { if (sub->pipe < 0) cupsd_start_notifier(sub); cupsdLogMessage(CUPSD_LOG_DEBUG2, "sub->pipe=%d", sub->pipe); if (sub->pipe < 0) break; event->attrs->state = IPP_IDLE; while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA) if (state == IPP_ERROR) break; if (state == IPP_ERROR) { if (errno == EPIPE) { /* * Notifier died, try restarting it... */ cupsdLogMessage(CUPSD_LOG_WARN, "Notifier for subscription %d (%s) went away, " "retrying!", sub->id, sub->recipient); cupsdEndProcess(sub->pid, 0); close(sub->pipe); sub->pipe = -1; continue; } cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to send event for subscription %d (%s)!", sub->id, sub->recipient); } /* * If we get this far, break out of the loop... */ break; } } /* * Bump the event sequence number... */ sub->next_event_id ++; }
int /* O - 1 if log file open */ cupsdCheckLogFile(cups_file_t **lf, /* IO - Log file */ const char *logname) /* I - Log filename */ { char backname[1024], /* Backup log filename */ filename[1024], /* Formatted log filename */ *ptr; /* Pointer into filename */ const char *logptr; /* Pointer into log filename */ /* * See if we have a log file to check... */ if (!lf || !logname || !logname[0]) return (1); /* * Handle logging to stderr... */ if (!strcmp(logname, "stderr")) { *lf = LogStderr; return (1); } /* * Format the filename as needed... */ if (!*lf || (strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize && MaxLogSize > 0)) { /* * Handle format strings... */ filename[sizeof(filename) - 1] = '\0'; if (logname[0] != '/') { strlcpy(filename, ServerRoot, sizeof(filename)); strlcat(filename, "/", sizeof(filename)); } else filename[0] = '\0'; for (logptr = logname, ptr = filename + strlen(filename); *logptr && ptr < (filename + sizeof(filename) - 1); logptr ++) if (*logptr == '%') { /* * Format spec... */ logptr ++; if (*logptr == 's') { /* * Insert the server name... */ strlcpy(ptr, ServerName, sizeof(filename) - (size_t)(ptr - filename)); ptr += strlen(ptr); } else { /* * Otherwise just insert the character... */ *ptr++ = *logptr; } } else *ptr++ = *logptr; *ptr = '\0'; } /* * See if the log file is open... */ if (!*lf) { /* * Nope, open the log file... */ if ((*lf = cupsFileOpen(filename, "a")) == NULL) { /* * If the file is in CUPS_LOGDIR then try to create a missing directory... */ if (!strncmp(filename, CUPS_LOGDIR, strlen(CUPS_LOGDIR))) { /* * Try updating the permissions of the containing log directory, using * the log file permissions as a basis... */ mode_t log_dir_perm = (mode_t)(0300 | LogFilePerm); /* LogFilePerm + owner write/search */ if (log_dir_perm & 0040) log_dir_perm |= 0010; /* Add group search */ if (log_dir_perm & 0004) log_dir_perm |= 0001; /* Add other search */ cupsdCheckPermissions(CUPS_LOGDIR, NULL, log_dir_perm, RunUser, Group, 1, -1); *lf = cupsFileOpen(filename, "a"); } if (*lf == NULL) { #ifdef HAVE_SYSTEMD_SD_JOURNAL_H sd_journal_print(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno)); #else syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno)); #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */ if (FatalErrors & CUPSD_FATAL_LOG) cupsdEndProcess(getpid(), 0); return (0); } } if (strncmp(filename, "/dev/", 5)) { /* * Change ownership and permissions of non-device logs... */ fchown(cupsFileNumber(*lf), RunUser, Group); fchmod(cupsFileNumber(*lf), LogFilePerm); } } /* * Do we need to rotate the log? */ if (strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize && MaxLogSize > 0) { /* * Rotate log file... */ cupsFileClose(*lf); strlcpy(backname, filename, sizeof(backname)); strlcat(backname, ".O", sizeof(backname)); unlink(backname); rename(filename, backname); if ((*lf = cupsFileOpen(filename, "a")) == NULL) { #ifdef HAVE_SYSTEMD_SD_JOURNAL_H sd_journal_print(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno)); #else syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename, strerror(errno)); #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */ if (FatalErrors & CUPSD_FATAL_LOG) cupsdEndProcess(getpid(), 0); return (0); } /* * Change ownership and permissions of non-device logs... */ fchown(cupsFileNumber(*lf), RunUser, Group); fchmod(cupsFileNumber(*lf), LogFilePerm); } return (1); }
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(); }
void cupsdStartListening(void) { int status; /* Bind result */ int p, /* Port number */ val; /* Parameter value */ 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 = socket(lis->address.addr.sa_family, SOCK_STREAM, 0); 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; } /* * 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... */ unlink(lis->address.un.sun_path); /* * Save the current umask and set it to 0 so that all users can access * the domain socket... */ 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), sizeof(lis->address.ipv4)); if (status < 0) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to bind socket for address %s:%d - %s.", s, p, strerror(errno)); close(lis->fd); lis->fd = -1; if (FatalErrors & CUPSD_FATAL_LISTEN) cupsdEndProcess(getpid(), 0); continue; } /* * Listen for new clients. */ if (listen(lis->fd, ListenBackLog) < 0) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to listen for clients on address %s:%d - %s.", s, p, strerror(errno)); close(lis->fd); lis->fd = -1; if (FatalErrors & CUPSD_FATAL_LISTEN) cupsdEndProcess(getpid(), 0); continue; } } fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC); 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); if (chmod(s, 0140777)) cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to change permisssions on domain socket " "\"%s\" - %s", s, strerror(errno)); } /* * 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(); }