diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 96836bbd7..7fd21c9b4 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -3,26 +3,30 @@ name: File bug about: File bug to improve SRS title: '' labels: '' -assignees: '' +assignees: 'winlinvip' --- -> 注意:不提供以下信息的Issue会被直接删除(Please follow issue template, or we will delete it) - -> 注意:咨询和讨论请提交到SRS星球(Please ask question at) http://bbs.ossrs.net +> 注意:提问前,请先看FAQ(Please read FAQ before file an issue) https://github.com/ossrs/srs/issues/2716 **描述(Description)** > 描述你遇到了什么问题(Please description your issue here) 1. SRS版本(Version): `xxxxxx` + 1. SRS的日志如下(Log): ``` + xxxxxxxxxxxx + ``` + 1. SRS的配置如下(Config): ``` + xxxxxxxxxxxx + ``` **重现(Replay)** diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index dea714815..90775c293 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -30,6 +30,8 @@ The changelog for SRS. ## SRS 4.0 Changelog +* v4.0, 2021-11-07, Merge [#2711](https://github.com/ossrs/srs/pull/2711): Config: Guess config files by [FHS](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard). v4.0.194 +* v4.0, 2021-11-07, Merge [#2714](https://github.com/ossrs/srs/pull/2714): DVR: copy req from publish. v4.0.193 * v4.0, 2021-11-04, Merge [#2707](https://github.com/ossrs/srs/pull/2707): Refuse edge request when state is stopping. v4.0.192 * v4.0, 2021-11-02, Auto create package by github actions. v4.0.191 * v4.0, 2021-10-30, Merge [#2552](https://github.com/ossrs/srs/pull/2552): Script: Refine CentOS7 service script to restart SRS. v4.0.190 diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index ef476be1a..ba6a203ff 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1956,8 +1956,8 @@ srs_error_t SrsConfig::parse_options(int argc, char** argv) } } - // config - show_help = true; + // Show help if has any argv + show_help = argc > 1; for (int i = 1; i < argc; i++) { if ((err = parse_argv(i, argv)) != srs_success) { return srs_error_wrap(err, "parse argv"); @@ -1980,21 +1980,45 @@ srs_error_t SrsConfig::parse_options(int argc, char** argv) // first hello message. srs_trace(_srs_version); - - if (config_file.empty()) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "no config, read help: %s -h", argv[0]); + + // Try config files as bellow: + // config_file Specified by user, like conf/srs.conf + // try_docker_config Guess by SRS, like conf/docker.conf + // try_fhs_config For FHS, try /etc/srs/srs.conf first, @see https://github.com/ossrs/srs/pull/2711 + if (!srs_path_exists(config_file)) { + vector try_config_files; + if (!config_file.empty()) { + try_config_files.push_back(config_file); + if (srs_string_ends_with(config_file, "docker.conf")) { + try_config_files.push_back(srs_string_replace(config_file, "docker.conf", "srs.conf")); + } + } + try_config_files.push_back(SRS_CONF_DEFAULT_COFNIG_FILE); + if (srs_string_ends_with(SRS_CONF_DEFAULT_COFNIG_FILE, "docker.conf")) { + try_config_files.push_back(srs_string_replace(SRS_CONF_DEFAULT_COFNIG_FILE, "docker.conf", "srs.conf")); + } + try_config_files.push_back("/etc/srs/srs.conf"); + + string exists_config_file; + for (int i = 0; i < (int) try_config_files.size(); i++) { + string try_config_file = try_config_files.at(i); + if (srs_path_exists(try_config_file)) { + exists_config_file = try_config_file; + break; + } + } + + if (exists_config_file.empty()) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "no config file at %s", srs_join_vector_string(try_config_files, ", ").c_str()); + } + + if (config_file != exists_config_file) { + srs_warn("user config %s does not exists, use %s instead", config_file.c_str(), exists_config_file.c_str()); + config_file = exists_config_file; + } } - // For docker, if config is not specified, try srs.conf instead. - string try_config = srs_string_replace(config_file, "docker.conf", "srs.conf"); - if (!srs_path_exists(config_file) && try_config != config_file) { - if (!srs_path_exists(try_config)) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "no config file %s or %s", config_file.c_str(), try_config.c_str()); - } - srs_warn("user config %s does not exists, use %s instead", config_file.c_str(), try_config.c_str()); - config_file = try_config; - } - + // Parse the matched config file. err = parse_file(config_file.c_str()); if (test_conf) { @@ -3567,8 +3591,8 @@ int SrsConfig::get_rtc_server_reuseport() #if !defined(SO_REUSEPORT) if (v > 1) { - srs_warn("REUSEPORT not supported, reset %d to %d", reuseport, DEFAULT); - v = 1 + srs_warn("REUSEPORT not supported, reset to 1"); + v = 1; } #endif diff --git a/trunk/src/core/srs_core_version4.hpp b/trunk/src/core/srs_core_version4.hpp index eef3bfb57..60ecfe0c1 100644 --- a/trunk/src/core/srs_core_version4.hpp +++ b/trunk/src/core/srs_core_version4.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 4 #define VERSION_MINOR 0 -#define VERSION_REVISION 192 +#define VERSION_REVISION 194 #endif diff --git a/trunk/src/utest/srs_utest_service.cpp b/trunk/src/utest/srs_utest_service.cpp index 197383555..14ea28e09 100644 --- a/trunk/src/utest/srs_utest_service.cpp +++ b/trunk/src/utest/srs_utest_service.cpp @@ -69,28 +69,19 @@ class MockTcpHandler : public ISrsTcpHandler private: srs_netfd_t fd; public: - MockTcpHandler(); - virtual ~MockTcpHandler(); + MockTcpHandler() { + fd = NULL; + } + virtual ~MockTcpHandler() { + srs_close_stfd(fd); + } public: - virtual srs_error_t on_tcp_client(srs_netfd_t stfd); + virtual srs_error_t on_tcp_client(srs_netfd_t stfd) { + fd = stfd; + return srs_success; + } }; -MockTcpHandler::MockTcpHandler() -{ - fd = NULL; -} - -MockTcpHandler::~MockTcpHandler() -{ - srs_close_stfd(fd); -} - -srs_error_t MockTcpHandler::on_tcp_client(srs_netfd_t stfd) -{ - fd = stfd; - return srs_success; -} - VOID TEST(TCPServerTest, PingPong) { srs_error_t err; @@ -1459,7 +1450,7 @@ public: int r0; int r1; SrsFastCoroutine trd; - MockStopSelfThread() : trd("mock", this), r0(0), r1(0) { + MockStopSelfThread() : r0(0), r1(0), trd("mock", this) { } virtual ~MockStopSelfThread() { } @@ -1476,12 +1467,88 @@ public: } }; -VOID TEST(StopSelfThreadTest, ShouldFailWhenStopSelf) +VOID TEST(ThreadCriticalTest, ShouldFailWhenStopSelf) { + srs_error_t err; MockStopSelfThread trd; - trd.start(); + HELPER_EXPECT_SUCCESS(trd.start()); + + // Switch to thread cycle, should fail. srs_usleep(0); EXPECT_EQ(-1, trd.r0); EXPECT_EQ(EDEADLK, trd.r1); } +class MockAsyncReaderThread : public ISrsCoroutineHandler +{ +public: + SrsFastCoroutine trd; + srs_netfd_t fd; + MockAsyncReaderThread(srs_netfd_t v) : trd("mock", this), fd(v) { + } + virtual ~MockAsyncReaderThread() { + } + srs_error_t start() { + return trd.start(); + } + void stop() { + trd.stop(); + } + virtual srs_error_t cycle() { + srs_error_t err = srs_success; + while (true) { + if ((err = trd.pull()) != srs_success) { + return err; + } + char buf[16] = {0}; + if (st_read((st_netfd_t)fd, buf, sizeof(buf), SRS_UTIME_NO_TIMEOUT) <= 0) { + break; + } + } + return err; + } +}; + +VOID TEST(ThreadCriticalTest, FailIfCloseActiveFD) +{ + srs_error_t err; + + MockTcpHandler h; + SrsTcpListener l(&h, _srs_tmp_host, _srs_tmp_port); + HELPER_EXPECT_SUCCESS(l.listen()); + + SrsTcpClient c0(_srs_tmp_host, _srs_tmp_port, _srs_tmp_timeout); + HELPER_EXPECT_SUCCESS(c0.connect()); + + srs_usleep(30 * SRS_UTIME_MILLISECONDS); + EXPECT_TRUE(h.fd != NULL); + + MockAsyncReaderThread trd0(h.fd); + HELPER_EXPECT_SUCCESS(trd0.start()); + + MockAsyncReaderThread trd1(h.fd); + HELPER_EXPECT_SUCCESS(trd1.start()); + + // Wait for all threads to run. + srs_usleep(10 * SRS_UTIME_MILLISECONDS); + + // Should fail when close, because there is 2 threads reading fd. + int r0 = st_netfd_close((st_netfd_t)h.fd); + EXPECT_EQ(-1, r0); + EXPECT_EQ(EBUSY, errno); + + // Stop thread1, still fail because thread0 is reading fd. + trd1.stop(); + r0 = st_netfd_close((st_netfd_t)h.fd); + EXPECT_EQ(-1, r0); + EXPECT_EQ(EBUSY, errno); + + // Stop thread0, should success, no threads is reading fd. + trd0.stop(); + r0 = st_netfd_close((st_netfd_t)h.fd); + EXPECT_EQ(0, r0); + + // Set fd to NULL to avoid close fail for EBADF. + h.fd = NULL; +} +