/* process.c -- process management functions Copyright (C) 1999,2000 Ivo Timmermans , 2000 Guus Sliepen 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 2 of the License, 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. $Id: process.c,v 1.1.2.1 2000/11/16 17:54:28 zarq Exp $ */ #include "config.h" #include /* A list containing all our children */ list_t *child_pids; /* If zero, don't detach from the terminal. */ int do_detach = 1; /* Detach from current terminal, write pidfile, kill parent */ int detach(void) { int fd; pid_t pid; if(do_detach) { ppid = getpid(); if((pid = fork()) < 0) { perror("fork"); return -1; } if(pid) /* parent process */ { signal(SIGTERM, parent_exit); sleep(600); /* wait 10 minutes */ exit(1); } } if(write_pidfile()) return -1; if(do_detach) { if((fd = open("/dev/tty", O_RDWR)) >= 0) { if(ioctl(fd, TIOCNOTTY, NULL)) { perror("ioctl"); return -1; } close(fd); } if(setsid() < 0) return -1; kill(ppid, SIGTERM); } chdir("/"); /* avoid keeping a mointpoint busy */ openlog(identname, LOG_CONS | LOG_PID, LOG_DAEMON); if(debug_lvl > DEBUG_NOTHING) syslog(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"), VERSION, __DATE__, __TIME__, debug_lvl); else syslog(LOG_NOTICE, _("tincd %s starting"), VERSION); xalloc_fail_func = memory_full; return 0; } /* Execute the program name, with sane environment. All output will be redirected to syslog. */ void _execute_script(const char *name) { int error = 0; char *scriptname; char *s; int fd; if(netname) { asprintf(&s, "NETNAME=%s", netname); putenv(s); /* Don't free s! see man 3 putenv */ } #ifdef HAVE_UNSETENV else { unsetenv("NETNAME"); } #endif if(chdir(confbase) < 0) /* This cannot fail since we already read config files from this directory. - Guus */ /* Yes this can fail, somebody could have removed this directory when we didn't pay attention. - Ivo */ { if(chdir("/") < 0) /* Now if THIS fails, something wicked is going on. - Ivo */ syslog(LOG_ERR, _("Couldn't chdir to `/': %m")); /* Continue anyway. */ } asprintf(&scriptname, "%s/%s", confbase, name); /* Close all file descriptors */ closelog(); fcloseall(); /* Open standard input */ if(open("/dev/null", O_RDONLY) < 0) { syslog(LOG_ERR, _("Opening `/dev/null' failed: %m")); error = 1; } if(!error) { /* Standard output directly goes to syslog */ openlog(name, LOG_CONS | LOG_PID, LOG_DAEMON); /* Standard error as well */ if(dup2(1, 2) < 0) { syslog(LOG_ERR, _("System call `%s' failed: %m"), "dup2"); error = 1; } } if(error && debug_lvl > 1) syslog(LOG_INFO, _("This means that any output the script generates will not be shown in syslog.")); execl(scriptname, NULL); /* No return on success */ if(errno != ENOENT) /* Ignore if the file does not exist */ syslog(LOG_WARNING, _("Error executing `%s': %m"), scriptname); /* No need to free things */ exit(0); } /* Fork and execute the program pointed to by name. */ int execute_script(const char *name) { pid_t pid; if((pid = fork()) < 0) { syslog(LOG_ERR, _("System call `%s' failed: %m"), "fork"); return -1; } if(pid) { list_append(child_pids, pid); return 0; } /* Child here */ _execute_script(name); } /* Check a child (the pointer data is actually an integer, the PID of that child. A non-zero return value means that the child has exited and can be removed from our list. */ int check_child(void *data) { pid_t pid; int status; pid = (pid_t) data; pid = waitpid(pid, &status, WNOHANG); if(WIFEXITED(status)) { if(WIFSIGNALED(status)) /* Child was killed by a signal */ { syslog(LOG_ERR, _("Child with PID %d was killed by signal %d (%s)"), pid, WTERMSIG(status), strsignal(WTERMSIG(status))); return -1; } if(WEXITSTATUS(status) != 0) { syslog(LOG_INFO, _("Child with PID %d exited with code %d"), WEXITSTATUS(status)); } return -1; } /* Child is still running */ return 0; } /* Check the status of all our children. */ void check_children(void) { list_forall_nodes(child_pids, check_child); } /* Signal handlers. */ RETSIGTYPE sigterm_handler(int a) { if(debug_lvl > DEBUG_NOTHING) syslog(LOG_NOTICE, _("Got TERM signal")); cleanup_and_exit(0); } RETSIGTYPE sigquit_handler(int a) { if(debug_lvl > DEBUG_NOTHING) syslog(LOG_NOTICE, _("Got QUIT signal")); cleanup_and_exit(0); } RETSIGTYPE sigsegv_square(int a) { syslog(LOG_ERR, _("Got another SEGV signal: not restarting")); exit(0); } RETSIGTYPE sigsegv_handler(int a) { syslog(LOG_ERR, _("Got SEGV signal")); cp_trace(); if(do_detach) { syslog(LOG_NOTICE, _("Trying to re-execute in 5 seconds...")); signal(SIGSEGV, sigsegv_square); close_network_connections(); sleep(5); remove_pid(pidfilename); execvp(g_argv[0], g_argv); } else { syslog(LOG_NOTICE, _("Not restarting.")); exit(0); } } RETSIGTYPE sighup_handler(int a) { if(debug_lvl > DEBUG_NOTHING) syslog(LOG_NOTICE, _("Got HUP signal")); sighup = 1; } RETSIGTYPE sigint_handler(int a) { if(debug_lvl > DEBUG_NOTHING) syslog(LOG_NOTICE, _("Got INT signal, exiting")); cleanup_and_exit(0); } RETSIGTYPE sigusr1_handler(int a) { dump_conn_list(); } RETSIGTYPE sigusr2_handler(int a) { dump_subnet_list(); } RETSIGTYPE sighuh(int a) { syslog(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a)); cp_trace(); } void setup_signals(void) { int i; for(i=0;i<32;i++) signal(i,sighuh); if(signal(SIGTERM, SIG_IGN) != SIG_ERR) signal(SIGTERM, sigterm_handler); if(signal(SIGQUIT, SIG_IGN) != SIG_ERR) signal(SIGQUIT, sigquit_handler); if(signal(SIGSEGV, SIG_IGN) != SIG_ERR) signal(SIGSEGV, sigsegv_handler); if(signal(SIGHUP, SIG_IGN) != SIG_ERR) signal(SIGHUP, sighup_handler); signal(SIGPIPE, SIG_IGN); if(signal(SIGINT, SIG_IGN) != SIG_ERR) signal(SIGINT, sigint_handler); signal(SIGUSR1, sigusr1_handler); signal(SIGUSR2, sigusr2_handler); signal(SIGCHLD, SIG_IGN); } RETSIGTYPE parent_exit(int a) { exit(0); }