summaryrefslogtreecommitdiff
path: root/httpd/tntnet.cpp
diff options
context:
space:
mode:
authorSascha Volkenandt <sascha (at) akv-soft (dot) de>2007-01-02 19:18:27 +0000
committerSascha Volkenandt <sascha (at) akv-soft (dot) de>2007-01-02 19:18:27 +0000
commit48c46dfdd986ad4a7a0692d05992f7882bef6a88 (patch)
tree88a3a88a7ab43632850569cba3ab48a1924d9e52 /httpd/tntnet.cpp
downloadvdr-plugin-live-48c46dfdd986ad4a7a0692d05992f7882bef6a88.tar.gz
vdr-plugin-live-48c46dfdd986ad4a7a0692d05992f7882bef6a88.tar.bz2
- initial checkin
Diffstat (limited to 'httpd/tntnet.cpp')
-rw-r--r--httpd/tntnet.cpp832
1 files changed, 832 insertions, 0 deletions
diff --git a/httpd/tntnet.cpp b/httpd/tntnet.cpp
new file mode 100644
index 0000000..ebaad62
--- /dev/null
+++ b/httpd/tntnet.cpp
@@ -0,0 +1,832 @@
+/* tntnet.cpp
+ * Copyright (C) 2003-2005 Tommi Maekitalo
+ *
+ * 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
+ * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
+ * NON-INFRINGEMENT. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "tnt/worker.h"
+#include "tnt/tntnet.h"
+#include "tnt/listener.h"
+#include "tnt/http.h"
+#include "tnt/httpreply.h"
+#include "tnt/sessionscope.h"
+
+#include <cxxtools/tcpstream.h>
+#include <cxxtools/log.h>
+#include <cxxtools/loginit.h>
+
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <errno.h>
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef TNTNET_CONF
+# define TNTNET_CONF "/etc/tntnet.conf"
+#endif
+
+#ifndef TNTNET_PID
+# define TNTNET_PID "/var/run/tntnet.pid"
+#endif
+
+log_define("tntnet.tntnet")
+
+#define log_error_master(expr) \
+ do { \
+ std::cout << "ERROR: " << expr << std::endl; \
+ } while(false)
+
+#define log_warn_master(expr) \
+ do { \
+ std::cout << "WARN: " << expr << std::endl; \
+ } while(false)
+
+#define log_info_master(expr) \
+ do { \
+ std::cout << "INFO: " << expr << std::endl; \
+ } while(false)
+
+#define log_debug_master(expr) \
+ do { \
+ std::cout << "DEBUG: " << expr << std::endl; \
+ } while(false)
+
+namespace
+{
+ void sigEnd(int)
+ {
+ tnt::Tntnet::shutdown();
+ }
+
+ void sigReload(int)
+ {
+ // stopping child with 111 signals monitor-process to restart child
+ tnt::Tntnet::restart();
+ }
+
+ void configureDispatcher(tnt::Dispatcher& dis, const tnt::Tntconfig& config)
+ {
+ typedef tnt::Dispatcher::CompidentType CompidentType;
+
+ const tnt::Tntconfig::config_entries_type& params = config.getConfigValues();
+
+ tnt::Tntconfig::config_entries_type::const_iterator vi;
+ for (vi = params.begin(); vi != params.end(); ++vi)
+ {
+ const tnt::Tntconfig::config_entry_type& v = *vi;
+ const tnt::Tntconfig::params_type& args = v.params;
+ if (v.key == "MapUrl")
+ {
+ if (args.size() < 2)
+ {
+ std::ostringstream msg;
+ msg << "invalid number of parameters (" << args.size() << ") in MapUrl";
+ throw std::runtime_error(msg.str());
+ }
+
+ std::string url = args[0];
+
+ CompidentType ci = CompidentType(args[1]);
+ if (args.size() > 2)
+ {
+ ci.setPathInfo(args[2]);
+ if (args.size() > 3)
+ ci.setArgs(CompidentType::args_type(args.begin() + 3, args.end()));
+ }
+
+ dis.addUrlMapEntry(url, ci);
+ }
+ }
+ }
+
+ bool checkChildSuccess(int fd)
+ {
+ log_debug("checkChildSuccess");
+
+ char buffer;
+ int ret = ::read(fd, &buffer, 1);
+ if (ret < 0)
+ throw std::runtime_error(
+ std::string("error in read: ") + strerror(errno));
+ close(fd);
+ return ret > 0;
+ }
+
+ void signalParentSuccess(int fd)
+ {
+ log_debug("signalParentSuccess");
+
+ ssize_t s = write(fd, "1", 1);
+ if (s < 0)
+ throw std::runtime_error(
+ std::string("error in write(): ") + strerror(errno));
+ close(fd);
+ }
+
+}
+
+namespace tnt
+{
+ ////////////////////////////////////////////////////////////////////////
+ // Tntnet
+ //
+ bool Tntnet::stop = false;
+ int Tntnet::ret = 0;
+ std::string Tntnet::pidFileName;
+
+ Tntnet::Tntnet(int& argc, char* argv[])
+ : propertyfilename(argc, argv, 'P'),
+ debug(argc, argv, 'd'),
+ queue(1000),
+ pollerthread(queue)
+ {
+ // check for argument -c
+ cxxtools::Arg<const char*> conf(argc, argv, 'c');
+ if (conf.isSet())
+ configFile = conf;
+ else
+ {
+ // read 1st parameter from argument-list
+ cxxtools::Arg<const char*> conf(argc, argv);
+ if (conf.isSet())
+ configFile = conf;
+ else
+ {
+ // check environment-variable TNTNET_CONF
+ const char* tntnetConf = ::getenv("TNTNET_CONF");
+ if (tntnetConf)
+ configFile = tntnetConf;
+ else
+ configFile = TNTNET_CONF; // take default
+ }
+ }
+ }
+
+ void Tntnet::setGroup() const
+ {
+ Tntconfig::params_type group = config.getConfigValue("Group");
+ if (group.size() >= 1)
+ {
+ struct group * gr = getgrnam(group.begin()->c_str());
+ if (gr == 0)
+ throw std::runtime_error("unknown group " + *group.begin());
+
+ log_debug("change group to " << *group.begin() << '(' << gr->gr_gid << ')');
+
+ int ret = setgid(gr->gr_gid);
+ if (ret != 0)
+ {
+ std::ostringstream msg;
+ msg << "cannot change group to " << *group.begin()
+ << '(' << gr->gr_gid << "): " << strerror(errno);
+ throw std::runtime_error(msg.str());
+ }
+ }
+ }
+
+ void Tntnet::setDir(const char* def) const
+ {
+ std::string dir = config.getValue("Dir", def);
+
+ if (!dir.empty())
+ {
+ log_debug("chdir(" << dir << ')');
+ if (chdir(dir.c_str()) == -1)
+ {
+ throw std::runtime_error(
+ std::string("error in chdir(): ")
+ + strerror(errno));
+ }
+ }
+
+ std::string chrootdir = config.getValue("Chroot");
+ if (!chrootdir.empty() && chroot(chrootdir.c_str()) == -1)
+ throw std::runtime_error(
+ std::string("error in chroot(): ")
+ + strerror(errno));
+ }
+
+ void Tntnet::setUser() const
+ {
+ Tntconfig::params_type user = config.getConfigValue("User");
+ if (user.size() >= 1)
+ {
+ struct passwd * pw = getpwnam(user.begin()->c_str());
+ if (pw == 0)
+ throw std::runtime_error("unknown user " + *user.begin());
+
+ log_debug("change user to " << *user.begin() << '(' << pw->pw_uid << ')');
+
+ int ret = setuid(pw->pw_uid);
+ if (ret != 0)
+ {
+ std::ostringstream msg;
+ msg << "cannot change user to " << *user.begin()
+ << '(' << pw->pw_uid << "): " << strerror(errno);
+ throw std::runtime_error(msg.str());
+ }
+ }
+ }
+
+ int Tntnet::mkDaemon() const
+ {
+ log_info("start daemon-mode");
+
+ int filedes[2];
+
+ if (pipe(filedes) != 0)
+ throw std::runtime_error(
+ std::string("error in pipe(int[2]): ") + strerror(errno));
+
+ int pid = fork();
+ if (pid > 0)
+ {
+ // parent
+
+ close(filedes[1]); // close write-fd
+
+ // exit with error, when nothing read
+ ::exit (checkChildSuccess(filedes[0]) ? 0 : 1);
+ }
+ else if (pid < 0)
+ throw std::runtime_error(
+ std::string("error in fork(): ") + strerror(errno));
+
+ // child
+
+ close(filedes[0]); // close read-fd
+
+ // setsid
+ if (setsid() == -1)
+ throw std::runtime_error(
+ std::string("error in setsid(): ")
+ + strerror(errno));
+
+ // return write-fd
+ return filedes[1];
+ }
+
+ void Tntnet::closeStdHandles() const
+ {
+ // close stdin, stdout and stderr
+ bool noclosestd = config.getBoolValue("NoCloseStdout", false);
+ if (noclosestd)
+ {
+ log_debug("not closing stdout");
+ return;
+ }
+
+ if (freopen("/dev/null", "r", stdin) == 0)
+ throw std::runtime_error(
+ std::string("unable to replace stdin with /dev/null: ")
+ + strerror(errno));
+
+ if (freopen("/dev/null", "w", stdout) == 0)
+ throw std::runtime_error(
+ std::string("unable to replace stdout with /dev/null: ")
+ + strerror(errno));
+
+ if (freopen("/dev/null", "w", stderr) == 0)
+ throw std::runtime_error(
+ std::string("unable to replace stderr with /dev/null: ")
+ + strerror(errno));
+ }
+
+ int Tntnet::run()
+ {
+ loadConfiguration();
+
+ if (debug)
+ {
+ log_init_debug();
+ log_warn("Debugmode");
+ isDaemon = false;
+ }
+ else
+ {
+ isDaemon = config.getBoolValue("Daemon", false);
+ }
+
+ if (isDaemon)
+ {
+ int filedes = mkDaemon();
+
+ setDir("");
+
+ bool nomonitor = config.getBoolValue("NoMonitor", false);
+ if (nomonitor)
+ {
+ log_debug("start worker-process without monitor");
+ writePidfile(getpid());
+ initWorkerProcess();
+
+ // change group and user
+ setGroup();
+ setUser();
+
+ initLogging();
+ workerProcess(filedes);
+ }
+ else
+ {
+ initWorkerProcess();
+ do
+ {
+ int filedes_monitor[2];
+
+ if (pipe(filedes_monitor) != 0)
+ throw std::runtime_error(
+ std::string("error in pipe(int[2]): ") + strerror(errno));
+
+ // fork workerprocess
+ int pid = fork();
+ if (pid < 0)
+ throw std::runtime_error(
+ std::string("error in forking workerprocess: ")
+ + strerror(errno));
+
+ if (pid == 0)
+ {
+ // workerprocess
+
+ close(filedes_monitor[0]); // close read-fd
+
+ // change group and user
+ setGroup();
+ setUser();
+
+ initLogging();
+ workerProcess(filedes_monitor[1]);
+ return ret;
+ }
+ else
+ {
+ close(filedes_monitor[1]); // close write-fd
+
+ // write child-pid
+ writePidfile(pid);
+
+ // wait for worker to signal success
+ if (!checkChildSuccess(filedes_monitor[0]))
+ ::exit(1);
+ if (filedes >= 0)
+ {
+ signalParentSuccess(filedes);
+ filedes = -1;
+ }
+
+ monitorProcess(pid);
+ if (!stop)
+ sleep(1);
+ }
+
+ } while (!stop);
+ }
+ }
+ else
+ {
+ log_info("no daemon-mode");
+ initLogging();
+ initWorkerProcess();
+ workerProcess();
+ }
+
+ return 0;
+ }
+
+ void Tntnet::initLogging()
+ {
+ if (debug)
+ return; // logging already initialized
+
+ std::string pf;
+ if (propertyfilename.isSet())
+ pf = propertyfilename.getValue();
+ else
+ pf = config.getValue("PropertyFile");
+
+ if (pf.empty())
+ log_init();
+ else
+ {
+ struct stat properties_stat;
+ if (stat(pf.c_str(), &properties_stat) != 0)
+ throw std::runtime_error("propertyfile " + pf + " not found");
+
+ log_init(pf.c_str());
+ }
+ }
+
+ void Tntnet::writePidfile(int pid)
+ {
+ pidFileName = config.getValue("PidFile", TNTNET_PID);
+
+ log_debug("pidfile=" << pidFileName);
+
+ if (!pidFileName.empty())
+ {
+ if (pidFileName[0] != '/')
+ {
+ // prepend current working-directory to pidfilename if not absolute
+ std::vector<char> buf(256);
+ const char* cwd;
+ while (true)
+ {
+ cwd = ::getcwd(&buf[0], buf.size());
+ if (cwd)
+ break;
+ else if (errno == ERANGE)
+ buf.resize(buf.size() * 2);
+ else
+ throw std::runtime_error(
+ std::string("error in getcwd: ") + strerror(errno));
+ }
+ pidFileName = std::string(cwd) + '/' + pidFileName;
+ log_debug("pidfile=" << pidFileName);
+ }
+
+ std::ofstream pidfile(pidFileName.c_str());
+ if (!pidfile)
+ throw std::runtime_error("unable to open pid-file " + pidFileName);
+ pidfile << pid;
+ }
+ }
+
+ void Tntnet::monitorProcess(int workerPid)
+ {
+ setDir("");
+
+ // close stdin, stdout and stderr
+ closeStdHandles();
+
+ int status;
+ waitpid(workerPid, &status, 0);
+
+ if (WIFSIGNALED(status))
+ {
+ // SIGTERM means normal exit
+ if (WTERMSIG(status) == SIGTERM)
+ {
+ log_info_master("child terminated normally");
+ stop = true;
+ }
+ else
+ {
+ log_warn_master("child terminated with signal "
+ << WTERMSIG(status) << " - restart child");
+ }
+ }
+ else if (WEXITSTATUS(status) == 111)
+ {
+ log_info_master("child requested restart");
+ }
+ else
+ {
+ log_info_master("child exited with exitcode " << WEXITSTATUS(status));
+ stop = true;
+ }
+
+ if (unlink(pidFileName.c_str()) != 0)
+ log_error_master("failed to remove pidfile \"" << pidFileName << "\" error " << errno);
+ }
+
+ void Tntnet::initWorkerProcess()
+ {
+ log_debug("init workerprocess");
+
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGABRT, SIG_IGN);
+ signal(SIGTERM, sigEnd);
+ signal(SIGHUP, sigReload);
+
+ configureDispatcher(d_dispatcher, config);
+
+ // create listener-threads
+ Tntconfig::config_entries_type configListen;
+ config.getConfigValues("Listen", configListen);
+
+ if (configListen.empty())
+ {
+ log_warn("no listeners defined - using 0.0.0.0:80");
+ ListenerBase* s = new tnt::Listener("0.0.0.0", 80, queue);
+ listeners.insert(s);
+ }
+ else
+ {
+ for (Tntconfig::config_entries_type::const_iterator it = configListen.begin();
+ it != configListen.end(); ++it)
+ {
+ if (it->params.empty())
+ throw std::runtime_error("empty Listen-entry");
+
+ unsigned short int port = 80;
+ if (it->params.size() >= 2)
+ {
+ std::istringstream p(it->params[1]);
+ p >> port;
+ if (!p)
+ {
+ std::ostringstream msg;
+ msg << "invalid port " << it->params[1];
+ throw std::runtime_error(msg.str());
+ }
+ }
+
+ std::string ip(it->params[0]);
+ log_debug("create listener ip=" << ip << " port=" << port);
+ ListenerBase* s = new tnt::Listener(ip, port, queue);
+ listeners.insert(s);
+ }
+ }
+
+#ifdef USE_SSL
+ // create ssl-listener-threads
+ std::string defaultCertificateFile = config.getValue("SslCertificate");
+ std::string defaultCertificateKey = config.getValue("SslKey");
+ configListen.clear();
+ config.getConfigValues("SslListen", configListen);
+
+ for (Tntconfig::config_entries_type::const_iterator it = configListen.begin();
+ it != configListen.end(); ++it)
+ {
+ if (it->params.empty())
+ throw std::runtime_error("empty SslListen-entry");
+
+ unsigned short int port = 443;
+ if (it->params.size() >= 2)
+ {
+ std::istringstream p(it->params[1]);
+ p >> port;
+ if (!p)
+ {
+ std::ostringstream msg;
+ msg << "invalid port " << it->params[1];
+ throw std::runtime_error(msg.str());
+ }
+ }
+
+ std::string certificateFile =
+ it->params.size() >= 3 ? it->params[2]
+ : defaultCertificateFile;
+ std::string certificateKey =
+ it->params.size() >= 4 ? it->params[3] :
+ it->params.size() >= 3 ? it->params[2] : defaultCertificateKey;
+
+ if (certificateFile.empty())
+ throw std::runtime_error("Ssl-certificate not configured");
+
+ std::string ip(it->params[0]);
+ log_debug("create ssl-listener ip=" << ip << " port=" << port);
+ ListenerBase* s = new Ssllistener(certificateFile.c_str(),
+ certificateKey.c_str(), ip, port, queue);
+ listeners.insert(s);
+ }
+#endif // USE_SSL
+
+ // configure worker (static)
+ Comploader::configure(config);
+
+ // configure http
+ HttpMessage::setMaxRequestSize(
+ config.getValue("MaxRequestSize", HttpMessage::getMaxRequestSize()));
+ Job::setSocketReadTimeout(
+ config.getValue("SocketReadTimeout", Job::getSocketReadTimeout()));
+ Job::setSocketWriteTimeout(
+ config.getValue("SocketWriteTimeout", Job::getSocketWriteTimeout()));
+ Job::setKeepAliveMax(
+ config.getValue("KeepAliveMax", Job::getKeepAliveMax()));
+ Job::setSocketBufferSize(
+ config.getValue("BufferSize", Job::getSocketBufferSize()));
+ HttpReply::setMinCompressSize(
+ config.getValue("MinCompressSize", HttpReply::getMinCompressSize()));
+ HttpReply::setKeepAliveTimeout(
+ config.getValue("KeepAliveTimeout", HttpReply::getKeepAliveTimeout()));
+ HttpReply::setDefaultContentType(
+ config.getValue("DefaultContentType", HttpReply::getDefaultContentType()));
+
+ log_debug("listeners.size()=" << listeners.size());
+ }
+
+ void Tntnet::workerProcess(int filedes)
+ {
+ log_debug("worker-process");
+
+ // reload configuration
+ config = Tntconfig();
+ loadConfiguration();
+
+ // initialize worker-process
+ minthreads = config.getValue<unsigned>("MinThreads", 5);
+ maxthreads = config.getValue<unsigned>("MaxThreads", 100);
+ threadstartdelay = config.getValue<unsigned>("ThreadStartDelay", 10);
+ Worker::setMinThreads(minthreads);
+ Worker::setMaxRequestTime(config.getValue<unsigned>("MaxRequestTime", Worker::getMaxRequestTime()));
+ Worker::setEnableCompression(config.getBoolValue("EnableCompression", Worker::getEnableCompression()));
+ queue.setCapacity(config.getValue<unsigned>("QueueSize", queue.getCapacity()));
+ Sessionscope::setDefaultTimeout(config.getValue<unsigned>("SessionTimeout", Sessionscope::getDefaultTimeout()));
+ Listener::setBacklog(config.getValue<int>("ListenBacklog", Listener::getBacklog()));
+ Listener::setListenRetry(config.getValue<int>("ListenRetry", Listener::getListenRetry()));
+ Dispatcher::setMaxUrlMapCache(config.getValue<unsigned>("MaxUrlMapCache", Dispatcher::getMaxUrlMapCache()));
+
+ Tntconfig::config_entries_type configSetEnv;
+ config.getConfigValues("SetEnv", configSetEnv);
+ for (Tntconfig::config_entries_type::const_iterator it = configSetEnv.begin();
+ it != configSetEnv.end(); ++it)
+ {
+ if (it->params.size() >= 2)
+ {
+#ifdef HAVE_SETENV
+ log_debug("setenv " << it->params[0] << "=\"" << it->params[1] << '"');
+ ::setenv(it->params[0].c_str(), it->params[1].c_str(), 1);
+#else
+ std::string name = it->params[0];
+ std::string value = it->params[1];
+
+ char* env = new char[name.size() + value.size() + 2];
+ name.copy(env, name.size());
+ env[name.size()] = '=';
+ value.copy(env + name.size() + 1, value.size());
+ env[name.size() + value.size() + 1] = '\0';
+
+ log_debug("putenv(" << env);
+ ::putenv(env);
+#endif
+ }
+ }
+
+ // create worker-threads
+ log_info("create " << minthreads << " worker threads");
+ for (unsigned i = 0; i < minthreads; ++i)
+ {
+ log_debug("create worker " << i);
+ Worker* s = new Worker(*this);
+ s->create();
+ }
+
+ // create poller-thread
+ log_debug("start poller thread");
+ pollerthread.create();
+
+ // launch listener-threads
+ log_info("create " << listeners.size() << " listener threads");
+ for (listeners_type::iterator it = listeners.begin();
+ it != listeners.end(); ++it)
+ (*it)->create();
+
+ log_debug("start timer thread");
+ cxxtools::MethodThread<Tntnet, cxxtools::AttachedThread> timerThread(*this, &Tntnet::timerTask);
+ timerThread.create();
+
+ if (filedes >= 0)
+ {
+ signalParentSuccess(filedes);
+ closeStdHandles();
+ }
+
+ // mainloop
+ cxxtools::Mutex mutex;
+ while (!stop)
+ {
+ {
+ cxxtools::MutexLock lock(mutex);
+ queue.noWaitThreads.wait(lock);
+ }
+
+ if (stop)
+ break;
+
+ if (Worker::getCountThreads() < maxthreads)
+ {
+ log_info("create workerthread");
+ Worker* s = new Worker(*this);
+ s->create();
+ }
+ else
+ log_warn("max worker-threadcount " << maxthreads << " reached");
+
+ if (threadstartdelay > 0)
+ usleep(threadstartdelay);
+ }
+
+ log_warn("stopping Tntnet");
+
+ // join-loop
+ while (!listeners.empty())
+ {
+ listeners_type::value_type s = *listeners.begin();
+ log_debug("remove listener from listener-list");
+ listeners.erase(s);
+
+ log_debug("request listener to stop");
+ s->doStop();
+
+ log_debug("join listener-thread");
+ s->join();
+ delete s;
+
+ log_debug("listener stopped");
+ }
+
+ log_info("listeners stopped");
+ }
+
+ void Tntnet::timerTask()
+ {
+ log_debug("timer thread");
+
+ while (!stop)
+ {
+ sleep(1);
+
+ log_debug("check sessiontimeout");
+ getScopemanager().checkSessionTimeout();
+
+ log_debug("worker-timer");
+ Worker::timer();
+ }
+
+ log_warn("stopping Tntnet");
+
+ if (!pidFileName.empty())
+ unlink(pidFileName.c_str());
+
+ queue.noWaitThreads.signal();
+ Worker::setMinThreads(0);
+ pollerthread.doStop();
+ }
+
+ void Tntnet::loadConfiguration()
+ {
+ config = Tntconfig();
+ config.load(configFile.c_str());
+ }
+
+ void Tntnet::shutdown()
+ {
+ stop = true;
+ }
+
+ void Tntnet::restart()
+ {
+ // stopping child with 111 signals monitor-process to restart child
+ stop = true;
+ ret = 111;
+ }
+
+}
+
+////////////////////////////////////////////////////////////////////////
+// main
+//
+#ifdef HAVE_TNTNET_MAIN
+int main(int argc, char* argv[])
+{
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGABRT, SIG_IGN);
+ signal(SIGTERM, sigEnd);
+ std::ios::sync_with_stdio(false);
+
+ try
+ {
+ tnt::Tntnet app(argc, argv);
+ if (argc != 1)
+ {
+ std::cout << PACKAGE_STRING "\n\n" <<
+ "usage: " << argv[0] << " {options}\n\n"
+ " -c file configurationfile (default: " TNTNET_CONF ")\n"
+ " -d enable all debug output (ignoring properties-file)\n";
+ return -1;
+ }
+
+ return app.run();
+ }
+ catch(const std::exception& e)
+ {
+ log_fatal(e.what());
+ std::cerr << e.what() << std::endl;
+ return -1;
+ }
+}
+#endif