480 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			480 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include <dlfcn.h>
 | |
| #include <client/linux/handler/exception_handler.h>
 | |
| #include <iostream>
 | |
| #include <misc/strobf.h>
 | |
| #include <CXXTerminal/QuickTerminal.h>
 | |
| #include <event2/thread.h>
 | |
| #include <log/LogUtils.h>
 | |
| #include <ThreadPool/Timer.h>
 | |
| 
 | |
| #include "src/Configuration.h"
 | |
| #include "src/VirtualServer.h"
 | |
| #include "src/InstanceHandler.h"
 | |
| #include "src/server/QueryServer.h"
 | |
| #include "src/terminal/CommandHandler.h"
 | |
| #include "src/client/InternalClient.h"
 | |
| #include "src/SignalHandler.h"
 | |
| #include "src/build.h"
 | |
| 
 | |
| using namespace std;
 | |
| using namespace std::chrono;
 | |
| 
 | |
| #define BUILD_CREATE_TABLE(tblName, types) "CREATE TABLE IF NOT EXISTS `" tblName "` (" types ")"
 | |
| 
 | |
| #define CREATE_TABLE(table, types)   \
 | |
| result = sql::command(sqlData, BUILD_CREATE_TABLE(table, types)).execute();\
 | |
| if(!result){\
 | |
|     logger::logger(0)->critical("Could not setup sql tables. Command '{}' returns {}", BUILD_CREATE_TABLE(table, types), result.fmtStr());\
 | |
|     goto stopApp;\
 | |
| }
 | |
| 
 | |
| bool mainThreadActive = true;
 | |
| bool mainThreadDone = false;
 | |
| 
 | |
| 
 | |
| ts::server::InstanceHandler* serverInstance = nullptr;
 | |
| 
 | |
| extern void testTomMath();
 | |
| 
 | |
| #ifndef FUCK_CLION
 | |
|     #define DB_NAME_BEG "TeaData"
 | |
|     #define DB_NAME_END ".sqlite"
 | |
|     #define DB_NAME DB_NAME_BEG DB_NAME_END
 | |
| #else
 | |
|     #define DB_NAME "TeaData.sqlite"
 | |
| #endif
 | |
| 
 | |
| #include <codecvt>
 | |
| #include "src/client/music/internal_provider/channel_replay/ChannelProvider.h"
 | |
| 
 | |
| class CLIParser {
 | |
|     public:
 | |
|         CLIParser (int &argc, char **argv){
 | |
|             for (int i = 1; i < argc; i++)
 | |
|                 this->tokens.emplace_back(argv[i]);
 | |
|         }
 | |
| 
 | |
|         std::deque<std::string> getCmdOptions(const std::string &option) const {
 | |
|             std::deque<std::string> result;
 | |
| 
 | |
|             auto itr = this->tokens.begin();
 | |
|             while(true) {
 | |
|                 itr = std::find(itr, this->tokens.end(), option);
 | |
|                 if (itr != this->tokens.end() && ++itr != this->tokens.end()){
 | |
|                     result.push_back(*itr);
 | |
|                     itr++;
 | |
|                 } else break;
 | |
|             }
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         std::deque<std::string> getCmdOptionsBegins(const std::string &option) const {
 | |
|             std::deque<std::string> result;
 | |
| 
 | |
|             for(const auto& token : this->tokens)
 | |
|                 if(token.find(option) == 0)
 | |
|                     result.push_back(token);
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         const std::string& get_option(const std::string &option) const {
 | |
|             auto itr =  std::find(this->tokens.begin(), this->tokens.end(), option);
 | |
|             if (itr != this->tokens.end() && ++itr != this->tokens.end()){
 | |
|                 return *itr;
 | |
|             }
 | |
|             static const std::string empty_string;
 | |
|             return empty_string;
 | |
|         }
 | |
| 
 | |
|         bool cmdOptionExists(const std::string &option) const{
 | |
|             return std::find(this->tokens.begin(), this->tokens.end(), option) != this->tokens.end();
 | |
|         }
 | |
|     private:
 | |
|         std::vector <std::string> tokens;
 | |
| };
 | |
| 
 | |
| /* addr is where the exception identifier is stored
 | |
|    id is the exception identifier.  */
 | |
| void __raise_exception (void **addr, void *id);
 | |
| 
 | |
| #define T(address) \
 | |
| std::cout << "Testing: " << address << " => "; \
 | |
| {\
 | |
|     sockaddr_storage storage;\
 | |
|     net::resolve_address(address, storage);\
 | |
|     std::cout << manager.contains(storage) << std::endl;\
 | |
| }
 | |
| 
 | |
| #define CONFIG_NAME "config.yml"
 | |
| const char *malloc_conf = ""; //retain:false"; //,dirty_decay_ms:0";
 | |
| int main(int argc, char** argv) {
 | |
| #ifdef HAVE_JEMALLOC
 | |
|     (void*) malloc_conf;
 | |
| #endif
 | |
| 
 | |
|     CLIParser arguments(argc, argv);
 | |
|     SSL_load_error_strings();
 | |
|     OpenSSL_add_ssl_algorithms();
 | |
|     ts::permission::setup_permission_resolve();
 | |
| 
 | |
|     {
 | |
|         auto evthread_use_pthreads_result = evthread_use_pthreads();
 | |
|         assert(evthread_use_pthreads_result == 0);
 | |
|         (void) evthread_use_pthreads_result;
 | |
|     }
 | |
| 
 | |
|     if(!arguments.cmdOptionExists("--no-terminal")) {
 | |
|         terminal::install();
 | |
|         if(!terminal::active()){ cerr << "could not setup terminal!" << endl; return -1; }
 | |
|     }
 | |
| 
 | |
|     if(arguments.cmdOptionExists("--help") || arguments.cmdOptionExists("-h")) {
 | |
|         #define HELP_FMT "  {} {} | {}"
 | |
|         logMessageFmt(true, LOG_GENERAL, "Available command line parameters:");
 | |
|         logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-h", "--help", "Shows this page");
 | |
|         logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-q", "--set_query_password", "Changed the server admin query password");
 | |
|         logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-P<property>=<value>", "--property:<property>=<value>", "Override a config value manual");
 | |
|         logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-l", "--property-list", "List all available properties");
 | |
|         terminal::uninstall();
 | |
|         return 0;
 | |
|     }
 | |
|     if(arguments.cmdOptionExists("--property-list") || arguments.cmdOptionExists("-l")) {
 | |
|         logMessageFmt(true, LOG_GENERAL, "Available properties:");
 | |
|         auto properties = ts::config::create_bindings();
 | |
|         for(const auto& property : properties) {
 | |
|             logMessageFmt(true, LOG_GENERAL, "  " + property->key);
 | |
|             for(const auto& entry : property->description) {
 | |
|                 if(entry.first.empty()) {
 | |
|                     for(const auto& line : entry.second)
 | |
|                         logMessageFmt(true, LOG_GENERAL, "    " + line);
 | |
|                 } else {
 | |
|                     logMessageFmt(true, LOG_GENERAL, "    " + entry.first + ":");
 | |
|                     for(const auto& line : entry.second)
 | |
|                         logMessageFmt(true, LOG_GENERAL, "      " + line);
 | |
|                 }
 | |
|             }
 | |
|             logMessageFmt(true, LOG_GENERAL, "    " + property->value_description());
 | |
|         }
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     if(!arguments.cmdOptionExists("--valgrind")) {
 | |
|         ts::syssignal::setup();
 | |
|     }
 | |
|     ts::syssignal::setup_threads();
 | |
| 
 | |
|     map<string, string> override_settings;
 | |
|     {
 | |
|         auto short_override = arguments.getCmdOptionsBegins("-P");
 | |
|         for(const auto& entry : short_override) {
 | |
|             if(entry.length() < 2) continue;
 | |
|             auto ei = entry.find('=');
 | |
|             if(ei == string::npos || ei == 2) {
 | |
|                 logErrorFmt(true, LOG_GENERAL, "Invalid command line parameter. (\"" + entry + "\")");
 | |
|                 return 1;
 | |
|             }
 | |
| 
 | |
|             auto key = entry.substr(2, ei - 2);
 | |
|             auto value = entry.substr(ei + 1);
 | |
|             override_settings[key] = value;
 | |
|         }
 | |
|     }
 | |
|     {
 | |
|         auto short_override = arguments.getCmdOptionsBegins("--property:");
 | |
|         for(const auto& entry : short_override) {
 | |
|             if(entry.length() < 11) continue;
 | |
|             auto ei = entry.find('=');
 | |
|             if(ei == string::npos || ei == 11) {
 | |
|                 logErrorFmt(true, LOG_GENERAL, "Invalid command line parameter. (\"" + entry + "\")");
 | |
|                 return 1;
 | |
|             }
 | |
| 
 | |
|             auto key = entry.substr(11, ei - 11);
 | |
|             auto value = entry.substr(ei + 1);
 | |
|             override_settings[key] = value;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     {
 | |
|         auto bindings = ts::config::create_bindings();
 | |
|         for(const auto& setting : bindings) {
 | |
|             for(auto it = override_settings.begin(); it != override_settings.end(); it++) {
 | |
|                 if(it->first == setting->key) {
 | |
|                     try {
 | |
|                         setting->read_argument(it->second);
 | |
|                     } catch(const std::exception& ex) {
 | |
|                         logErrorFmt(true, LOG_GENERAL, "Failed to apply value for given property '" + it->first + "': " + ex.what());
 | |
|                     }
 | |
|                     override_settings.erase(it);
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         for(const auto& entry : override_settings) {
 | |
|             logMessageFmt(true, LOG_GENERAL, "Missing property " + entry.first + ". Value unused!");
 | |
|         }
 | |
|     }
 | |
|     /*
 | |
|     std::string error;
 | |
|     if(!interaction::waitForAttach(error)){
 | |
|         cerr << "Rsult: " << error << endl;
 | |
|     }
 | |
| 
 | |
|     while(interaction::memoryInfo()){
 | |
|         usleep(1 * 1000 * 1000);
 | |
|         logMessage("Current instances: " + to_string(interaction::memoryInfo()->instanceCount) + "/" + to_string(interaction::memoryInfo()->maxInstances));
 | |
|     }
 | |
| 
 | |
|     interaction::removeMemoryHook();
 | |
|      if(true) return 0;
 | |
|      */
 | |
| 
 | |
|     {
 | |
|         //http://git.mcgalaxy.de/WolverinDEV/tomcrypt/blob/develop/src/misc/crypt/crypt_inits.c#L40-86
 | |
|         std::string descriptors = "LTGE";
 | |
|         bool crypt_init = false;
 | |
|         for(const auto& c : descriptors)
 | |
|             if((crypt_init |= crypt_mp_init(&c)))
 | |
|                 break;
 | |
|         if(!crypt_init) {
 | |
|             logCritical(LOG_GENERAL, "Could not initialise libtomcrypt mp descriptors!");
 | |
|             return 1;
 | |
|         }
 | |
|         if(register_prng(&sprng_desc) == -1) {
 | |
|             logCritical(LOG_GENERAL, "could not setup prng");
 | |
|             return EXIT_FAILURE;
 | |
|         }
 | |
|         if (register_cipher(&rijndael_desc) == -1) {
 | |
|             logCritical(LOG_GENERAL, "could not setup rijndael");
 | |
|             return EXIT_FAILURE;
 | |
|         }
 | |
|         testTomMath();
 | |
|     }
 | |
| 
 | |
|     ts::server::SqlDataManager* sql = nullptr;
 | |
|     std::string errorMessage;
 | |
|     shared_ptr<logger::LoggerConfig> logConfig = nullptr;
 | |
|     std::string line;
 | |
| 
 | |
|     logMessageFmt(true, LOG_GENERAL, "Loading configuration");
 | |
|     auto cfgErrors = ts::config::parseConfig(CONFIG_NAME);
 | |
|     if(!cfgErrors.empty()){
 | |
|         logErrorFmt(true, LOG_GENERAL, "Could not load configuration. Errors: (" + to_string(cfgErrors.size()) + ")");
 | |
|         for(const auto& entry : cfgErrors)
 | |
|             logErrorFmt(true, LOG_GENERAL, " - {}", entry);
 | |
|         logErrorFmt(true, LOG_GENERAL, "Stopping server...");
 | |
|         goto stopApp;
 | |
|     }
 | |
| 
 | |
|     logMessage(LOG_GENERAL, "Setting up logging");
 | |
|     logConfig = make_shared<logger::LoggerConfig>();
 | |
|     logConfig->logfileLevel = (spdlog::level::level_enum) ts::config::log::logfileLevel;
 | |
|     logConfig->terminalLevel = (spdlog::level::level_enum) ts::config::log::terminalLevel;
 | |
|     logConfig->file_colored = ts::config::log::logfileColored;
 | |
|     logConfig->logPath = ts::config::log::path;
 | |
|     logConfig->vs_group_size = ts::config::log::vs_size;
 | |
|     logConfig->sync = !terminal::instance();
 | |
| 
 | |
|     logger::setup(logConfig);
 | |
|     threads::timer::function_log = [](const std::string& message, bool debug) {
 | |
|         auto msg = message.find('\n') == std::string::npos ? message : message.substr(0, message.find('\n'));
 | |
|         if(debug)
 | |
|             debugMessage(LOG_GENERAL, msg);
 | |
|         else
 | |
|             logWarning(LOG_GENERAL, msg);
 | |
|     };
 | |
| 
 | |
|     logger::updateLogLevels();
 | |
|     if(ts::config::license_original && ts::config::license_original->data.type != license::LicenseType::DEMO){
 | |
|         logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string());
 | |
|         logMessageFmt(true, LOG_GENERAL, strobf("  §aThank you for buying the TeaSpeak-§lPremium-§aSoftware!  ").string());
 | |
|         logMessageFmt(true, LOG_GENERAL, strobf("  §aLicense information:").string());
 | |
|         logMessageFmt(true, LOG_GENERAL, strobf("     §aLicense owner  : §e").string() + ts::config::license_original->owner());
 | |
|         logMessageFmt(true, LOG_GENERAL, strobf("     §aLicense type   : §e").string() + license::LicenseTypeNames[ts::config::license_original->data.type]);
 | |
| 
 | |
|         if(ts::config::license_original->end().time_since_epoch().count() == 0){
 | |
|             logMessageFmt(true, LOG_GENERAL, strobf("     §aLicense expires: §enever").string());
 | |
|         } else {
 | |
|             char timeBuffer[32];
 | |
|             time_t t = duration_cast<seconds>(ts::config::license_original->end().time_since_epoch()).count();
 | |
|             tm* stime = localtime(&t);
 | |
|             strftime(timeBuffer, 32, "%c", stime);
 | |
|             logMessageFmt(true, LOG_GENERAL, strobf("     §aLicense expires: §e").string() + string(timeBuffer));
 | |
|         }
 | |
|         logMessageFmt(true, LOG_GENERAL, string() + strobf("     §aLicense valid  : ").string() + (ts::config::license_original->isValid() ? strobf("§ayes").string() : strobf("§cno").string()));
 | |
|         logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string());
 | |
|     }
 | |
| 
 | |
|     {
 | |
|         rlimit rlimit{0, 0};
 | |
|         //forum.teaspeak.de/index.php?threads/2570/
 | |
|         constexpr auto seek_help_message = "Fore more help visit the forum and read this thread (https://forum.teaspeak.de/index.php?threads/2570/).";
 | |
|         if(getrlimit(RLIMIT_NOFILE, &rlimit) != 0) {
 | |
|             //prlimit -n4096 -p pid_of_process
 | |
|             logWarningFmt(true, LOG_INSTANCE, "Failed to get open file rlimit ({}). Please ensure its over 16384.", strerror(errno));
 | |
|             logWarningFmt(true, LOG_INSTANCE, seek_help_message);
 | |
|         } else {
 | |
|             const auto original = rlimit.rlim_cur;
 | |
|             rlimit.rlim_cur = std::max(rlimit.rlim_cur, std::min(rlimit.rlim_max, (rlim_t) 16384));
 | |
|             if(original != rlimit.rlim_cur) {
 | |
|                 if(setrlimit(RLIMIT_NOFILE, &rlimit) != 0) {
 | |
|                     logErrorFmt(true, LOG_INSTANCE, "Failed to set open file rlimit to {} ({}). Please ensure its over 16384.", rlimit.rlim_cur, strerror(errno));
 | |
|                     logWarningFmt(true, LOG_INSTANCE, seek_help_message);
 | |
|                     goto rlimit_updates;
 | |
|                 }
 | |
|             }
 | |
|             if(rlimit.rlim_cur < 16384) {
 | |
|                 logWarningFmt(true, LOG_INSTANCE, "Open file rlimit is bellow 16384 ({}). Please increase the system file descriptor limits.", rlimit.rlim_cur);
 | |
|                 logWarningFmt(true, LOG_INSTANCE, seek_help_message);
 | |
|             }
 | |
|         }
 | |
|         rlimit_updates:;
 | |
|     }
 | |
| 
 | |
|     logMessage(LOG_GENERAL, "Starting TeaSpeak-Server v{}", build::version()->string(true));
 | |
|     logMessage(LOG_GENERAL, "Starting music providers");
 | |
| 
 | |
|     if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§aloading music§7]");
 | |
|     if(ts::config::music::enabled && !arguments.cmdOptionExists("--no-providers")) {
 | |
|         ::music::manager::loadProviders("providers");
 | |
|         ::music::manager::register_provider(::music::provider::ChannelProvider::create_provider());
 | |
|     }
 | |
| 
 | |
|     if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§aloading geoloc§7]");
 | |
| 
 | |
|     if(!ts::config::geo::staticFlag) {
 | |
|         if(ts::config::geo::type == geoloc::PROVIDER_SOFTWARE77)
 | |
|             geoloc::provider = new geoloc::Software77Provider(ts::config::geo::mappingFile);
 | |
|         else if(ts::config::geo::type == geoloc::PROVIDER_IP2LOCATION)
 | |
|             geoloc::provider = new geoloc::IP2LocationProvider(ts::config::geo::mappingFile);
 | |
|         else {
 | |
|             logCritical(LOG_GENERAL,"Invalid geo resolver type!");
 | |
|         }
 | |
|         if(geoloc::provider && !geoloc::provider->load(errorMessage)) {
 | |
|             logCritical(LOG_GENERAL,"Could not setup geoloc! Fallback to default flag!");
 | |
|             logCritical(LOG_GENERAL,"Message: {}", errorMessage);
 | |
|             geoloc::provider = nullptr;
 | |
|             errorMessage = "";
 | |
|         }
 | |
|     }
 | |
|     if(ts::config::geo::vpn_block) {
 | |
|         geoloc::provider_vpn = new geoloc::IPCatBlocker(ts::config::geo::vpn_file);
 | |
| 
 | |
|         if(geoloc::provider_vpn && !geoloc::provider_vpn->load(errorMessage)) {
 | |
|             logCritical(LOG_GENERAL,"Could not setup vpn detector!");
 | |
|             logCritical(LOG_GENERAL,"Message: {}", errorMessage);
 | |
|             geoloc::provider_vpn = nullptr;
 | |
|             errorMessage = "";
 | |
|         }
 | |
|     }
 | |
|     if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§aloading sql§7]");
 | |
| 
 | |
|     sql = new ts::server::SqlDataManager();
 | |
|     if(!sql->initialize(errorMessage)) {
 | |
|         logCriticalFmt(true, LOG_GENERAL, "Could not initialize SQL!");
 | |
|         if(errorMessage.find("database is locked") != string::npos) {
 | |
|             logCriticalFmt(true, LOG_GENERAL, "----------------------------[ ATTENTION ]----------------------------");
 | |
|             logCriticalFmt(true, LOG_GENERAL, "{:^69}", "You're database is already in use!");
 | |
|             logCriticalFmt(true, LOG_GENERAL, "{:^69}", "Stop the other instance first!");
 | |
|             logCriticalFmt(true, LOG_GENERAL, "----------------------------[ ATTENTION ]----------------------------");
 | |
|         } else {
 | |
|             logCriticalFmt(true, LOG_GENERAL, errorMessage);
 | |
|         }
 | |
|         goto stopApp;
 | |
|     }
 | |
| 
 | |
|     if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§astarting instance§7]");
 | |
| 
 | |
|     serverInstance = new ts::server::InstanceHandler(sql); //if error than mainThreadActive = false
 | |
|     if(!mainThreadActive || !serverInstance->startInstance())
 | |
|         goto stopApp;
 | |
| 
 | |
|     if(arguments.cmdOptionExists("-q") || arguments.cmdOptionExists("--set_query_password")) {
 | |
|         auto password = arguments.cmdOptionExists("-q") ? arguments.get_option("-q") : arguments.get_option("--set_query_password");
 | |
|         if(!password.empty()) {
 | |
|             logMessageFmt(true, LOG_GENERAL, "Updating server admin query password to \"{}\"", password);
 | |
|             auto account = serverInstance->getQueryServer()->find_query_account_by_name("serveradmin");
 | |
|             if(!account) {
 | |
|                 logErrorFmt(true, LOG_GENERAL, "Failed to update server admin query password! Login does not exists!");
 | |
|             } else {
 | |
|                 if(!serverInstance->getQueryServer()->change_query_password(account, password)) {
 | |
|                     logErrorFmt(true, LOG_GENERAL, "Failed to update server admin query password! (Internal error)");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if(terminal::instance()) terminal::instance()->setPrompt("§7> §f");
 | |
|     while(mainThreadActive) {
 | |
|         usleep(5 * 1000);
 | |
| 
 | |
|         if(terminal::instance()) {
 | |
|             if(terminal::instance()->linesAvailable() > 0){
 | |
|                 while(!(line = terminal::instance()->readLine("§7> §f")).empty())
 | |
|                     threads::Thread(THREAD_DETACHED, [line](){ terminal::chandler::handleCommand(line); });
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     stopApp:
 | |
|     logMessageFmt(true, LOG_GENERAL, "Stopping application");
 | |
|     ::music::manager::finalizeProviders();
 | |
| 
 | |
|     if(serverInstance)
 | |
|         serverInstance->stopInstance();
 | |
|     delete serverInstance;
 | |
|     serverInstance = nullptr;
 | |
| 
 | |
|     ts::music::MusicBotManager::shutdown();
 | |
|     if(sql)
 | |
|         sql->finalize();
 | |
|     delete sql;
 | |
|     logMessageFmt(true, LOG_GENERAL, "Application suspend successful!");
 | |
| 
 | |
|     logger::uninstall();
 | |
|     if(terminal::active())
 | |
|         terminal::uninstall();
 | |
|     mainThreadDone = true;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Fix for Virtuzzo 7 where sometimes the pthread create fails! */
 | |
| 
 | |
| typedef int (*pthread_create_t)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
 | |
| pthread_create_t original_pthread_create{nullptr};
 | |
| int	pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) {
 | |
|     if(!original_pthread_create) {
 | |
|         original_pthread_create = (pthread_create_t) dlsym(RTLD_NEXT, "pthread_create");
 | |
|         if(!original_pthread_create) {
 | |
|             std::cerr << "[CRITICAL] Missing original pthread_create function. Aborting execution!" << std::endl;
 | |
|             std::abort();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     int result, attempt{0}, sleep{5};
 | |
|     while((result = original_pthread_create(thread, attr, start_routine, arg)) != 0 && errno == EAGAIN) {
 | |
|         if(attempt > 55) {
 | |
|             std::cerr << "[CRITICAL] pthread_create(...) cause EAGAIN for the last 50 attempts (~4.7seconds)! Aborting application execution!" << std::endl;
 | |
|             std::abort();
 | |
|         } else if(attempt > 5) {
 | |
|             /* let some other threads do work */
 | |
|             pthread_yield();
 | |
|         } else if(attempt == 0) {
 | |
|             std::string message{"[CRITICAL] Failed to spawn thread (Resource temporarily unavailable). Trying to recover."};
 | |
|             std::cerr << message << std::endl;
 | |
|         }
 | |
| 
 | |
|         //std::string message{"[CRITICAL] pthread_create(...) cause EAGAIN! Trying again in " + std::to_string(sleep) + "usec (Attempt: " + std::to_string(attempt) + ")"};
 | |
|         //std::cerr << message << std::endl;
 | |
|         usleep(sleep);
 | |
|         attempt++;
 | |
|         sleep = (int) (sleep * 1.25);
 | |
|     }
 | |
|     if(attempt > 0) {
 | |
|         std::string message{"[CRITICAL] Successfully recovered from pthread_create() EAGAIN error. Took " + std::to_string(attempt) + " attempts."};
 | |
|         std::cerr << message << std::endl;
 | |
|     }
 | |
|     return result;
 | |
| } |