diff --git a/README.md b/README.md index 4d1cb22c0..21a10b52f 100755 --- a/README.md +++ b/README.md @@ -168,6 +168,8 @@ For previous versions, please read: ## V3 changes +* v3.0, 2020-03-12, For [#1635][bug #1635], inotify watch ConfigMap for reload. 3.0.134 +* 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 @@ -1701,6 +1703,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 [bug #1631]: https://github.com/ossrs/srs/issues/1631 diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index c7ddf4bd9..1a23b87c9 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 84d5e609e..f21d464ed 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -3490,6 +3490,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()); } @@ -4126,6 +4127,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 4ea19c2e4..103e6f25c 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 272954d82..e4fdf4db2 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 @@ -460,6 +461,147 @@ 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; +} + +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"); + } + + // 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", fd); + } + + if (((err = srs_fd_closeexec(fd))) != srs_success) { + return srs_error_new(ERROR_INOTIFY_OPENFD, "closeexec fd=%d", fd); + } + + // /* the following are legal, implemented events that user-space can watch for */ + // #define IN_ACCESS 0x00000001 /* File was accessed */ + // #define IN_MODIFY 0x00000002 /* File was modified */ + // #define IN_ATTRIB 0x00000004 /* Metadata changed */ + // #define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed */ + // #define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */ + // #define IN_OPEN 0x00000020 /* File was opened */ + // #define IN_MOVED_FROM 0x00000040 /* File was moved from X */ + // #define IN_MOVED_TO 0x00000080 /* File was moved to Y */ + // #define IN_CREATE 0x00000100 /* Subfile was created */ + // #define IN_DELETE 0x00000200 /* Subfile was deleted */ + // #define IN_DELETE_SELF 0x00000400 /* Self was deleted */ + // #define IN_MOVE_SELF 0x00000800 /* Self was moved */ + // + // /* the following are legal events. they are sent as needed to any watch */ + // #define IN_UNMOUNT 0x00002000 /* Backing fs was unmounted */ + // #define IN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ + // #define IN_IGNORED 0x00008000 /* File was ignored */ + // + // /* helper events */ + // #define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */ + // #define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* moves */ + // + // /* special flags */ + // #define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */ + // #define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */ + // #define IN_EXCL_UNLINK 0x04000000 /* exclude events on unlinked objects */ + // #define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */ + // #define IN_ISDIR 0x40000000 /* event occurred against dir */ + // #define IN_ONESHOT 0x80000000 /* only send event once */ + + // Watch the config directory events. + string config_dir = srs_path_dirname(_srs_config->config()); + uint32_t mask = IN_MODIFY | IN_CREATE | IN_MOVED_TO; int watch_conf = 0; + if ((watch_conf = ::inotify_add_watch(fd, config_dir.c_str(), mask)) < 0) { + return srs_error_new(ERROR_INOTIFY_WATCH, "watch file=%s, fd=%d, watch=%d, mask=%#x", + config_dir.c_str(), fd, watch_conf, mask); + } + srs_trace("auto reload watching fd=%d, watch=%d, file=%s", fd, watch_conf, config_dir.c_str()); + + 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; + + string config_path = _srs_config->config(); + string config_file = srs_path_basename(config_path); + string k8s_file = "..data"; + + 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. + inotify_event* ie = NULL; + for (char* ptr = buf; ptr < buf + nn; ptr += sizeof(inotify_event) + ie->len) { + ie = (inotify_event*)ptr; + + if (!ie->len || !ie->name) { + continue; + } + + string name = ie->name; + if ((name == k8s_file || name == config_file) && ie->mask & (IN_MODIFY|IN_CREATE|IN_MOVED_TO)) { + do_reload = true; + } + + srs_trace("inotify event wd=%d, mask=%#x, len=%d, name=%s, reload=%d", ie->wd, ie->mask, ie->len, ie->name, do_reload); + } + + // Notify server to do reload. + if (do_reload && srs_path_exists(config_path)) { + server->on_signal(SRS_SIGNAL_RELOAD); + } + + srs_usleep(3000 * SRS_UTIME_MILLISECONDS); + } + + return err; +} + ISrsServerCycle::ISrsServerCycle() { } @@ -847,7 +989,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 87407bb9f..56fa945df 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -185,6 +185,24 @@ 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; +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..5ff02aa5a 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 134 #endif diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index f7375780a..558bde405 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_OPENFD 3093 +#define ERROR_INOTIFY_WATCH 3094 /////////////////////////////////////////////////////// // HTTP/StreamCaster protocol error. diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index a3fb7fa0b..61f4f1d0d 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -356,11 +356,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) { @@ -379,7 +380,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; @@ -389,18 +390,17 @@ srs_error_t run_directly_or_daemon() { srs_error_t err = srs_success; + // Ignore any error while detecting docker. + if ((err = srs_detect_docker()) != srs_success) { + srs_error_reset(err); + } + // 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