/* * bitz-server, An ICAP server implementation in C++ * Copyright (C) 2012 Uditha Atukorala * * 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 3 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include "bitz-server.h" namespace bitz { namespace server { static server_t globals; void init() { // initialise defaults globals.pid_handle = -1; globals.manager = NULL; globals.terminating = 0; globals.daemon = false; // logger (syslog) setlogmask( LOG_UPTO( LOG_INFO ) ); openlog( PACKAGE_NAME, LOG_CONS, LOG_USER ); // signal handlers init_signal_handlers(); } void init_signal_handlers() { sigset_t ignore_mask; // set signal mask - signals we want to block sigemptyset(&ignore_mask); sigaddset(&ignore_mask, SIGTSTP); // ignore Tty stop signals sigaddset(&ignore_mask, SIGTTOU); // ignore Tty background writes sigaddset(&ignore_mask, SIGTTIN); // ignore Tty background reads sigprocmask(SIG_BLOCK, &ignore_mask, NULL); // block the above specified signals init_sigchld_handler(); init_sigterm_handler(); init_sigquit_handler(); init_sigint_handler(); } void init_sigchld_handler() { struct sigaction sa; // block all other signals sigfillset( &sa.sa_mask ); // signal handler proc sa.sa_sigaction = &sigchld_handler; // flags sa.sa_flags = SA_SIGINFO; if ( sigaction( SIGCHLD, &sa, NULL ) < 0 ) { exit( EXIT_FAILURE ); } } void sigchld_handler( int sig, siginfo_t *siginfo, void *context ) { pid_t worker_pid; int status; std::cout << "[" << getpid() << "] inside zombie deleter: "; while ( ( worker_pid = waitpid( WAIT_ANY, &status, WNOHANG ) ) > 0 ) { std::cout << "child " << worker_pid << " terminated with status " << status << std::endl; if ( globals.manager != NULL ) { globals.manager->reap_worker( worker_pid ); } } } void init_sigterm_handler() { struct sigaction sa; // block all other signals sigfillset( &sa.sa_mask ); // signal handler proc sa.sa_sigaction = &sigterm_handler; // flags sa.sa_flags = SA_SIGINFO; if ( sigaction( SIGTERM, &sa, NULL ) < 0 ) { exit( EXIT_FAILURE ); } } void sigterm_handler( int sig, siginfo_t *siginfo, void *context ) { std::cout << "[" << getpid() << "] inside SIGTERM handler" << std::endl; termination_handler( sig, siginfo, context ); } void init_sigquit_handler() { struct sigaction sa; // block all other signals sigfillset( &sa.sa_mask ); // signal handler proc sa.sa_sigaction = &sigquit_handler; // flags sa.sa_flags = SA_SIGINFO; if ( sigaction( SIGQUIT, &sa, NULL ) < 0 ) { exit( EXIT_FAILURE ); } } void sigquit_handler( int sig, siginfo_t *siginfo, void *context ) { std::cout << "[" << getpid() << "] inside SIGQUIT handler" << std::endl; termination_handler( sig, siginfo, context ); } void init_sigint_handler() { struct sigaction sa; // block all other signals sigfillset( &sa.sa_mask ); // signal handler proc sa.sa_sigaction = &sigint_handler; // flags sa.sa_flags = SA_SIGINFO; if ( sigaction( SIGINT, &sa, NULL ) < 0 ) { exit( EXIT_FAILURE ); } } void sigint_handler( int sig, siginfo_t *siginfo, void *context ) { std::cout << "[" << getpid() << "] inside SIGQINT handler" << std::endl; termination_handler( sig, siginfo, context ); } void daemonize( const char *rundir, const char *pidfile ) { pid_t pid, sid; long i; char str[10]; // notify syslog( LOG_NOTICE, "starting daemon (version %s)", PACKAGE_VERSION ); // check parent process id value if ( getppid() == 1 ) { // we are already a daemon return; } /* fork daemon */ pid = fork(); if ( pid < 0 ) { exit( EXIT_FAILURE ); } // exit the parent if ( pid > 0 ) { exit( EXIT_SUCCESS ); } /* child (a.k.a daemon) continues */ // set file permissions (750) umask( 027 ); // get a new process group sid = setsid(); if ( sid < 0 ) { exit(EXIT_FAILURE); } // route I/O connections close( STDIN_FILENO ); close( STDOUT_FILENO ); close( STDERR_FILENO ); // change running directory chdir( rundir ); /* lock pid file to ensure we have only one copy */ globals.pid_handle = open( pidfile, O_RDWR | O_CREAT, 0600 ); if ( globals.pid_handle == -1 ) { syslog( LOG_ERR, "could not open pid lock file: %s", pidfile ); exit( EXIT_FAILURE ); } if ( lockf( globals.pid_handle, F_TLOCK, 0 ) == -1 ) { syslog( LOG_ERR, "could not lock pid lock file: %s", pidfile); exit( EXIT_FAILURE ); } // get and format pid sprintf( str, "%d\n", getpid() ); // write pid to lockfile write( globals.pid_handle, str, strlen( str ) ); // update status globals.daemon = true; } void shutdown() { // notify if ( globals.daemon && ( getppid() == 1 ) ) { syslog( LOG_NOTICE, "shutting down daemon (version %s)", PACKAGE_VERSION ); } // close pid file if ( globals.pid_handle != -1 ) { close( globals.pid_handle ); } // close logger (syslog) closelog(); } void termination_handler( int sig, siginfo_t * sig_info, void * context ) { std::cout << "[" << getpid() << "] inside termination handler" << std::endl; // exit by re-raising the signal if termination // already in progress if ( globals.terminating ) { std::cout << "[" << getpid() << "] already terminating" << std::endl; raise( sig ); } // update termination status globals.terminating = 1; if ( globals.manager != NULL ) { // shutdown the manager globals.manager->shutdown(); // clean-up delete globals.manager; } // cleanup shutdown(); // re-raise the signal after reactivating the signal's default action signal( sig, SIG_DFL ); raise( sig ); } options_t read_options( int argc, char **argv ) { int optidx, optchar; options_t options; options.config_file = ""; struct option lopts[] = { { "config", required_argument, 0, 'c' }, { "debug", no_argument, &options.debug_flag, 1 }, { "help", no_argument, 0, 'h' }, { "usage", no_argument, 0, 'h' }, { "version", no_argument, 0, 'v' }, { 0, 0, 0, 0 } }; while ( true ) { optidx = 0; optchar = getopt_long( argc, argv, "c:hv", lopts, &optidx ); // sanity check if ( optchar == -1 ) { break; } switch ( optchar ) { case 0: // TODO: break; case 'c': options.config_file = optarg; break; case 'h': print_usage(); exit( EXIT_SUCCESS ); break; case 'v': print_version(); exit( EXIT_SUCCESS ); case '?': print_usage(); exit( EXIT_FAILURE ); break; default: // TODO: break; } } return options; } void print_version() { std::cout << PACKAGE_STRING << std::endl; std::cout << "" << std::endl; std::cout << "Copyright (C) 2012-2013 Uditha Atukorala" << std::endl; std::cout << "" << std::endl; std::cout << "This program is free software; you can redistribute it and/or modify" << std::endl; std::cout << "it under the terms of the GNU General Public License as published by" << std::endl; std::cout << "the Free Software Foundation; either version 3 of the License, or" << std::endl; std::cout << "(at your option) any later version." << std::endl; std::cout << "" << std::endl; std::cout << "This program is distributed in the hope that it will be useful," << std::endl; std::cout << "but WITHOUT ANY WARRANTY; without even the implied warranty of" << std::endl; std::cout << "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" << std::endl; std::cout << "GNU General Public License (http://gnu.org/licenses/gpl.html)" << std::endl; std::cout << "for more details." << std::endl; std::cout << "" << std::endl; } void print_usage() { std::cout << "usage: " << PACKAGE_NAME << " " << "[--version] [--help] [--usage] [--debug] [--config=]" << std::endl; std::cout << "" << std::endl; std::cout << "See the man pages for more information" << std::endl; } void start( int port, unsigned int children, int max_requests, int comm_timeout ) { try { globals.manager = new bitz::Manager( port ); globals.manager->spawn( children, max_requests, comm_timeout ); } catch ( bitz::ManagerException &mex ) { syslog( LOG_ERR, "failed to start, exception: %s", mex.what() ); exit( EXIT_FAILURE ); } } void run() { sigset_t mask, oldmask; // sanity check if ( globals.manager == NULL ) { return; } // block termination signals until we are ready sigemptyset( &mask ); sigaddset( &mask, SIGTERM ); sigaddset( &mask, SIGQUIT ); sigaddset( &mask, SIGINT ); sigprocmask ( SIG_BLOCK, &mask, &oldmask ); // loop until a termination signal is received while (! globals.terminating ) { // capture any signals sigsuspend( &oldmask ); // unblock termination signals sigprocmask( SIG_UNBLOCK, &mask, NULL ); // manage workers globals.manager->manager_workers(); // block termination signals sigprocmask( SIG_BLOCK, &mask, &oldmask ); } } } /* end of namespace server */ } /* end of namespace bitz */