#include "gskdaemonize.h"
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
static gboolean has_restart_exit_code = FALSE;
static gint global_restart_exit_code = 0;
static gboolean restart_on_error_signals = FALSE;
static gboolean do_fork = FALSE;
static char *pid_filename = NULL;
static guint restart_sleep_length = 1;
static inline gboolean
is_error_signal (guint signal_no)
{
return signal_no == SIGILL
|| signal_no == SIGILL
|| signal_no == SIGABRT
|| signal_no == SIGSEGV
|| signal_no == SIGIOT
|| signal_no == SIGBUS
|| signal_no == SIGFPE;
}
void gsk_daemonize_set_defaults (GskDaemonizeFlags flags,
guint restart_exit_code)
{
if (flags & GSK_DAEMONIZE_RESTART_ERROR_SIGNALS)
restart_on_error_signals = TRUE;
else
restart_on_error_signals = FALSE;
if (flags & GSK_DAEMONIZE_FORK)
do_fork = TRUE;
else
do_fork = FALSE;
if (flags & GSK_DAEMONIZE_SUPPORT_RESTART_EXIT_CODE)
{
has_restart_exit_code = TRUE;
global_restart_exit_code = restart_on_error_signals;
}
else
has_restart_exit_code = FALSE;
}
#define SWALLOW_ARG(count) \
{ memmove ((*argv_inout) + i, \
(*argv_inout) + (i + count), \
sizeof(char*) * (*argc_inout + 1 - (i + count))); \
*argc_inout -= count; }
static const char *
test_opt (const char *opt_name,
gint i,
int *argc_inout,
char ***argv_inout)
{
const char *arg = (*argv_inout)[i];
guint opt_len = strlen (opt_name);
if (arg[0] == '-' && arg[1] == '-')
{
if (strcmp (arg + 2, opt_name) == 0)
{
const char *rv;
if (i + 1 >= *argc_inout)
return NULL;
rv = (*argv_inout)[i+1];
SWALLOW_ARG (2);
return rv;
}
if (strncmp (arg + 2, opt_name, opt_len) == 0
&& arg[2 + opt_len] == '=')
{
const char *rv = arg + 2 + opt_len + 1;
SWALLOW_ARG (1);
return rv;
}
}
return NULL;
}
void
gsk_daemonize_parse_options (int *argc_inout,
char ***argv_inout)
{
gint i;
for (i = 0; i < *argc_inout; )
{
const char *arg = (*argv_inout)[i];
const char *opt;
if (strcmp (arg, "--foreground") == 0)
{
do_fork = FALSE;
SWALLOW_ARG (1);
}
else if (strcmp (arg, "--background") == 0)
{
do_fork = TRUE;
SWALLOW_ARG (1);
}
else if ((opt=test_opt("pidfile",i,argc_inout,argv_inout)) != NULL)
{
g_free (pid_filename);
pid_filename = g_strdup (opt);
}
else if (strcmp (arg, "--no-autorestart") == 0)
{
restart_on_error_signals = FALSE;
SWALLOW_ARG (1);
}
else if (strcmp (arg, "--autorestart") == 0)
{
restart_on_error_signals = TRUE;
SWALLOW_ARG (1);
}
else
{
i++;
}
}
}
void gsk_daemonize_set_pid_filename (const char *filename)
{
char *dup = g_strdup (filename);
g_free (pid_filename);
pid_filename = dup;
}
void gsk_daemonize_print_options (void)
{
g_print (" --background Fork to put this process in the background.%s\n"
" --foreground Do not fork: put this process in the foreground.%s\n"
" --pidfile=FILE Write pid to this file.\n",
do_fork ? " [default]" : "",
do_fork ? "" : " [default]");
/* only print options in the opposite of their default sense */
if (restart_on_error_signals)
g_print (" --no-autorestart Do not restart the process on error signals.\n");
else
g_print (" --autorestart Restart the process on error signals.\n");
}
void
gsk_maybe_daemonize (void)
{
if (do_fork)
{
int fork_rv;
fflush (stdin);
fflush (stdout);
while ((fork_rv=fork()) < 0)
{
if (errno == EINTR)
{
continue;
}
#if 0 /* the code quoted here is the traditional fork guard. */
/* it seems dubious though -- let's abort instead. */
if (errno == EAGAIN)
{
sleep(2);
continue;
}
#endif
g_error ("error forking: %s", g_strerror (errno));
}
if (fork_rv > 0)
{
FILE *pid_fp;
pid_fp = fopen (pid_filename, "w");
if (pid_fp == NULL)
g_error ("error opening pid file %s", pid_filename);
fprintf (pid_fp, "%u\n", fork_rv);
fclose (pid_fp);
exit (0);
}
/* child continues */
}
if (has_restart_exit_code || restart_on_error_signals)
{
int fork_rv;
restart:
while ((fork_rv=fork()) < 0)
{
if (errno == EINTR)
{
continue;
}
#if 0 /* the code quoted here is the traditional fork guard. */
/* it seems dubious though -- let's abort instead. */
if (errno == EAGAIN)
{
sleep(2);
continue;
}
#endif
g_error ("error forking: %s", g_strerror (errno));
}
if (fork_rv > 0)
{
int status;
if (pid_filename != NULL)
{
FILE *pid_fp = fopen (pid_filename, "w");
if (pid_fp == NULL)
g_error ("error opening pid file %s", pid_filename);
fprintf (pid_fp, "%u\n", fork_rv);
fclose (pid_fp);
}
if (waitpid (fork_rv, &status, 0) < 0)
{
g_error ("error running waitpid itself");
}
if (pid_filename != NULL)
unlink (pid_filename);
if (WIFEXITED (status))
{
int exit_status = WEXITSTATUS (status);
if (has_restart_exit_code && exit_status == global_restart_exit_code)
{
sleep (restart_sleep_length);
goto restart;
}
_exit (exit_status);
}
else if (WIFSIGNALED (status))
{
int signalno = WTERMSIG (status);
if (restart_on_error_signals && is_error_signal (signalno))
{
sleep (restart_sleep_length);
goto restart;
}
kill (getpid (), signalno);
}
else
{
g_error ("program terminated, but not by signal or exit?");
}
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1