404 lines
8.8 KiB
C++
404 lines
8.8 KiB
C++
/*
|
|
* 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 Library 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.
|
|
*
|
|
* Olad.cpp
|
|
* Main file for olad, parses the options, forks if required and runs the
|
|
* daemon.
|
|
* Copyright (C) 2005-2007 Simon Newton
|
|
*
|
|
*/
|
|
|
|
#include <execinfo.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/resource.h>
|
|
#include <unistd.h>
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
#include "ola/Logging.h"
|
|
#include "olad/OlaDaemon.h"
|
|
|
|
using ola::OlaDaemon;
|
|
using std::string;
|
|
using std::cout;
|
|
using std::endl;
|
|
|
|
// the daemon
|
|
OlaDaemon *olad;
|
|
|
|
// options struct
|
|
typedef struct {
|
|
ola::log_level level;
|
|
ola::log_output output;
|
|
bool daemon;
|
|
bool help;
|
|
int httpd;
|
|
int http_quit;
|
|
int http_port;
|
|
int rpc_port;
|
|
string http_data_dir;
|
|
} ola_options;
|
|
|
|
|
|
/*
|
|
* Print a stack trace on seg fault
|
|
*/
|
|
static void sig_segv(int signo) {
|
|
enum {STACK_SIZE = 64};
|
|
void *array[STACK_SIZE];
|
|
size_t size = backtrace(array, STACK_SIZE);
|
|
|
|
cout << "Recieved SIGSEGV or SIGBUS" << endl;
|
|
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
|
exit(1);
|
|
(void) signo;
|
|
}
|
|
|
|
/*
|
|
* Terminate cleanly on interrupt
|
|
*/
|
|
static void sig_interupt(int signo) {
|
|
signo = 0;
|
|
olad->Terminate();
|
|
}
|
|
|
|
/*
|
|
* Reload plugins
|
|
*/
|
|
static void sig_hup(int signo) {
|
|
signo = 0;
|
|
olad->ReloadPlugins();
|
|
}
|
|
|
|
/*
|
|
* Change logging level
|
|
*
|
|
* need to fix race conditions here
|
|
*/
|
|
static void sig_user1(int signo) {
|
|
signo = 0;
|
|
ola::IncrementLogLevel();
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Set up the interrupt signal
|
|
*
|
|
* @return true on success, false on failure
|
|
*/
|
|
static bool InstallSignals() {
|
|
struct sigaction act, oact;
|
|
|
|
act.sa_handler = sig_interupt;
|
|
sigemptyset(&act.sa_mask);
|
|
act.sa_flags = 0;
|
|
|
|
if (sigaction(SIGINT, &act, &oact) < 0) {
|
|
OLA_WARN << "Failed to install signal SIGINT";
|
|
return false;
|
|
}
|
|
|
|
if (sigaction(SIGTERM, &act, &oact) < 0) {
|
|
OLA_WARN << "Failed to install signal SIGTERM";
|
|
return false;
|
|
}
|
|
|
|
act.sa_handler = sig_segv;
|
|
if (sigaction(SIGSEGV, &act, &oact) < 0) {
|
|
OLA_WARN << "Failed to install signal SIGSEGV";
|
|
return false;
|
|
}
|
|
if (sigaction(SIGBUS, &act, &oact) < 0) {
|
|
OLA_WARN << "Failed to install signal SIGSEGV";
|
|
return false;
|
|
}
|
|
|
|
act.sa_handler = sig_hup;
|
|
|
|
if (sigaction(SIGHUP, &act, &oact) < 0) {
|
|
OLA_WARN << "Failed to install signal SIGHUP";
|
|
return false;
|
|
}
|
|
|
|
act.sa_handler = sig_user1;
|
|
|
|
if (sigaction(SIGUSR1, &act, &oact) < 0) {
|
|
OLA_WARN << "Failed to install signal SIGUSR1";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Display the help message
|
|
*/
|
|
static void DisplayHelp() {
|
|
cout <<
|
|
"Usage: olad [options]\n"
|
|
"\n"
|
|
"Start the ola daemon.\n"
|
|
"\n"
|
|
" -d, --http-data-dir Path to the static content.\n"
|
|
" -f, --daemon Fork into background.\n"
|
|
" -h, --help Display this help message and exit.\n"
|
|
" -l, --log-level <level> Set the loggging level 0 .. 4 .\n"
|
|
" -p, --http-port Port to run the http server on (default " <<
|
|
ola::OlaServer::DEFAULT_HTTP_PORT << ")\n" <<
|
|
" -r, --rpc-port Port to listen for RPCs on (default " <<
|
|
ola::OlaDaemon::DEFAULT_RPC_PORT << ")\n" <<
|
|
" -s, --syslog Log to syslog rather than stderr.\n"
|
|
" --no-http Don't run the http server\n"
|
|
" --no-http-quit Disable the /quit handler\n"
|
|
<< endl;
|
|
}
|
|
|
|
|
|
/*
|
|
* Parse the command line options
|
|
*
|
|
* @param argc
|
|
* @param argv
|
|
* @param opts pointer to the options struct
|
|
*/
|
|
static void ParseOptions(int argc, char *argv[], ola_options *opts) {
|
|
static struct option long_options[] = {
|
|
{"help", no_argument, 0, 'h'},
|
|
{"http-data-dir", required_argument, 0, 'd'},
|
|
{"http-port", required_argument, 0, 'p'},
|
|
{"log-level", required_argument, 0, 'l'},
|
|
{"no-daemon", no_argument, 0, 'f'},
|
|
{"no-http", no_argument, &opts->httpd, 0},
|
|
{"no-http-quit", no_argument, &opts->http_quit, 0},
|
|
{"rpc-port", required_argument, 0, 'r'},
|
|
{"syslog", no_argument, 0, 's'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
int c, ll;
|
|
int option_index = 0;
|
|
|
|
while (1) {
|
|
c = getopt_long(argc, argv, "l:p:fd:hsr:", long_options, &option_index);
|
|
if (c == -1)
|
|
break;
|
|
|
|
switch (c) {
|
|
case 0:
|
|
break;
|
|
case 'd':
|
|
opts->http_data_dir = optarg;
|
|
break;
|
|
case 'f':
|
|
opts->daemon = true;
|
|
break;
|
|
case 'h':
|
|
opts->help = true;
|
|
break;
|
|
case 's':
|
|
opts->output = ola::OLA_LOG_SYSLOG;
|
|
break;
|
|
case 'l':
|
|
ll = atoi(optarg);
|
|
switch (ll) {
|
|
case 0:
|
|
// nothing is written at this level
|
|
// so this turns logging off
|
|
opts->level = ola::OLA_LOG_NONE;
|
|
break;
|
|
case 1:
|
|
opts->level = ola::OLA_LOG_FATAL;
|
|
break;
|
|
case 2:
|
|
opts->level = ola::OLA_LOG_WARN;
|
|
break;
|
|
case 3:
|
|
opts->level = ola::OLA_LOG_INFO;
|
|
break;
|
|
case 4:
|
|
opts->level = ola::OLA_LOG_DEBUG;
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
break;
|
|
case 'p':
|
|
opts->http_port = atoi(optarg);
|
|
break;
|
|
case 'r':
|
|
opts->rpc_port = atoi(optarg);
|
|
break;
|
|
case '?':
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Run as a daemon
|
|
*/
|
|
static int Daemonise() {
|
|
pid_t pid;
|
|
unsigned int i;
|
|
int fd0, fd1, fd2;
|
|
struct rlimit rl;
|
|
struct sigaction sa;
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
|
|
cout << "Could not determine file limit" << endl;
|
|
exit(1);
|
|
}
|
|
|
|
// fork
|
|
if ((pid = fork()) < 0) {
|
|
cout << "Could not fork\n" << endl;
|
|
exit(1);
|
|
} else if (pid != 0) {
|
|
exit(0);
|
|
}
|
|
|
|
// start a new session
|
|
setsid();
|
|
|
|
sa.sa_handler = SIG_IGN;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
|
|
if (sigaction(SIGHUP, &sa, NULL) < 0) {
|
|
cout << "Could not install signal\n" << endl;
|
|
exit(1);
|
|
}
|
|
|
|
if ((pid= fork()) < 0) {
|
|
cout << "Could not fork\n" << endl;
|
|
exit(1);
|
|
} else if (pid != 0) {
|
|
exit(0);
|
|
}
|
|
|
|
// close all fds
|
|
if (rl.rlim_max == RLIM_INFINITY)
|
|
rl.rlim_max = 1024;
|
|
for (i = 0; i < rl.rlim_max; i++)
|
|
close(i);
|
|
|
|
// send stdout, in and err to /dev/null
|
|
fd0 = open("/dev/null", O_RDWR);
|
|
fd1 = dup(0);
|
|
fd2 = dup(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Parse the options, and take action
|
|
*
|
|
* @param argc
|
|
* @param argv
|
|
* @param opts a pointer to the ola_options struct
|
|
*/
|
|
static void Setup(int argc, char*argv[], ola_options *opts) {
|
|
opts->level = ola::OLA_LOG_WARN;
|
|
opts->output = ola::OLA_LOG_STDERR;
|
|
opts->daemon = false;
|
|
opts->help = false;
|
|
opts->httpd = 1;
|
|
opts->http_quit = 1;
|
|
opts->http_port = ola::OlaServer::DEFAULT_HTTP_PORT;
|
|
opts->rpc_port = ola::OlaDaemon::DEFAULT_RPC_PORT;
|
|
opts->http_data_dir = "";
|
|
|
|
ParseOptions(argc, argv, opts);
|
|
|
|
if (opts->help) {
|
|
DisplayHelp();
|
|
exit(0);
|
|
}
|
|
|
|
// setup the logging
|
|
ola::InitLogging(opts->level, opts->output);
|
|
|
|
if (opts->daemon)
|
|
Daemonise();
|
|
}
|
|
|
|
|
|
static void InitExportMap(ola::ExportMap *export_map, int argc, char*argv[]) {
|
|
struct rlimit rl;
|
|
ola::StringVariable *var = export_map->GetStringVar("binary");
|
|
var->Set(argv[0]);
|
|
|
|
var = export_map->GetStringVar("cmd-line");
|
|
|
|
std::stringstream out;
|
|
for (int i = 1; i < argc; i++) {
|
|
out << argv[i] << " ";
|
|
}
|
|
var->Set(out.str());
|
|
|
|
var = export_map->GetStringVar("fd-limit");
|
|
if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
|
|
var->Set("undertermined");
|
|
} else {
|
|
std::stringstream out;
|
|
out << rl.rlim_cur;
|
|
var->Set(out.str());
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Main
|
|
*
|
|
*/
|
|
int main(int argc, char *argv[]) {
|
|
ola_options opts;
|
|
ola::ExportMap export_map;
|
|
Setup(argc, argv, &opts);
|
|
|
|
if (!geteuid()) {
|
|
OLA_FATAL << "Attempting to run as root, aborting.";
|
|
return -1;
|
|
}
|
|
|
|
InitExportMap(&export_map, argc, argv);
|
|
|
|
if (!InstallSignals())
|
|
OLA_WARN << "Failed to install signal handlers";
|
|
|
|
ola::ola_server_options ola_options;
|
|
ola_options.http_enable = opts.httpd;
|
|
ola_options.http_enable_quit = opts.http_quit;
|
|
ola_options.http_port = opts.http_port;
|
|
ola_options.http_data_dir = opts.http_data_dir;
|
|
|
|
olad = new OlaDaemon(ola_options, &export_map, opts.rpc_port);
|
|
bool ret = olad->Init();
|
|
|
|
if (ret) {
|
|
olad->Run();
|
|
}
|
|
delete olad;
|
|
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|