| /* |
| * (C) 2006-2012 by Pablo Neira Ayuso <pablo@netfilter.org> |
| * (C) 2011 by Vyatta Inc. <http://www.vyatta.com> |
| * |
| * 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. |
| * |
| * Part of this code has been sponsored by Vyatta Inc. <http://www.vyatta.com> |
| */ |
| |
| #include "conntrackd.h" |
| #include "netlink.h" |
| #include "filter.h" |
| #include "log.h" |
| #include "alarm.h" |
| #include "fds.h" |
| #include "traffic_stats.h" |
| #include "process.h" |
| #include "origin.h" |
| #include "date.h" |
| #include "internal.h" |
| #include "systemd.h" |
| |
| #include <errno.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <sys/wait.h> |
| #include <string.h> |
| #include <time.h> |
| #include <fcntl.h> |
| |
| void killer(int signo) |
| { |
| /* Signals are re-entrant, disable signal handling to avoid problems |
| * in case we receive SIGINT and SIGTERM in a row. This function is |
| * also called via -k from the unix socket context, we already disabled |
| * signals in that path, so don't do it. |
| */ |
| if (signo) |
| sigprocmask(SIG_BLOCK, &STATE(block), NULL); |
| |
| local_server_destroy(&STATE(local)); |
| |
| if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) |
| ctnl_kill(); |
| |
| #ifdef BUILD_CTHELPER |
| if (CONFIG(flags) & CTD_HELPER) |
| cthelper_kill(); |
| #endif |
| destroy_fds(STATE(fds)); |
| unlink(CONFIG(lockfile)); |
| dlog(LOG_NOTICE, "---- shutdown received ----"); |
| close_log(); |
| |
| sd_ct_stop(); |
| exit(0); |
| } |
| |
| static void child(int foo) |
| { |
| int status, ret; |
| |
| while ((ret = waitpid(0, &status, WNOHANG)) != 0) { |
| if (ret == -1) { |
| if (errno == EINTR) |
| continue; |
| if (errno == ECHILD) |
| break; |
| STATE(stats).wait_failed++; |
| break; |
| } |
| /* delete process from list and run the callback */ |
| fork_process_delete(ret); |
| |
| if (!WIFSIGNALED(status)) |
| continue; |
| |
| switch(WTERMSIG(status)) { |
| case SIGSEGV: |
| dlog(LOG_ERR, "child process (pid=%u) has aborted, " |
| "received signal SIGSEGV (crashed)", ret); |
| STATE(stats).child_process_failed++; |
| STATE(stats).child_process_error_segfault++; |
| break; |
| case SIGINT: |
| case SIGTERM: |
| case SIGKILL: |
| dlog(LOG_ERR, "child process (pid=%u) has aborted, " |
| "received termination signal (%u)", |
| ret, WTERMSIG(status)); |
| STATE(stats).child_process_failed++; |
| STATE(stats).child_process_error_term++; |
| break; |
| default: |
| dlog(LOG_NOTICE, "child process (pid=%u) " |
| "received signal (%u)", |
| ret, WTERMSIG(status)); |
| STATE(stats).child_process_failed++; |
| break; |
| } |
| } |
| } |
| |
| static void uptime(char *buf, size_t bufsiz) |
| { |
| time_t tmp; |
| int updays, upminutes, uphours; |
| size_t size = 0; |
| |
| time(&tmp); |
| tmp = tmp - STATE(stats).daemon_start_time; |
| updays = (int) tmp / (60*60*24); |
| if (updays) { |
| size = snprintf(buf, bufsiz, "%d day%s ", |
| updays, (updays != 1) ? "s" : ""); |
| } |
| upminutes = (int) tmp / 60; |
| uphours = (upminutes / 60) % 24; |
| upminutes %= 60; |
| if(uphours) { |
| snprintf(buf + size, bufsiz, "%d h %d min", uphours, upminutes); |
| } else { |
| snprintf(buf + size, bufsiz, "%d min", upminutes); |
| } |
| } |
| |
| static void dump_stats_runtime(int fd) |
| { |
| char buf[1024], uptime_string[512]; |
| int size; |
| |
| uptime(uptime_string, sizeof(uptime_string)); |
| size = snprintf(buf, sizeof(buf), |
| "daemon uptime: %s\n\n" |
| "netlink stats:\n" |
| "\tevents received:\t%20llu\n" |
| "\tevents filtered:\t%20llu\n" |
| "\tevents unknown type:\t\t%12u\n" |
| "\tcatch event failed:\t\t%12u\n" |
| "\tdump unknown type:\t\t%12u\n" |
| "\tnetlink overrun:\t\t%12u\n" |
| "\tflush kernel table:\t\t%12u\n" |
| "\tresync with kernel table:\t%12u\n" |
| "\tcurrent buffer size (in bytes):\t%12u\n\n" |
| "runtime stats:\n" |
| "\tchild process failed:\t\t%12u\n" |
| "\t\tchild process segfault:\t%12u\n" |
| "\t\tchild process termsig:\t%12u\n" |
| "\tselect failed:\t\t\t%12u\n" |
| "\twait failed:\t\t\t%12u\n" |
| "\tlocal read failed:\t\t%12u\n" |
| "\tlocal unknown request:\t\t%12u\n\n", |
| uptime_string, |
| (unsigned long long)STATE(stats).nl_events_received, |
| (unsigned long long)STATE(stats).nl_events_filtered, |
| STATE(stats).nl_events_unknown_type, |
| STATE(stats).nl_catch_event_failed, |
| STATE(stats).nl_dump_unknown_type, |
| STATE(stats).nl_overrun, |
| STATE(stats).nl_kernel_table_flush, |
| STATE(stats).nl_kernel_table_resync, |
| CONFIG(netlink_buffer_size), |
| STATE(stats).child_process_failed, |
| STATE(stats).child_process_error_segfault, |
| STATE(stats).child_process_error_term, |
| STATE(stats).select_failed, |
| STATE(stats).wait_failed, |
| STATE(stats).local_read_failed, |
| STATE(stats).local_unknown_request); |
| |
| send(fd, buf, size, 0); |
| } |
| |
| static int local_handler(int fd, void *data) |
| { |
| int ret = LOCAL_RET_OK; |
| int type; |
| |
| if (read(fd, &type, sizeof(type)) <= 0) { |
| STATE(stats).local_read_failed++; |
| return LOCAL_RET_OK; |
| } |
| switch(type) { |
| case KILL: |
| killer(0); |
| break; |
| case STATS_RUNTIME: |
| dump_stats_runtime(fd); |
| break; |
| case STATS_PROCESS: |
| fork_process_dump(fd); |
| break; |
| } |
| |
| if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) |
| return ctnl_local(fd, type, data); |
| |
| #ifdef BUILD_CTHELPER |
| if (CONFIG(flags) & CTD_HELPER) |
| return cthelper_local(fd, type, data); |
| #endif |
| return ret; |
| } |
| |
| /* order received via UNIX socket */ |
| static void local_cb(void *data) |
| { |
| do_local_server_step(&STATE(local), NULL, local_handler); |
| } |
| |
| int |
| init(void) |
| { |
| do_gettimeofday(); |
| |
| STATE(fds) = create_fds(); |
| if (STATE(fds) == NULL) { |
| dlog(LOG_ERR, "can't create file descriptor pool"); |
| return -1; |
| } |
| |
| /* local UNIX socket */ |
| if (local_server_create(&STATE(local), &CONFIG(local)) == -1) { |
| dlog(LOG_ERR, "can't open unix socket!"); |
| return -1; |
| } |
| register_fd(STATE(local).fd, local_cb, NULL, STATE(fds)); |
| |
| /* Signals handling */ |
| sigemptyset(&STATE(block)); |
| sigaddset(&STATE(block), SIGTERM); |
| sigaddset(&STATE(block), SIGINT); |
| sigaddset(&STATE(block), SIGCHLD); |
| |
| if (signal(SIGINT, killer) == SIG_ERR) |
| return -1; |
| |
| if (signal(SIGTERM, killer) == SIG_ERR) |
| return -1; |
| |
| /* ignore connection reset by peer */ |
| if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) |
| return -1; |
| |
| if (signal(SIGCHLD, child) == SIG_ERR) |
| return -1; |
| |
| /* Initialization */ |
| if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) |
| if (ctnl_init() < 0) |
| return -1; |
| |
| #ifdef BUILD_CTHELPER |
| if (CONFIG(flags) & CTD_HELPER) { |
| if (cthelper_init() < 0) |
| return -1; |
| } |
| #endif |
| time(&STATE(stats).daemon_start_time); |
| |
| dlog(LOG_NOTICE, "initialization completed"); |
| |
| return 0; |
| } |