summaryrefslogtreecommitdiff
path: root/src/main.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cc')
-rw-r--r--src/main.cc187
1 files changed, 187 insertions, 0 deletions
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 0000000..f9e1af6
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,187 @@
+// -*- mode: c++; c-basic-offset: 2; -*-
+
+#include "common.hh"
+
+#include <cstring>
+#include <memory>
+#include <signal.h>
+#include <unistd.h>
+
+#include "args.hh"
+#include "config.hh"
+#include "io.hh"
+#include "logger.hh"
+#include "proxy.hh"
+
+namespace {
+
+Proxy* g_proxy;
+
+void proxy_quit(int UNUSED(sig)) {
+ g_proxy->quit();
+}
+
+void proxy_reload(int UNUSED(sig)) {
+ g_proxy->reload();
+}
+
+void setup_signals(Proxy* proxy) {
+ g_proxy = proxy;
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = proxy_quit;
+ action.sa_flags = SA_RESTART;
+ sigaction(SIGINT, &action, nullptr);
+ sigaction(SIGQUIT, &action, nullptr);
+ action.sa_handler = proxy_reload;
+ sigaction(SIGHUP, &action, nullptr);
+}
+
+std::string get_cwd() {
+ std::string ret;
+ char buf[128];
+ auto ptr = getcwd(buf, 128);
+ if (ptr) {
+ ret.assign(ptr, strlen(ptr));
+ return ret;
+ }
+ if (errno != ERANGE) return ret;
+ ret.resize(1024);
+ while (true) {
+ ptr = getcwd(&ret[0], ret.size());
+ if (ptr) {
+ ret.resize(strlen(ret.data()));
+ break;
+ }
+ if (errno != ERANGE) {
+ ret.resize(0);
+ break;
+ }
+ ret.resize(ret.size() * 2);
+ }
+ return ret;
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ std::unique_ptr<Args> args(Args::create());
+ args->add('C', "config", "FILE", "load config from FILE instead of default");
+ args->add('F', "foreground", "do not fork into background and log to stdout");
+ args->add('m', "monitor", "HOST:PORT", "accept monitors on HOST:PORT");
+ args->add('h', "help", "display this text and exit.");
+ args->add('V', "version", "display version and exit.");
+ if (!args->run(argc, argv)) {
+ std::cerr << "Try `tp --help` for usage." << std::endl;
+ return EXIT_FAILURE;
+ }
+ if (args->is_set('h')) {
+ std::cout << "Usage: `tp [OPTIONS...] [BIND][:PORT]`\n"
+ << "Transparent proxy.\n"
+ << '\n';
+ args->print_help();
+ return EXIT_SUCCESS;
+ }
+ if (args->is_set('V')) {
+ std::cout << "TransparentProxy version " VERSION
+ << " written by Joel Klinghed <the_jk@yahoo.com>" << std::endl;
+ return EXIT_SUCCESS;
+ }
+ std::unique_ptr<Config> config(Config::create());
+ switch (args->arguments().size()) {
+ case 0:
+ break;
+ case 1: {
+ auto arg = args->arguments().front();
+ auto colon = arg.find(':');
+ if (colon == std::string::npos) {
+ config->set("proxy_bind", arg);
+ } else {
+ if (colon > 0) {
+ config->set("proxy_bind", arg.substr(0, colon));
+ }
+ config->set("proxy_port", arg.substr(colon + 1));
+ }
+ break;
+ }
+ default:
+ std::cerr << "Too many arguments.\n"
+ << "Try `tp --help` for usage." << std::endl;
+ return EXIT_FAILURE;
+ }
+ if (args->is_set('F')) {
+ config->set("foreground", "true");
+ }
+ auto monitor = args->arg('m', nullptr);
+ if (monitor) {
+ auto str = std::string(monitor);
+ auto colon = str.find(':');
+ if (colon == 0 || colon == std::string::npos) {
+ std::cerr << "Invalid argument to monitor, expected HOST:PORT not: "
+ << str << std::endl;
+ return EXIT_FAILURE;
+ }
+ config->set("monitor", "true");
+ config->set("monitor_bind", str.substr(0, colon));
+ config->set("monitor_port", str.substr(colon + 1));
+ }
+ auto configfile = args->arg('C', nullptr);
+ if (configfile) {
+ config->load_file(configfile);
+ } else {
+ config->load_name("tp");
+ }
+ if (!config->good()) {
+ std::cerr << "Error loading config\n"
+ << config->last_error() << std::endl;
+ return EXIT_FAILURE;
+ }
+ std::unique_ptr<Logger> logger(Logger::create_stderr());
+ std::unique_ptr<Logger> file_logger;
+ auto logfile = args->arg('l', nullptr);
+ bool logfile_from_argument;
+ if (!logfile) {
+ logfile = config->get("logfile", nullptr);
+ logfile_from_argument = false;
+ } else {
+ logfile_from_argument = true;
+ }
+ if (logfile) {
+ if (logfile[0] != '/') {
+ logger->out(Logger::ERR, "Logfile need to be an absolute path, not: %s",
+ logfile);
+ return EXIT_FAILURE;
+ }
+ file_logger.reset(Logger::create_file(logfile));
+ if (!file_logger) {
+ logger->out(Logger::ERR, "Unable to open %s for logging: %s",
+ logfile, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ }
+ io::auto_fd accept_socket(Proxy::setup_accept_socket(config.get(),
+ logger.get()));
+ if (!accept_socket) return EXIT_FAILURE;
+ io::auto_fd monitor_socket;
+ if (config->get("monitor", false)) {
+ monitor_socket.reset(Proxy::setup_monitor_socket(config.get(),
+ logger.get()));
+ }
+ auto foreground = config->get("foreground", false);
+ auto cwd = get_cwd();
+ std::unique_ptr<Proxy> proxy(
+ Proxy::create(config.get(), cwd, configfile,
+ foreground ? "bogus" :
+ (logfile_from_argument ? logfile : nullptr),
+ foreground ? logger.get() : file_logger.get(),
+ accept_socket.release(),
+ monitor_socket.release()));
+ if (!foreground) {
+ if (daemon(0, 0)) {
+ logger->out(Logger::ERR, "Failed to fork: %s", strerror(errno));
+ return EXIT_FAILURE;
+ }
+ }
+ setup_signals(proxy.get());
+ return proxy->run() ? EXIT_SUCCESS : EXIT_FAILURE;
+}