static void signal_plugins(int sig) { lnode *conf; plist_first(&plugin_conf); conf = plist_get_cur(&plugin_conf); while (conf) { if (conf->p && conf->p->pid) kill(conf->p->pid, sig); conf = plist_next(&plugin_conf); } }
/* unlocked internal variant */ static inline int pm_qos_get_value(struct pm_qos_constraints *c) { if (plist_head_empty(&c->list)) return c->default_value; switch (c->type) { case PM_QOS_MIN: return plist_first(&c->list)->prio; case PM_QOS_MAX: return plist_last(&c->list)->prio; default: /* runtime check for not using enum */ BUG(); } }
static int start_plugins(conf_llist *plugin) { /* spawn children */ lnode *conf; int active = 0; plist_first(plugin); conf = plist_get_cur(plugin); if (conf == NULL || conf->p == NULL) return active; do { if (conf->p && conf->p->active == A_YES) { if (start_one_plugin(conf)) active++; } } while ((conf = plist_next(plugin))); return active; }
/* * SIGCHLD handler */ static void child_handler( int sig ) { int status; pid_t pid; pid = waitpid(-1, &status, WNOHANG); if (pid > 0) { // Mark the child pid as 0 in the configs lnode *tpconf; plist_first(&plugin_conf); tpconf = plist_get_cur(&plugin_conf); while (tpconf) { if (tpconf->p && tpconf->p->pid == pid) { tpconf->p->pid = 0; break; } tpconf = plist_next(&plugin_conf); } } }
/* Returns 0 on stop, and 1 on HUP */ static int event_loop(void) { char *name = NULL, tmp_name[255]; /* Get the host name representation */ switch (daemon_config.node_name_format) { case N_NONE: break; case N_HOSTNAME: if (gethostname(tmp_name, sizeof(tmp_name))) { syslog(LOG_ERR, "Unable to get machine name"); name = strdup("?"); } else name = strdup(tmp_name); break; case N_USER: if (daemon_config.name) name = strdup(daemon_config.name); else { syslog(LOG_ERR, "User defined name missing"); name = strdup("?"); } break; case N_FQD: if (gethostname(tmp_name, sizeof(tmp_name))) { syslog(LOG_ERR, "Unable to get machine name"); name = strdup("?"); } else { int rc; struct addrinfo *ai; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; hints.ai_socktype = SOCK_STREAM; rc = getaddrinfo(tmp_name, NULL, &hints, &ai); if (rc != 0) { syslog(LOG_ERR, "Cannot resolve hostname %s (%s)", tmp_name, gai_strerror(rc)); name = strdup("?"); break; } name = strdup(ai->ai_canonname); freeaddrinfo(ai); } break; case N_NUMERIC: if (gethostname(tmp_name, sizeof(tmp_name))) { syslog(LOG_ERR, "Unable to get machine name"); name = strdup("?"); } else { int rc; struct addrinfo *ai; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; rc = getaddrinfo(tmp_name, NULL, &hints, &ai); if (rc != 0) { syslog(LOG_ERR, "Cannot resolve hostname %s (%s)", tmp_name, gai_strerror(rc)); name = strdup("?"); break; } inet_ntop(ai->ai_family, ai->ai_family == AF_INET ? (void *) &((struct sockaddr_in *)ai->ai_addr)->sin_addr : (void *) &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, tmp_name, INET6_ADDRSTRLEN); freeaddrinfo(ai); name = strdup(tmp_name); } break; } /* Figure out the format for the af_unix socket */ while (stop == 0) { event_t *e; const char *type; char *v, *ptr, unknown[32]; unsigned int len; lnode *conf; /* This is where we block until we have an event */ e = dequeue(); if (e == NULL) { if (hup) { free(name); return 1; } continue; } /* Get the event formatted */ type = audit_msg_type_to_name(e->hdr.type); if (type == NULL) { snprintf(unknown, sizeof(unknown), "UNKNOWN[%d]", e->hdr.type); type = unknown; } if (daemon_config.node_name_format != N_NONE) { len = asprintf(&v, "node=%s type=%s msg=%.*s\n", name, type, e->hdr.size, e->data); } else len = asprintf(&v, "type=%s msg=%.*s\n", type, e->hdr.size, e->data); if (len <= 0) { free(e); /* Either corrupted event or no memory */ continue; } /* Strip newlines from event record */ ptr = v; while ((ptr = strchr(ptr, 0x0A)) != NULL) { if (ptr != &v[len-1]) *ptr = ' '; else break; /* Done - exit loop */ } /* Distribute event to the plugins */ plist_first(&plugin_conf); conf = plist_get_cur(&plugin_conf); do { if (conf == NULL || conf->p == NULL) continue; if (conf->p->active == A_NO) continue; /* Now send the event to the right child */ if (conf->p->type == S_SYSLOG) send_syslog(v); else if (conf->p->type == S_AF_UNIX) { if (conf->p->format == F_STRING) send_af_unix_string(v, len); else send_af_unix_binary(e); } else if (conf->p->type == S_ALWAYS) { int rc; rc = write_to_plugin(e, v, len, conf); if (rc < 0 && errno == EPIPE) { /* Child disappeared ? */ syslog(LOG_ERR, "plugin %s terminated unexpectedly", conf->p->path); conf->p->pid = 0; conf->p->restart_cnt++; if (conf->p->restart_cnt > daemon_config.max_restarts) { syslog(LOG_ERR, "plugin %s has exceeded max_restarts", conf->p->path); } close(conf->p->plug_pipe[1]); conf->p->plug_pipe[1] = -1; conf->p->active = A_NO; if (start_one_plugin(conf)) { rc = write_to_plugin(e, v, len, conf); syslog(LOG_NOTICE, "plugin %s was restarted", conf->p->path); conf->p->active = A_YES; } } } } while ((conf = plist_next(&plugin_conf))); /* Done with the memory...release it */ free(v); free(e); if (hup) break; } free(name); if (stop) return 0; else return 1; }
int main(int argc, char *argv[]) { lnode *conf; struct sigaction sa; int i; #ifndef DEBUG /* Make sure we are root */ if (getuid() != 0) { fprintf(stderr, "You must be root to run this program.\n"); return 4; } #endif set_aumessage_mode(MSG_SYSLOG, DBG_YES); /* Register sighandlers */ sa.sa_flags = 0; sigemptyset(&sa.sa_mask); /* Ignore all signals by default */ sa.sa_handler = SIG_IGN; for (i=1; i<NSIG; i++) sigaction(i, &sa, NULL); /* Set handler for the ones we care about */ sa.sa_handler = term_handler; sigaction(SIGTERM, &sa, NULL); sa.sa_handler = hup_handler; sigaction(SIGHUP, &sa, NULL); sa.sa_handler = alarm_handler; sigaction(SIGALRM, &sa, NULL); sa.sa_handler = child_handler; sigaction(SIGCHLD, &sa, NULL); /* move stdin to its own fd */ if (argc == 3 && strcmp(argv[1], "--input") == 0) audit_fd = open(argv[2], O_RDONLY); else audit_fd = dup(0); if (audit_fd < 0) { syslog(LOG_ERR, "Failed setting up input, exiting"); return 1; } /* Make all descriptors point to dev null */ i = open("/dev/null", O_RDWR); if (i >= 0) { if (dup2(0, i) < 0 || dup2(1, i) < 0 || dup2(2, i) < 0) { syslog(LOG_ERR, "Failed duping /dev/null %s, exiting", strerror(errno)); return 1; } close(i); } else { syslog(LOG_ERR, "Failed opening /dev/null %s, exiting", strerror(errno)); return 1; } if (fcntl(audit_fd, F_SETFD, FD_CLOEXEC) < 0) { syslog(LOG_ERR, "Failed protecting input %s, exiting", strerror(errno)); return 1; } /* init the daemon's config */ if (load_config(&daemon_config, config_file)) return 6; load_plugin_conf(&plugin_conf); /* if no plugins - exit */ if (plist_count(&plugin_conf) == 0) { syslog(LOG_ERR, "No plugins found, exiting"); return 0; } /* Plugins are started with the auditd priority */ i = start_plugins(&plugin_conf); /* Now boost priority to make sure we are getting time slices */ if (daemon_config.priority_boost != 0) { errno = 0; (void) nice((int)-daemon_config.priority_boost); if (errno) { syslog(LOG_ERR, "Cannot change priority (%s)", strerror(errno)); /* Stay alive as this is better than stopping */ } } /* Let the queue initialize */ init_queue(daemon_config.q_depth); syslog(LOG_NOTICE, "audispd initialized with q_depth=%d and %d active plugins", daemon_config.q_depth, i); /* Tell it to poll the audit fd */ if (add_event(audit_fd, process_inbound_event) < 0) { syslog(LOG_ERR, "Cannot add event, exiting"); return 1; } /* Create inbound thread */ pthread_create(&inbound_thread, NULL, inbound_thread_main, NULL); /* Start event loop */ while (event_loop()) { hup = 0; reconfigure(); } /* Tell plugins we are going down */ signal_plugins(SIGTERM); /* Cleanup builtin plugins */ destroy_af_unix(); destroy_syslog(); /* Give it 5 seconds to clear the queue */ alarm(5); pthread_join(inbound_thread, NULL); /* Release configs */ plist_first(&plugin_conf); conf = plist_get_cur(&plugin_conf); while (conf) { free_pconfig(conf->p); conf = plist_next(&plugin_conf); } plist_clear(&plugin_conf); /* Cleanup the queue */ destroy_queue(); free_config(&daemon_config); return 0; }
static void reconfigure(void) { int rc; daemon_conf_t tdc; conf_llist tmp_plugin; lnode *tpconf; /* Read new daemon config */ rc = load_config(&tdc, config_file); if (rc == 0) { if (tdc.q_depth > daemon_config.q_depth) { increase_queue_depth(tdc.q_depth); daemon_config.q_depth = tdc.q_depth; } daemon_config.overflow_action = tdc.overflow_action; reset_suspended(); /* We just fill these in because they are used by this * same thread when we return */ daemon_config.node_name_format = tdc.node_name_format; free((char *)daemon_config.name); daemon_config.name = tdc.name; } /* The idea for handling SIGHUP to children goes like this: * 1) load the current config in temp list * 2) mark all in real list unchecked * 3) for each one in tmp list, scan old list * 4) if new, start it, append to list, mark done * 5) else check if there was a change to active state * 6) if so, copy config over and start * 7) If no change, send sighup to non-builtins and mark done * 8) Finally, scan real list for unchecked, terminate and deactivate */ load_plugin_conf(&tmp_plugin); plist_mark_all_unchecked(&plugin_conf); plist_first(&tmp_plugin); tpconf = plist_get_cur(&tmp_plugin); while (tpconf && tpconf->p) { lnode *opconf; opconf = plist_find_name(&plugin_conf, tpconf->p->name); if (opconf == NULL) { /* We have a new service */ if (tpconf->p->active == A_YES) { tpconf->p->checked = 1; plist_last(&plugin_conf); plist_append(&plugin_conf, tpconf->p); free(tpconf->p); tpconf->p = NULL; start_one_plugin(plist_get_cur(&plugin_conf)); } } else { if (opconf->p->active == tpconf->p->active) { if (opconf->p->type == S_ALWAYS) kill(opconf->p->pid, SIGHUP); opconf->p->checked = 1; } else { /* A change in state */ if (tpconf->p->active == A_YES) { /* starting - copy config and exec */ free_pconfig(opconf->p); free(opconf->p); opconf->p = tpconf->p; opconf->p->checked = 1; start_one_plugin(opconf); tpconf->p = NULL; } } } tpconf = plist_next(&tmp_plugin); } /* Now see what's left over */ while ( (tpconf = plist_find_unchecked(&plugin_conf)) ) { /* Anything not checked is something removed from the config */ tpconf->p->active = A_NO; kill(tpconf->p->pid, SIGTERM); tpconf->p->pid = 0; tpconf->p->checked = 1; } /* Release memory from temp config */ plist_first(&tmp_plugin); tpconf = plist_get_cur(&tmp_plugin); while (tpconf) { free_pconfig(tpconf->p); tpconf = plist_next(&tmp_plugin); } plist_clear(&tmp_plugin); }