syslog_kernel.c
author viric@llimona
Fri, 16 Jun 2006 18:25:03 +0200
changeset 61 4cd174a9b698
parent 57 de776a4548da
permissions -rw-r--r--
Added the setsockopt for IPV6_V6ONLY, in order to have IPv6 and IPv4 in two different sockets for the same port.

#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <signal.h>
#include <stdio.h> // Per sprintf, fprintf
#include <string.h> // Per memset
#include <assert.h> // Pels assert
#include <stdlib.h> // Per l'abort
#include <fcntl.h> // Per l'open
#include <sys/stat.h> // Per l'open
#include <sys/select.h> // Pel select()

#include "rfc3164.h"
#include "syslog.h"

/* Program names */
static const char syslog_in_unix[] = "syslog_in_unix";
static const char syslog_in_npipe[] = "syslog_in_npipe";
static const char syslog_in_udp[] = "syslog_in_udp";

/* Global variables */
static bool reconfig = false;

/* Code */
static void term_handler(int parameter)
{
	term_childs();
}

static void reconfig_handler(int parameter)
{
	reconfig = true;
}


static int run_program(const char * restrict programname,
		const char * restrict parameter1, const int comm_pipe[])
{
	int pid;

	pid = child_fork();
	assert(pid != -1);
	if (pid == 0)
	{
		close(0);
		close(1);
		dup(comm_pipe[0]);
		dup(comm_pipe[1]);
		execl(programname, programname, parameter1, NULL);
		/* Unreachable if everything goes well */
		fprintf(stderr, "Child exec failed(%s %s): %s\n",
			programname, parameter1, strerror(errno));
		abort();
		return 0;
	}
	else
	{
		fprintf(stderr, "Child forked(%s %s): %i\n",
			programname, parameter1, pid);
		return pid;
	}
}

static int init_in_unix(const char * restrict socketname,
		const int input_pipe[2])
{
	return run_program(syslog_in_unix, socketname, input_pipe);
}

static int init_in_udp(const char * restrict port, const int input_pipe[2])
{
	return run_program(syslog_in_udp, port, input_pipe);
}

static int init_in_npipe(const char * restrict pipename,
		const int input_pipe[2])
{
	return run_program(syslog_in_npipe, pipename, input_pipe);
}

static void start_childs(const int input_pipe[2])
{
	char cvalue[MAX_STRING];

	/* The childs should not run */
	if (get_config(FROM_UNIX, cvalue, MAX_STRING) > 0)
	{
		init_in_unix(cvalue, input_pipe);
	}

	if (get_config(FROM_NPIPE, cvalue, MAX_STRING) > 0)
	{
		init_in_npipe(cvalue, input_pipe);
	}

	if (get_config(FROM_UDP, cvalue, MAX_STRING) > 0)
	{
		init_in_udp(cvalue, input_pipe);
	}
}

static int output_message(const char * restrict msg, const int len)
{
	int res1, res2, res;

	res = 0;

	res1 = write_out_udp(msg, len);
	res2 = write_out_file(msg, len);

	if (res1 == -1 || res2 == -1)
		res = -1;

	return res;
}

static void kernel_loop()
{
	int input_pipe[2];
	char missatge[MESSAGE_LENGTH+1];
	int res; /* resultat de la crida read */
	fd_set read_fd_set;
	struct timeval read_timeout;

	/* Pipe for the childs */
	pipe(input_pipe);

	start_childs(input_pipe);

	/* We don't want to write to the programs */
	close(input_pipe[1]);

	FD_ZERO(&read_fd_set);
	while(true)
	{
		/* pipe read handle */
		if (FD_ISSET(input_pipe[0], &read_fd_set))
		{
			res = read(input_pipe[0], missatge, MESSAGE_LENGTH);
			assert( res != -1 );

			if (res > 0)
			{
				/* Output to screen */
				/* Add a ZERO for displaying */
				missatge[res] = '\0';
				fprintf(stderr, "Received: %s\n",missatge);
				
				process_message(missatge);
				output_message(missatge, res);
			} else if (res == 0) /* EOF */
			{
				break;
			}
		}

		/* The select call may be interrupted by a signal */
		if (get_childs_alive() == 0)
			break;

		if (reconfig == true)
			break;

		/* Prepare the fd_set */
		FD_ZERO(&read_fd_set);
		FD_SET(input_pipe[0], &read_fd_set);
		/* We set the timeout in order to save us easily from the race
		 * condition for signal receiving (select/pselect) */
		read_timeout.tv_sec = 5;
		read_timeout.tv_usec = 0;
		res = select(input_pipe[0]+1, &read_fd_set, NULL, NULL,
				&read_timeout);
		if (res == -1)
			FD_ZERO(&read_fd_set);

		assert( !(res == -1 && errno != EINTR) );
	}

	close(input_pipe[0]);
}

static void install_signal_handlers()
{
	program_child_handler(child_handler);
	program_handler(SIGTERM, term_handler);
	program_handler(SIGINT, term_handler);
	program_handler(SIGHUP, reconfig_handler);
}

static void check_pid()
{
	int fh;
	char name[MAX_STRING];
	char buffer[MAX_STRING];
	int buffer_size;
	int pid, res;

	assert (get_config(PID_FILE, name, MAX_STRING) > 0);

	fh = open(name, O_RDONLY);
	if (fh == -1)
	{
		/* If the file doesn't exist, it's normal. */
		return;
	}

	buffer_size = MAX_STRING;
	buffer_size = read(fh, buffer, buffer_size);
	if (buffer_size <= 0)
	{
		fprintf(stderr, "Error reading the pid file: %s\n",
				strerror(errno));
		close(fh);
		return;
	}
	buffer[buffer_size] = '\0';

	pid = atoi(buffer);

	if (pid > 0)
	{
		res = kill(pid, SIGHUP);
		if (res == 0)
		{
			/* The syslog_kernel seems to be running and restared */
			fprintf(stderr,"syslog_kernel running. Restarted.\n");
			close(fh);
			exit(0);
		} else if (res == -1)
		{
			fprintf(stderr,"An old pid file has been found. "
				"Ignoring it.\n");
		}
	}

	close(fh);
}

static void write_pid()
{
	int fh;
	char name[MAX_STRING];
	char buffer[MAX_STRING];
	int buffer_size;
	int res;

	assert (get_config(PID_FILE, name, MAX_STRING) > 0);

	fh = open(name, O_WRONLY | O_TRUNC | O_CREAT, 0644);
	if (fh == -1)
	{
		fprintf(stderr, "Error opening the pid file for write: %s\n",
				strerror(errno));
		return;
	}

	buffer_size = snprintf(buffer, MAX_STRING, "%i", getpid());
	if (buffer_size < MAX_STRING)
	{
		buffer[buffer_size++] = '\n';
	}

	res = write(fh, buffer, buffer_size);
	if (buffer_size <= 0)
		fprintf(stderr, "Error writting the pid file: %s\n",
				strerror(errno));

	close(fh);
}

static void remove_pid()
{
	int res;
	char name[MAX_STRING];

	assert (get_config(PID_FILE, name, MAX_STRING) > 0);
	res = unlink(name);

	if (res == -1)
		fprintf(stderr, "Error unlinking the pid file: %s\n",
				strerror(errno));
}



int main(int argn, char *argv[])
{
	int res;

	check_pid();
	write_pid();

	install_signal_handlers();

	do
	{
		reconfig = false;
		init_config();

		res = init_out_udp();
		if (res == -2)
			fprintf(stderr, "UDP output disabled.\n");
		else if (res == -1)
			fprintf(stderr, "error setting up the UDP output.\n");

		res = init_out_file();
		if (res == -2)
			fprintf(stderr, "File output disabled.\n");
		else if (res == -1)
			fprintf(stderr, "error setting up the File output.\n");

		init_tcp_server();

		kernel_loop();

		if (reconfig == true)
			fprintf(stderr, "reconfiguring...\n");

		close_out_tcp();

		term_childs();

		close_out_udp();
		close_out_file();

		wait_childs_die();
	}
	while(reconfig == true);

	remove_pid();
}