diff --git a/README.md b/README.md index d843ac0f8..f1e6b8814 100755 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ For previous versions, please read: ## V3 changes +* v3.0, 2020-03-12, For [#1635][bug #1635], support auto reaload config by inotify. 3.0.129 * v3.0, 2020-03-12, For [#1630][bug #1630], disable cache for stream changing, and drop dup header. 3.0.128 * v3.0, 2020-03-12, For [#1594][bug #1594], detect and disable daemon for docker. 3.0.127 * v3.0, 2020-03-12, For [#1634][bug #1634], always check status in thread loop. 3.0.126 @@ -1668,6 +1669,7 @@ Winlin [bug #1634]: https://github.com/ossrs/srs/issues/1634 [bug #1594]: https://github.com/ossrs/srs/issues/1594 [bug #1630]: https://github.com/ossrs/srs/issues/1630 +[bug #1635]: https://github.com/ossrs/srs/issues/1635 [bug #yyyyyyyyyyyyy]: https://github.com/ossrs/srs/issues/yyyyyyyyyyyyy [exo #828]: https://github.com/google/ExoPlayer/pull/828 diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 95defe80f..a29b71bc1 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -97,6 +97,13 @@ force_grace_quit off; # If on, it will set daemon to off in docker, even daemon is on. # default: on disable_daemon_for_docker on; +# Whether auto reload by watching the config file by inotify. +# default: off +inotify_auto_reload off; +# Whether enable inotify_auto_reload for docker. +# If on, it will set inotify_auto_reload to on in docker, even it's off. +# default: on +auto_reload_for_docker on; ############################################################################################# # heartbeat/stats sections diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 80d03383d..1018108e9 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -3489,6 +3489,7 @@ srs_error_t SrsConfig::check_normal_config() && n != "utc_time" && n != "work_dir" && n != "asprocess" && n != "ff_log_level" && n != "grace_final_wait" && n != "force_grace_quit" && n != "grace_start_wait" && n != "empty_ip_ok" && n != "disable_daemon_for_docker" + && n != "inotify_auto_reload" && n != "auto_reload_for_docker" ) { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal directive %s", n.c_str()); } @@ -4111,6 +4112,30 @@ bool SrsConfig::disable_daemon_for_docker() return SRS_CONF_PERFER_TRUE(conf->arg0()); } +bool SrsConfig::inotify_auto_reload() +{ + static bool DEFAULT = false; + + SrsConfDirective* conf = root->get("inotify_auto_reload"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); +} + +bool SrsConfig::auto_reload_for_docker() +{ + static bool DEFAULT = true; + + SrsConfDirective* conf = root->get("auto_reload_for_docker"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_TRUE(conf->arg0()); +} + vector SrsConfig::get_stream_casters() { srs_assert(root); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 7669632cc..02c6b842b 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -478,6 +478,10 @@ public: virtual bool is_force_grace_quit(); // Whether disable daemon for docker. virtual bool disable_daemon_for_docker(); + // Whether use inotify to auto reload by watching config file changes. + virtual bool inotify_auto_reload(); + // Whether enable auto reload config for docker. + virtual bool auto_reload_for_docker(); // stream_caster section public: // Get all stream_caster in config file. diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 99724f993..6c9d737bf 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -30,6 +30,7 @@ #include #include #include +#include using namespace std; #include @@ -457,6 +458,114 @@ void SrsSignalManager::sig_catcher(int signo) errno = err; } +// Whether we are in docker, defined in main module. +extern bool _srs_in_docker; + +SrsInotifyWorker::SrsInotifyWorker(SrsServer* s) +{ + server = s; + trd = new SrsSTCoroutine("inotify", this); + inotify_fd = NULL; + watch_fd = 0; +} + +SrsInotifyWorker::~SrsInotifyWorker() +{ + srs_freep(trd); + srs_close_stfd(inotify_fd); +} + +srs_error_t SrsInotifyWorker::start() +{ + srs_error_t err = srs_success; + + // Whether enable auto reload config. + bool auto_reload = _srs_config->inotify_auto_reload(); + if (!auto_reload && _srs_in_docker && _srs_config->auto_reload_for_docker()) { + srs_warn("enable auto reload for docker"); + auto_reload = true; + } + + if (!auto_reload) { + return err; + } + + // Create inotify to watch config file. + int fd = ::inotify_init1(IN_NONBLOCK); + if (fd < 0) { + return srs_error_new(ERROR_INOTIFY_CREATE, "create inotify"); + } + + // Watch the config file events. + string config_file = _srs_config->config(); + watch_fd = ::inotify_add_watch(fd, config_file.c_str(), IN_ALL_EVENTS); + if (watch_fd < 0) { + ::close(fd); + return srs_error_new(ERROR_INOTIFY_WATCH, "watch %s", config_file.c_str()); + } + + // Open as stfd to read by ST. + if ((inotify_fd = srs_netfd_open(fd)) == NULL) { + ::close(fd); + return srs_error_new(ERROR_INOTIFY_OPENFD, "open fd=%d, file=%s, wd=%d", fd, config_file.c_str(), watch_fd); + } + + if (((err = srs_fd_closeexec(fd))) != srs_success) { + return srs_error_new(ERROR_INOTIFY_OPENFD, "closeexec fd=%d, file=%s, wd=%d", fd, config_file.c_str(), watch_fd); + } + + srs_trace("auto reload watching %s, fd=%d, wd=%d", config_file.c_str(), fd, watch_fd); + + if ((err = trd->start()) != srs_success) { + return srs_error_wrap(err, "inotify"); + } + + return err; +} + +srs_error_t SrsInotifyWorker::cycle() +{ + srs_error_t err = srs_success; + + while (true) { + char buf[4096]; + ssize_t nn = srs_read(inotify_fd, buf, (size_t)sizeof(buf), SRS_UTIME_NO_TIMEOUT); + if (nn < 0) { + srs_warn("inotify ignore read failed, nn=%d", (int)nn); + break; + } + + // Whether config file changed. + bool do_reload = false; + + // Parse all inotify events. + for (int i = 0; i < (int)nn && !do_reload; i += (int)sizeof(inotify_event)) { + inotify_event* ie = (inotify_event*)(buf + i); + if (ie->wd != watch_fd) { + continue; + } + + // Only care about the modify or create event. + // @see https://github.com/ossrs/srs/issues/1635#issuecomment-598077374 + if ((ie->mask&IN_MODIFY) != IN_MODIFY && (ie->mask&IN_CREATE) != IN_CREATE) { + continue; + } + + do_reload = true; + srs_trace("inotify event wd=%d, mask=%#x, len=%d, name=%s", ie->wd, ie->mask, ie->len, ie->name); + } + + // Notify server to do reload. + if (do_reload) { + server->on_signal(SRS_SIGNAL_RELOAD); + } + + srs_usleep(100 * SRS_UTIME_MILLISECONDS); + } + + return err; +} + ISrsServerCycle::ISrsServerCycle() { } @@ -849,7 +958,16 @@ srs_error_t SrsServer::ingest() srs_error_t SrsServer::cycle() { - srs_error_t err = do_cycle(); + srs_error_t err = srs_success; + + // Start the inotify auto reload by watching config file. + SrsInotifyWorker inotify(this); + if ((err = inotify.start()) != srs_success) { + return srs_error_wrap(err, "start inotify"); + } + + // Do server main cycle. + err = do_cycle(); #ifdef SRS_AUTO_GPERF_MC destroy(); diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 28154600e..1a08898e8 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -184,6 +184,25 @@ private: static void sig_catcher(int signo); }; +// Auto reload by inotify. +// @see https://github.com/ossrs/srs/issues/1635 +class SrsInotifyWorker : public ISrsCoroutineHandler +{ +private: + SrsServer* server; + SrsCoroutine* trd; + srs_netfd_t inotify_fd; + int watch_fd; +public: + SrsInotifyWorker(SrsServer* s); + virtual ~SrsInotifyWorker(); +public: + virtual srs_error_t start(); +// Interface ISrsEndlessThreadHandler. +public: + virtual srs_error_t cycle(); +}; + // A handler to the handle cycle in SRS RTMP server. class ISrsServerCycle { diff --git a/trunk/src/core/srs_core_version3.hpp b/trunk/src/core/srs_core_version3.hpp index c3a667511..ecc7e9cc2 100644 --- a/trunk/src/core/srs_core_version3.hpp +++ b/trunk/src/core/srs_core_version3.hpp @@ -24,6 +24,6 @@ #ifndef SRS_CORE_VERSION3_HPP #define SRS_CORE_VERSION3_HPP -#define SRS_VERSION3_REVISION 128 +#define SRS_VERSION3_REVISION 129 #endif diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index f7375780a..1194d8c60 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -284,6 +284,9 @@ #define ERROR_MP4_ILLEGAL_MOOF 3089 #define ERROR_OCLUSTER_DISCOVER 3090 #define ERROR_OCLUSTER_REDIRECT 3091 +#define ERROR_INOTIFY_CREATE 3092 +#define ERROR_INOTIFY_WATCH 3093 +#define ERROR_INOTIFY_OPENFD 3094 /////////////////////////////////////////////////////// // HTTP/StreamCaster protocol error. diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index aa90f2711..5ded5d40a 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -352,11 +352,12 @@ string srs_getenv(const char* name) } // Detect docker by https://stackoverflow.com/a/41559867 -srs_error_t srs_detect_docker(bool* is_docker) +bool _srs_in_docker = false; +srs_error_t srs_detect_docker() { srs_error_t err = srs_success; - *is_docker = false; + _srs_in_docker = false; SrsFileReader fr; if ((err = fr.open("/proc/1/cgroup")) != srs_success) { @@ -375,7 +376,7 @@ srs_error_t srs_detect_docker(bool* is_docker) string s(buf, nn); if (srs_string_contains(s, "/docker")) { - *is_docker = true; + _srs_in_docker = true; } return err; @@ -385,6 +386,11 @@ srs_error_t run(SrsServer* svr) { srs_error_t err = srs_success; + // Ignore any error while detecting docker. + if ((err = srs_detect_docker()) != srs_success) { + srs_error_reset(err); + } + // Initialize the whole system, set hooks to handle server level events. if ((err = svr->initialize(NULL)) != srs_success) { return srs_error_wrap(err, "server initialize"); @@ -393,15 +399,9 @@ srs_error_t run(SrsServer* svr) // Load daemon from config, disable it for docker. // @see https://github.com/ossrs/srs/issues/1594 bool in_daemon = _srs_config->get_daemon(); - if (in_daemon && _srs_config->disable_daemon_for_docker()) { - bool is_docker = false; - err = srs_detect_docker(&is_docker); - srs_error_reset(err); // Ignore any error while detecting docker. - - if (is_docker) { - srs_warn("disable daemon for docker"); - in_daemon = false; - } + if (in_daemon && _srs_in_docker && _srs_config->disable_daemon_for_docker()) { + srs_warn("disable daemon for docker"); + in_daemon = false; } // If not daemon, directly run master. diff --git a/trunk/src/utest/srs_utest.cpp b/trunk/src/utest/srs_utest.cpp index 19f3d8499..ec5eee727 100644 --- a/trunk/src/utest/srs_utest.cpp +++ b/trunk/src/utest/srs_utest.cpp @@ -45,6 +45,7 @@ ISrsThreadContext* _srs_context = new ISrsThreadContext(); // app module. SrsConfig* _srs_config = NULL; SrsServer* _srs_server = NULL; +bool _srs_in_docker = false; #include