/*
* Playground+ - angel.c v1.7
* Watches over the talker and reboots it when it goes down
* ---------------------------------------------------------------------------
*
* This is a major update to the PG96 angel including better
* memory usage, angel PID storing, soft messages inclusion and the ability
* to clean compile --Silver
*
*/
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/socket.h>
#ifdef LINUX
#include "include/un.h"
#else
#include <sys/un.h>
#endif
#include <sys/ioctl.h>
#if !defined(linux)
#include <sys/filio.h>
#endif /* LINUX */
#include <assert.h>
#include "include/config.h"
#include "xstring.c"
/* a trade off, we have to define these here cause angel
doesnt access softmessages */
typedef struct file
{
char *where;
int length;
}
file;
file config_msg;
file load_file(char *);
char *get_config_msg(char *);
char angel_name[256];
char server_name[256];
char *stack, *stack_start;
int fh = 0, die = 0, crashes = 0, syncing = 0;
long int time_out = 0, t = 0;
int no_tty = 0;
/* we need to have these here for xstring stuffs */
char *end_string(char *str)
{
str = strchr(str, 0);
str++;
return str;
}
void lower_case(char *str)
{
assert(str);
while (*str)
{
*str = tolower((unsigned char)*str);
str++;
}
}
/* return a string of the system time */
char *sys_time(void)
{
time_t tt;
static char time_string[27];
tt = time(0);
strftime(time_string, 27, "%H:%M:%S - %d/%m/%Y", localtime(&tt));
return time_string;
}
/* log errors and things to file */
void log(char *filename, char *string)
{
int fd, length;
sprintf(stack, "logs/%s.log", filename);
#ifdef BSDISH
fd = open(stack, O_CREAT | O_WRONLY | S_IRUSR | S_IWUSR);
#else
fd = open(stack, O_CREAT | O_WRONLY | O_SYNC, S_IRUSR | S_IWUSR);
#endif
length = lseek(fd, 0, SEEK_END);
if (length > (atoi(get_config_msg("max_log_size")) * 1024))
{
close(fd);
#ifdef BSDISH
fd = open(stack, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
#else
fd = open(stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR);
#endif
}
sprintf(stack, "%s - %s\n", sys_time(), string);
if (!no_tty)
printf(stack);
write(fd, stack, strlen(stack));
close(fd);
}
void error(char *str)
{
log("angel", str);
exit(-1);
}
void sigpipe(int c)
{
error("Sigpipe received.");
}
void sighup(int c)
{
kill(fh, SIGHUP);
die = 1;
}
void sigquit(int c)
{
error("Quit signal received.");
}
void sigill(int c)
{
error("Illegal instruction.");
}
void sigfpe(int c)
{
error("Floating Point Error.");
}
void sigbus(int c)
{
error("Bus Error.");
}
void sigsegv(int c)
{
error("Segmentation Violation.");
}
void sigsys(int c)
{
error("Bad system call.");
}
void sigterm(int c)
{
error("Terminate signal received.");
}
void sigxfsz(int c)
{
error("File descriptor limit exceeded.");
}
void sigchld(int c)
{
log("angel", "Received SIGCHLD");
return;
}
/* Woo woo, the main function thang */
int main(int argc, char *argv[])
{
#ifdef HAVE_SOCKLEN_T
socklen_t length;
#else
unsigned int length;
#endif /* HAVE_SOCKLEN_T */
int status, alive_fd = 0, sock_fd, dieing;
FILE *angel_pid_fd;
struct sockaddr_un sa;
char dummy;
fd_set fds;
struct timeval timeout;
#if defined(hpux) | defined(linux) | defined(BSD3) | defined(NETBSD) | defined(BSDISH)
struct sigaction siga;
#endif /* hpux | linux | bsd3 | netbsd | bsdish */
stack_start = (char *) malloc(1000);
stack = stack_start;
if (chdir(ROOT) < 0)
{
fprintf(stderr, "\n\nCant change to '" ROOT "'!!\n\n");
exit(-1);
}
config_msg = load_file("soft/config.msg"); /* get it loaded in */
/* process names */
sprintf(angel_name, "-=> %s <=- Guardian Angel watching port",
get_config_msg("talker_name"));
/* Store angel_pid */
unlink("junk/ANGEL_PID");
if (!(angel_pid_fd = fopen("junk/ANGEL_PID", "w")))
{
fprintf(stderr, "Unable to create pid log file!\n");
exit(1);
}
fprintf(angel_pid_fd, "%d\n", (int) getpid());
fclose(angel_pid_fd);
if (strcmp(angel_name, argv[0]))
{
if (!argv[1])
{
sprintf(stack, "%d", atoi(get_config_msg("port")));
execlp("bin/angel", angel_name, stack, 0);
}
else
{
argv[0] = angel_name;
execvp("bin/angel", argv);
}
error("exec failed");
}
if (nice(5) < 0)
error("Failed to renice");
t = time(0);
time_out = t + 60;
#ifdef HAVE_SIGEMPTYSET
sigemptyset(&siga.sa_mask);
#else
siga.sa_mask = 0;
#endif
#if defined(hpux) | defined(linux)
siga.sa_handler = sigpipe;
#ifdef HAVE_SIGEMPTYSET
sigemptyset(&siga.sa_mask);
#else
siga.sa_mask = 0;
#endif
siga.sa_flags = 0;
sigaction(SIGPIPE, &siga, 0);
siga.sa_handler = sighup;
sigaction(SIGHUP, &siga, 0);
siga.sa_handler = sigquit;
sigaction(SIGQUIT, &siga, 0);
siga.sa_handler = sigill;
sigaction(SIGILL, &siga, 0);
siga.sa_handler = sigfpe;
sigaction(SIGFPE, &siga, 0);
siga.sa_handler = sigbus;
sigaction(SIGBUS, &siga, 0);
siga.sa_handler = sigsegv;
sigaction(SIGSEGV, &siga, 0);
#if !defined(linux)
siga.sa_handler = sigsys;
sigaction(SIGSYS, &siga, 0);
#endif /* LINUX */
siga.sa_handler = sigterm;
sigaction(SIGTERM, &siga, 0);
siga.sa_handler = sigxfsz;
sigaction(SIGXFSZ, &siga, 0);
siga.sa_handler = sigchld;
sigaction(SIGCHLD, &siga, 0);
#else /* hpux | linux */
signal(SIGPIPE, sigpipe);
signal(SIGHUP, sighup);
signal(SIGQUIT, sigquit);
signal(SIGILL, sigill);
signal(SIGFPE, sigfpe);
signal(SIGBUS, sigbus);
signal(SIGSEGV, sigsegv);
signal(SIGSYS, sigsys);
signal(SIGTERM, sigterm);
signal(SIGXFSZ, sigxfsz);
signal(SIGCHLD, sigchld);
#endif /* hpux | linux */
while (!die)
{
if (config_msg.where) /* reload it every time the talker reboots */
FREE(config_msg.where);
config_msg = load_file("soft/config.msg");
t = time(0);
if (crashes >= 4 && time_out >= t)
{
log("error", "Crashing lots!! - Giving up.");
exit(-1);
}
else if (time_out < t)
{
time_out = t + 30;
crashes = 0;
}
crashes++;
printf("\n\n"); /* Silly formatting reason :o) */
log("angel", "Playground+ guardian angel bootloader v1.7 (19.07.99)");
dieing = 0;
fh = fork();
switch (fh)
{
case 0:
setsid();
sprintf(server_name, "-=> %s <=- Talk Server on port",
get_config_msg("talker_name"));
argv[0] = server_name;
argv[1] = get_config_msg("port");
execvp("bin/" TALKER_EXEC, argv);
error("failed to exec talk server!");
break;
case -1:
error("Failed to fork()");
break;
default:
no_tty = 1;
unlink(SOCKET_PATH);
sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0)
error("failed to create socket!");
sa.sun_family = AF_UNIX;
strcpy(sa.sun_path, SOCKET_PATH);
if (bind(sock_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
error("failed to bind!");
if (listen(sock_fd, 1) < 0)
error("failed to listen!");
timeout.tv_sec = 120;
timeout.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(sock_fd, &fds);
if (select(FD_SETSIZE, &fds, 0, 0, &timeout) <= 0)
{
kill(fh, SIGKILL);
log("angel", "Killed server before connect");
waitpid(fh, &status, 0);
}
else
{
length = sizeof(sa);
alive_fd = accept(sock_fd, (struct sockaddr *) &sa, &length);
if (alive_fd < 0)
error("bad accept!?");
close(sock_fd);
while (waitpid(fh, &status, WNOHANG) <= 0)
{
timeout.tv_sec = 300;
timeout.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(alive_fd, &fds);
if (select(FD_SETSIZE, &fds, 0, 0, &timeout) <= 0)
{
if (errno != EINTR)
{
if (dieing)
{
kill(fh, SIGKILL);
log("angel", "Server KILLED");
}
else
{
kill(fh, SIGTERM);
log("angel", "Server TERMINATED");
dieing = 1;
}
}
}
else
{
if (ioctl(alive_fd, FIONREAD, &length) < 0)
error("bad FIONREAD");
if (!length)
{
kill(fh, SIGKILL);
log("angel", "Server disconnected");
dieing = 1;
}
else
{
for (; length; length--)
{
read(alive_fd, &dummy, 1);
}
}
}
}
}
close(alive_fd);
switch ((status & 255))
{
case 0:
log("angel", "Server exited safely");
break;
case 127:
sprintf(stack, "Server stopped due to signal %d.",
(status >> 8) & 255);
while (*stack)
stack++;
stack++;
log("angel", stack_start);
stack = stack_start;
break;
default:
sprintf(stack, "Server terminated due to signal %d.",
status & 127);
while (*stack)
stack++;
stack++;
log("angel", stack_start);
stack = stack_start;
if (status & 128)
log("angel", "Core dump produced - oops!?");
break;
}
break;
}
}
exit(0);
}
/* we need this here as well... */
file load_file(char *filename)
{
file f;
int d;
char *oldstack;
oldstack = stack;
d = open(filename, O_RDONLY);
if (d < 0)
{
sprintf(oldstack, "Angel can't find file:%s", filename);
stack = end_string(oldstack);
log("error", oldstack);
f.where = (char *) MALLOC(1);
*(char *) f.where = 0;
f.length = 0;
stack = oldstack;
return f;
}
f.length = lseek(d, 0, SEEK_END);
lseek(d, 0, SEEK_SET);
f.where = (char *) MALLOC(f.length + 1);
memset(f.where, 0, f.length + 1);
if (read(d, f.where, f.length) < 0)
{
sprintf(oldstack, "Angel error reading file:%s", filename);
stack = end_string(oldstack);
log("error", oldstack);
f.where = (char *) MALLOC(1);
*(char *) f.where = 0;
f.length = 0;
stack = oldstack;
return f;
}
close(d);
stack = oldstack;
*(f.where + f.length) = 0;
return f;
}
char *get_config_msg(char *type)
{
char *got;
static char smbuf[1024];
memset(smbuf, 0, 1024);
if (!config_msg.where || !*config_msg.where)
{
log("error", "Angel : Softmsg file for config_msg aint loaded!");
return "error";
}
got = lineval(config_msg.where, type);
if (!got || !*got)
{
log("error", "Angel : Softmsg in config_msg isnt there!");
return "error";
}
strncpy(smbuf, got, 1023);
return smbuf;
}