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();
}