/*- * Copyright (c) 2001, 2002, 2004 Lev Walkin . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: main.c,v 1.44 2004/09/17 05:37:58 vlm Exp $ */ #include "ipcad.h" #include "cfgvar.h" #include "servers.h" #include "opt.h" #include "pidfile.h" #include "storage.h" enum phase { DRAW_CHROOT, DRAW_SETXID, }; int draw_restricted_envir(enum phase); /* 0: chroot(), 1: set?id() */ void terminate_threads(); void sigalarm(int z) { signal(z, sigalarm); } void set_display_now(int z) { display_now = 1; signal(z, set_display_now); } void sigquit(int z) { signoff_now = 1; signal(z, sigquit); } int save_on_exit = 0; double self_started; int main(int ac, char **av) { struct timeval tv; char *config_file = CONFIG_FILE; int restore = 0; int disable_servers = 0; int pidfile_fd = -1; int devnull_fd = -1; int c; unsigned i; gettimeofday(&tv, NULL); self_started = tv.tv_sec + (double)tv.tv_usec / 1000000; while((c = getopt(ac, av, "dhc:rsSv")) != -1) switch(c) { case 'c': config_file = optarg; if(!config_file) usage(); break; case 'd': daemon_mode = 1; break; case 'h': usage(); break; case 'r': restore = 1; break; case 's': save_on_exit = 1; break; case 'S': disable_servers = 1; break; case 'v': fprintf(stderr, IPCAD_VERSION_STRING IPCAD_COPYRIGHT "\n"); exit(EX_USAGE); default: usage(); } if(init_pthread_options()) { fprintf(stderr, "Can't initialize thread options.\n"); exit(EX_OSERR); } /* * Set defaults. */ conf->set_gid = (gid_t)-1; conf->set_uid = (uid_t)-1; conf->rsh_ttl = 3; /* IP TTL for RSH */ conf->netflow_version = 5; conf->netflow_timeout_active = 30 * 60; /* Seconds */ conf->netflow_timeout_inactive = 15; /* Seconds */ conf->netflow_packet_interval = 1; /* packets of packets */ for(i = 0; i < sizeof(agr_portmap)/sizeof(agr_portmap[0]); i++) agr_portmap[i] = i; /* Default is 1:1 mapping. */ /* * Open /dev/null for daemonizing. */ devnull_fd = open(_PATH_DEVNULL, O_RDWR, 0); if(devnull_fd == -1) { fprintf(stderr, "Can't open " _PATH_DEVNULL ": %s\n", strerror(errno)); exit(EX_OSERR); } /* * Read specified or default configuration file. */ if(cfgread(config_file)) exit(EX_NOINPUT); /******************************/ /* Process configuration data */ /******************************/ /* * Simple checks */ if(conf->packet_sources_head == NULL) { fprintf(stderr, "No interfaces initialized.\n"); exit(EX_NOINPUT); } if(save_on_exit || restore) { if(conf->dump_table_file == NULL) { fprintf(stderr, "Dump file is not defined.\n"); exit(EX_NOINPUT); } } /* * Simple deeds. */ /* Pre-open certain files before doing chroot(). */ if(ifst_preopen()) exit(EX_OSERR); /* Security */ if(draw_restricted_envir(DRAW_CHROOT)) /* Function will cry loudly if anything wrong */ exit(EX_DATAERR); /* PID file */ if(conf->pidfile) { pidfile_fd = make_pid_file(conf->pidfile); if(pidfile_fd == -1) { fprintf(stderr, "Can't initialize pid file %s%s%s: %s\n", conf->chroot_to?conf->chroot_to:"", conf->chroot_to?"/":"", conf->pidfile, strerror(errno)); if(conf->chroot_to) { fprintf(stderr, "Make sure you have %s under %s " "used as new root. man 2 chroot.\n", dirname(conf->pidfile), conf->chroot_to); } exit(EX_DATAERR); } } time(&active_storage.create_time); time(&netflow_storage.create_time); /* Restore previously saved table */ if(restore) { import_table(conf->dump_table_file, stderr, 1); } /* Daemon mode should be entered BEFORE threads created. */ if(daemon_mode) { fflush(NULL); switch(fork()) { case -1: perror("ipcad"); exit(EX_OSERR); case 0: break; default: _exit(0); } if(setsid() == -1) { perror("setsid() failed"); exit(EX_OSERR); } fprintf(stderr, "Daemonized.\n"); fflush(stderr); /* As a remainder */ (void)dup2(devnull_fd, 0); (void)dup2(devnull_fd, 1); (void)dup2(devnull_fd, 2); if(devnull_fd > 2) close(devnull_fd); /* Update pid value */ if(pidfile_fd != -1) { (void)write_pid_file(pidfile_fd, getpid(), NULL); /* Ignore virtually impossible errors */ } } /***********************************/ /* Set appropriate signal handlers */ /***********************************/ /* * Ignore signals, by default */ for(c = 1; c < 32; c++) { switch(c) { case SIGCHLD: /* * Setting SIG_IGN breaks threading support * in Linux 2.4.20-20.9 */ case SIGSEGV: case SIGBUS: /* * No use to continue after SIGSEGV/SIGBUS. * Program must not generate SIGSEGV/SIGBUS * in the first place. */ continue; } signal(c, SIG_IGN); } signal(SIGALRM, sigalarm); signal(SIGINT, set_display_now); signal(SIGHUP, set_display_now); signal(SIGQUIT, sigquit); signal(SIGPIPE, SIG_IGN); signal(SIGTERM, sigquit); signal(SIGTTIN, SIG_IGN); siginterrupt(SIGALRM, 1); /*******************************************/ /* Start servers to serve clients requests */ /*******************************************/ if(disable_servers == 0) { if( start_servers() != 0 ) { fprintf(stderr, "Failed to start one or more servers.\n"); exit(EX_OSERR); } } #ifdef HAVE_SETPRIORITY /* * Set nice, but safe priority. * Required to process high loads without * significant packet loss. */ setpriority(PRIO_PROCESS, 0, -15); #endif /* Security */ if(draw_restricted_envir(DRAW_SETXID)) /* Function will cry loudly if anything wrong */ exit(EX_DATAERR); /* * The main working loop. */ process_packet_sources(conf->packet_sources_head); /* Termination */ terminate_threads(); if(save_on_exit) make_dump(conf->dump_table_file, stderr); /* Leave the pid file gracefully */ if(pidfile_fd != -1) { int fd = pidfile_fd; pidfile_fd = -1; /* Empty the .pid file */ (void)write_pid_file(fd, 0, ""); close(fd); } fprintf(stderr, "Quit.\n"); return 0; } /* * Block signals that must be handled by the main thread only. */ int block_certain_signals(sigset_t *old_set) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGINT); sigaddset(&set, SIGHUP); sigaddset(&set, SIGQUIT); sigaddset(&set, SIGTERM); return sigprocmask(SIG_BLOCK, &set, old_set); } int draw_restricted_envir(enum phase phase) { if(phase == DRAW_CHROOT) { if(conf->chroot_to == NULL) /* We were not told to do chroot() */ return 0; if(conf->chroot_to[0] != '/') /* Chroot directory is not an absolute pathname */ return -1; if(chroot(conf->chroot_to) != 0) { fprintf(stderr, "Can't do chroot(%s): %s\n", conf->chroot_to, strerror(errno)); return -1; } if(chdir("/") != 0) { perror("Can't do chdir(/)"); return -1; } } else if(phase == DRAW_SETXID) { if(conf->set_gid != (gid_t)-1) { if(setgid(conf->set_gid) == -1) { perror("Can't do setgid()"); return -1; } } if(conf->set_uid != (uid_t)-1) { if(setuid(conf->set_uid) == -1) { perror("Can't do setuid()"); return -1; } } } return 0; } void terminate_threads() { packet_source_t *ps; end_servers(); printf("Signalling interface threads"); for(ps = conf->packet_sources_head; ps; ps = ps->next) { if(!ps->thid) continue; printf("."); fflush(stdout); #ifdef HAVE_PTHREAD_CANCEL /* * When libpcap is being used, the dispatcher function * does not return when -1/EINTR is received from the * capture device socket. It just retries to read, * blocking the whole process. * This pthread_cancel() ensures that the thread will * be killed doing such a nasty loop. */ pthread_cancel(ps->thid); #endif /* Notify the process */ pthread_kill(ps->thid, SIGALRM); } printf("\nWaiting for interface processing threads to terminate...\n"); for(ps = conf->packet_sources_head; ps; ps = ps->next) { if(!ps->thid) continue; if(pthread_join(ps->thid, NULL)) { if(errno == EINVAL) printf("Thread processing %s " "is not a joinable thread.\n", IFNameBySource(ps)); if(errno == ESRCH) printf("No thread running " "for processing %s\n", IFNameBySource(ps)); if(errno == EDEADLK) /* Impossible state */ printf("Deadlock avoided " "for thread processing %s\n", IFNameBySource(ps)); } else { printf("Thread processing %s terminated.\n", IFNameBySource(ps)); } } } /* * Code for people Сын: - Папа давай в цирк сходим. Отец: - Отстань. - Ну давай сходим. - Да отстань от меня! - Па, а там голые тети на львах катаются. - Ну ладно пойдем, а то давненько я уже львов не видел. */