Exemplo n.º 1
0
int main(int argc, char *argv[])
{
	struct daemon_conf config;
	struct rlimit limit;
	int rc;

	/* Check params and build regexpr */
	setlocale (LC_ALL, "");
	if (check_params(argc, argv))
		return 1;

	/* Raise the rlimits in case we're being started from a shell
	* with restrictions. Not a fatal error.  */
	limit.rlim_cur = RLIM_INFINITY;
	limit.rlim_max = RLIM_INFINITY;
	setrlimit(RLIMIT_FSIZE, &limit);
	setrlimit(RLIMIT_CPU, &limit);
	set_aumessage_mode(MSG_STDERR, DBG_NO);
	(void) umask( umask( 077 ) | 027 );
	very_first_event.sec = 0;
	reset_counters();

	if (user_file == NULL) {
		/* Load config so we know where logs are */
        	if (load_config(&config, TEST_SEARCH))
			fprintf(stderr, 
				"NOTE - using built-in logs: %s\n",
				config.log_file);
	} else {
		config.sender_ctx = NULL;
		config.log_file = NULL;
		config.dispatcher = NULL;
		config.node_name = NULL;
		config.space_left_exe = NULL;
		config.action_mail_acct = NULL;
		config.admin_space_left_exe = NULL;
		config.disk_full_exe = NULL;
		config.disk_error_exe = NULL;
		config.krb5_principal = NULL;
		config.krb5_key_file = NULL;
	}
		
	print_title();
	lol_create(&lo);
	if (user_file)
		rc = process_file(user_file);
	else if (force_logs)
		rc = process_logs(&config);
	else if (is_pipe(0))
		rc = process_stdin();
	else
		rc = process_logs(&config);
	lol_clear(&lo);
	if (rc) {
		free_config(&config); 
		return rc;
	}

	if (!found && report_detail == D_DETAILED && report_type != RPT_TIME) {
		printf("<no events of interest were found>\n\n");
		destroy_counters();
		aulookup_destroy_uid_list();
		aulookup_destroy_gid_list();
		free_config(&config); 
		return 1;
	} else 
		print_wrap_up();
	destroy_counters();
	aulookup_destroy_uid_list();
	aulookup_destroy_gid_list();
	free_config(&config); 
	free(user_file);
	return 0;
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
0
static int setup_log_file_array(auparse_state_t *au)
{
        struct daemon_conf config;
        char *filename, **tmp;
        int len, num = 0, i = 0;

        /* Load config so we know where logs are */
	set_aumessage_mode(au, MSG_STDERR, DBG_NO);
	aup_load_config(au, &config, TEST_SEARCH);

	/* for each file */
	len = strlen(config.log_file) + 16;
	filename = malloc(len);
	if (!filename) {
		fprintf(stderr, "No memory\n");
		free_config(&config);
		return 1;
	}
	/* Find oldest log file */
	snprintf(filename, len, "%s", config.log_file);
	do {
		if (access(filename, R_OK) != 0)
			break;
		num++;
		snprintf(filename, len, "%s.%d", config.log_file, num);
	} while (1);

	if (num == 0) {
		fprintf(stderr, "No log file\n");
		free_config(&config);
		free(filename);
		return 1;
	}
	num--;
	tmp = malloc((num+2)*sizeof(char *));

        /* Got it, now process logs from last to first */
	if (num > 0)
		snprintf(filename, len, "%s.%d", config.log_file, num);
	else
		snprintf(filename, len, "%s", config.log_file);
	do {
		tmp[i++] = strdup(filename);

		/* Get next log file */
		num--;
		if (num > 0)
			snprintf(filename, len, "%s.%d", config.log_file, num);
		else if (num == 0)
			snprintf(filename, len, "%s", config.log_file);
		else
			break;
	} while (1);
	free_config(&config);
	free(filename);

	// Terminate the list
	tmp[i] = NULL; 
	au->source_list = tmp;
	return 0;
}
Exemplo n.º 4
0
int main(int argc, char *argv[])
{
	struct rlimit limit;
	int rc;

	/* Check params and build regexpr */
	setlocale (LC_ALL, "");
	if (check_params(argc, argv))
		return 1;

	/* Raise the rlimits in case we're being started from a shell
	* with restrictions. Not a fatal error.  */
	limit.rlim_cur = RLIM_INFINITY;
	limit.rlim_max = RLIM_INFINITY;
	setrlimit(RLIMIT_FSIZE, &limit);
	setrlimit(RLIMIT_CPU, &limit);
	set_aumessage_mode(MSG_STDERR, DBG_NO);
	(void) umask( umask( 077 ) | 027 );
	very_first_event.sec = 0;
	reset_counters();

	print_title();
	lol_create(&lo);
	if (user_file) {
		struct stat sb;
		if (stat(user_file, &sb) == -1) {
			perror("stat");
			return 1;
		} else {
			switch (sb.st_mode & S_IFMT) {
				case S_IFDIR: 
					userfile_is_dir = 1;
					rc = process_logs();
					break;
				case S_IFREG:
				default:
					rc = process_file(user_file);
					break;
			}
		}
	} else if (force_logs)
		rc = process_logs();
	else if (is_pipe(0))
		rc = process_stdin();
	else
		rc = process_logs();
	lol_clear(&lo);
	if (rc)
		return rc;

	if (!found && report_detail == D_DETAILED && report_type != RPT_TIME) {
		printf("<no events of interest were found>\n\n");
		destroy_counters();
		aulookup_destroy_uid_list();
		aulookup_destroy_gid_list();
		return 1;
	} else 
		print_wrap_up();
	destroy_counters();
	aulookup_destroy_uid_list();
	aulookup_destroy_gid_list();
	free(user_file);
	return 0;
}
Exemplo n.º 5
0
/*
 * This function is called after setopt to handle the return code.
 * On entry, status = 0 means just get the reply. Greater than 0 means we
 * are adding or deleting a rule or watch. -1 means an error occurred.
 * -2 means everything is OK and no reply needed. Even if there's an 
 * error, we need to call this routine to close up the audit fd.
 * The return code from this function is 0 success and -1 error.
 */
static int handle_request(int status)
{
	if (status == 0) {
		if (_audit_syscalladded) {
			fprintf(stderr, "Error - no list specified\n");
			return -1;
		}
		get_reply();
	} else if (status == -2)
		status = 0;  // report success 
	else if (status > 0) {
		int rc;
		if (add != AUDIT_FILTER_UNSET) {
			// if !task add syscall any if not specified
			if ((add & AUDIT_FILTER_MASK) != AUDIT_FILTER_TASK && 
					_audit_syscalladded != 1) {
					audit_rule_syscallbyname_data(
							rule_new, "all");
			}
			set_aumessage_mode(MSG_QUIET, DBG_NO);
			rc = audit_add_rule_data(fd, rule_new, add, action);
			set_aumessage_mode(MSG_STDERR, DBG_NO);
			/* Retry for legacy kernels */
			if (rc < 0) {
				if (errno == EINVAL &&
				rule_new->fields[0] == AUDIT_DIR) {
					rule_new->fields[0] = AUDIT_WATCH;
					rc = audit_add_rule_data(fd, rule_new,
							add, action);
				} else {
					fprintf(stderr,
				"Error sending add rule data request (%s)\n",
					errno == EEXIST ?
					"Rule exists" : strerror(-rc));
				}
			}
		}
		else if (del != AUDIT_FILTER_UNSET) {
			if ((del & AUDIT_FILTER_MASK) != AUDIT_FILTER_TASK && 
					_audit_syscalladded != 1) {
					audit_rule_syscallbyname_data(
							rule_new, "all");
			}
			set_aumessage_mode(MSG_QUIET, DBG_NO);
			rc = audit_delete_rule_data(fd, rule_new,
								 del, action);
			set_aumessage_mode(MSG_STDERR, DBG_NO);
			/* Retry for legacy kernels */
			if (rc < 0) {
				if (errno == EINVAL &&
					rule_new->fields[0] == AUDIT_DIR) {
					rule_new->fields[0] = AUDIT_WATCH;
					rc = audit_delete_rule_data(fd,rule_new,
								del, action);
				} else {
					fprintf(stderr,
			       "Error sending delete rule data request (%s)\n",
					errno == EEXIST ?
					"Rule exists" : strerror(-rc));
				}
			}
		} else {
        		usage();
	    		audit_close(fd);
			exit(1);
	    	}
		if (rc <= 0) 
			status = -1;
		else
			status = 0;
	} else 
		status = -1;

	audit_close(fd);
	fd = -1;
	return status;
}
Exemplo n.º 6
0
int main(int argc, char *argv[])
{
	int retval = 1;

	set_aumessage_mode(MSG_STDERR, DBG_NO);

	if (argc == 1) {
		usage();
		return 1;
	}
#ifndef DEBUG
	/* Make sure we are root */
	if (geteuid() != 0) {
		fprintf(stderr, "You must be root to run this program.\n");
		return 4;
	}
#endif
	/* Check where the rules are coming from: commandline or file */
	if ((argc == 3) && (strcmp(argv[1], "-R") == 0)) {
		fd = audit_open();
		if (audit_is_enabled(fd) == 2) {
			fprintf(stderr,
				"The audit system is in immutable "
				"mode, no rule changes allowed\n");
			return 0;
		} else if (errno == ECONNREFUSED) {
			fprintf(stderr, "The audit system is disabled\n");
			return 0;
		} else if (fileopt(argv[2]))
			return 1;
		else {
			if (continue_error < 0)
				return 1;
			return 0;
		}
	} else {
		if (reset_vars()) {
			free(rule_new);
			return 1;
		}
		retval = setopt(argc, 0, argv);
		if (retval == -3) {
			free(rule_new);
			return 0;
		}
	}

	if (add != AUDIT_FILTER_UNSET || del != AUDIT_FILTER_UNSET) {
		fd = audit_open();
		if (audit_is_enabled(fd) == 2) {
			fprintf(stderr,
				"The audit system is in immutable "
				"mode, no rule changes allowed\n");
			free(rule_new);
			return 0;
		} else if (errno == ECONNREFUSED) {
			fprintf(stderr, "The audit system is disabled\n");
			free(rule_new);
			return 0;
		}
	}
	retval = handle_request(retval);
	free(rule_new);
	return retval;
}
Exemplo n.º 7
0
/*
 * Algorithm:
 * check that user is root
 * check to see if program exists
 * if so fork, child waits for parent
 * parent clears audit rules, loads audit all syscalls with child's pid
 * parent tells child to go & waits for sigchld
 * child exec's program
 * parent deletes rules after getting sigchld
 */
int main(int argc, char *argv[])
{
	int fd[2];
	int pid,cmd=1;
	char buf[2];

	if (argc < 2) {
		usage();
		return 1;
	}
	if (strcmp(argv[cmd], "-h") == 0) {
		usage();
		return 1;
	}
	if (strcmp(argv[cmd], "-r") == 0) {
		threat = 1;
		cmd++;
	}
	if (getuid() != 0) {
		fprintf(stderr, "You must be root to run this program.\n");
		return 1;
	}
	if (access(argv[cmd], X_OK)) {
		if (errno == ENOENT)
			fprintf(stderr, "Error - can't find: %s\n", argv[cmd]); 
		else
			fprintf(stderr, "Error checking %s (%s)\n", 
				argv[cmd], strerror(errno));
		return 1;
	}
	set_aumessage_mode(MSG_STDERR, DBG_NO);
	switch (count_rules())
	{
		case -1:
			if (errno == ECONNREFUSED)
		                fprintf(stderr,
					"The audit system is disabled\n");
			else
				fprintf(stderr,
					"Error - can't get rule count.\n");
			return 1;
		case 0:
			break;
		default:
			fprintf(stderr, 
			"autrace cannot be run with rules loaded.\n"
			"Please delete all rules using 'auditctl -D' if you "
			"really wanted to\nrun this command.\n");
			return 1;
	}
	if (pipe(fd) != 0) {
		fprintf(stderr, "Error creating pipe.\n");
		return 1;
	}
	
	switch ((pid=fork()))
	{
		case -1:
			fprintf(stderr, "Error forking.\n");
			return 1;
		case 0: /* Child */
			close(fd[1]);
			printf("Waiting to execute: %s\n", argv[cmd]);
			while (read(fd[0], buf, 1) == -1 && errno == EINTR)
				/* blank */ ;
			close(fd[0]);
			execvp(argv[cmd], &argv[cmd]);
			fprintf(stderr, "Failed to exec %s\n", argv[cmd]);
			return 1;
		default: /* Parent */
			close(fd[0]);
			fcntl(fd[1], F_SETFD, FD_CLOEXEC);
			{
				char field[16];
				int audit_fd;
  				audit_fd = audit_open();
				if (audit_fd < 0)
					exit(1);
				snprintf(field, sizeof(field), "pid=%d", pid);
				if (insert_rule(audit_fd, field)) {
					kill(pid,SIGTERM);
					(void)delete_all_rules(audit_fd);
					exit(1);
				}
				snprintf(field, sizeof(field), "ppid=%d", pid);
				if (insert_rule(audit_fd, field)) {
					kill(pid,SIGTERM);
					(void)delete_all_rules(audit_fd);
					exit(1);
				}
				sleep(1);
				if (write(fd[1],"1", 1) != 1) {
					kill(pid,SIGTERM);
					(void)delete_all_rules(audit_fd);
					exit(1);
				}
				waitpid(pid, NULL, 0);
				close(fd[1]);
				puts("Cleaning up...");
				(void)delete_all_rules(audit_fd);
				close(audit_fd);
			}
			printf("Trace complete. "
				"You can locate the records with "
				"\'ausearch -i -p %d\'\n",
				pid);
			break;
	}

	return 0;
}
Exemplo n.º 8
0
int main(int argc, char *argv[])
{
	struct sigaction sa;
	struct rlimit limit;
	int i, c, rc;
	int opt_foreground = 0, opt_allow_links = 0;
	enum startup_state opt_startup = startup_enable;
	extern char *optarg;
	extern int optind;
	struct ev_loop *loop;
	struct ev_io netlink_watcher;
	struct ev_signal sigterm_watcher;
	struct ev_signal sighup_watcher;
	struct ev_signal sigusr1_watcher;
	struct ev_signal sigusr2_watcher;
	struct ev_signal sigchld_watcher;

	/* Get params && set mode */
	while ((c = getopt(argc, argv, "flns:")) != -1) {
		switch (c) {
		case 'f':
			opt_foreground = 1;
			break;
		case 'l':
			opt_allow_links=1;
			break;
		case 'n':
			do_fork = 0;
			break;
		case 's':
			for (i=0; i<startup_INVALID; i++) {
				if (strncmp(optarg, startup_states[i],
					strlen(optarg)) == 0) {
					opt_startup = i;
					break;
				}
			}
			if (i == startup_INVALID) {
				fprintf(stderr, "unknown startup mode '%s'\n",
					optarg);
				usage();
			}
			break;
		default:
			usage();
		}
	}

	/* check for trailing command line following options */
	if (optind < argc) {
		usage();
	}

	if (opt_allow_links)
		set_allow_links(1);

	if (opt_foreground) {
		config.daemonize = D_FOREGROUND;
		set_aumessage_mode(MSG_STDERR, DBG_YES);
	} else {
		config.daemonize = D_BACKGROUND;
		set_aumessage_mode(MSG_SYSLOG, DBG_NO);
		(void) umask( umask( 077 ) | 022 );
	}

#ifndef DEBUG
	/* Make sure we are root */
	if (getuid() != 0) {
		fprintf(stderr, "You must be root to run this program.\n");
		return 4;
	}
#endif

	/* 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 );

	atexit(clean_exit);

	/* Raise the rlimits in case we're being started from a shell
         * with restrictions. Not a fatal error.  */
	limit.rlim_cur = RLIM_INFINITY;
	limit.rlim_max = RLIM_INFINITY;
	setrlimit(RLIMIT_FSIZE, &limit);
	setrlimit(RLIMIT_CPU, &limit);

	/* Load the Configuration File */
	if (load_config(&config, TEST_AUDITD))
		return 6;

	if (config.priority_boost != 0) {
		errno = 0;
		rc = nice((int)-config.priority_boost);
		if (rc == -1 && errno) {
			audit_msg(LOG_ERR, "Cannot change priority (%s)", 
					strerror(errno));
			return 1;
		}
	} 
	
	/* Daemonize or stay in foreground for debugging */
	if (config.daemonize == D_BACKGROUND) {
		if (become_daemon() != 0) {
			audit_msg(LOG_ERR, "Cannot daemonize (%s)",
				strerror(errno));
			tell_parent(FAILURE);
			return 1;
		} 
		openlog("auditd", LOG_PID, LOG_DAEMON);
	}

	/* Init netlink */
	if ((fd = audit_open()) < 0) {
        	audit_msg(LOG_ERR, "Cannot open netlink audit socket");
		tell_parent(FAILURE);
		return 1;
	}

	/* Init the event handler thread */
	write_pid_file();
	if (init_event(&config)) {
		if (pidfile)
			unlink(pidfile);
		tell_parent(FAILURE);
		return 1;
	}

	if (init_dispatcher(&config)) {
		if (pidfile)
			unlink(pidfile);
		tell_parent(FAILURE);
		return 1;
	}

	/* Get machine name ready for use */
	if (resolve_node(&config)) {
		if (pidfile)
			unlink(pidfile);
		tell_parent(FAILURE);
		return 1;
	}

	/* Write message to log that we are alive */
	{
		struct utsname ubuf;
		char start[DEFAULT_BUF_SZ];
		const char *fmt = audit_lookup_format((int)config.log_format);
		if (fmt == NULL)
			fmt = "UNKNOWN";
		if (uname(&ubuf) != 0) {
			if (pidfile)
				unlink(pidfile);
			tell_parent(FAILURE);
			return 1;
		}
		if (getsubj(subj))
			snprintf(start, sizeof(start),
				"auditd start, ver=%s format=%s "
			    "kernel=%.56s auid=%u pid=%d subj=%s res=success",
				VERSION, fmt, ubuf.release,
				audit_getloginuid(), getpid(), subj);
		else
			snprintf(start, sizeof(start),
				"auditd start, ver=%s format=%s "
				"kernel=%.56s auid=%u pid=%d res=success",
				VERSION, fmt, ubuf.release,
				audit_getloginuid(), getpid());
		if (send_audit_event(AUDIT_DAEMON_START, start)) {
        		audit_msg(LOG_ERR, "Cannot send start message");
			if (pidfile)
				unlink(pidfile);
			shutdown_dispatcher();
			tell_parent(FAILURE);
			return 1;
		}
	}

	/* Tell kernel not to kill us */
	avoid_oom_killer();

	/* let config manager init */
	init_config_manager();

	if (opt_startup != startup_nochange && (audit_is_enabled(fd) < 2) &&
	    audit_set_enabled(fd, (int)opt_startup) < 0) {
		char emsg[DEFAULT_BUF_SZ];
		if (*subj)
			snprintf(emsg, sizeof(emsg),
			"auditd error halt, auid=%u pid=%d subj=%s res=failed",
				audit_getloginuid(), getpid(), subj);
		else
			snprintf(emsg, sizeof(emsg),
				"auditd error halt, auid=%u pid=%d res=failed",
				audit_getloginuid(), getpid());
		stop = 1;
		send_audit_event(AUDIT_DAEMON_ABORT, emsg);
		audit_msg(LOG_ERR,
		"Unable to set initial audit startup state to '%s', exiting",
			startup_states[opt_startup]);
		close_down();
		if (pidfile)
			unlink(pidfile);
		shutdown_dispatcher();
		tell_parent(FAILURE);
		return 1;
	}

	/* Tell the kernel we are alive */
	if (audit_set_pid(fd, getpid(), WAIT_YES) < 0) {
		char emsg[DEFAULT_BUF_SZ];
		if (*subj)
			snprintf(emsg, sizeof(emsg),
			"auditd error halt, auid=%u pid=%d subj=%s res=failed",
				audit_getloginuid(), getpid(), subj);
		else
			snprintf(emsg, sizeof(emsg),
				"auditd error halt, auid=%u pid=%d res=failed",
				audit_getloginuid(), getpid());
		stop = 1;
		send_audit_event(AUDIT_DAEMON_ABORT, emsg);
		audit_msg(LOG_ERR, "Unable to set audit pid, exiting");
		close_down();
		if (pidfile)
			unlink(pidfile);
		shutdown_dispatcher();
		tell_parent(FAILURE);
		return 1;
	}

	/* Depending on value of opt_startup (-s) set initial audit state */
	loop = ev_default_loop (EVFLAG_NOENV);

	ev_io_init (&netlink_watcher, netlink_handler, fd, EV_READ);
	ev_io_start (loop, &netlink_watcher);

	ev_signal_init (&sigterm_watcher, term_handler, SIGTERM);
	ev_signal_start (loop, &sigterm_watcher);

	ev_signal_init (&sighup_watcher, hup_handler, SIGHUP);
	ev_signal_start (loop, &sighup_watcher);

	ev_signal_init (&sigusr1_watcher, user1_handler, SIGUSR1);
	ev_signal_start (loop, &sigusr1_watcher);

	ev_signal_init (&sigusr2_watcher, user2_handler, SIGUSR2);
	ev_signal_start (loop, &sigusr2_watcher);

	ev_signal_init (&sigchld_watcher, child_handler, SIGCHLD);
	ev_signal_start (loop, &sigchld_watcher);

	if (auditd_tcp_listen_init (loop, &config)) {
		char emsg[DEFAULT_BUF_SZ];
		if (*subj)
			snprintf(emsg, sizeof(emsg),
			"auditd error halt, auid=%u pid=%d subj=%s res=failed",
				audit_getloginuid(), getpid(), subj);
		else
			snprintf(emsg, sizeof(emsg),
				"auditd error halt, auid=%u pid=%d res=failed",
				audit_getloginuid(), getpid());
		stop = 1;
		send_audit_event(AUDIT_DAEMON_ABORT, emsg);
		tell_parent(FAILURE);
	} else {
		/* Now tell parent that everything went OK */
		tell_parent(SUCCESS);
		audit_msg(LOG_NOTICE,
	    "Init complete, auditd %s listening for events (startup state %s)",
			VERSION,
			startup_states[opt_startup]);
	}

	/* Parent should be gone by now...   */
	if (do_fork)
		close(init_pipe[1]);

	// Init complete, start event loop
	if (!stop)
		ev_loop (loop, 0);

	auditd_tcp_listen_uninit (loop, &config);

	// Tear down IO watchers Part 1
	ev_signal_stop (loop, &sighup_watcher);
	ev_signal_stop (loop, &sigusr1_watcher);
	ev_signal_stop (loop, &sigusr2_watcher);
	ev_signal_stop (loop, &sigterm_watcher);

	/* Write message to log that we are going down */
	rc = audit_request_signal_info(fd);
	if (rc > 0) {
		struct audit_reply trep;

		rc = get_reply(fd, &trep, rc);
		if (rc > 0) {
			char txt[MAX_AUDIT_MESSAGE_LENGTH];
			snprintf(txt, sizeof(txt),
				"auditd normal halt, sending auid=%u "
				"pid=%d subj=%s res=success",
				 trep.signal_info->uid,
				 trep.signal_info->pid, 
				 trep.signal_info->ctx); 
			send_audit_event(AUDIT_DAEMON_END, txt);
		} 
	} 
	if (rc <= 0)
		send_audit_event(AUDIT_DAEMON_END, 
				"auditd normal halt, sending auid=? "
				"pid=? subj=? res=success");
	free(rep);

	// Tear down IO watchers Part 2
	ev_io_stop (loop, &netlink_watcher);

	// Give DAEMON_END event a little time to be sent in case
	// of remote logging
	usleep(10000); // 10 milliseconds
	shutdown_dispatcher();

	// Tear down IO watchers Part 3
	ev_signal_stop (loop, &sigchld_watcher);

	close_down();
	free_config(&config);
	ev_default_destroy();

	return 0;
}