00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <fcntl.h>
00019 #include <errno.h>
00020 #include <string>
00021 #include <sys/time.h>
00022
00023 #include <oasys/debug/FatalSignals.h>
00024 #include <oasys/debug/Log.h>
00025 #include <oasys/io/NetUtils.h>
00026 #include <oasys/memory/Memory.h>
00027 #include <oasys/tclcmd/ConsoleCommand.h>
00028 #include <oasys/tclcmd/TclCommand.h>
00029 #include <oasys/thread/Timer.h>
00030 #include <oasys/util/Getopt.h>
00031 #include <oasys/util/Random.h>
00032 #include <oasys/util/StringBuffer.h>
00033
00034 #include "applib/APIServer.h"
00035 #include "cmd/TestCommand.h"
00036 #include "servlib/DTNServer.h"
00037 #include "storage/DTNStorageConfig.h"
00038
00039 extern const char* dtn_version;
00040
00044 namespace dtn {
00045
00049 class DTND {
00050 public:
00051 DTND();
00052 int main(int argc, char* argv[]);
00053
00054 protected:
00055 bool daemonize_;
00056 int daemonize_pipe_[2];
00057 int random_seed_;
00058 bool random_seed_set_;
00059 std::string conf_file_;
00060 bool conf_file_set_;
00061 bool print_version_;
00062 std::string loglevelstr_;
00063 oasys::log_level_t loglevel_;
00064 std::string logfile_;
00065 TestCommand* testcmd_;
00066 oasys::ConsoleCommand* consolecmd_;
00067 DTNStorageConfig storage_config_;
00068
00069 void get_options(int argc, char* argv[]);
00070 void daemonize();
00071 void notify_parent(char status);
00072 void notify_and_exit(char status);
00073 void seed_random();
00074 void init_log();
00075 void init_testcmd(int argc, char* argv[]);
00076 void run_console();
00077 };
00078
00079
00080 DTND::DTND()
00081 : daemonize_(false),
00082 random_seed_(0),
00083 random_seed_set_(false),
00084 conf_file_(""),
00085 conf_file_set_(false),
00086 print_version_(false),
00087 loglevelstr_(""),
00088 loglevel_(LOG_DEFAULT_THRESHOLD),
00089 logfile_("-"),
00090 testcmd_(NULL),
00091 consolecmd_(NULL),
00092 storage_config_("storage",
00093 "berkeleydb",
00094 "DTN",
00095 "/var/lib/dtn/db")
00096 {
00097
00098 storage_config_.db_max_tx_ = 1000;
00099
00100 daemonize_pipe_[0] = -1;
00101 daemonize_pipe_[1] = -1;
00102
00103 testcmd_ = new TestCommand();
00104 consolecmd_ = new oasys::ConsoleCommand("dtn% ");
00105 }
00106
00107
00108 void
00109 DTND::get_options(int argc, char* argv[])
00110 {
00111
00112
00113 oasys::Getopt opts;
00114 opts.addopt(
00115 new oasys::BoolOpt('v', "version", &print_version_,
00116 "print version information and exit"));
00117
00118 opts.addopt(
00119 new oasys::StringOpt('o', "output", &logfile_, "<output>",
00120 "file name for logging output "
00121 "(default - indicates stdout)"));
00122
00123 opts.addopt(
00124 new oasys::StringOpt('l', NULL, &loglevelstr_, "<level>",
00125 "default log level [debug|warn|info|crit]"));
00126
00127 opts.addopt(
00128 new oasys::StringOpt('c', "conf", &conf_file_, "<conf>",
00129 "set the configuration file", &conf_file_set_));
00130 opts.addopt(
00131 new oasys::BoolOpt('d', "daemonize", &daemonize_,
00132 "run as a daemon"));
00133
00134 opts.addopt(
00135 new oasys::BoolOpt('t', "tidy", &storage_config_.tidy_,
00136 "clear database and initialize tables on startup"));
00137
00138 opts.addopt(
00139 new oasys::BoolOpt(0, "init-db", &storage_config_.init_,
00140 "initialize database on startup"));
00141
00142 opts.addopt(
00143 new oasys::IntOpt('s', "seed", &random_seed_, "<seed>",
00144 "random number generator seed", &random_seed_set_));
00145
00146 opts.addopt(
00147 new oasys::InAddrOpt(0, "console-addr", &consolecmd_->addr_, "<addr>",
00148 "set the console listening addr (default off)"));
00149
00150 opts.addopt(
00151 new oasys::UInt16Opt(0, "console-port", &consolecmd_->port_, "<port>",
00152 "set the console listening port (default off)"));
00153
00154 opts.addopt(
00155 new oasys::IntOpt('i', 0, &testcmd_->id_, "<id>",
00156 "set the test id"));
00157
00158 int remainder = opts.getopt(argv[0], argc, argv);
00159 if (remainder != argc)
00160 {
00161 fprintf(stderr, "invalid argument '%s'\n", argv[remainder]);
00162 opts.usage("dtnd");
00163 exit(1);
00164 }
00165 }
00166
00167
00168 void
00169 DTND::daemonize()
00170 {
00171 if (!daemonize_) {
00172 return;
00173 }
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183 fclose(stdin);
00184
00185 if (pipe(daemonize_pipe_) != 0) {
00186 fprintf(stderr, "error creating pipe for daemonize process: %s",
00187 strerror(errno));
00188 exit(1);
00189 }
00190
00191 pid_t pid = fork();
00192 if (pid == -1) {
00193 fprintf(stderr, "error forking daemon process: %s",
00194 strerror(errno));
00195 exit(1);
00196 }
00197
00198 if (pid > 0) {
00199
00200
00201 close(daemonize_pipe_[1]);
00202
00203 char status;
00204 int count = read(daemonize_pipe_[0], &status, 1);
00205 if (count != 1) {
00206 fprintf(stderr, "error reading from daemon pipe: %s",
00207 strerror(errno));
00208 exit(1);
00209 }
00210
00211 close(daemonize_pipe_[1]);
00212 exit(status);
00213
00214 } else {
00215
00216
00217 close(daemonize_pipe_[0]);
00218 setsid();
00219 }
00220 }
00221
00222
00223 void
00224 DTND::notify_parent(char status)
00225 {
00226 if (daemonize_) {
00227 write(daemonize_pipe_[1], &status, 1);
00228 close(daemonize_pipe_[1]);
00229 }
00230 }
00231
00232
00233 void
00234 DTND::notify_and_exit(char status)
00235 {
00236 notify_parent(status);
00237 exit(status);
00238 }
00239
00240
00241 void
00242 DTND::seed_random()
00243 {
00244
00245 if (!random_seed_set_)
00246 {
00247 struct timeval tv;
00248 gettimeofday(&tv, NULL);
00249 random_seed_ = tv.tv_usec;
00250 }
00251
00252 log_notice_p("/dtnd", "random seed is %u\n", random_seed_);
00253 oasys::Random::seed(random_seed_);
00254 }
00255
00256
00257 void
00258 DTND::init_log()
00259 {
00260
00261 if (loglevelstr_.length() == 0)
00262 {
00263 loglevel_ = oasys::LOG_NOTICE;
00264 }
00265 else
00266 {
00267 loglevel_ = oasys::str2level(loglevelstr_.c_str());
00268 if (loglevel_ == oasys::LOG_INVALID)
00269 {
00270 fprintf(stderr, "invalid level value '%s' for -l option, "
00271 "expected debug | info | warning | error | crit\n",
00272 loglevelstr_.c_str());
00273 notify_and_exit(1);
00274 }
00275 }
00276 oasys::Log::init(logfile_.c_str(), loglevel_, "", "~/.dtndebug");
00277 oasys::Log::instance()->add_reparse_handler(SIGHUP);
00278 oasys::Log::instance()->add_rotate_handler(SIGUSR1);
00279
00280 if (daemonize_) {
00281 if (logfile_ == "-") {
00282 fprintf(stderr, "daemon mode requires setting of -o <logfile>\n");
00283 notify_and_exit(1);
00284 }
00285
00286 oasys::Log::instance()->redirect_stdio();
00287 }
00288 }
00289
00290
00291 void
00292 DTND::init_testcmd(int argc, char* argv[])
00293 {
00294 for (int i = 0; i < argc; ++i) {
00295 testcmd_->argv_.append(argv[i]);
00296 testcmd_->argv_.append(" ");
00297 }
00298
00299 testcmd_->bind_vars();
00300 oasys::TclCommandInterp::instance()->reg(testcmd_);
00301 }
00302
00303
00304 void
00305 DTND::run_console()
00306 {
00307
00308 if (consolecmd_->port_ != 0) {
00309 log_info_p("/dtnd", "starting console on %s:%d",
00310 intoa(consolecmd_->addr_), consolecmd_->port_);
00311
00312 oasys::TclCommandInterp::instance()->
00313 command_server(consolecmd_->prompt_.c_str(),
00314 consolecmd_->addr_, consolecmd_->port_);
00315 }
00316
00317 if (daemonize_ || (consolecmd_->stdio_ == false)) {
00318 oasys::TclCommandInterp::instance()->event_loop();
00319 } else {
00320 oasys::TclCommandInterp::instance()->
00321 command_loop(consolecmd_->prompt_.c_str());
00322 }
00323 }
00324
00325
00326 int
00327 DTND::main(int argc, char* argv[])
00328 {
00329 #ifdef OASYS_DEBUG_MEMORY_ENABLED
00330 oasys::DbgMemInfo::init();
00331 #endif
00332
00333 get_options(argc, argv);
00334
00335 if (print_version_)
00336 {
00337 printf("%s\n", dtn_version);
00338 exit(0);
00339 }
00340
00341 daemonize();
00342
00343 init_log();
00344
00345 log_notice_p("/dtnd", "DTN daemon starting up... (pid %d)", getpid());
00346 oasys::FatalSignals::init("dtnd");
00347
00348 if (oasys::TclCommandInterp::init(argv[0]) != 0)
00349 {
00350 log_crit_p("/dtnd", "Can't init TCL");
00351 notify_and_exit(1);
00352 }
00353
00354 seed_random();
00355
00356
00357 oasys::Thread::activate_start_barrier();
00358
00359 DTNServer* dtnserver = new DTNServer("/dtnd", &storage_config_);
00360 APIServer* apiserver = new APIServer();
00361
00362 dtnserver->init();
00363
00364 oasys::TclCommandInterp::instance()->reg(consolecmd_);
00365 init_testcmd(argc, argv);
00366
00367 if (! dtnserver->parse_conf_file(conf_file_, conf_file_set_)) {
00368 log_err_p("/dtnd", "error in configuration file, exiting...");
00369 notify_and_exit(1);
00370 }
00371
00372 if (storage_config_.init_)
00373 {
00374 log_notice_p("/dtnd", "initializing persistent data store");
00375 }
00376
00377 if (! dtnserver->init_datastore()) {
00378 log_err_p("/dtnd", "error initializing data store, exiting...");
00379 notify_and_exit(1);
00380 }
00381
00382
00383 if (storage_config_.init_ && !storage_config_.tidy_)
00384 {
00385 dtnserver->close_datastore();
00386 log_info_p("/dtnd", "database initialization complete.");
00387 notify_and_exit(0);
00388 }
00389
00390
00391
00392 notify_parent(0);
00393
00394 dtnserver->start();
00395 apiserver->bind_listen_start(apiserver->local_addr(),
00396 apiserver->local_port());
00397 oasys::Thread::release_start_barrier();
00398
00399
00400
00401 if (testcmd_->initscript_.length() != 0) {
00402 oasys::TclCommandInterp::instance()->
00403 exec_command(testcmd_->initscript_.c_str());
00404 }
00405
00406
00407
00408 oasys::Thread::yield();
00409 usleep(500000);
00410
00411 run_console();
00412
00413 log_notice_p("/dtnd", "command loop exited... shutting down daemon");
00414 oasys::TclCommandInterp::shutdown();
00415 dtnserver->shutdown();
00416
00417
00418 delete dtnserver;
00419 delete apiserver;
00420
00421
00422 oasys::Thread::yield();
00423 sleep(1);
00424
00425
00426 oasys::Log::shutdown();
00427
00428 return 0;
00429 }
00430
00431 }
00432
00433 int
00434 main(int argc, char* argv[])
00435 {
00436 dtn::DTND dtnd;
00437 dtnd.main(argc, argv);
00438 }