/* * Copyright (C) 1998 Bjorn Borud * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef lint static char rcsid[] = "@(#)$Id: ircdwatch.c,v 1.3 1999/02/21 00:33:44 kalt Exp $"; #endif #include #include /* atol() */ #include /* fork() exec() */ #include #include /* stat() */ #include #include #include /* strncmp() */ #include #include "os.h" #include "config.h" /* * Try and find the correct name to use with getrlimit() for setting * the max. number of files allowed to be open by this process. * (borrowed from ircd/s_bsd.c) */ #ifdef RLIMIT_FDMAX # define RLIMIT_FD_MAX RLIMIT_FDMAX #else # ifdef RLIMIT_NOFILE # define RLIMIT_FD_MAX RLIMIT_NOFILE # else # ifdef RLIMIT_OPEN_MAX # define RLIMIT_FD_MAX RLIMIT_OPEN_MAX # else # undef RLIMIT_FD_MAX # endif # endif #endif #define PID_LEN 7 /* overkill, but hey */ #define MAX_OPEN_FILEDESCRIPTORS_WILD_GUESS 256 static int want_to_quit = 0; void finalize(int i) { #ifdef IRCDWATCH_USE_SYSLOG syslog(LOG_NOTICE, "ircdwatch daemon exiting"); closelog(); #endif exit(i); } int daemonize(void) { pid_t pid; int i; int open_max; #ifdef RLIMIT_FD_MAX struct rlimit rlim; if (getrlimit(RLIMIT_FD_MAX, &rlim) != 0) { perror("getrlimit"); finalize(1); } /* if we get a lame answer we just take a wild guess */ if (rlim.rlim_max == 0) { open_max = MAX_OPEN_FILEDESCRIPTORS_WILD_GUESS; } open_max = rlim.rlim_max; #else /* no RLIMIT_FD_MAX? take a wild guess */ open_max = MAX_OPEN_FILEDESCRIPTORS_WILD_GUESS; #endif pid = fork(); if (pid < 0) { perror("fork"); exit(1); } /* parent process dies */ if (pid != 0) { exit(0); } setsid(); umask(0); /* close file descriptors */ for (i=0; i < open_max; i++) { close(i); } return(0); } static void sig_handler (int signo) { if (signo == SIGHUP) { want_to_quit = 1; return; } if (signo == SIGALRM) { #ifndef POSIX_SIGNALS (void)signal(SIGALRM, &sig_handler); #endif; return; } } void set_up_signals(void) { #ifdef POSIX_SIGNALS struct sigaction act; act.sa_handler = sig_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGHUP, &act, NULL) < 0) { perror("sigaction"); } if (sigaction(SIGALRM, &act, NULL) < 0) { perror("sigaction"); } #else (void)signal(SIGHUP, &sig_handler); (void)signal(SIGALRM, &sig_handler); #endif /* ignore it if child processes die */ signal(SIGCHLD, SIG_IGN); } int write_my_pid(void) { FILE *f; f = fopen(IRCDWATCH_PID_FILENAME, "w"); if (f == NULL) { return(-1); } fprintf(f, "%d\n", getpid()); fclose(f); return(0); } int file_modified(char *s) { struct stat st; if (stat(s, &st) < 0) { return(-1); } return(st.st_ctime); } int spawn (char *cmd) { pid_t pid; pid = fork(); if (pid == -1) { #ifdef IRCDWATCH_USE_SYSLOG syslog(LOG_ERR, "spawn() unable to fork, errno=%d", errno); #endif return(-1); } if (pid == 0) { execl("/bin/sh", "sh", "-c", cmd, (char *) NULL); _exit(127); } return(0); } int read_pid(char *pid_filename) { FILE *f; char pidbuf[PID_LEN]; pid_t pid; f = fopen(pid_filename, "r"); if (f == NULL) { #ifdef IRCDWATCH_USE_SYSLOG syslog(LOG_ERR, "unable to fopen() %s: %s", pid_filename, strerror(errno)); #endif return(-1); } if (fgets(pidbuf, PID_LEN, f) != NULL) { pid = atol(pidbuf); } else { #ifdef IRCDWATCH_USE_SYSLOG syslog(LOG_ERR, "fgets() %s: %s", pid_filename, strerror(errno)); #endif fclose(f); return(-1); } fclose(f); return(pid); } int file_exists (char *s) { struct stat st; if ((stat(s, &st) < 0) && (errno == ENOENT)) { return(0); } return(1); } /* yeah, I'll get around to these in some later version */ int file_readable (char *s) { return(access(s, R_OK) == 0); } int file_writable (char *s) { return(access(s, W_OK) == 0); } int file_executable (char *s) { int rc; rc = (access(IRCD_PATH, X_OK) == 0); return rc; } int verify_pid(int pid) { int res; res = kill(pid, 0); if (res < 0 && errno == EPERM) { fprintf(stderr, "Not process owner\n"); exit(1); } return(res == 0); } int ircdwatch_running () { int pid; if (file_exists(IRCDWATCH_PID_FILENAME)) { pid = read_pid(IRCDWATCH_PID_FILENAME); if (pid > 0) { return(verify_pid(pid) == 1); } else { return(-1); } } return(0); } int ircd_running () { int pid; if (file_exists(IRCDPID_PATH)) { pid = read_pid(IRCDPID_PATH); if (pid > 0) { return(verify_pid(pid) == 1); } else { return(-1); } } return(0); } void hup_ircd () { int pid; int res; if (file_exists(IRCDPID_PATH)) { pid = read_pid(IRCDPID_PATH); if (pid > 0) { res = kill(pid, SIGHUP); if (res < 0 && errno == EPERM) { #ifdef IRCDWATCH_USE_SYSLOG syslog(LOG_ERR, "not allowed to send SIGHUP to ircd"); #endif finalize(1); } } } } void daemon_run () { int i; int last_config_time = 0; /* is ircdwatch already running? */ i = ircdwatch_running(); if (i == -1) { /* unable to open pid file. wrong user? */ fprintf(stderr, "ircdwatch pid file exists but is unreadable\n"); exit(1); } else if (i) { fprintf(stderr, "ircdwatch is already running\n"); exit(0); } /* is ircd running? */ i = ircd_running(); if (i == -1) { /* unable to open pid file. wrong user? */ fprintf(stderr, "ircdwatch pid file exists but is unreadable\n"); exit(1); } else if (!i) { fprintf(stderr, "ircd not running. attempting to start ircd...\n"); if (file_exists(IRCD_PATH)) { if (file_executable(IRCD_PATH)) { spawn(IRCD_PATH); } else { fprintf(stderr, "%s not executable\n", IRCD_PATH); exit(1); } } else { fprintf(stderr, "%s does not exist\n", IRCD_PATH); exit(1); } } set_up_signals(); closelog(); /* daemonize(); */ #ifdef IRCDWATCH_USE_SYSLOG openlog(IRCDWATCH_SYSLOG_IDENT, IRCDWATCH_SYSLOG_OPTIONS, IRCDWATCH_SYSLOG_FACILITY); syslog(LOG_NOTICE, "starting ircdwatch daemon"); #endif alarm(IRCDWATCH_POLLING_INTERVAL); pause(); while (!want_to_quit) { if (! ircd_running() ) { #ifdef IRCDWATCH_USE_SYSLOG syslog(LOG_ERR, "spawning %s", IRCD_PATH); #endif spawn(IRCD_PATH); } #ifdef IRCDWATCH_HUP_ON_CONFIG_CHANGE i = file_modified(IRCDCONF_PATH); if (i != -1) { if (last_config_time == 0) { last_config_time = i; } else if (i > last_config_time) { last_config_time = i; hup_ircd(); #ifdef IRCDWATCH_USE_SYSLOG syslog(LOG_NOTICE, "config change, HUPing ircd"); #endif } } #endif alarm(IRCDWATCH_POLLING_INTERVAL); pause(); } return; } void kill_ircd () { int pid; int res; if (file_exists(IRCDPID_PATH)) { pid = read_pid(IRCDPID_PATH); if (pid > 0) { res = kill(pid, SIGTERM); if (res < 0) { perror("ircd kill"); finalize(1); } else { fprintf(stderr, "killed ircd\n"); } } } else { fprintf(stderr, "File %s does not exist\n", IRCDPID_PATH); } } void kill_ircdwatch () { int pid; int res; if (file_exists(IRCDWATCH_PID_FILENAME)) { pid = read_pid(IRCDWATCH_PID_FILENAME); if (pid > 0) { res = kill(pid, SIGHUP); if (res < 0) { perror("ircdwatch kill"); finalize(1); } else { fprintf(stderr, "Sent HUP to ircdwatch. it will die soon...\n"); } } } else { fprintf(stderr, "File %s does not exist\n", IRCDWATCH_PID_FILENAME); } } void usage (void) { fprintf(stderr,"\n\ Usage:\n\ ircdwatch [--kill | --rest | --help]\n\ \n\ --kill, stop both ircdwatch and ircd\n\ --rest, stop ircdwatch but let ircd alone\n\ --help, display this text\n\ \n\ %s\n", rcsid); } int main (int argc, char **argv) { int i; #ifdef IRCDWATCH_USE_SYSLOG openlog(IRCDWATCH_SYSLOG_IDENT, IRCDWATCH_SYSLOG_OPTIONS, IRCDWATCH_SYSLOG_FACILITY); #endif if (argc > 1) { if (strncmp(argv[1], "--rest", 6) == 0) { kill_ircdwatch(); exit(0); } if (strncmp(argv[1], "--kill", 6) == 0) { kill_ircdwatch(); kill_ircd(); exit(0); } usage(); exit(0); } daemon_run(); finalize(0); }