text: #include "global.h"
  #include "shutdown.h"
  #include "RoleFactory.h"
  #include "config/constraints.h"
  
  #include <cstring>
  #include <string>  // std::string
  #include <vector>  // std::vector
  #include <fstream> // std::ifstream
  #ifdef __linux__
  #include <signal.h>
  #include <pthread.h> // for setting thread-local storage size
  #endif
  #include "util/TaskQueue.h"
  #include "util/filesystem.h"
  
  using namespace std;
  
  static LogCategory mainlog("main", "Main");
  
  static ConfigGroup general_config("general");
  static ConfigVariable<vector<string> > dc_files("dc_files", vector<string>(), general_config);
  static ConfigVariable<string> eventlogger_addr("eventlogger", "", general_config);
  static ValidAddressConstraint valid_eventlogger_addr(eventlogger_addr);
  
  static ConfigList uberdogs_config("uberdogs");
  static ConfigVariable<doid_t> uberdog_id("id", INVALID_DO_ID, uberdogs_config);
  static ConfigVariable<string> uberdog_class("class", "", uberdogs_config);
  static ConfigVariable<bool> uberdog_anon("anonymous", false, uberdogs_config);
  static InvalidDoidConstraint id_not_invalid(uberdog_id);
  static ReservedDoidConstraint id_not_reserved(uberdog_id);
  static BooleanValueConstraint anonymous_is_boolean(uberdog_anon);
  
  static void printHelp(ostream &s);
  static void printCompiledOptions(ostream &s);
  
  int main(int argc, char *argv[])
  {
      string cfg_file;
  
  #ifdef _WIN32
      bool prettyPrint = false;
  #else
      bool prettyPrint = true;
  #endif
  
  #ifdef __linux__
      // This is a bit of a kludge, but it's necessary:
      // We need to make sure that we don't exceed the default TLS threshold with stdlibs such as musl.
      pthread_attr_t attr;
  
      if(pthread_attr_init(&attr))
          return 1;
  
      // 2 << 20 is the glibc default (page aligned).
      if(pthread_attr_setstacksize(&attr, 2 << 20))
          return 1;
  
      if(pthread_setattr_default_np(&attr))
          return 1;
  
      // We need to ignore SIGPIPE issues ourselves for Linux (libuv issue #1254)
      signal(SIGPIPE, SIG_IGN);
  #endif
  
      int config_arg_index = -1;
      cfg_file = "otpd.yml";
      LogSeverity sev = g_logger->get_min_severity();
      for(int i = 1; i < argc; i++) {
          if((strcmp(argv[i], "--log") == 0 || strcmp(argv[i], "-L") == 0) && i + 1 < argc) {
              g_logger.reset(new Logger(argv[++i], sev));
          } else if((strcmp(argv[i], "--loglevel") == 0 || strcmp(argv[i], "-l") == 0) && i + 1 < argc) {
              string llstr(argv[++i]);
              if(llstr == "packet") {
                  sev = LSEVERITY_PACKET;
                  g_logger->set_min_severity(sev);
              } else if(llstr == "trace") {
                  sev = LSEVERITY_TRACE;
                  g_logger->set_min_severity(sev);
              } else if(llstr == "debug") {
                  sev = LSEVERITY_DEBUG;
                  g_logger->set_min_severity(sev);
              } else if(llstr == "info") {
                  sev = LSEVERITY_INFO;
                  g_logger->set_min_severity(sev);
              } else if(llstr == "warning") {
                  sev = LSEVERITY_INFO;
                  g_logger->set_min_severity(sev);
              } else if(llstr == "security") {
                  sev = LSEVERITY_SECURITY;
                  g_logger->set_min_severity(sev);
              } else if(llstr != "error" && llstr != "fatal") {
                  cerr << "Unknown log-level \"" << llstr << "\"." << endl;
                  printHelp(cerr);
                  return 1;
              }
          } else if(strcmp(argv[i], "--pretty") == 0 || strcmp(argv[i], "-p") == 0) {
              prettyPrint = true;
          } else if(strcmp(argv[i], "--boring") == 0 || strcmp(argv[i], "-b") == 0) {
              prettyPrint = false;
          } else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
              printHelp(cout);
              return 0;
          } else if(strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
              cout << "The OTP Server\n"
  
  #ifdef GIT_SHA1
                   "Revision: " << GIT_SHA1 << "\n"
  #else
                   "Revision: NOT-IN-GIT\n"
  #endif
                   "Compiled at " << __TIME__ << " on " << __DATE__ << endl;
  
              printCompiledOptions(cout);
  
              return 0;
          } else if(argv[i][0] == '-') {
              cerr << "Unrecognized option \"" << string(argv[i]) << "\".\n";
              printHelp(cerr);
              return 1;
          } else {
              if(config_arg_index != -1) {
                  cerr << "Received additional positional argument \""
                       << string(argv[i]) << "\" but can only accept one."
                       << "\n  First positional: \""
                       << string(argv[config_arg_index]) << "\".\n";
              } else {
                  config_arg_index = i;
              }
          }
      }
  
      g_loop = uvw::Loop::getDefault();
      g_main_thread_id = std::this_thread::get_id();
  
      g_logger->set_color_enabled(prettyPrint);
  
      if(config_arg_index != -1) {
          cfg_file = argv[config_arg_index];
  
          // seperate path
          string filename = fs::filename(cfg_file);
          string dir_str = fs::parent_of(cfg_file);
          string cur_dir = fs::current_path();
  
          if(cur_dir == "") {
              mainlog.fatal() << "Failed to get current working directory.\n";
              return 1;
          }
  
          if(dir_str == cur_dir) {
              // we're already in the directory that the configuration is under.
              dir_str = "";
          }
  
          // change directory
          if(!dir_str.empty()) {
              if(!fs::current_path(dir_str)) {
                  mainlog.fatal() << "Could not change working directory to config directory.\n";
                  return 1;
              }
          }
  
          cfg_file = filename;
      }
  
      if(!fs::file_exists(cfg_file) || !fs::is_readable(cfg_file)) {
          mainlog.fatal() << "Config file " << cfg_file << " does not exist or isn't readable.\n";
          return 1;
      }
  
      mainlog.info() << "Reading configuration and DC files...\n";
  
      ifstream file(cfg_file.c_str());
      if(!file.is_open()) {
          mainlog.fatal() << "Failed to open configuration file \"" << cfg_file << "\".\n";
          return 1;
      }
  
      if(!g_config->load(file)) {
          mainlog.fatal() << "Errors parsing YAML configuration file \"" << cfg_file << "\"!\n";
          return 1;
      }
      file.close();
  
      if(!ConfigGroup::root().validate(g_config->copy_node())) {
          mainlog.fatal() << "Configuration file contains errors.\n";
          return 1;
      }
  
      std::vector<std::string> dc_file_names = dc_files.get_val();
      for(auto it = dc_file_names.begin(); it != dc_file_names.end(); ++it)
      {
          if(!g_dcf->read(*it))
          {
              mainlog.fatal() << "Could not read DC file " << *it << std::endl;
              return 1;
          }
      }
  
      // Now hook up our speciailize signal handler
      otp_handle_signals();
  
      try {
          TaskQueue::singleton.init_queue();
          // Initialize configured MessageDirector
          MessageDirector::singleton.init_network();
          g_eventsender.init(eventlogger_addr.get_val());
  
          // Load uberdog metadata from configuration
          ConfigNode udnodes = g_config->copy_node()["uberdogs"];
          if(!udnodes.IsNull()) {
              for(auto it = udnodes.begin(); it != udnodes.end(); ++it) {
                  ConfigNode udnode = *it;
                  Uberdog ud;
  
                  // Get the uberdog's class
                  ud.dcc = g_dcf->get_class_by_name(udnode["class"].as<std::string>());
                  if(!ud.dcc) {
                      // Make sure it exists
                      mainlog.fatal() << "For uberdog " << udnode["id"].as<doid_t>()
                                      << " Distributed class " << udnode["class"].as<std::string>()
                                      << " does not exist!" << std::endl;
                      return 1;
                  }
  
                  // Setup uberdog
                  ud.anonymous = udnode["anonymous"].as<bool>();
                  g_uberdogs[udnode["id"].as<doid_t>()] = ud;
              }
          }
  
          // Initialize configured roles
          ConfigNode node = g_config->copy_node();
          node = node["roles"];
          for(auto it = node.begin(); it != node.end(); ++it) {
              RoleFactory::singleton().instantiate_role((*it)["type"].as<std::string>(), *it);
          }
      }
      // This exception is propogated if otp_shutdown is called
      catch(const ShutdownException& e) {
          return e.exit_code();
      }
  
      // Log that the daemon has been initialized.
      mainlog.info() << "Started OTP server.\n";
  
      // Run the main event loop
      int exit_code = 0;
      try {
          g_loop->run();
      }
  
      // This exception is propogated if otp_shutdown is called
      catch(const ShutdownException& e) {
          exit_code = e.exit_code();
      }
  
      // Catch any other exception that propogates
      catch(const exception &e) {
          mainlog.fatal() << "Uncaught exception from the main event loop: "
                          << e.what() << endl;
          return 1;
      }
  
      return exit_code;
  }
  
  // printHelp outputs the cli help-text to the given stream.
  void printHelp(ostream &s)
  {
      s << "Usage:    otp_server [options]... [CONFIG_FILE]\n"
        "\n"
        "otp_server is a distributed server daemon.\n"
        "By default the OTP server looks for a configuration file in the current\n"
        "working directory as \"otpd.yml\".  A different config file path\n"
        "can be specified as a positional argument.\n"
        "\n"
        "-h, --help      Print this help dialog.\n"
        "-v, --version   Print Version, Module and Compilation Information\n"
        "-L, --log       Specify a file to write log messages to.\n"
        "-p, --pretty    Enables colored pretty printing. \n"
        "-b, --boring    Disables colored pretty printing. \n"
        "-l, --loglevel  Specify the minimum log level that should be logged;\n"
        "                  Security, Error, and Fatal will always be logged;\n"
  #ifdef OTP_DEBUG_MESSAGES
        "                (available): packet, trace, debug, info, warning, security\n"
  #else
        "                (available): info, warning, security\n"
        "                (unavailable): packet, trace, debug\n"
        "                        [build with -DCMAKE_BUILD_TYPE=Debug]\n"
  #endif
        "\n"
        "Example:\n"
        "    otp_server /tmp/my_config_file.yaml\n"
        "\n";
      s.flush();
  }
  
  void printCompiledOptions(ostream &s)
  {
      s << "Compilation options: "
  
        //If on, datagrams and dclass fields will use 32-bit length tags instead of 16-bit.
  #ifdef OTP_32BIT_DATAGRAMS
        "32-bit length tag Datagrams"
  #else
        "16-bit length tag Datagrams"
  #endif
  
        << "\n";
  
      //Now print what parts are compiled in.
      s << "Components: "
  #ifdef BUILD_STATESERVER
        "State Server "
  #ifdef BUILD_STATESERVER_DBSS
        "(With DBSS capablities)"
  #endif //End DBSS
        ", "
  #endif //End SS
  
  #ifdef BUILD_EVENTLOGGER
        "Event Logger, "
  #endif
  
  #ifdef BUILD_CLIENTAGENT
        "Client Agent, "
  #endif
  
  #ifdef BUILD_BUNGEECORD
        "Bungee Cord, "
  #endif
  
  #ifdef BUILD_DBSERVER
        "Database "
  
  #ifdef BUILD_DB_YAML
        "(With YAML Support) "
  #endif //End DB_YAML
  
  #endif //End DBSERVER
        "\n";
  
  
  
  }
  
  // http://stackoverflow.com/a/34207323
  #if defined(_MSC_VER) && (_MSC_VER < 1900)
  struct VS2013_threading_fix
  {
      VS2013_threading_fix()
      {
          _Cnd_do_broadcast_at_thread_exit();
      }
  } threading_fix;
  #endif
  