static status_e set_fd_modes( struct service *sp ) { int sd = SVC_FD( sp ) ; const char *func = "set_fd_modes" ; /* * There is a possibility of blocking on a send/write if * * the service does not require forking (==> is internal) AND * it does not accept connections * * To avoid this, we put the descriptor in FNDELAY mode. * (if the service accepts connections, we still need to put the * 'accepted' connection in FNDELAY mode but this is done elsewhere) */ if ( ! SVC_FORKS( sp ) && ! SVC_ACCEPTS_CONNECTIONS( sp ) && fcntl( sd, F_SETFL, FNDELAY ) == -1 ) { msg( LOG_ERR, func, "fcntl failed (%m) for FNDELAY. service = %s", SVC_ID( sp ) ) ; return( FAILED ) ; } /* * Always set the close-on-exec flag */ if ( fcntl( sd, F_SETFD, FD_CLOEXEC ) == -1 ) { msg( LOG_ERR, func, "fcntl failed (%m) for close-on-exec. service = %s", SVC_ID( sp ) ) ; return( FAILED ) ; } return( OK ) ; }
/* * Check that the counts of running and retry servers stored in struct service * are accurate */ static unsigned service_count_check( struct service *sp, unsigned running_servers, unsigned retry_servers ) { char *sid = SVC_ID( sp ) ; int error_count = 0 ; const char *func = "service_count_check" ; if ( SVC_RUNNING_SERVERS( sp ) != running_servers ) { msg( LOG_ERR, func, "service %s: actual running servers = %d, known running servers = %d", sid, running_servers, SVC_RUNNING_SERVERS( sp ) ) ; error_count++ ; } if ( SVC_RETRIES( sp ) != retry_servers ) { msg( LOG_ERR, func, "service %s: actual retry servers = %d, known retry servers = %d", sid, retry_servers, SVC_RETRIES( sp ) ) ; error_count++ ; } if ( error_count && debug.on ) msg( LOG_DEBUG, func, "%s: %d errors detected", sid, error_count ) ; return( error_count ) ; }
/* * Steps: * 1. Deactivate the service * 2. Free all memory used by the service and free the service itself * * Since this function may free all memory associated with the service as * well as the memory pointed by sp, only the value of sp should be used * after this call if the return value is 0 (i.e. no dereferencing of sp). * * Special services are never deactivated. */ int svc_release( struct service *sp ) { char *sid = SVC_ID( sp ) ; const char *func = "svc_release" ; if ( SVC_REFCOUNT(sp) == 0 ) { msg( LOG_ERR, func, "%s: svc_release with 0 count", sid ) ; return( 0 ) ; } SVC_REFCOUNT(sp)-- ; if ( SVC_REFCOUNT(sp) == 0 ) { if ( debug.on ) msg( LOG_DEBUG, func, "ref count of service %s dropped to 0", sid ) ; if ( ! SC_IS_SPECIAL( SVC_CONF( sp ) ) ) { if ( SVC_LOG(sp) ) log_end( SC_LOG( SVC_CONF( sp ) ), SVC_LOG(sp) ) ; svc_deactivate( sp ) ; svc_free( sp ) ; sp = NULL; } else /* this shouldn't happen */ msg( LOG_WARNING, func, "ref count of special service %s dropped to 0", sid ) ; return( 0 ) ; } else return( SVC_REFCOUNT(sp) ) ; }
/* * Suspend a service */ void svc_suspend( struct service *sp ) { const char *func = "svc_suspend" ; if ( ! SVC_IS_ACTIVE( sp ) ) { msg( LOG_ERR, func, "service %s is not active", SVC_ID( sp ) ) ; return ; } FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ; ps.rws.active_services-- ; if ( debug.on ) msg( LOG_DEBUG, func, "Suspended service %s", SVC_ID( sp ) ) ; SUSPEND( sp ) ; }
/* * Resume a suspended service. */ void svc_resume( struct service *sp ) { const char *func = "svc_resume" ; FD_SET( SVC_FD( sp ), &ps.rws.socket_mask ) ; ps.rws.active_services++ ; if ( debug.on ) msg( LOG_DEBUG, func, "Resumed service %s", SVC_ID( sp ) ) ; RESUME( sp ) ; }
static int banner_fail( const struct service *sp, const connection_s *cp ) { const char *func = "banner_fail"; const struct service_config *scp = SVC_CONF( sp ) ; if ( SC_BANNER_FAIL(scp) != NULL ) { char tmpbuf[TMPSIZE]; int retval; int bannerfd = open(SC_BANNER_FAIL(scp), O_RDONLY); if( bannerfd < 0 ) { msg( LOG_ERR, func, "service = %s, open of banner %s failed", SVC_ID( sp ), SC_BANNER_FAIL(scp)); return(-1); } while( (retval = read(bannerfd, tmpbuf, sizeof(tmpbuf))) ) { if (retval == -1) { if (errno == EINTR) continue; else { msg(LOG_ERR, func, "service %s, Error %m reading banner %s", SVC_ID( sp ), SC_BANNER(scp)); break; } } Swrite(cp->co_descriptor, tmpbuf, retval); } Sclose(bannerfd); Sflush ( cp->co_descriptor ); } return(0); }
void conn_dump( const connection_s *cp, int fd ) { const char *name = conn_addrstr( cp ); tabprint( fd, 1, "service = %s\n", SVC_ID( cp->co_sp ) ) ; tabprint( fd, 1, "descriptor = %d\n", cp->co_descriptor ) ; #if defined(__GNUC__) && !defined(__arch64__) && !defined(__alpha__) tabprint( fd, 1, "flags = %#llx\n", cp->co_flags ) ; #else tabprint( fd, 1, "flags = %#lx\n", cp->co_flags ) ; #endif tabprint( fd, 1, "remote_address = %s,%d\n", name, ntohs( cp->co_remote_address.sa_in.sin_port ) ) ; }
/* * Check for reference counting errors. * Returns number of errors found. * Always set the number of running and retry servers. */ static unsigned refcount_check( struct service *sp, unsigned *running_servers, unsigned *retry_servers ) { char *sid = SVC_ID( sp ) ; unsigned errors = 0 ; int refs ; int refcount = SVC_REFCOUNT( sp ) ; const char *func = "refcount_check" ; if ( refcount <= 0 ) { msg( LOG_ERR, func, "%s service has bad refcount: %d", sid, refcount ) ; errors++ ; } /* * The service table holds a reference to the service. The remaining * references must be from servers and connections. */ refcount-- ; refs = count_refs( sp, SERVERS( ps ), running_servers ) ; if ( ! errors && refs > refcount ) { msg( LOG_ERR, func, "running servers: too many references for %s (%d with max=%d)", sid, refs, refcount ) ; errors++ ; } refs = count_refs( sp, RETRIES( ps ), retry_servers ) ; if ( ! errors && refs > refcount ) { msg( LOG_ERR, func, "retry servers: too many references for %s (%d with max=%d)", sid, refs, refcount ) ; errors++ ; } if ( errors && debug.on ) msg( LOG_DEBUG, func, "%s: %d errors detected", sid, errors ) ; return( errors ) ; }
/* * Invoked when a server of the specified service dies */ void svc_postmortem( struct service *sp, struct server *serp ) { struct service *co_sp = SERVER_CONNSERVICE( serp ) ; connection_s *cp = SERVER_CONNECTION( serp ) ; const char *func = "svc_postmortem" ; SVC_DEC_RUNNING_SERVERS( sp ) ; /* * Log information about the server that died */ if ( SVC_IS_LOGGING( sp ) ) { if ( SERVER_WRITES_TO_LOG(serp) ) { if ( debug.on ) msg( LOG_DEBUG, func, "Checking log size of %s service", SVC_ID( sp ) ) ; xlog_control( SVC_LOG( sp ), XLOG_SIZECHECK ) ; } svc_log_exit( sp, serp ) ; } /* * Now check if we have to check the log size of the service that owns * the connection */ if ( co_sp != sp && SVC_IS_LOGGING( co_sp ) ) xlog_control( SVC_LOG( co_sp ), XLOG_SIZECHECK ) ; if (!SVC_WAITS(sp)) { conn_free( cp, 1 ) ; cp = NULL; } else { if (cp) { if ( SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM ) drain( cp->co_descriptor ) ; free(cp); cp = NULL; if( SVC_RELE( sp ) == 0 ) svc_release( sp ); /* shouldn't be 0, but should remove from * pset if it is... */ } svc_resume(sp); } }
/* * If the service is single-threaded: * if the descriptor is set in the socket mask, there must * be a server running (or to be retried) * If the service is multi-threaded: * the descriptor must be always set */ static unsigned thread_check( struct service *sp, unsigned running_servers, unsigned retry_servers ) { unsigned error_count = 0 ; int sd = SVC_FD( sp ) ; char *sid = SVC_ID( sp ) ; const char *func = "thread_check" ; if ( SVC_WAITS( sp ) ) { bool_int has_servers = ( running_servers + retry_servers != 0 ) ; if ( has_servers && FD_ISSET( sd, &ps.rws.socket_mask ) ) { msg( LOG_ERR, func, "Active single-threaded service %s: server running, descriptor set", sid ) ; error_count++ ; } if ( !has_servers && !FD_ISSET( sd, &ps.rws.socket_mask ) ) { msg( LOG_ERR, func, "Active single-threaded service %s: no server running, descriptor not set", sid ) ; error_count++ ; } } else if ( ! FD_ISSET( sd, &ps.rws.socket_mask ) ) { msg( LOG_ERR, func, "Active multi-threaded service %s: descriptor not set", sid ) ; error_count++ ; } if ( error_count && debug.on ) msg( LOG_DEBUG, func, "%s: %d errors detected", sid, error_count ) ; return( error_count ) ; }
static void consistency_check( enum check_type type ) { int fd ; fd_set socket_mask_copy ; unsigned u ; int errors ; unsigned total_running_servers = 0 ; unsigned total_retry_servers = 0 ; unsigned error_count = 0 ; bool_int service_count_check_failed = FALSE ; const char *func = "consistency_check" ; socket_mask_copy = ps.rws.socket_mask ; for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { register struct service *sp = SP( pset_pointer( SERVICES( ps ), u ) ) ; char *sid = SVC_ID( sp ) ; unsigned running_servers ; unsigned retry_servers ; error_count += refcount_check( sp, &running_servers, &retry_servers ) ; if ( SVC_IS_AVAILABLE( sp ) || SVC_IS_DISABLED ( sp ) ) { /* * In this case, there may be some servers running */ if ( FD_ISSET( SVC_FD( sp ), &socket_mask_copy ) ) { if ( SVC_IS_DISABLED( sp ) ) { msg( LOG_ERR, func, "fd of disabled service %s still in socket mask", sid ) ; error_count++ ; } FD_CLR( SVC_FD( sp ), &socket_mask_copy ) ; } error_count += thread_check( sp, running_servers, retry_servers ) ; errors = service_count_check( sp, running_servers, retry_servers ) ; if ( ! errors && ! service_count_check_failed ) { total_retry_servers += retry_servers ; total_running_servers += running_servers ; } if ( errors ) { service_count_check_failed = TRUE ; error_count += errors ; } if ( SVC_IS_DISABLED( sp ) && SVC_RUNNING_SERVERS( sp ) == 0 ) { msg( LOG_ERR, func, "disabled service %s has 0 running servers\n", sid ) ; error_count++ ; continue ; } } else { msg( LOG_ERR, func, "service %s not started", SVC_ID( sp ) ) ; error_count++ ; } } if ( ! service_count_check_failed ) { if ( total_running_servers != pset_count( SERVERS( ps ) ) ) { msg( LOG_ERR, func, "total running servers (%d) != number of running servers (%d)", total_running_servers, pset_count( SERVERS( ps ) ) ) ; error_count++ ; } if ( total_retry_servers != pset_count( RETRIES( ps ) ) ) { msg( LOG_ERR, func, "total retry servers (%d) != number of retry servers (%d)", total_retry_servers, pset_count( RETRIES( ps ) ) ) ; error_count++ ; } } /* * Check if there are any descriptors set in socket_mask_copy */ for ( fd = 0 ; fd < ps.ros.max_descriptors ; fd++ ) if ( FD_ISSET( fd, &socket_mask_copy ) && ((fd != signals_pending[0]) && fd != signals_pending[1])) { msg( LOG_ERR, func, "descriptor %d set in socket mask but there is no service for it", fd ) ; error_count++ ; } if ( error_count != 0 ) msg( LOG_WARNING, func, "Consistency check detected %d errors", error_count ) ; else if ( type == USER_REQUESTED || debug.on ) msg( LOG_INFO, func, "Consistency check passed" ) ; if( type == PERIODIC ) if ( xtimer_add( periodic_check, ps.ros.cc_interval ) == -1 ) msg( LOG_ERR, func, "Failed to start consistency timer" ) ; }
/* * Get a new connection request and initialize 'cp' appropriately */ static status_e get_connection( struct service *sp, connection_s *cp ) { struct service_config *scp = SVC_CONF( sp ); socklen_t sin_len; const char *func = "get_connection" ; int on = 1; if( SC_IPV4(scp) ) sin_len = sizeof(struct sockaddr_in); if( SC_IPV6(scp) ) sin_len = sizeof(struct sockaddr_in6); if ( SVC_SOCKET_TYPE( sp ) == SOCK_STREAM ) { /* If it's a TCP socket, and we're set to wait, the accept is * done by the child process. Don't set NEW_DESCRIPTOR, since * there isn't one. The descriptor will be/was removed from * the descriptor set in svc_suspend and re-enabled in svc_resume. */ if( SC_WAITS( scp ) ) { cp->co_descriptor = SVC_FD( sp ); } else { cp->co_descriptor = accept( SVC_FD( sp ), &(cp->co_remote_address.sa), &sin_len ) ; if (cp->co_descriptor != -1) M_SET( cp->co_flags, COF_NEW_DESCRIPTOR ) ; } if ( cp->co_descriptor == -1 ) { if ((errno == EMFILE) || (errno == ENFILE)) cps_service_stop(sp, "no available descriptors"); else msg( LOG_ERR, func, "service %s, accept: %m", SVC_ID( sp ) ) ; return( FAILED ) ; } if( SC_NODELAY( scp ) && (SC_PROTOVAL( scp ) == IPPROTO_TCP) ) if( setsockopt(SVC_FD(sp), IPPROTO_TCP, TCP_NODELAY, (char *)&on, sizeof( on ) ) < 0 ) msg( LOG_WARNING, func, "service %s, setsockopt: %m", SVC_ID(sp)); if( SC_KEEPALIVE( scp ) && (SC_PROTOVAL( scp ) == IPPROTO_TCP) ) { if( setsockopt(SVC_FD(sp), SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof( on ) ) < 0 ) msg( LOG_WARNING, func, "service %s, setsockopt: %m", SVC_ID(sp)); } if( SC_IPV6(scp) && !(SC_V6ONLY( scp )) && (IN6_IS_ADDR_V4MAPPED(&cp->co_remote_address.sa_in6.sin6_addr) || IN6_IS_ADDR_V4COMPAT(&cp->co_remote_address.sa_in6.sin6_addr)) ) { int af = AF_INET; if( setsockopt(cp->co_descriptor, IPPROTO_IPV6, IPV6_ADDRFORM, &af, sizeof( af ) ) ) { if( debug.on ) msg( LOG_WARNING, func, "service %s, IPV6_ADDRFORM setsockopt() failed: %m", SVC_ID( sp) ); } } M_SET( cp->co_flags, COF_HAVE_ADDRESS ) ; } else { if ( SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM ) { char t_ch ; ssize_t val; /* * This trick is done to get the remote address. * select(2) guaranteed that we won't block on the recvfrom */ val = recvfrom( SVC_FD( sp ), &t_ch, 1, MSG_PEEK, &cp->co_remote_address.sa, &sin_len ); if ( val == (ssize_t)-1 ) { msg( LOG_ERR, func, "service %s, recvfrom: %m", SVC_ID( sp ) ) ; return( FAILED ) ; } M_SET( cp->co_flags, COF_HAVE_ADDRESS ) ; } cp->co_descriptor = SVC_FD( sp ) ; } return( OK ) ; }