//============================================================================= // YHTTPD // Main Program //============================================================================= // system #include #include #include #include #include #include #include #include #include // yhttpd #include "yconfig.h" #include #include #include #ifdef Y_CONFIG_USE_YPARSER #include static CyParser yParser; #endif //----------------------------------------------------------------------------- // Setting yhttpd Instance //----------------------------------------------------------------------------- #include "yhttpd.h" static Cyhttpd *yhttpd = NULL; CStringList Cyhttpd::ConfigList; //============================================================================= // HOOKS: Definition & Instance for Hooks, attach/detach Hooks //============================================================================= #ifdef Y_CONFIG_USE_AUTHHOOK #include static CmAuth *auth = NULL; #endif #ifdef Y_CONFIG_USE_WEBLOG #include static CmWebLog *weblog = NULL; #endif #ifdef Y_CONFIG_USE_SENDFILE #include static CmodSendfile *mod_sendfile = NULL; #endif #ifdef Y_CONFIG_USE_CACHE #include static CmodCache mod_cache; // static instance #endif //----------------------------------------------------------------------------- #if defined(CONFIG_SYSTEM_TUXBOX) || defined(CONFIG_SYSTEM_TUXBOX_COOLSTREAM) #include "neutrinoapi.h" #include static CNeutrinoAPI *NeutrinoAPI; #endif //============================================================================= // Main: Main Entry, Command line passing, Webserver Instance creation & Loop //============================================================================= volatile sig_atomic_t Cyhttpd::sig_do_shutdown = 0; //----------------------------------------------------------------------------- // Signal Handling //----------------------------------------------------------------------------- #ifdef Y_CONFIG_BUILD_AS_DAEMON static void sig_catch(int msignal) { aprintf("!!! SIGNAL !!! :%d!\n",msignal); switch (msignal) { // case SIGTERM: // case SIGINT: case SIGPIPE: aprintf("got signal PIPE, nice!\n"); break; case SIGHUP: case SIGUSR1: aprintf("got signal HUP/USR1, reading config\n"); if (yhttpd) yhttpd->ReadConfig(); break; default: aprintf("No special SIGNAL-Handler:%d!\n",msignal); // log_level_printf(1, "Got SIGTERM\n"); Cyhttpd::sig_do_shutdown = 1; yhttpd->stop_webserver(); delete yhttpd; exit(EXIT_SUCCESS); //FIXME: return to main() some way... break; } } #endif //----------------------------------------------------------------------------- void yhttpd_reload_config() { if (yhttpd) yhttpd->ReadConfig(); } //----------------------------------------------------------------------------- // Main Entry //----------------------------------------------------------------------------- void thread_cleanup (void *p) { Cyhttpd *y = (Cyhttpd *)p; if (y) { y->stop_webserver(); delete y; } y = NULL; } #ifndef Y_CONFIG_BUILD_AS_DAEMON void * nhttpd_main_thread(void *) { set_threadname(__func__); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); aprintf("Webserver %s tid %ld\n", WEBSERVERNAME, syscall(__NR_gettid)); yhttpd = new Cyhttpd(); //CLogging::getInstance()->setDebug(true); //CLogging::getInstance()->LogLevel = 9; if (!yhttpd) { aprintf("Error initializing WebServer\n"); return (void *) EXIT_FAILURE; } /* we pthread_cancel this thread from the main thread, but still want to clean up */ pthread_cleanup_push(thread_cleanup, yhttpd); #ifndef Y_CONFIG_FEATURE_THREADING yhttpd->flag_threading_off = true; #endif yhttpd->hooks_attach(); yhttpd->ReadConfig(); if (yhttpd->Configure()) { // Start Webserver: fork ist if not in debug mode aprintf("Webserver starting...\n"); dprintf("Start in Debug-Mode\n"); // non forked debugging loop yhttpd->run(); } pthread_cleanup_pop(0); delete yhttpd; yhttpd = NULL; aprintf("Main end\n"); return (void *) EXIT_SUCCESS; } #endif #ifdef Y_CONFIG_BUILD_AS_DAEMON int main(int argc, char **argv) { aprintf("Webserver %s\n", WEBSERVERNAME); bool do_fork = true; yhttpd = new Cyhttpd(); if(!yhttpd) { aprintf("Error initializing WebServer\n"); return EXIT_FAILURE; } for (int i = 1; i < argc; i++) { if ((!strncmp(argv[i], "-d", 2)) || (!strncmp(argv[i], "--debug", 7))) { CLogging::getInstance()->setDebug(true); do_fork = false; } else if ((!strncmp(argv[i], "-f", 2)) || (!strncmp(argv[i], "--fork", 6))) { do_fork = false; } else if ((!strncmp(argv[i], "-h", 2)) || (!strncmp(argv[i], "--help", 6))) { yhttpd->usage(stdout); return EXIT_SUCCESS; } else if ((!strncmp(argv[i], "-v", 2)) || (!strncmp(argv[i],"--version", 9))) { yhttpd->version(stdout); return EXIT_SUCCESS; } else if ((!strncmp(argv[i], "-t", 2)) || (!strncmp(argv[i],"--thread-off", 12))) { yhttpd->flag_threading_off = true; } else if ((!strncmp(argv[i], "-l", 2)) ) { if(argv[i][2] >= '0' && argv[i][2] <= '9') CLogging::getInstance()->LogLevel = (argv[i][2]-'0'); } else { yhttpd->usage(stderr); return EXIT_FAILURE; } } // setup signal catching (subscribing) signal(SIGPIPE, sig_catch); signal(SIGINT, sig_catch); signal(SIGHUP, sig_catch); signal(SIGUSR1, sig_catch); signal(SIGTERM, sig_catch); signal(SIGCLD, SIG_IGN); // signal(SIGALRM, sig_catch); yhttpd->hooks_attach(); yhttpd->ReadConfig(); if(yhttpd->Configure()) { // Start Webserver: fork ist if not in debug mode aprintf("Webserver starting...\n"); if (do_fork) { log_level_printf(9,"do fork\n"); switch (fork()) { case -1: dperror("fork"); return -1; case 0: break; default: return EXIT_SUCCESS; } if (setsid() == -1) { dperror("Error setsid"); return EXIT_FAILURE; } } dprintf("Start in Debug-Mode\n"); // non forked debugging loop yhttpd->run(); } delete yhttpd; aprintf("Main end\n"); return EXIT_SUCCESS; } #endif //============================================================================= // Class yhttpd //============================================================================= Cyhttpd::Cyhttpd() { webserver = new CWebserver(); flag_threading_off = false; } //----------------------------------------------------------------------------- Cyhttpd::~Cyhttpd() { if (webserver) delete webserver; CLanguage::deleteInstance(); webserver = NULL; } //----------------------------------------------------------------------------- // Change to Root //----------------------------------------------------------------------------- bool Cyhttpd::Configure() { if (!getuid()) // you must be root to do that! { // Get user and group data #ifdef Y_CONFIG_FEATURE_HTTPD_USER struct passwd *pwd = NULL; struct group *grp = NULL; std::string username = ConfigList["server.user_name"]; std::string groupname= ConfigList["server.group_name"]; // get user data if(!username.empty()) { if((pwd = getpwnam(username.c_str())) == NULL) { dperror("Dont know user to set uid\n"); return false; } } // get group data if(!groupname.empty()) { if((grp = getgrnam(groupname.c_str())) == NULL) { aprintf("Can not get Group-Information. Group: %s\n", groupname.c_str()); return false; } } #endif // change root directory #ifdef Y_CONFIG_FEATURE_CHROOT if(!ConfigList["server.chroot"].empty()) { log_level_printf(2, "do chroot to dir:%s\n", ConfigList["server.chroot"].c_str() ); // do change Root if(chroot(ConfigList["server.chroot"].c_str()) == -1) { dperror("Change Root failed\n"); return false; } // Set Working Dir if(chdir("/") == -1) { dperror("Change Directory to Root failed\n"); return false; } } #endif #ifdef Y_CONFIG_FEATURE_HTTPD_USER if(!username.empty() && pwd != NULL && grp != NULL) { log_level_printf(2, "set user and groups\n"); // drop root privileges setgid(grp->gr_gid); setgroups(0, NULL); // set user group if(!groupname.empty()) initgroups(username.c_str(), grp->gr_gid); // set user if(setuid(pwd->pw_uid) == -1) { dperror("Change User Context failed\n"); return false; } } #endif } return true; } //----------------------------------------------------------------------------- // Main Webserver call //----------------------------------------------------------------------------- void Cyhttpd::run() { set_threadname(__func__); if (webserver) { if (flag_threading_off) webserver->is_threading = false; webserver->run(); stop_webserver(); } else aprintf("Error initializing WebServer\n"); } //----------------------------------------------------------------------------- // Show Version Text and Number //----------------------------------------------------------------------------- void Cyhttpd::version(FILE *dest) { fprintf(dest, "%s - Webserver v%s\n", HTTPD_NAME, HTTPD_VERSION); } //----------------------------------------------------------------------------- // Show Usage //----------------------------------------------------------------------------- void Cyhttpd::usage(FILE *dest) { version(dest); fprintf(dest, "command line parameters:\n"); fprintf(dest, "-d, --debug enable debugging code (implies -f)\n"); fprintf(dest, "-f, --fork do not fork\n"); fprintf(dest, "-h, --help display this text and exit\n\n"); fprintf(dest, "-v, --version display version and exit\n"); fprintf(dest, "-l, set loglevel (0 .. 9)\n"); fprintf(dest, "-t, --thread-off set threading off\n"); } //----------------------------------------------------------------------------- // Stop WebServer //----------------------------------------------------------------------------- void Cyhttpd::stop_webserver() { aprintf("stop requested......\n"); if (webserver) { webserver->stop(); hooks_detach(); } } //----------------------------------------------------------------------------- // Attach hooks (use hook order carefully) //----------------------------------------------------------------------------- void Cyhttpd::hooks_attach() { #ifdef Y_CONFIG_USE_AUTHHOOK // First Check Authentication auth = new CmAuth(); CyhookHandler::attach(auth); #endif #ifdef Y_CONFIG_USE_TESTHOOK testhook = new CTesthook(); CyhookHandler::attach(testhook); #endif #if defined(CONFIG_SYSTEM_TUXBOX) || defined(CONFIG_SYSTEM_TUXBOX_COOLSTREAM) NeutrinoAPI = new CNeutrinoAPI(); CyhookHandler::attach(NeutrinoAPI->NeutrinoYParser); CyhookHandler::attach(NeutrinoAPI->ControlAPI); #else #ifdef Y_CONFIG_USE_YPARSER CyhookHandler::attach(&yParser); #endif #endif #ifdef Y_CONFIG_USE_CACHE CyhookHandler::attach(&mod_cache); #endif #ifdef Y_CONFIG_USE_SENDFILE mod_sendfile = new CmodSendfile(); CyhookHandler::attach(mod_sendfile); #endif #ifdef Y_CONFIG_USE_WEBLOG weblog = new CmWebLog(); CyhookHandler::attach(weblog); #endif } //----------------------------------------------------------------------------- // Detach hooks & Destroy //----------------------------------------------------------------------------- void Cyhttpd::hooks_detach() { #ifdef Y_CONFIG_USE_AUTHHOOK CyhookHandler::detach(auth); delete auth; #endif #ifdef Y_CONFIG_USE_TESTHOOK CyhookHandler::detach(testhook); delete testhook; #endif #if defined(CONFIG_SYSTEM_TUXBOX) || defined(CONFIG_SYSTEM_TUXBOX_COOLSTREAM) CyhookHandler::detach(NeutrinoAPI->NeutrinoYParser); #else #ifdef Y_CONFIG_USE_YPARSER CyhookHandler::detach(&yParser); #endif #endif #ifdef Y_CONFIG_USE_CACHE CyhookHandler::detach(&mod_cache); #endif #ifdef Y_CONFIG_USE_SENDFILE CyhookHandler::detach(mod_sendfile); #endif #ifdef Y_CONFIG_USE_WEBLOG CyhookHandler::detach(weblog); delete weblog; #endif } //----------------------------------------------------------------------------- // Read Webserver Configurationfile // Call "Hooks_ReadConfig" so Hooks can read/write own Configuration Values //----------------------------------------------------------------------------- void Cyhttpd::ReadConfig(void) { log_level_printf(3, "ReadConfig Start\n"); CConfigFile *Config = new CConfigFile(','); bool have_config = false; if (access(HTTPD_CONFIGFILE, R_OK) == 0) have_config = true; Config->loadConfig(HTTPD_CONFIGFILE); // convert old config files if (have_config) { if (Config->getInt32("configfile.version", 0) == 0) { CConfigFile OrgConfig = *Config; Config->clear(); Config->setInt32("server.log.loglevel", OrgConfig.getInt32( "LogLevel", 0)); Config->setInt32("configfile.version", CONF_VERSION); Config->setString("webserver.websites", "WebsiteMain"); Config->setBool("webserver.threading", OrgConfig.getBool("THREADS", true)); Config->setInt32("WebsiteMain.port", OrgConfig.getInt32("Port", HTTPD_STANDARD_PORT)); Config->setString("WebsiteMain.directory", OrgConfig.getString( "PrivatDocRoot", PRIVATEDOCUMENTROOT)); if (!OrgConfig.getString("PublicDocRoot", "").empty()) Config->setString("WebsiteMain.override_directory", OrgConfig.getString("PublicDocRoot", PRIVATEDOCUMENTROOT)); // mod_auth Config->setString("mod_auth.username", OrgConfig.getString( "AuthUser", AUTHUSER)); Config->setString("mod_auth.password", OrgConfig.getString( "AuthPassword", AUTHPASSWORD)); Config->setString("mod_auth.no_auth_client", OrgConfig.getString( "NoAuthClient", "")); Config->setString("mod_auth.authenticate", OrgConfig.getString( "Authenticate", "false")); Config->setString("mod_sendfile.mime_types", HTTPD_SENDFILE_EXT); Config->saveConfig(HTTPD_CONFIGFILE); } // Add Defaults for Version 2 if (Config->getInt32("configfile.version") < 2) { Config->setString("mod_sendfile.mime_types", HTTPD_SENDFILE_EXT); Config->setInt32("configfile.version", CONF_VERSION); Config->setString("mod_sendfile.sendAll", "false"); Config->saveConfig(HTTPD_CONFIGFILE); } // Add Defaults for Version 4 if (Config->getInt32("configfile.version") < 4) { Config->setInt32("configfile.version", CONF_VERSION); Config->setString("Language.selected", HTTPD_DEFAULT_LANGUAGE); Config->setString("Language.directory", HTTPD_LANGUAGEDIR); if (Config->getString("WebsiteMain.hosted_directory", "").empty()) Config->setString("WebsiteMain.hosted_directory", HOSTEDDOCUMENTROOT); Config->saveConfig(HTTPD_CONFIGFILE); } } // configure debugging & logging if (CLogging::getInstance()->LogLevel == 0) CLogging::getInstance()->LogLevel = Config->getInt32("server.log.loglevel", 0); if (CLogging::getInstance()->LogLevel > 0) CLogging::getInstance()->setDebug(true); // get variables webserver->init(Config->getInt32("WebsiteMain.port", HTTPD_STANDARD_PORT), Config->getBool("webserver.threading", true)); // informational use ConfigList["WebsiteMain.port"] = itoa(Config->getInt32("WebsiteMain.port", HTTPD_STANDARD_PORT)); ConfigList["webserver.threading"] = Config->getString( "webserver.threading", "true"); ConfigList["configfile.version"] = Config->getInt32("configfile.version", CONF_VERSION); ConfigList["server.log.loglevel"] = itoa(Config->getInt32( "server.log.loglevel", 0)); ConfigList["server.no_keep-alive_ips"] = Config->getString( "server.no_keep-alive_ips", ""); webserver->conf_no_keep_alive_ips = Config->getStringVector( "server.no_keep-alive_ips"); // MainSite ConfigList["WebsiteMain.directory"] = Config->getString( "WebsiteMain.directory", PRIVATEDOCUMENTROOT); ConfigList["WebsiteMain.override_directory"] = Config->getString( "WebsiteMain.override_directory", PUBLICDOCUMENTROOT); ConfigList["WebsiteMain.hosted_directory"] = Config->getString( "WebsiteMain.hosted_directory", HOSTEDDOCUMENTROOT); ConfigList["Tuxbox.DisplayLogos"] = Config->getString("Tuxbox.DisplayLogos", "true"); // Check location of logos if (Config->getString("Tuxbox.LogosURL", "").empty()) { if (access(ConfigList["WebsiteMain.override_directory"] + "/logos", R_OK) == 0) { Config->setString("Tuxbox.LogosURL", ConfigList["WebsiteMain.override_directory"] + "/logos"); have_config = false; //save config } else if (access(ConfigList["WebsiteMain.directory"] + "/logos", R_OK) == 0){ Config->setString("Tuxbox.LogosURL", ConfigList["WebsiteMain.directory"] + "/logos"); have_config = false; //save config } #ifdef Y_CONFIG_USE_HOSTEDWEB else if (access(ConfigList["WebsiteMain.hosted_directory"] + "/logos", R_OK) == 0){ Config->setString("Tuxbox.LogosURL", ConfigList["WebsiteMain.hosted_directory"] + "/logos"); have_config = false; //save config } #endif //Y_CONFIG_USE_HOSTEDWEB } ConfigList["Tuxbox.LogosURL"] = Config->getString("Tuxbox.LogosURL", ""); #ifdef Y_CONFIG_USE_OPEN_SSL ConfigList["SSL"] = Config->getString("WebsiteMain.ssl", "false"); ConfigList["SSL_pemfile"] = Config->getString("WebsiteMain.ssl_pemfile", SSL_PEMFILE); ConfigList["SSL_CA_file"] = Config->getString("WebsiteMain.ssl_ca_file", SSL_CA_FILE); CySocket::SSL_pemfile = ConfigList["SSL_pemfile"]; CySocket::SSL_CA_file = ConfigList["SSL_CA_file"]; if(ConfigList["SSL"] == "true") CySocket::initSSL(); #endif ConfigList["server.user_name"] = Config->getString("server.user_name", ""); ConfigList["server.group_name"] = Config->getString("server.group_name", ""); ConfigList["server.chroot"] = Config->getString("server.chroot", ""); // language ConfigList["Language.directory"] = Config->getString("Language.directory", HTTPD_LANGUAGEDIR); ConfigList["Language.selected"] = Config->getString("Language.selected", HTTPD_DEFAULT_LANGUAGE); yhttpd->ReadLanguage(); // Read App specifig settings by Hook CyhookHandler::Hooks_ReadConfig(Config, ConfigList); // Save if new defaults are set if (!have_config) Config->saveConfig(HTTPD_CONFIGFILE); log_level_printf(3, "ReadConfig End\n"); delete Config; } //----------------------------------------------------------------------------- // Read Webserver Configurationfile for languages //----------------------------------------------------------------------------- void Cyhttpd::ReadLanguage(void) { // Init Class vars CLanguage *lang = CLanguage::getInstance(); log_level_printf(3, "ReadLanguage:%s\n", ConfigList["Language.selected"].c_str()); lang->setLanguage(ConfigList["Language.selected"]); }