int context_resume(Context * ctx, int mode, ContextAddress range_start, ContextAddress range_end) { switch (mode) { case RM_RESUME: return context_continue(ctx); case RM_STEP_INTO: return context_single_step(ctx); case RM_TERMINATE: return context_terminate(ctx); } errno = ERR_UNSUPPORTED; return -1; }
int logger_shutdown(context_t *ctx) { logger_config_t *cf = (logger_config_t *) ctx->data; if( cf->logger ) { context_terminate( cf->logger ); cf->logger = 0L; } if( cf->log_fd >= 0 ) { close( cf->log_fd ); cf->log_fd = -1; } free(cf); return 1; }
ssize_t logger_handler(context_t *ctx, event_t event, driver_data_t *event_data ) { event_data_t *data = 0L; logger_config_t *cf = (logger_config_t *) ctx->data; //x_printf(ctx, "<%s> Event = \"%s\" (%d)\n", ctx->name, event_map[event], event); if( event_data->type == TYPE_DATA ) data = & event_data->event_data; switch( event ) { case EVENT_INIT: if(( cf->log_driver = config_get_item( ctx->config, "logdriver" ) )) { if( strchr( cf->log_driver, '/' ) ) cf->log_fd = open( cf->log_driver, O_RDWR|O_APPEND|O_CREAT, 0777 ); else start_service( &cf->logger, cf->log_driver, ctx->config, ctx, 0L ); } cf->state = LOGGER_STATE_RUNNING; case EVENT_START: break; case EVENT_TERMINATE: context_terminate( ctx ); break; case EVENT_DATA_INCOMING: case EVENT_DATA_OUTGOING: case EVENT_LOGGING: if( data ) { char *logbuffer = alloca( LOG_BUFFER_MAX ); snprintf(logbuffer, LOG_BUFFER_MAX, "%s: %s", event_data->source?event_data->source->name:"(unknown)", (char *)data->data); logbuffer[LOG_BUFFER_MAX-1] = 0; time_t spec; char spec_buffer[32]; if( cf->logger ) { driver_data_t log_event = { TYPE_DATA, .source = ctx, {} }; log_event.event_data.data = logbuffer; log_event.event_data.bytes = strlen(logbuffer); emit(cf->logger, EVENT_DATA_OUTGOING, &log_event); } else {
ssize_t unicorn_handler(context_t *ctx, event_t event, driver_data_t *event_data) { event_data_t *data = 0L; event_child_t *child = 0L; unicorn_config_t *cf = (unicorn_config_t *) ctx->data; //x_printf(ctx, "<%s> Event = \"%s\" (%d)\n", ctx->name, event_map[event], event); if (event_data->type == TYPE_DATA) data = &event_data->event_data; else if( event_data->type == TYPE_CHILD ) child = & event_data->event_child; switch (event) { case EVENT_INIT: { x_printf(ctx,"calling event add SIGQUIT\n"); event_add( ctx, SIGQUIT, EH_SIGNAL ); x_printf(ctx,"calling event add SIGTERM\n"); event_add( ctx, SIGTERM, EH_SIGNAL ); x_printf(ctx,"calling event 1000 EH_WANT_TICK\n"); event_add( ctx, 1000, EH_WANT_TICK ); cf->driver = config_get_item( ctx->config, "endpoint" ); if( ! config_get_timeval( ctx->config, "retry", &cf->retry_time ) ) cf->retry_time = 120*1000; if( cf->driver ) start_service( &cf->modem, cf->driver, ctx->config, ctx, 0L ); if( !cf->modem ) { logger( ctx, "Unable to launch modem driver. Exiting\n" ); cf->state = UNICORN_STATE_ERROR; context_terminate( ctx ); return -1; } cf->state = UNICORN_STATE_IDLE; } break; case EVENT_TERMINATE: { cf->pending_action_timeout = rel_time(0L); cf->flags |= UNICORN_TERMINATING; // In process of terminating the modem driver cf->state = UNICORN_STATE_STOPPING; // In process of terminating self // Ensure 'exec' driver known not to restart when the modem driver terminates. // If the modem driver is something other than 'exec', this should be ignored. uint8_t flag = 0; driver_data_t notification = { TYPE_CUSTOM, ctx, {} }; notification.event_custom = &flag; emit(cf->modem, EXEC_SET_RESPAWN, ¬ification); if( cf->driver_state == CMD_ST_ONLINE ) { x_printf(ctx,"Driver is online - sending disconnect\n"); send_unicorn_command( ctx, CMD_DISCONNECT, CMD_ST_OFFLINE, 0, 0L ); } else { x_printf(ctx,"Driver is offline - sending shutdown\n"); send_unicorn_command( ctx, CMD_STATE, CMD_ST_OFFLINE, 0, 0L ); } } break; case EVENT_RESTART: // This event is used to signal that the modem driver needs to resync. // set the 'reconnecting' flag and send a disconnect x_printf(ctx,"EVENT_RESTART: - sending disconnect to modem\n"); //logger(ctx, "Sending disconnect command to modem driver"); if( event_data->source == ctx->owner ) { cf->pending_action_timeout = rel_time(0L); cf->flags |= UNICORN_WAITING_FOR_CONNECT; if( cf->modem ) { x_printf(ctx, "Sending CMD_DISCONNECT to modem driver (%s)\n",cf->modem->name); if( (event_data->type == TYPE_CUSTOM) && event_data->event_custom ) { logger(ctx, "Sending abort command to modem driver due to unexpected disconnect"); send_unicorn_command( ctx, CMD_ABORT, CMD_ST_OFFLINE, 0, 0L ); } else { logger(ctx, "Sending disconnect command to modem driver"); send_unicorn_command( ctx, CMD_DISCONNECT, CMD_ST_OFFLINE, 0, 0L ); } } else { x_printf(ctx, "Modem driver not running.. doing nothing.\n"); } } else { x_printf(ctx,"Forwarding EVENT_RESTART to owner (%s)\n",ctx->name); emit2( ctx, EVENT_RESTART, event_data); } break; case EVENT_CHILD: x_printf(ctx,"Got a message from a child (%s:%d).. probably starting\n", child->ctx->name, child->action); if ( child->ctx == cf->modem ) { if( child->action == CHILD_STARTING ) { cf->state = UNICORN_STATE_RUNNING; cf->flags &= ~(unsigned int) UNICORN_RESTARTING; // Assume ensure the first time the modem driver starts it skips the connection delay cf->flags |= UNICORN_FIRST_START; } if ( child->action == CHILD_STOPPED ) { x_printf(ctx,"Modem driver terminated - restart or terminate\n"); // modem driver terminated. Restart or exit. cf->state = UNICORN_STATE_IDLE; if ( cf->flags & UNICORN_TERMINATING ) { x_printf(ctx,"Terminating immediately\n"); context_terminate( ctx ); } else { x_printf(ctx,"Need to restart modem driver\n"); cf->flags |= UNICORN_RESTARTING; cf->pending_action_timeout = rel_time(0L); // Reset the driver state, and notify the parent that we are offline cf->driver_state = CMD_ST_UNKNOWN; context_owner_notify( ctx, CHILD_EVENT, UNICORN_MODE_OFFLINE ); } } } break; case EVENT_DATA_INCOMING: case EVENT_DATA_OUTGOING: if( event_data->source == cf->modem ) { size_t bytes = data->bytes; size_t offset = 0; while( bytes ) { size_t to_read = u_ringbuf_avail( &cf->input ); if( to_read > bytes ) to_read = bytes; u_ringbuf_write( &cf->input, &((char *)data->data)[offset], to_read ); bytes -= to_read; offset += to_read; while(process_unicorn_packet(ctx) >= 0); } return (ssize_t) offset; } else { send_unicorn_command( ctx, CMD_DATA, CMD_ST_ONLINE, data->bytes,data->data); return (ssize_t) data->bytes; } break; case EVENT_READ: break; case EVENT_EXCEPTION: break; case EVENT_SIGNAL: x_printf(ctx,"Woa! Got a sign from the gods... %d\n", event_data->event_signal); if( event_data->event_signal == SIGQUIT || event_data->event_signal == SIGTERM ) emit( ctx, EVENT_TERMINATE, 0L ); break; case EVENT_TICK: { time_t now = rel_time(0L); // Handle case where a massive time shift due to NTP resync causes all timeouts to fire simultaneously // This is technically deprecated due to the use of rel_time() if( (now - cf->last_message) > MAXIMUM_SAFE_TIMEDELTA ) { logger(ctx, "WARNING: Resetting timeout due to RTC time change"); cf->last_message = now; } if( ((now - cf->last_message) > UNICORN_KEEPALIVE_TIMEOUT ) && ( cf->driver_state != CMD_ST_UNKNOWN )) { if( ~ cf->flags & UNICORN_LAGGED ) { // Its been a couple of minutes since the last keepalive, reset the driver_state // to unknown and prompt for one. logger(ctx,"Forcing connection state request due to communications timeout.\n"); cf->flags |= UNICORN_LAGGED; cf->retry_count = UNICORN_KEEPALIVE_RETRY_MAX; } if( cf->retry_count ) { send_unicorn_command( ctx, CMD_STATE, CMD_ST_OFFLINE, 0, 0L ); cf->retry_count --; } else { // Its been a long time since the last message, despite prompting for one // restart the modem driver logger(ctx, "Communications timeout. Restarting modem driver."); uint8_t sig = SIGHUP; driver_data_t notification = { TYPE_CUSTOM, ctx, {} }; notification.event_custom = &sig; emit( cf->modem, EVENT_RESTART, ¬ification ); } cf->last_message = now; } if( (cf->flags & UNICORN_RESTARTING) && ((now - cf->pending_action_timeout) > UNICORN_RESTART_DELAY )) { x_printf(ctx,"Restart delay expired - restarting modem driver\n"); cf->pending_action_timeout = rel_time(0L); if( cf->driver ) start_service( &cf->modem, cf->driver, ctx->config, ctx, 0L ); } else if( (cf->flags & UNICORN_RECONNECTING) && ((now - cf->pending_action_timeout) > cf->retry_time )) { x_printf(ctx,"Reconnect delay expired - attempting reconnect\n"); cf->pending_action_timeout = rel_time(0L); cf->flags &= ~(unsigned int)UNICORN_RECONNECTING; if( cf->modem ) send_unicorn_command(ctx, CMD_CONNECT, CMD_ST_ONLINE, 0, 0 ); } else if( (cf->flags & UNICORN_WAITING_FOR_CONNECT) && ((now - cf->pending_action_timeout) > UNICORN_CONNECT_TIMEOUT )) { x_printf(ctx,"Timeout during connect - terminating modem driver\n"); cf->flags &= ~(unsigned int) UNICORN_WAITING_FOR_CONNECT; cf->state = UNICORN_STATE_IDLE; if( cf->modem ) emit( cf->modem, EVENT_TERMINATE, 0L ); } if( (cf->flags & UNICORN_TERMINATING) && ((now - cf->pending_action_timeout) > UNICORN_PROCESS_TERMINATION_TIMEOUT)) { x_printf(ctx,"termination timeout - killing the modem driver with prejudice\n"); cf->state = UNICORN_STATE_IDLE; if( cf->modem ) context_terminate( cf->modem ); context_terminate( ctx ); } // Special case.. If I am expecting a data frame, and it takes too long to arrive, // reset state. if( (cf->flags & UNICORN_EXPECTING_DATA) && ((now - cf->last_message) > FRAME_TIMEOUT)) { x_printf(ctx,"FRAME TIMEOUT - resetting input buffer\n"); u_ringbuf_init( &cf->input ); cf->flags &= ~(unsigned int)UNICORN_EXPECTING_DATA; } #ifndef NDEBUG size_t bytes = u_ringbuf_ready( &cf->input ); if( bytes ) x_printf(ctx,"Un-processed data in ring buffer... %d bytes\n",(int)bytes); #endif } break; default: x_printf(ctx,"\n *\n *\n * Emitted some kind of event \"%s\" (%d)\n *\n *\n", event_map[event], event); } return 0; }
ssize_t syslog_handler(context_t *ctx, event_t event, driver_data_t *event_data) { event_data_t *data = 0L; syslog_config_t *cf = (syslog_config_t *) ctx->data; //x_printf(ctx, "<%s> Event = \"%s\" (%d)\n", ctx->name, event_map[event], event); if( event_data->type == TYPE_DATA ) data = & event_data->event_data; switch (event) { case EVENT_INIT: { const char *options = get_env( ctx, "options" ); const char *facility = get_env( ctx, "facility" ); const char *priority = get_env( ctx, "prio" ); if( options ) { if( (cf->options = syslog_options_lookup( options )) < 0) { fprintf(stderr,"Warning: invalid options specified for syslog %s\n",options ); cf->options = LOG_PID; } } else cf->options = LOG_PID; if( facility ) { if( (cf->facility = syslog_options_lookup( facility )) < 0) { fprintf(stderr,"Warning: invalid syslog facility %s. Defaulting to USER\n",facility); cf->facility = LOG_USER; } } else cf->facility = LOG_USER; if( priority ) { if( (cf->prio = syslog_options_lookup( priority )) < 0) { fprintf(stderr,"Warning: invalid syslog priority %s. Defaulting to NOTICE\n",facility); cf->facility = LOG_NOTICE; } } else cf->facility = LOG_NOTICE; cf->ident = get_env( ctx, "ident" ); if( !cf->ident ) cf->ident = programname; } case EVENT_START: openlog( cf->ident, cf->options, cf->facility ); break; case EVENT_TERMINATE: context_terminate(ctx); break; case EVENT_DATA_INCOMING: case EVENT_DATA_OUTGOING: case EVENT_LOGGING: syslog( cf->prio, "%s", (char *) data->data ); break; default: break; } return 0; }
ssize_t dns_handler(context_t *ctx, event_t event, driver_data_t *event_data ) { dns_config_t *cf = (dns_config_t *) ctx->data; x_printf(ctx, "<%s> Event = \"%s\" (%d)\n", ctx->name, event_map[event], event); switch( event ) { case EVENT_INIT: if( event_data->type == TYPE_CUSTOM && event_data->event_custom ) { dns_conf_t *init = (dns_conf_t *) (event_data->event_custom); dns_load_servers( ctx, init->dns_resolver ); cf->dns_timeout = init->dns_timeout; cf->current_host = strdup( init->dns_host ); } else { const char *resolver = config_get_item( ctx->config, "resolver" ); const char *host = config_get_item( ctx->config, "host" ); if( ! config_get_timeval( ctx->config, "timeout", & cf->dns_timeout )) cf->dns_timeout = DNS_DEFAULT_TIMEOUT; if( ! config_get_intval( ctx->config, "retry", & cf->dns_max_retry )) cf->dns_max_retry = 5; dns_load_servers(ctx, resolver); cf->current_host = strdup( host ); if( cf->dns_max_retry < cf->dns_max_servers ) cf->dns_max_retry = cf->dns_max_servers; } cf->sock_fd = dns_resolve_host( ctx, cf->current_host ); if( cf->sock_fd >= 0 ) { cf->dns_retries = cf->dns_max_retry ; cf->dns_timer = event_alarm_add( ctx, cf->dns_timeout, ALARM_TIMER ); event_add( ctx, cf->sock_fd, EH_READ ); x_printf(ctx,"attempting to resolve hostname %s (%d attempts)\n",cf->current_host, cf->dns_retries ); } else { x_printf(ctx,"Failed to send query to socket...\n"); } case EVENT_START: cf->state = DNS_STATE_RUNNING; break; case EVENT_TERMINATE: context_terminate( ctx ); break; case EVENT_ALARM: { if( event_data->event_alarm == cf->dns_timer ) { #ifdef ENABLE_GETADDRINFO // This is entirely pointless, because it can't block!! if( (cf->flags & DNS_NETWORK_UP) && (cf->flags & DNS_DNS_ENABLE) ) { x_printf(ctx,"Attempting DNS resolver check for %s\n",cf->dns_host); struct addrinfo *addrinfo = NULL; int rc = getaddrinfo( cf->dns_host, NULL, NULL, &addrinfo ); if( rc == 0 ) { x_printf(ctx,"Name resolver completed..\n"); freeaddrinfo( addrinfo ); } else { x_printf(ctx,"Failure performing DNS name resolution. Disconnecting network\n"); logger(ctx,"Failure performing DNS name resolution. Disconnecting network"); } } #endif x_printf(ctx,"TIMEOUT: dns failed to respond, trying next server\n"); if( cf->sock_fd >= 0 ) { close( cf->sock_fd ); cf->sock_fd = -1; event_delete( ctx, cf->sock_fd, EH_NONE ); } if( cf->dns_retries-- > 0 ) { cf->dns_current = ( cf->dns_current + 1 ) % cf->dns_max_servers; cf->sock_fd = dns_resolve_host( ctx, cf->current_host ); if( cf->sock_fd >= 0 ) { cf->dns_timer = event_alarm_add( ctx, cf->dns_timeout, ALARM_TIMER ); event_add( ctx, cf->sock_fd, EH_READ ); } } else { x_printf(ctx,"DNS RETRIES EXHAUSTED. Terminating\n"); context_owner_notify( ctx, CHILD_EVENT, 0 ); context_terminate( ctx ); } } } break; case EVENT_READ: if( event_data->event_request.fd == cf->sock_fd ) { x_printf(ctx,"Got a read event on file descriptor %ld\n",event_data->event_request.fd); in_addr_t rc = dns_handle_dns_response( ctx, &( event_data->event_request )); x_printf(ctx, "handle response returned %d (0x%08x) (%s)\n", ntohl(rc), ntohl(rc), inet_ntoa( *(struct in_addr *) &rc )); if( rc == (unsigned long) -1 ) { x_printf(ctx,"Error reading from socket, skipping\n"); } else { event_alarm_delete( ctx, cf->dns_timer ); event_delete( ctx, cf->sock_fd, EH_NONE ); close( cf->sock_fd ); cf->sock_fd = -1; context_owner_notify( ctx, CHILD_EVENT, rc ); context_terminate( ctx ); } } break; default: x_printf(ctx,"\n *\n *\n * Emitted some kind of event \"%s\" (%d)\n *\n *\n", event_map[event], event); } return 0; }