SRS源码框架,配置文件(SrsConfig)的使用

本章内容解读SRS开源代码框架,无二次开发,以学习交流为目的。

SRS是国人开发的开源流媒体服务器,C++语言开发,本章使用版本:https://github.com/ossrs/srs/tree/5.0release。

目录

    • SRS配置文件的使用
    • 源码
    • 源码测试

SRS配置文件的使用

SRS封装了SrsConfig类,定义了使用方便且功能强大的配置文件,部分参考了nginx的配置文件,形式上类似与JSON,一个配置项里面可以包含子配置项,套娃的形式和JSON数组很像;可重新加载配置,重载配置时通过回调函数的形式执行其他模块的重载。

功能概述:
1、通过命令行传参,解析main()函数命令行参数(parse_options),定义了传参选项:-h(打印帮助),-t(测试配置文件),-e(使用环境变量配置),-v(打印版本号),-g(打印签名),-c(读配置文件)。
2、SRS支持从系统环境变量读取配置信息,不使用配置文件(env_only_),包括配置文件名也可以从环境变量读取(srs_getenv(“srs.config.file”))。
3、SRS默认策略是:如果系统环境变量有配置,优先使用系统环境变量的配置,其次使用配置文件的配置,最后使用默认值。
4、SRS封装了配置指令类(SrsConfDirective),一个配置指令包括:指令所在配置文件的行号(conf_line),指令名称,指令值数组(args,多个参数),子指令数组(directives,一个指令可包含多个子指令,数据结构一样)。
5、定义了根指令对象(root = new SrsConfDirective),先把配置文件读入缓存区(SrsConfigBuffer),再从缓存区解析配置,存入根指令(parse_conf)。
6、SRS提供了检测配置是否合法的接口(check_config),检测内容包括:各项配置字段是否合法,配置指令值是否合法(很细致的检测,配置文件一个标点符号都不能错);检测配置的最大连接数是否小于等于系统支持的最大连接数(sysconf(_SC_OPEN_MAX))。
7、提供了重载配置文件的接口(reload),可通过linux信号量实现配置文件重新加载。
8、当重载配置文件时,可执行各模块的回调函数,实现重载模块功能,模块类继承于ISrsReloadHandler,使用_srs_config->subscribe(this)注册重载,unsubscribe可取消注册重载,注册重载的模块加入数组(subscribes),重载时遍历执行数组(subscribes)里的回调函数。
9、配置重载流程:收到信号SRS_SIGNAL_RELOAD,_srs_config->reload(),重新解析配置文件parse_file,检测配置check_config,重载配置reload_conf,遍历执行subscribes注册的回调。
10、配置文件包括大量vhost内容,本章不讨论。

上述功能结合源码看,方便理解。

源码

配置模块代码量1W+,本章做了裁剪,基本可以直接运行,代码结构如下:
├── chw_adapt.cpp
├── chw_adapt.h
├── main.cpp
├── srs_app_config.cpp
├── srs_app_config.hpp
├── srs_app_reload.cpp
├── srs_app_reload.hpp
├── srs_kernel_error.cpp
├── srs_kernel_error.hpp
├── srs_kernel_file.cpp
├── srs_kernel_file.hpp
├── srs_kernel_io.cpp
├── srs_kernel_io.hpp
├── srs_protocol_json.cpp
├── srs_protocol_json.hpp
├── Test_ReloadWork.cpp
└── Test_ReloadWork.h

其中srs_app_reload、srs_kernel_file、srs_kernel_io、srs_protocol_json源码和开源类似,这里不贴了。
srs_kernel_error源码参考:https://blog.csdn.net/weixin_40355471/article/details/131940488?spm=1001.2014.3001.5502。

此外还包括配置文件模板,使用开源自带的:srs.conf。

srs_app_config.hpp

#ifndef SRS_APP_CONFIG_HPP
#define SRS_APP_CONFIG_HPP


#include 
#include 
#include 
#include 
#include 
#include "chw_adapt.h"
#include "srs_kernel_error.hpp"
#include 
//#include 
//#include 

class SrsRequest;
class SrsFileWriter;
class SrsJsonObject;
class SrsJsonArray;
class SrsJsonAny;

class SrsConfig;
class SrsRequest;
class SrsJsonArray;
class SrsConfDirective;

/**
 * whether the two vector actual equals, for instance,
 *      srs_vector_actual_equals([0, 1, 2], [0, 1, 2])      ==== true
 *      srs_vector_actual_equals([0, 1, 2], [2, 1, 0])      ==== true
 *      srs_vector_actual_equals([0, 1, 2], [0, 2, 1])      ==== true
 *      srs_vector_actual_equals([0, 1, 2], [0, 1, 2, 3])   ==== false
 *      srs_vector_actual_equals([1, 2, 3], [0, 1, 2])      ==== false
 */
 // 两个vector数组是否完全相等,即a的元素都在b里面,b的元素也都在a里面
template<typename T>
bool srs_vector_actual_equals(const std::vector<T>& a, const std::vector<T>& b)
{
    // all elements of a in b.
    for (int i = 0; i < (int)a.size(); i++) {
        const T& e = a.at(i);
        if (std::find(b.begin(), b.end(), e) == b.end()) {
            return false;
        }
    }

    // all elements of b in a.
    for (int i = 0; i < (int)b.size(); i++) {
        const T& e = b.at(i);
        if (std::find(a.begin(), a.end(), e) == a.end()) {
            return false;
        }
    }

    return true;
}

namespace srs_internal
{
    // The buffer of config content.
    // 用于保存配置内容的缓冲区
    class SrsConfigBuffer
    {
    protected:
        // The last available position.最后一个可用位置
        char* last;
        // The end of buffer.缓冲区的末尾。
        char* end;
        // The start of buffer.缓冲区的开始。
        char* start;
    public:
        // Current consumed position.当前消耗的位置。
        char* pos;
        // Current parsed line.当前解析的行。
        int line;
    public:
        SrsConfigBuffer();
        virtual ~SrsConfigBuffer();
    public:
        // Fullfill the buffer with content of file specified by filename.
        // 用文件名所指定的文件的内容来填充缓冲区。
        virtual srs_error_t fullfill(const char* filename);
        // Whether buffer is empty.
        // 缓冲区是否为空
        virtual bool empty();
    };
};

// Deep compare directive.  深度比较两个配置指令是否相等
extern bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b);
extern bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b, std::string except);

// The helper utilities, used for compare the consts values.
// 辅助工具,用于比较常量字符串
extern bool srs_config_hls_is_on_error_ignore(std::string strategy);
extern bool srs_config_hls_is_on_error_continue(std::string strategy);
extern bool srs_config_ingest_is_file(std::string type);
extern bool srs_config_ingest_is_stream(std::string type);
extern bool srs_config_dvr_is_plan_segment(std::string plan);
extern bool srs_config_dvr_is_plan_session(std::string plan);
extern bool srs_stream_caster_is_udp(std::string caster);
extern bool srs_stream_caster_is_flv(std::string caster);
extern bool srs_stream_caster_is_gb28181(std::string caster);


// Convert bool in str to on/off
// 传入true返回on,传入false返回off
extern std::string srs_config_bool2switch(std::string sbool);

// Parse loaded vhost directives to compatible mode.
// For exmaple, SRS1/2 use the follow refer style:
//          refer   a.domain.com b.domain.com;
// while SRS3 use the following:
//          refer {
//              enabled on;
//              all a.domain.com b.domain.com;
//          }
// so we must transform the vhost directive anytime load the config.
// @param root the root directive to transform, in and out parameter.
// 将加载的vhost指令解析到兼容模式。
//extern srs_error_t srs_config_transform_vhost(SrsConfDirective* root);

// @global config object. 配置全局对象
extern SrsConfig* _srs_config;

// The config directive.
// The config file is a group of directives,
// all directive has name, args and child-directives.
// For example, the following config text:
//      vhost vhost.ossrs.net {
//       enabled         on;
//       ingest livestream {
//      	 enabled      on;
//      	 ffmpeg       /bin/ffmpeg;
//       }
//      }
// will be parsed to:
//      SrsConfDirective: name="vhost", arg0="vhost.ossrs.net", child-directives=[
//          SrsConfDirective: name="enabled", arg0="on", child-directives=[]
//          SrsConfDirective: name="ingest", arg0="livestream", child-directives=[
//              SrsConfDirective: name="enabled", arg0="on", child-directives=[]
//              SrsConfDirective: name="ffmpeg", arg0="/bin/ffmpeg", child-directives=[]
//          ]
//      ]
// @remark, allow empty directive, for example: "dir0 {}"
// @remark, don't allow empty name, for example: ";" or "{dir0 arg0;}
//配置指令类。配置文件是一组指令,所有的指令都有名称、参数和子指令。 套娃的形式个JSON数组很像。
class SrsConfDirective
{
public:
    // The line of config file in which the directive from
    // 指令来自配置文件的哪一行
    int conf_line;
    // The name of directive, for example, the following config text:
    //       enabled     on;
    // will be parsed to a directive, its name is "enalbed"
    std::string name;//指令名称
    // The args of directive, for example, the following config text:
    //       listen      1935 1936;
    // will be parsed to a directive, its args is ["1935", "1936"].
    std::vector<std::string> args;//指令数组,可以存放多个参数值
    // The child directives, for example, the following config text:
    //       vhost vhost.ossrs.net {
    //           enabled         on;
    //       }
    // will be parsed to a directive, its directives is a vector contains
    // a directive, which is:
    //       name:"enalbed", args:["on"], directives:[]
    //
    // @remark, the directives can contains directives.
    std::vector<SrsConfDirective*> directives;//子指令数组,指令里可以包含一个或多个子指令
public:
    SrsConfDirective();
    virtual ~SrsConfDirective();
public:
    // Deep copy the directive, for SrsConfig to use it to support reload in upyun cluster,
    // For when reload the upyun dynamic config, the root will be changed,
    // so need to copy it to an old root directive, and use the copy result to do reload.
    virtual SrsConfDirective* copy();
    // @param except the name of sub directive.
    //深度复制该指令,让SrsConfig使用它来支持在upyun集群中重新加载,
    //因为当重新加载upyun动态配置时,根目录将会被改变,
    //因此,需要将它复制到一个旧的根指令中,并使用复制结果来进行重新加载。
    virtual SrsConfDirective* copy(std::string except);
// args 参数
public:
    // Get the args0,1,2, if user want to get more args,
    // directly use the args.at(index).
    // 从指令数组args中获取第几个指令
    virtual std::string arg0();
    virtual std::string arg1();
    virtual std::string arg2();
    virtual std::string arg3();
// directives 子指令数组
public:
    // Get the directive by index.
    // @remark, assert the index
    // 从子指令数组directives中获取第index个指令
    virtual SrsConfDirective* at(int index);
    // Get the directive by name, return the first match.
    // 按名称获取指令,返回第一个匹配项。
    virtual SrsConfDirective* get(std::string _name);
    // Get the directive by name and its arg0, return the first match.
    //获取指令及其第一个参数arg0,返回第一个匹配。
    virtual SrsConfDirective* get(std::string _name, std::string _arg0);
    // RAW 
public:
    virtual SrsConfDirective* get_or_create(std::string n);
    virtual SrsConfDirective* get_or_create(std::string n, std::string a0);
    virtual SrsConfDirective* get_or_create(std::string n, std::string a0, std::string a1);
    virtual SrsConfDirective* set_arg0(std::string a0);
    // Remove the v from sub directives, user must free the v.
    // 从子指令数组directives中删除一个指令v,用户自己管理释放v
    virtual void remove(SrsConfDirective* v);
// help utilities
public:
    // Whether current directive is vhost.
    // 当前指令是不是vhost
    virtual bool is_vhost();
    // Whether current directive is stream_caster.
    // 当前指令是不是stream_caster
    virtual bool is_stream_caster();
// Parse utilities
public:
    // Parse config directive from file buffer.
    // 从配置缓冲区中解析指令
    virtual srs_error_t parse(srs_internal::SrsConfigBuffer* buffer, SrsConfig* conf = NULL);
    // Marshal the directive to writer.
    // @param level, the root is level0, all its directives are level1, and so on.
    // 将指令写入writer,level是级别,根指令是level0,根指令的所有子指令都是level1,以此类推。
    virtual srs_error_t persistence(SrsFileWriter* writer, int level);
    // Dumps the args[0-N] to array(string).
    // 将参数转储到JSON数组(字符串)中。
    virtual SrsJsonArray* dumps_args();
    // Dumps arg0 to str, number or boolean.
    // 把第一个参数arg0转换为字符串,数字,或布尔类型
    virtual SrsJsonAny* dumps_arg0_to_str();
    virtual SrsJsonAny* dumps_arg0_to_integer();
    virtual SrsJsonAny* dumps_arg0_to_number();
    virtual SrsJsonAny* dumps_arg0_to_boolean();
// private parse.
private:
    // The directive parsing context.指令解析上下文
    enum SrsDirectiveContext {
        // The root directives, parsing file.
        // 根指令,解析文件。
        SrsDirectiveContextFile,
        // For each directive, parsing text block.
        // 对每个指令,解析文本
        SrsDirectiveContextBlock,
    };
    enum SrsDirectiveState {//指令解析状态
        // Init state
        SrsDirectiveStateInit,
        // The directive terminated by ';' found
        SrsDirectiveStateEntire,
        // The token terminated by '{' found
        SrsDirectiveStateBlockStart,
        // The '}' found
        SrsDirectiveStateBlockEnd,
        // The config file is done
        SrsDirectiveStateEOF,
    };
    // Parse the conf from buffer. the work flow:
    // 1. read a token(directive args and a ret flag),
    // 2. initialize the directive by args, args[0] is name, args[1-N] is args of directive,
    // 3. if ret flag indicates there are child-directives, read_conf(directive, block) recursively.
    //从缓冲区解析conf。工作流程:
    //1. 读取一个标记(指令args和一个ret标志),
    //2. 通过args初始化指令,args[0]是名称,args[1-N]是指令的args,
    //3. 如果ret标志表示有子指令,则read_conf(指令,块)递归化。
    virtual srs_error_t parse_conf(srs_internal::SrsConfigBuffer* buffer, SrsDirectiveContext ctx, SrsConfig* conf);
    // Read a token from buffer.
    // A token, is the directive args and a flag indicates whether has child-directives.
    // @param args, the output directive args, the first is the directive name, left is the args.
    // @param line_start, the actual start line of directive.
    // @return, an error code indicates error or has child-directives.
    // 从缓冲区中读取一个标记。
    // 一个标记,是指令参数,并有一个标志指示是否有子指令。
    // @param args,输出指令的args,第一个是指令名,左边是args。
    // @param line_start,指令的实际起始行。
    // @return,错误代码指示错误或有子指令。
    virtual srs_error_t read_token(srs_internal::SrsConfigBuffer* buffer, std::vector<std::string>& args, int& line_start, SrsDirectiveState& state);
};

// The config service provider.
// For the config supports reload, so never keep the reference cross st-thread,
// that is, never save the SrsConfDirective* get by any api of config,
// For it maybe free in the reload st-thread cycle.
// You could keep it before st-thread switch, or simply never keep it.
// 提供配置服务,因为配置支持重新加载,不要在多协程中使用SrsConfDirective的引用或保存其指针,因为其可能在重新加载时被释放
// 可以在st线程切换之前保存它,或者干脆永远不要保存它。
class SrsConfig
{
    friend class SrsConfDirective;
// user command
private:
    // Whether show help and exit.是否显示帮助并退出
    bool show_help;
    // Whether test config file and exit.是否测试配置文件并退出
    bool test_conf;
    // Whether show SRS version and exit.是否显示SRS版本并退出
    bool show_version;
    // Whether show SRS signature and exit.是否显示SRS签名并退出
    bool show_signature;
    // Whether only use environment variable, ignore config file.
    // Set it by argv "-e" or env "SRS_ENV_ONLY=on".
    bool env_only_;//是否使用用户环境变量,忽略配置文件
// global env variables.
private://全局环境变量
    // The user parameters, the argc and argv.
    // The argv is " ".join(argv), where argv is from main(argc, argv).
    std::string _argv;//从main函数传参过来的argv参数组合
    // current working directory.
    std::string _cwd;//当前工作目录
    // Config section
private:
    // The last parsed config file.
    // If  reload, reload the config file.
    // 最后一次解析的配置文件名,重载会更新
    std::string config_file;
protected:
    // The directive root. 根指令
    SrsConfDirective* root;
// Reload  section
private:
    // The reload subscribers, when reload, callback all handlers.
    // 重新加载订阅集,在重新加载时,会回调所有处理程序。
    std::vector<ISrsReloadHandler*> subscribes;
public:
    SrsConfig();
    virtual ~SrsConfig();
// Reload
public:
    // For reload handler to register itself,
    // when config service do the reload, callback the handler.
    // 重载注册,当配置服务进行重新加载时,回调处理程序。
    virtual void subscribe(ISrsReloadHandler* handler);
    // For reload handler to unregister itself.
    // 取消重载注册
    virtual void unsubscribe(ISrsReloadHandler* handler);
    // Reload  the config file.
    // @remark, user can test the config before reload it.
    virtual srs_error_t reload();//重载配置文件

protected:
    // Reload  from the config. 从SrsConfig中重载配置
    // @remark, use protected for the utest to override with mock.
    virtual srs_error_t reload_conf(SrsConfig* conf);

// Parse options and file
public:
    // Parse the cli, the main(argc,argv) function.
    // 解析main函数传参
    virtual srs_error_t parse_options(int argc, char** argv);
    // initialize the cwd for server,
    // because we may change the workdir.
    // 初始化当前工作目录,因为可能会改变
    virtual srs_error_t initialize_cwd();
    // Marshal current config to file.
    // 将当前配置转换到文件。
    virtual srs_error_t persistence();
private:
    virtual srs_error_t do_persistence(SrsFileWriter* fw);
public:
    // Dumps the http_api sections to json for raw api info.

private:
    virtual srs_error_t do_reload_testwork();//chw
public:
    // Get the config file path.
    virtual std::string config();//获取配置文件路径
private:
    // Parse each argv.解析每一个参数
    virtual srs_error_t parse_argv(int& i, char** argv);
    // Print help and exit.打印帮助并退出
    virtual void print_help(char** argv);
public:
    // Parse the config file, which is specified by cli.
    // 解析由cli指定的配置文件。
    virtual srs_error_t parse_file(const char* filename);
private:
    // Build a buffer from a src, which is string content or filename.
    // 从src构建一个缓冲区,它是字符串内容或文件名。
    virtual srs_error_t build_buffer(std::string src, srs_internal::SrsConfigBuffer** pbuffer);
public:
    // Check the parsed config. 检查配置文件
    virtual srs_error_t check_config();
protected:
    virtual srs_error_t check_normal_config();
    virtual srs_error_t check_number_connections();
protected:
    // Parse config from the buffer.    从缓冲区解析配置
    // @param buffer, the config buffer, user must delete it.
    // @remark, use protected for the utest to override with mock.
    virtual srs_error_t parse_buffer(srs_internal::SrsConfigBuffer* buffer);
    // global env
public:
    // Get the current work directory. 获取当前工作目录
    virtual std::string cwd();
    // Get the cli, the main(argc,argv), program start command.
    // 获取main函数的传参
    virtual std::string argv();
// global section
public:
    // Get the directive root, corresponding to the config file.
    // The root directive, no name and args, contains directives.
    // All directive parsed can retrieve from root.
    //获取对应于配置文件的指令根目录。
    //根指令,没有名称和根参数,包含指令。
    //所有被解析的指令都可以从根目录中检索。
    virtual SrsConfDirective* get_root();
    // Get the daemon config.
    // If  true, SRS will run in daemon mode, fork and fork to reap the
    // grand-child process to init process.
    //获取守护进程配置。
    //如果为真,SRS将在守护进程模式、fork和fork中运行,以获取孙子进程到init进程。
    virtual bool get_daemon();
    // Whether srs in docker.
    virtual bool get_in_docker();
private:
    // Whether user use full.conf
    virtual bool is_full_config();
public:
    // Get the server id, generated a random one if not configured.
    // 获取服务器id,如果未配置,则生成一个随机的服务器id。
    virtual std::string get_server_id();
    // Get the max connections limit of system.
    // If  exceed the max connection, SRS will disconnect the connection.
    // @remark, linux will limit the connections of each process,
    //       for example, when you need SRS to service 10000+ connections,
    //       user must use "ulimit -HSn 10000" and config the max connections
    //       of SRS.
    //获取系统的最大连接限制。如果超过最大连接,SRS将断开连接。
    virtual int get_max_connections();
    // Get the listen port of SRS.
    // user can specifies multiple listen ports,
    // each args of directive is a listen port.
    //获取SRS的监听端口。
    //用户可以指定多个监听端口,
    //指令的每个参数都是一个侦听端口。
    virtual std::vector<std::string> get_listens();
    // Get the pid file path.
    // The pid file is used to save the pid of SRS,
    // use file lock to prevent multiple SRS starting.
    // @remark, if user need to run multiple SRS instance,
    //       for example, to start multiple SRS for multiple CPUs,
    //       user can use different pid file for each process.
    //获取pid文件路径。
    //pid文件用于保存SRS的pid,
    //使用文件锁定来防止多个SRS启动。
    virtual std::string get_pid_file();
    // Get pithy print pulse in srs_utime_t,
    // For example, all rtmp connections only print one message
    // every this interval in ms.
    // 在srs_utime_t中获得简洁的打印脉冲,例如,所有rtmp连接在ms内每此间隔只打印一条消息。
    virtual srs_utime_t get_pithy_print();
    // Whether use utc-time to format the time.
    // 是否使用utc时间来格式化时间。
    virtual bool get_utc_time();
    // Get the configed work dir.
    // ignore if empty string.
    virtual std::string get_work_dir();
    // Whether use asprocess mode.
    virtual bool get_asprocess();
    // Whether query the latest available version of SRS.
    virtual bool whether_query_latest_version();
private:
    SrsConfDirective* get_srt(std::string vhost);
public:
    bool get_srt_enabled(std::string vhost);
    bool get_srt_to_rtmp(std::string vhost);
public:
    // Whether log to file.
    virtual bool get_log_tank_file();
    // Get the log level.
    virtual std::string get_log_level();
    virtual std::string get_log_level_v2();
    // Get the log file path.
    virtual std::string get_log_file();
    // Whether ffmpeg log enabled
    virtual bool get_ff_log_enabled();
    // The ffmpeg log dir.
    // @remark, /dev/null to disable it.
    virtual std::string get_ff_log_dir();
    // The ffmpeg log level.
    virtual std::string get_ff_log_level();
// The MPEG-DASH section.

    virtual std::string get_http_server_listen();
private:
private:
    SrsConfDirective* get_https_api();
};

#endif


srs_app_config.cpp

#include 

#include 
#include 
#include 
#include 
#include 
// file operations.
#include 
#include 
#include 
#include 
#ifdef __linux__
#include 
#include 
#endif

#include 
#include 
using namespace std;

#include 
#include 
#include 


using namespace srs_internal;

// @global the version to identify the core.
const char* _srs_version = "XCORE-" RTMP_SIG_SRS_SERVER;

// when user config an invalid value, macros to perfer true or false.
#define SRS_CONF_PERFER_FALSE(conf_arg) conf_arg == "on"
#define SRS_CONF_PERFER_TRUE(conf_arg) conf_arg != "off"

// default config file.
#define SRS_DEFAULT_CONFIG "srs.conf"
#define SRS_CONF_DEFAULT_COFNIG_FILE SRS_DEFAULT_CONFIG

// '\n'
#define SRS_LF (char)SRS_CONSTS_LF

// '\r'
#define SRS_CR (char)SRS_CONSTS_CR

// Overwrite the config by env.
// 使用环境变量覆盖配置
#define SRS_OVERWRITE_BY_ENV_STRING(key) if (!srs_getenv(key).empty()) return srs_getenv(key)
#define SRS_OVERWRITE_BY_ENV_BOOL(key) if (!srs_getenv(key).empty()) return SRS_CONF_PERFER_FALSE(srs_getenv(key))
#define SRS_OVERWRITE_BY_ENV_BOOL2(key) if (!srs_getenv(key).empty()) return SRS_CONF_PERFER_TRUE(srs_getenv(key))
#define SRS_OVERWRITE_BY_ENV_INT(key) if (!srs_getenv(key).empty()) return ::atoi(srs_getenv(key).c_str())
#define SRS_OVERWRITE_BY_ENV_FLOAT(key) if (!srs_getenv(key).empty()) return ::atof(srs_getenv(key).c_str())
#define SRS_OVERWRITE_BY_ENV_SECONDS(key) if (!srs_getenv(key).empty()) return srs_utime_t(::atoi(srs_getenv(key).c_str()) * SRS_UTIME_SECONDS)
#define SRS_OVERWRITE_BY_ENV_MILLISECONDS(key) if (!srs_getenv(key).empty()) return (srs_utime_t)(::atoi(srs_getenv(key).c_str()) * SRS_UTIME_MILLISECONDS)
#define SRS_OVERWRITE_BY_ENV_FLOAT_SECONDS(key) if (!srs_getenv(key).empty()) return srs_utime_t(::atof(srs_getenv(key).c_str()) * SRS_UTIME_SECONDS)
#define SRS_OVERWRITE_BY_ENV_FLOAT_MILLISECONDS(key) if (!srs_getenv(key).empty()) return srs_utime_t(::atof(srs_getenv(key).c_str()) * SRS_UTIME_MILLISECONDS)
#define SRS_OVERWRITE_BY_ENV_DIRECTIVE(key) { \
        static SrsConfDirective* dir = NULL;      \
        if (!dir && !srs_getenv(key).empty()) {   \
            string v = srs_getenv(key);           \
            dir = new SrsConfDirective();         \
            dir->name = key;                      \
            dir->args.push_back(v);               \
        }                                         \
        if (dir) return dir;                      \
    }

/**
 * whether the ch is common space.
 */
bool is_common_space(char ch)
{
    return (ch == ' ' || ch == '\t' || ch == SRS_CR || ch == SRS_LF);
}

namespace srs_internal
{
    SrsConfigBuffer::SrsConfigBuffer()
    {
        line = 1;
        
        pos = last = start = NULL;
        end = start;
    }
    
    SrsConfigBuffer::~SrsConfigBuffer()
    {
        srs_freepa(start);
    }

    // LCOV_EXCL_START
    srs_error_t SrsConfigBuffer::fullfill(const char* filename)
    {
        srs_error_t err = srs_success;
        
        SrsFileReader reader;
        
        // open file reader.
        if ((err = reader.open(filename)) != srs_success) {
            return srs_error_wrap(err, "open file=%s", filename);
        }
        
        // read all.
        int filesize = (int)reader.filesize();
        
        // create buffer
        srs_freepa(start);
        pos = last = start = new char[filesize];
        end = start + filesize;
        
        // read total content from file.
        ssize_t nread = 0;
        if ((err = reader.read(start, filesize, &nread)) != srs_success) {
            return srs_error_wrap(err, "read %d only %d bytes", filesize, (int)nread);
        }
        
        return err;
    }
    // LCOV_EXCL_STOP
    
    bool SrsConfigBuffer::empty()
    {
        return pos >= end;
    }
};

bool srs_directive_equals_self(SrsConfDirective* a, SrsConfDirective* b)
{
    // both NULL, equal.
    if (!a && !b) {
        return true;
    }
    
    if (!a || !b) {
        return false;
    }
    
    if (a->name != b->name) {
        return false;
    }
    
    if (a->args.size() != b->args.size()) {
        return false;
    }
    
    for (int i = 0; i < (int)a->args.size(); i++) {
        if (a->args.at(i) != b->args.at(i)) {
            return false;
        }
    }
    
    if (a->directives.size() != b->directives.size()) {
        return false;
    }
    
    return true;
}

bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b)
{
    // both NULL, equal.
    if (!a && !b) {
        return true;
    }
    
    if (!srs_directive_equals_self(a, b)) {
        return false;
    }
    
    for (int i = 0; i < (int)a->directives.size(); i++) {
        SrsConfDirective* a0 = a->at(i);
        SrsConfDirective* b0 = b->at(i);
        
        if (!srs_directive_equals(a0, b0)) {
            return false;
        }
    }
    
    return true;
}

bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b, string except)
{
    // both NULL, equal.
    if (!a && !b) {
        return true;
    }
    
    if (!srs_directive_equals_self(a, b)) {
        return false;
    }
    
    for (int i = 0; i < (int)a->directives.size(); i++) {
        SrsConfDirective* a0 = a->at(i);
        SrsConfDirective* b0 = b->at(i);
        
        // donot compare the except child directive.
        if (a0->name == except) {
            continue;
        }
        
        if (!srs_directive_equals(a0, b0, except)) {
            return false;
        }
    }
    
    return true;
}

void set_config_directive(SrsConfDirective* parent, string dir, string value)
{
    SrsConfDirective* d = parent->get_or_create(dir);
    d->name = dir;
    d->args.clear();
    d->args.push_back(value);
}

bool srs_config_hls_is_on_error_ignore(string strategy)
{
    return strategy == "ignore";
}

bool srs_config_hls_is_on_error_continue(string strategy)
{
    return strategy == "continue";
}

bool srs_config_ingest_is_file(string type)
{
    return type == "file";
}

bool srs_config_ingest_is_stream(string type)
{
    return type == "stream";
}

bool srs_config_dvr_is_plan_segment(string plan)
{
    return plan == "segment";
}

bool srs_config_dvr_is_plan_session(string plan)
{
    return plan == "session";
}

bool srs_stream_caster_is_udp(string caster)
{
    return caster == "mpegts_over_udp";
}

bool srs_stream_caster_is_flv(string caster)
{
    return caster == "flv";
}

bool srs_stream_caster_is_gb28181(string caster)
{
    return caster == "gb28181";
}

string srs_config_bool2switch(string sbool)
{
    return sbool == "true"? "on":"off";
}

SrsConfDirective::SrsConfDirective()
{
    conf_line = 0;
}

SrsConfDirective::~SrsConfDirective()
{
    std::vector<SrsConfDirective*>::iterator it;
    for (it = directives.begin(); it != directives.end(); ++it) {
        SrsConfDirective* directive = *it;
        srs_freep(directive);
    }
    directives.clear();
}

SrsConfDirective* SrsConfDirective::copy()
{
    return copy("");
}

SrsConfDirective* SrsConfDirective::copy(string except)
{
    SrsConfDirective* cp = new SrsConfDirective();
    
    cp->conf_line = conf_line;
    cp->name = name;
    cp->args = args;
    
    for (int i = 0; i < (int)directives.size(); i++) {
        SrsConfDirective* directive = directives.at(i);
        if (!except.empty() && directive->name == except) {
            continue;
        }
        cp->directives.push_back(directive->copy(except));
    }
    
    return cp;
}

string SrsConfDirective::arg0()
{
    if (args.size() > 0) {
        return args.at(0);
    }
    
    return "";
}

string SrsConfDirective::arg1()
{
    if (args.size() > 1) {
        return args.at(1);
    }
    
    return "";
}

string SrsConfDirective::arg2()
{
    if (args.size() > 2) {
        return args.at(2);
    }
    
    return "";
}

string SrsConfDirective::arg3()
{
    if (args.size() > 3) {
        return args.at(3);
    }
    
    return "";
}

SrsConfDirective* SrsConfDirective::at(int index)
{
    srs_assert(index < (int)directives.size());
    return directives.at(index);
}

SrsConfDirective* SrsConfDirective::get(string _name)
{
    std::vector<SrsConfDirective*>::iterator it;
    for (it = directives.begin(); it != directives.end(); ++it) {
        SrsConfDirective* directive = *it;
        if (directive->name == _name) {
            return directive;
        }
    }
    
    return NULL;
}

SrsConfDirective* SrsConfDirective::get(string _name, string _arg0)
{
    std::vector<SrsConfDirective*>::iterator it;
    for (it = directives.begin(); it != directives.end(); ++it) {
        SrsConfDirective* directive = *it;
        if (directive->name == _name && directive->arg0() == _arg0) {
            return directive;
        }
    }
    
    return NULL;
}

SrsConfDirective* SrsConfDirective::get_or_create(string n)
{
    SrsConfDirective* conf = get(n);
    
    if (!conf) {
        conf = new SrsConfDirective();
        conf->name = n;
        directives.push_back(conf);
    }
    
    return conf;
}

SrsConfDirective* SrsConfDirective::get_or_create(string n, string a0)
{
    SrsConfDirective* conf = get(n, a0);
    
    if (!conf) {
        conf = new SrsConfDirective();
        conf->name = n;
        conf->args.push_back(a0);
        directives.push_back(conf);
    }
    
    return conf;
}

SrsConfDirective* SrsConfDirective::get_or_create(string n, string a0, string a1)
{
    SrsConfDirective* conf = get(n, a0);

    if (!conf) {
        conf = new SrsConfDirective();
        conf->name = n;
        conf->args.push_back(a0);
        conf->args.push_back(a1);
        directives.push_back(conf);
    }

    return conf;
}

SrsConfDirective* SrsConfDirective::set_arg0(string a0)
{
    if (arg0() == a0) {
        return this;
    }
    
    // update a0.
    if (!args.empty()) {
        args.erase(args.begin());
    }
    
    args.insert(args.begin(), a0);
    
    return this;
}

void SrsConfDirective::remove(SrsConfDirective* v)
{
    std::vector<SrsConfDirective*>::iterator it;
    if ((it = std::find(directives.begin(), directives.end(), v)) != directives.end()) {
        it = directives.erase(it);
    }
}

bool SrsConfDirective::is_vhost()
{
    return name == "vhost";
}

bool SrsConfDirective::is_stream_caster()
{
    return name == "stream_caster";
}

srs_error_t SrsConfDirective::parse(SrsConfigBuffer* buffer, SrsConfig* conf)
{
    return parse_conf(buffer, SrsDirectiveContextFile, conf);
}

srs_error_t SrsConfDirective::persistence(SrsFileWriter* writer, int level)
{
    srs_error_t err = srs_success;
    
    static char SPACE = SRS_CONSTS_SP;
    static char SEMICOLON = SRS_CONSTS_SE;
    static char LF = SRS_CONSTS_LF;
    static char LB = SRS_CONSTS_LB;
    static char RB = SRS_CONSTS_RB;
    static const char* INDENT = "    ";
    
    // for level0 directive, only contains sub directives.
    if (level > 0) {
        // indent by (level - 1) * 4 space.
        for (int i = 0; i < level - 1; i++) {
            if ((err = writer->write((char*)INDENT, 4, NULL)) != srs_success) {
                return srs_error_wrap(err, "write indent");
            }
        }
        
        // directive name.
        if ((err = writer->write((char*)name.c_str(), (int)name.length(), NULL)) != srs_success) {
            return srs_error_wrap(err, "write name");
        }
        if (!args.empty() && (err = writer->write((char*)&SPACE, 1, NULL)) != srs_success) {
            return srs_error_wrap(err, "write name space");
        }
        
        // directive args.
        for (int i = 0; i < (int)args.size(); i++) {
            std::string& arg = args.at(i);
            if ((err = writer->write((char*)arg.c_str(), (int)arg.length(), NULL)) != srs_success) {
                return srs_error_wrap(err, "write arg");
            }
            if (i < (int)args.size() - 1 && (err = writer->write((char*)&SPACE, 1, NULL)) != srs_success) {
                return srs_error_wrap(err, "write arg space");
            }
        }
        
        // native directive, without sub directives.
        if (directives.empty()) {
            if ((err = writer->write((char*)&SEMICOLON, 1, NULL)) != srs_success) {
                return srs_error_wrap(err, "write arg semicolon");
            }
        }
    }
    
    // persistence all sub directives.
    if (level > 0) {
        if (!directives.empty()) {
            if ((err = writer->write((char*)&SPACE, 1, NULL)) != srs_success) {
                return srs_error_wrap(err, "write sub-dir space");
            }
            if ((err = writer->write((char*)&LB, 1, NULL)) != srs_success) {
                return srs_error_wrap(err, "write sub-dir left-brace");
            }
        }
        
        if ((err = writer->write((char*)&LF, 1, NULL)) != srs_success) {
            return srs_error_wrap(err, "write sub-dir linefeed");
        }
    }
    
    for (int i = 0; i < (int)directives.size(); i++) {
        SrsConfDirective* dir = directives.at(i);
        if ((err = dir->persistence(writer, level + 1)) != srs_success) {
            return srs_error_wrap(err, "sub-dir %s", dir->name.c_str());
        }
    }
    
    if (level > 0 && !directives.empty()) {
        // indent by (level - 1) * 4 space.
        for (int i = 0; i < level - 1; i++) {
            if ((err = writer->write((char*)INDENT, 4, NULL)) != srs_success) {
                return srs_error_wrap(err, "write sub-dir indent");
            }
        }
        
        if ((err = writer->write((char*)&RB, 1, NULL)) != srs_success) {
            return srs_error_wrap(err, "write sub-dir right-brace");
        }
        
        if ((err = writer->write((char*)&LF, 1, NULL)) != srs_success) {
            return srs_error_wrap(err, "write sub-dir linefeed");
        }
    }
    
    
    return err;
}

// LCOV_EXCL_START
SrsJsonArray* SrsConfDirective::dumps_args()
{
    SrsJsonArray* arr = SrsJsonAny::array();
    for (int i = 0; i < (int)args.size(); i++) {
        string arg = args.at(i);
        arr->append(SrsJsonAny::str(arg.c_str()));
    }
    return arr;
}

SrsJsonAny* SrsConfDirective::dumps_arg0_to_str()
{
    return SrsJsonAny::str(arg0().c_str());
}

SrsJsonAny* SrsConfDirective::dumps_arg0_to_integer()
{
    return SrsJsonAny::integer(::atoll(arg0().c_str()));
}

SrsJsonAny* SrsConfDirective::dumps_arg0_to_number()
{
    return SrsJsonAny::number(::atof(arg0().c_str()));
}

SrsJsonAny* SrsConfDirective::dumps_arg0_to_boolean()
{
    return SrsJsonAny::boolean(arg0() == "on");
}
// LCOV_EXCL_STOP

// see: ngx_conf_parse
srs_error_t SrsConfDirective::parse_conf(SrsConfigBuffer* buffer, SrsDirectiveContext ctx, SrsConfig* conf)
{
    srs_error_t err = srs_success;
    
    while (true) {
        std::vector<string> args;
        int line_start = 0;
        SrsDirectiveState state = SrsDirectiveStateInit;
        if ((err = read_token(buffer, args, line_start, state)) != srs_success) {
            return srs_error_wrap(err, "read token, line=%d, state=%d", line_start, state);
        }

        if (state == SrsDirectiveStateBlockEnd) {
            return ctx == SrsDirectiveContextBlock ? srs_success : srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: unexpected \"}\"", buffer->line);
        }
        if (state == SrsDirectiveStateEOF) {
            return ctx != SrsDirectiveContextBlock ? srs_success : srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: unexpected end of file, expecting \"}\"", conf_line);
        }
        if (args.empty()) {
            return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: empty directive", conf_line);
        }
        
        // Build normal directive which is not "include".
        if (args.at(0) != "include") {
            SrsConfDirective* directive = new SrsConfDirective();

            directive->conf_line = line_start;
            directive->name = args[0];
            args.erase(args.begin());
            directive->args.swap(args);

            directives.push_back(directive);

            if (state == SrsDirectiveStateBlockStart) {
                if ((err = directive->parse_conf(buffer, SrsDirectiveContextBlock, conf)) != srs_success) {
                    return srs_error_wrap(err, "parse dir");
                }
            }
            continue;
        }

        // Parse including, allow multiple files.
        vector<string> files(args.begin() + 1, args.end());
        if (files.empty()) {
            return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: include is empty directive", buffer->line);
        }
        if (!conf) {
            return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: no config", buffer->line);
        }

        for (int i = 0; i < (int)files.size(); i++) {
            std::string file = files.at(i);
            srs_assert(!file.empty());
            printf("config parse include %s", file.c_str());

            SrsConfigBuffer* include_file_buffer = NULL;
            SrsAutoFree(SrsConfigBuffer, include_file_buffer);
            if ((err = conf->build_buffer(file, &include_file_buffer)) != srs_success) {
                return srs_error_wrap(err, "buffer fullfill %s", file.c_str());
            }

            if ((err = parse_conf(include_file_buffer, SrsDirectiveContextFile, conf)) != srs_success) {
                return srs_error_wrap(err, "parse include buffer");
            }
        }
    }
    
    return err;
}

// see: ngx_conf_read_token
srs_error_t SrsConfDirective::read_token(SrsConfigBuffer* buffer, vector<string>& args, int& line_start, SrsDirectiveState& state)
{
    srs_error_t err = srs_success;
    
    char* pstart = buffer->pos;
    
    bool sharp_comment = false;
    
    bool d_quoted = false;
    bool s_quoted = false;
    
    bool need_space = false;
    bool last_space = true;
    
    while (true) {
        if (buffer->empty()) {
            if (!args.empty() || !last_space) {
                return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID,
                    "line %d: unexpected end of file, expecting ; or \"}\"",
                    buffer->line);
            }
            printf("config parse complete\n");

            state = SrsDirectiveStateEOF;
            return err;
        }
        
        char ch = *buffer->pos++;
        
        if (ch == SRS_LF) {
            buffer->line++;
            sharp_comment = false;
        }
        
        if (sharp_comment) {
            continue;
        }
        
        if (need_space) {
            if (is_common_space(ch)) {
                last_space = true;
                need_space = false;
                continue;
            }
            if (ch == ';') {
                state = SrsDirectiveStateEntire;
                return err;
            }
            if (ch == '{') {
                state = SrsDirectiveStateBlockStart;
                return err;
            }
            return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: unexpected '%c'", buffer->line, ch);
        }
        
        // last charecter is space.
        if (last_space) {
            if (is_common_space(ch)) {
                continue;
            }
            pstart = buffer->pos - 1;
            switch (ch) {
                case ';':
                    if (args.size() == 0) {
                        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: unexpected ';'", buffer->line);
                    }
                    state = SrsDirectiveStateEntire;
                    return err;
                case '{':
                    if (args.size() == 0) {
                        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: unexpected '{'", buffer->line);
                    }
                    state = SrsDirectiveStateBlockStart;
                    return err;
                case '}':
                    if (args.size() != 0) {
                        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "line %d: unexpected '}'", buffer->line);
                    }
                    state = SrsDirectiveStateBlockEnd;
                    return err;
                case '#':
                    sharp_comment = 1;
                    continue;
                case '"':
                    pstart++;
                    d_quoted = true;
                    last_space = 0;
                    continue;
                case '\'':
                    pstart++;
                    s_quoted = true;
                    last_space = 0;
                    continue;
                default:
                    last_space = 0;
                    continue;
            }
        } else {
            // last charecter is not space
            if (line_start == 0) {
                line_start = buffer->line;
            }
            
            bool found = false;
            if (d_quoted) {
                if (ch == '"') {
                    d_quoted = false;
                    need_space = true;
                    found = true;
                }
            } else if (s_quoted) {
                if (ch == '\'') {
                    s_quoted = false;
                    need_space = true;
                    found = true;
                }
            } else if (is_common_space(ch) || ch == ';' || ch == '{') {
                last_space = true;
                found = 1;
            }
            
            if (found) {
                int len = (int)(buffer->pos - pstart);
                char* aword = new char[len];
                memcpy(aword, pstart, len);
                aword[len - 1] = 0;
                
                string word_str = aword;
                if (!word_str.empty()) {
                    args.push_back(word_str);
                }
                srs_freepa(aword);
                
                if (ch == ';') {
                    state = SrsDirectiveStateEntire;
                    return err;
                }
                if (ch == '{') {
                    state = SrsDirectiveStateBlockStart;
                    return err;
                }
            }
        }
    }
    
    return err;
}

SrsConfig::SrsConfig()
{
    env_only_ = false;
    
    show_help = false;
    show_version = false;
    test_conf = false;
    show_signature = false;
    
    root = new SrsConfDirective();
    root->conf_line = 0;
    root->name = "root";
}

SrsConfig::~SrsConfig()
{
    srs_freep(root);
}

void SrsConfig::subscribe(ISrsReloadHandler* handler)
{
    std::vector<ISrsReloadHandler*>::iterator it;
    
    it = std::find(subscribes.begin(), subscribes.end(), handler);
    if (it != subscribes.end()) {
        return;
    }
    
    subscribes.push_back(handler);
}

void SrsConfig::unsubscribe(ISrsReloadHandler* handler)
{
    std::vector<ISrsReloadHandler*>::iterator it;
    
    it = std::find(subscribes.begin(), subscribes.end(), handler);
    if (it == subscribes.end()) {
        return;
    }
    
    it = subscribes.erase(it);
}

// LCOV_EXCL_START
srs_error_t SrsConfig::reload()
{
    srs_error_t err = srs_success;
    
    SrsConfig conf;
    
    if ((err = conf.parse_file(config_file.c_str())) != srs_success) {
        return srs_error_wrap(err, "parse file");
    }
    printf("config reloader parse file success.\n");
    
    // transform config to compatible with previous style of config.
//    if ((err = srs_config_transform_vhost(conf.root)) != srs_success) {
//        return srs_error_wrap(err, "transform config");
//    }
    
    if ((err = conf.check_config()) != srs_success) {
        return srs_error_wrap(err, "check config");
    }
    
    if ((err = reload_conf(&conf)) != srs_success) {
        return srs_error_wrap(err, "reload config");
    }
    
    return err;
}
// LCOV_EXCL_STOP



srs_error_t SrsConfig::reload_conf(SrsConfig* conf)
{
    srs_error_t err = srs_success;
    
    SrsConfDirective* old_root = root;
    SrsAutoFree(SrsConfDirective, old_root);
    
    root = conf->root;
    conf->root = NULL;
    
    // never support reload:
    //      daemon
    //
    // always support reload without additional code:
    //      chunk_size, ff_log_dir,
    //      http_hooks, heartbeat,
    //      security

    if ((err = do_reload_testwork()) != srs_success) {
        return srs_error_wrap(err, "testwork");;
    }

    return err;
}


// see: ngx_get_options
// LCOV_EXCL_START
srs_error_t SrsConfig::parse_options(int argc, char** argv)
{
    srs_error_t err = srs_success;
    
    // argv
    for (int i = 0; i < argc; i++) {
        _argv.append(argv[i]);
        
        if (i < argc - 1) {
            _argv.append(" ");
        }
    }
    
    // Show help if it has no 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");
        }
    }
    
    if (show_help) {
        print_help(argv);
        exit(0);
    }
    
    if (show_version) {
        fprintf(stdout, "%s\n", RTMP_SIG_SRS_VERSION);
        exit(0);
    }
    if (show_signature) {
        fprintf(stdout, "%s\n", RTMP_SIG_SRS_SERVER);
        exit(0);
    }

    // The first hello message.
    printf(_srs_version);

    // Config the env_only_ by env.
    if (getenv("SRS_ENV_ONLY")) env_only_ = true;

    // Overwrite the config by env SRS_CONFIG_FILE.
    if (!env_only_ && !srs_getenv("srs.config.file").empty()) { // SRS_CONFIG_FILE
        string ov = config_file; config_file = srs_getenv("srs.config.file");
        printf("ENV: Overwrite config %s to %s", ov.c_str(), config_file.c_str());
    }

    // Make sure config file exists.
    if (!env_only_ && !srs_path_exists(config_file)) {
        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "no config file at %s", config_file.c_str());
    }

    // Parse the matched config file.
    if (!env_only_) {
        err = parse_file(config_file.c_str());
    }

    if (test_conf) {
        // the parse_file never check the config,
        // we check it when user requires check config file.
//        if (err == srs_success && (err = srs_config_transform_vhost(root)) == srs_success) {
//            if ((err = check_config()) == srs_success) {
//                printf("the config file %s syntax is ok", config_file.c_str());
//                printf("config file %s test is successful", config_file.c_str());
//                exit(0);
//            }
//        }

        printf("invalid config%s in %s", srs_error_summary(err).c_str(), config_file.c_str());
        printf("config file %s test is failed", config_file.c_str());
        exit(srs_error_code(err));
    }

    if (err != srs_success) {
        return srs_error_wrap(err, "invalid config");
    }

    // transform config to compatible with previous style of config.
    // 将配置转换为兼容以前的配置样式。
//    if ((err = srs_config_transform_vhost(root)) != srs_success) {
//        return srs_error_wrap(err, "transform");
//    }

    // If use env only, we set change to daemon(off) and console log.
    if (env_only_) {
        if (!getenv("SRS_DAEMON")) setenv("SRS_DAEMON", "off", 1);
        if (!getenv("SRS_SRS_LOG_TANK") && !getenv("SRS_LOG_TANK")) setenv("SRS_SRS_LOG_TANK", "console", 1);
        if (root->directives.empty()) root->get_or_create("vhost", "__defaultVhost__");
    }

    
    // check log name and level
    
    if (true) {
        std::string log_filename = this->get_log_file();
        if (get_log_tank_file() && log_filename.empty()) {
            return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "no log file");
        }
        if (get_log_tank_file()) {
            printf("you can check log by: tail -n 30 -f %s\n", log_filename.c_str());
            printf("please check SRS by: ./etc/init.d/srs status\n");
        } else {
            printf("write log to console");
        }
    }
    
    return err;
}

srs_error_t SrsConfig::initialize_cwd()
{
    // cwd
    char cwd[256];
    getcwd(cwd, sizeof(cwd));
    _cwd = cwd;
    
    return srs_success;
}

srs_error_t SrsConfig::persistence()
{
    srs_error_t err = srs_success;
    
    // write to a tmp file, then mv to the config.
    std::string path = config_file + ".tmp";
    
    // open the tmp file for persistence
    SrsFileWriter fw;
    if ((err = fw.open(path)) != srs_success) {
        return srs_error_wrap(err, "open file");
    }
    
    // do persistence to writer.
    if ((err = do_persistence(&fw)) != srs_success) {
        ::unlink(path.c_str());
        return srs_error_wrap(err, "persistence");
    }
    
    // rename the config file.
    if (::rename(path.c_str(), config_file.c_str()) < 0) {
        ::unlink(path.c_str());
        return srs_error_new(ERROR_SYSTEM_CONFIG_PERSISTENCE, "rename %s=>%s", path.c_str(), config_file.c_str());
    }
    
    return err;
}

srs_error_t SrsConfig::do_persistence(SrsFileWriter* fw)
{
    srs_error_t err = srs_success;
    
    // persistence root directive to writer.
    if ((err = root->persistence(fw, 0)) != srs_success) {
        return srs_error_wrap(err, "root persistence");
    }
    
    return err;
}

srs_error_t SrsConfig::do_reload_testwork()
{
    srs_error_t err = srs_success;

    vector<ISrsReloadHandler*>::iterator it;
    for (it = subscribes.begin(); it != subscribes.end(); ++it) {
        ISrsReloadHandler* subscribe = *it;
        if ((err = subscribe->on_reload_testwork()) != srs_success) {
            return srs_error_wrap(err, "notify subscribes reload testwork failed");
        }
    }
    printf("reload testwork success.\n");

    return err;
}


string SrsConfig::config()
{
    return config_file;
}

// LCOV_EXCL_START
srs_error_t SrsConfig::parse_argv(int& i, char** argv)
{
    srs_error_t err = srs_success;
    
    char* p = argv[i];
    
    if (*p++ != '-') {
        show_help = true;
        return err;
    }
    
    while (*p) {
        switch (*p++) {
            case '?':
            case 'h':
                show_help = true;
                return err;
            case 't':
                show_help = false;
                test_conf = true;
                break;
            case 'e':
                show_help = false;
                env_only_ = true;
                break;
            case 'v':
            case 'V':
                show_help = false;
                show_version = true;
                return err;
            case 'g':
            case 'G':
                show_help = false;
                show_signature = true;
                break;
            case 'c':
                show_help = false;
                if (*p) {
                    config_file = p;
                    continue;
                }
                if (argv[++i]) {
                    config_file = argv[i];
                    continue;
                }
                return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "-c requires params");
            case '-':
                continue;
            default:
                return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "invalid option: \"%c\", read help: %s -h",
                    *(p - 1), argv[0]);
        }
    }
    
    return err;
}

void SrsConfig::print_help(char** argv)
{
    printf(
           "%s, %s, %s, created by %sand %s\n\n"
           "Usage: %s <-h?vVgGe>|<[-t] -c filename>\n"
           "Options:\n"
           "   -?, -h, --help      : Show this help and exit 0.\n"
           "   -v, -V, --version   : Show version and exit 0.\n"
           "   -g, -G              : Show server signature and exit 0.\n"
           "   -e                  : Use environment variable only, ignore config file.\n"
           "   -t                  : Test configuration file, exit with error code(0 for success).\n"
           "   -c filename         : Use config file to start server.\n"
           "For example:\n"
           "   %s -v\n"
           "   %s -t -c %s\n"
           "   %s -c %s\n",
           RTMP_SIG_SRS_SERVER, RTMP_SIG_SRS_URL, RTMP_SIG_SRS_LICENSE,
           RTMP_SIG_SRS_AUTHORS, SRS_CONSTRIBUTORS,
           argv[0], argv[0], argv[0], SRS_CONF_DEFAULT_COFNIG_FILE,
           argv[0], SRS_CONF_DEFAULT_COFNIG_FILE);
}

srs_error_t SrsConfig::parse_file(const char* filename)
{
    srs_error_t err = srs_success;
    
    config_file = filename;
    
    if (config_file.empty()) {
        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "empty config");
    }

    SrsConfigBuffer* buffer = NULL;
    SrsAutoFree(SrsConfigBuffer, buffer);
    if ((err = build_buffer(config_file, &buffer)) != srs_success) {
        return srs_error_wrap(err, "buffer fullfill %s", config_file.c_str());
    }
    
    if ((err = parse_buffer(buffer)) != srs_success) {
        return srs_error_wrap(err, "parse buffer");
    }
    
    return err;
}

srs_error_t SrsConfig::build_buffer(string src, SrsConfigBuffer** pbuffer)
{
    srs_error_t err = srs_success;

    SrsConfigBuffer* buffer = new SrsConfigBuffer();

    if ((err = buffer->fullfill(src.c_str())) != srs_success) {
        srs_freep(buffer);
        return srs_error_wrap(err, "read from src %s", src.c_str());
    }

    *pbuffer = buffer;
    return err;
}
// LCOV_EXCL_STOP

srs_error_t SrsConfig::check_config()
{
    srs_error_t err = srs_success;
    
    if ((err = check_normal_config()) != srs_success) {
        return srs_error_wrap(err, "check normal");
    }
    
    if ((err = check_number_connections()) != srs_success) {
        return srs_error_wrap(err, "check connections");
    }

    // If use the full.conf, fail.
    if (is_full_config()) {
        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID,
            "never use full.conf(%s)", config_file.c_str());
    }
    
    return err;
}

srs_error_t SrsConfig::check_normal_config()
{
    srs_error_t err = srs_success;
    
    printf("srs checking config...\n");
    
    
    // check empty
    
    if (!env_only_ && root->directives.size() == 0) {
        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "conf is empty");
    }
    
    
    // check root directives.
    
    for (int i = 0; i < (int)root->directives.size(); i++) {
        SrsConfDirective* conf = root->at(i);
        std::string n = conf->name;
        if (n != "listen" && n != "pid" && n != "chunk_size" && n != "ff_log_dir"
            && n != "srs_log_tank" && n != "srs_log_level" && n != "srs_log_level_v2" && n != "srs_log_file"
            && n != "max_connections" && n != "daemon" && n != "heartbeat" && n != "tencentcloud_apm"
            && n != "http_api" && n != "stats" && n != "vhost" && n != "pithy_print_ms"
            && n != "http_server" && n != "stream_caster" && n != "rtc_server" && n != "srt_server"
            && n != "utc_time" && n != "work_dir" && n != "asprocess" && n != "server_id"
            && 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" && n != "tcmalloc_release_rate"
            && n != "query_latest_version" && n != "first_wait_for_qlv" && n != "threads"
            && n != "circuit_breaker" && n != "is_full" && n != "in_docker" && n != "tencentcloud_cls"
            && n != "exporter"
            ) {
            return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal directive %s", n.c_str());
        }
    }
    if (true) {
        SrsConfDirective* conf = root->get("http_api");
        for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
            SrsConfDirective* obj = conf->at(i);
            string n = obj->name;
            if (n != "enabled" && n != "listen" && n != "crossdomain" && n != "raw_api" && n != "auth" && n != "https") {
                return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal http_api.%s", n.c_str());
            }
            
            if (n == "raw_api") {
                for (int j = 0; j < (int)obj->directives.size(); j++) {
                    string m = obj->at(j)->name;
                    if (m != "enabled" && m != "allow_reload" && m != "allow_query" && m != "allow_update") {
                        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal http_api.raw_api.%s", m.c_str());
                    }
                }
            }

            if (n == "auth") {
                for (int j = 0; j < (int)obj->directives.size(); j++) {
                    string m = obj->at(j)->name;
                    if (m != "enabled" && m != "username" && m != "password") {
                        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal http_api.auth.%s", m.c_str());
                    }
                }
            }
        }
    }
    if (true) {
        SrsConfDirective* conf = root->get("http_server");
        for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
            string n = conf->at(i)->name;
            if (n != "enabled" && n != "listen" && n != "dir" && n != "crossdomain" && n != "https") {
                return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal http_stream.%s", n.c_str());
            }
        }
    }
    if (true) {
        SrsConfDirective* conf = root->get("srt_server");
        for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
            string n = conf->at(i)->name;
            if (n != "enabled" && n != "listen" && n != "maxbw"
                && n != "mss" && n != "latency" && n != "recvlatency"
                && n != "peerlatency" && n != "connect_timeout"
                && n != "sendbuf" && n != "recvbuf" && n != "payloadsize"
                && n != "default_app" && n != "sei_filter" && n != "mix_correct"
                && n != "tlpktdrop" && n != "tsbpdmode" && n != "passphrase" && n != "pbkeylen") {
                return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal srt_server.%s", n.c_str());
            }
        }
    }

    if (true) {
        SrsConfDirective* conf = root->get("rtc_server");
        for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
            string n = conf->at(i)->name;
            if (n != "enabled" && n != "listen" && n != "dir" && n != "candidate" && n != "ecdsa" && n != "tcp"
                && n != "encrypt" && n != "reuseport" && n != "merge_nalus" && n != "black_hole" && n != "protocol"
                && n != "ip_family" && n != "api_as_candidates" && n != "resolve_api_domain"
                && n != "keep_api_domain" && n != "use_auto_detect_network_ip") {
                return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal rtc_server.%s", n.c_str());
            }
        }
    }
    if (true) {
        SrsConfDirective* conf = root->get("exporter");
        for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
            string n = conf->at(i)->name;
            if (n != "enabled" && n != "listen" && n != "label" && n != "tag") {
                return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal exporter.%s", n.c_str());
            }
        }
    }

    
    // check listen for rtmp.
    
    if (true) {
        vector<string> listens = get_listens();
        if (!env_only_ && listens.size() <= 0) {
            return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen requires params");
        }
        for (int i = 0; i < (int)listens.size(); i++) {
            int port; string ip;
            srs_parse_endpoint(listens[i], ip, port);

            // check ip
            if (!srs_check_ip_addr_valid(ip)) {
                return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen.ip=%s is invalid", ip.c_str());
            }

            // check port
            if (port <= 0) {
                return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen.port=%d is invalid", port);
            }
        }
    }
    
    
    // check log name and level
    
    if (true) {
        std::string log_filename = this->get_log_file();
        if (get_log_tank_file() && log_filename.empty()) {
            return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "log file is empty");
        }
        if (get_log_tank_file()) {
            printf("you can check log by: tail -n 30 -f %s\n", log_filename.c_str());
            printf("please check SRS by: ./etc/init.d/srs status\n");
        } else {
            printf("write log to console");
        }
    }

    // asprocess conflict with daemon
    if (get_asprocess() && get_daemon()) {
        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "daemon conflicts with asprocess");
    }
    
    return err;
}

// LCOV_EXCL_START
srs_error_t SrsConfig::check_number_connections()
{
    srs_error_t err = srs_success;
    
    
    // check max connections
    
    int nb_connections = get_max_connections();
    if (nb_connections <= 0) {
        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "max_connections=%d is invalid", nb_connections);
    }

    // check max connections of system limits
    int nb_total = nb_connections + 128; // Simple reserved some fds.
    int max_open_files = (int)sysconf(_SC_OPEN_MAX);
    if (nb_total >= max_open_files) {
        printf("max_connections=%d, system limit to %d, please run: ulimit -HSn %d", nb_connections, max_open_files, srs_max(10000, nb_connections * 10));
        return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "%d exceed max open files=%d", nb_total, max_open_files);
    }

    return err;
}
// LCOV_EXCL_STOP

srs_error_t SrsConfig::parse_buffer(SrsConfigBuffer* buffer)
{
    srs_error_t err = srs_success;

    // We use a new root to parse buffer, to allow parse multiple times.
    srs_freep(root);
    root = new SrsConfDirective();

    // Parse root tree from buffer.
    if ((err = root->parse(buffer, this)) != srs_success) {
        return srs_error_wrap(err, "root parse");
    }
    
    return err;
}

string SrsConfig::cwd()
{
    return _cwd;
}

string SrsConfig::argv()
{
    return _argv;
}

bool SrsConfig::get_daemon()
{
    SRS_OVERWRITE_BY_ENV_BOOL2("srs.daemon");

    SrsConfDirective* conf = root->get("daemon");
    if (!conf || conf->arg0().empty()) {
        return true;
    }
    
    return SRS_CONF_PERFER_TRUE(conf->arg0());
}

bool SrsConfig::get_in_docker()
{
    SRS_OVERWRITE_BY_ENV_BOOL("srs.in_docker"); // SRS_IN_DOCKER

    static bool DEFAULT = false;

    SrsConfDirective* conf = root->get("in_docker");
    if (!conf) {
        return DEFAULT;
    }

    return SRS_CONF_PERFER_FALSE(conf->arg0());
}

bool SrsConfig::is_full_config()
{
    static bool DEFAULT = false;

    SrsConfDirective* conf = root->get("is_full");
    if (!conf) {
        return DEFAULT;
    }

    return SRS_CONF_PERFER_FALSE(conf->arg0());
}

SrsConfDirective* SrsConfig::get_root()
{
    return root;
}

string srs_server_id_path(string pid_file)
{
    string path = srs_string_replace(pid_file, ".pid", ".id");
    if (!srs_string_ends_with(path, ".id")) {
        path += ".id";
    }
    return path;
}

string srs_try_read_file(string path) {
    srs_error_t err = srs_success;

    SrsFileReader r;
    if ((err = r.open(path)) != srs_success) {
        srs_freep(err);
        return "";
    }

    static char buf[1024];
    ssize_t nn = 0;
    if ((err = r.read(buf, sizeof(buf), &nn)) != srs_success) {
        srs_freep(err);
        return "";
    }

    if (nn > 0) {
        return string(buf, nn);
    }
    return "";
}

void srs_try_write_file(string path, string content) {
    srs_error_t err = srs_success;

    SrsFileWriter w;
    if ((err = w.open(path)) != srs_success) {
        srs_freep(err);
        return;
    }

    if ((err = w.write((void*)content.data(), content.length(), NULL)) != srs_success) {
        srs_freep(err);
        return;
    }
}

string SrsConfig::get_server_id()
{
    static string DEFAULT = "";

    // Try to read DEFAULT from server id file.
    if (DEFAULT.empty()) {
        DEFAULT = srs_try_read_file(srs_server_id_path(get_pid_file()));
    }

    // Generate a random one if empty.
    if (DEFAULT.empty()) {
        DEFAULT = /*srs_generate_stat_vid()*/"1234567";
    }

    // Get the server id from env, config or DEFAULT.
    string server_id;

    if (!srs_getenv("srs.server_id").empty()) { // SRS_SERVER_ID
        server_id = srs_getenv("srs.server_id");
    } else {
        SrsConfDirective* conf = root->get("server_id");
        if (conf) {
            server_id = conf->arg0();
        }
    }

    if (server_id.empty()) {
        server_id = DEFAULT;
    }

    // Write server id to tmp file.
    srs_try_write_file(srs_server_id_path(get_pid_file()), server_id);

    return server_id;
}

int SrsConfig::get_max_connections()
{
    SRS_OVERWRITE_BY_ENV_INT("srs.max_connections"); // SRS_MAX_CONNECTIONS

    static int DEFAULT = 1000;
    
    SrsConfDirective* conf = root->get("max_connections");
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }
    
    return ::atoi(conf->arg0().c_str());
}

vector<string> SrsConfig::get_listens()
{
    std::vector<string> ports;

    if (!srs_getenv("srs.listen").empty()) { // SRS_LISTEN
        return srs_string_split(srs_getenv("srs.listen"), " ");
    }
    
    SrsConfDirective* conf = root->get("listen");
    if (!conf) {
        return ports;
    }
    
    for (int i = 0; i < (int)conf->args.size(); i++) {
        ports.push_back(conf->args.at(i));
    }
    
    return ports;
}

string SrsConfig::get_pid_file()
{
    SRS_OVERWRITE_BY_ENV_STRING("srs.pid"); // SRS_PID

    static string DEFAULT = "./objs/srs.pid";
    
    SrsConfDirective* conf = root->get("pid");
    
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }
    
    return conf->arg0();
}

srs_utime_t SrsConfig::get_pithy_print()
{
    SRS_OVERWRITE_BY_ENV_MILLISECONDS("srs.pithy_print_ms"); // SRS_PITHY_PRINT_MS

    static srs_utime_t DEFAULT = 10 * SRS_UTIME_SECONDS;
    
    SrsConfDirective* conf = root->get("pithy_print_ms");
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }
    
    return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_MILLISECONDS);
}

bool SrsConfig::get_utc_time()
{
    SRS_OVERWRITE_BY_ENV_BOOL("srs.utc_time"); // SRS_UTC_TIME

    static bool DEFAULT = false;
    
    SrsConfDirective* conf = root->get("utc_time");
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }
    
    return SRS_CONF_PERFER_FALSE(conf->arg0());
}

string SrsConfig::get_work_dir()
{
    SRS_OVERWRITE_BY_ENV_STRING("srs.work_dir"); // SRS_WORK_DIR

    static string DEFAULT = "./";
    
    SrsConfDirective* conf = root->get("work_dir");
    if( !conf || conf->arg0().empty()) {
        return DEFAULT;
    }
    
    return conf->arg0();
}

bool SrsConfig::get_asprocess()
{
    SRS_OVERWRITE_BY_ENV_BOOL("srs.asprocess"); // SRS_ASPROCESS

    static bool DEFAULT = false;
    
    SrsConfDirective* conf = root->get("asprocess");
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }
    
    return SRS_CONF_PERFER_FALSE(conf->arg0());
}

bool SrsConfig::whether_query_latest_version()
{
    SRS_OVERWRITE_BY_ENV_BOOL2("srs.query_latest_version"); // SRS_QUERY_LATEST_VERSION

    static bool DEFAULT = true;

    SrsConfDirective* conf = root->get("query_latest_version");
    if (!conf) {
        return DEFAULT;
    }

    return SRS_CONF_PERFER_TRUE(conf->arg0());
}

bool SrsConfig::get_log_tank_file()
{
    if (!srs_getenv("srs.srs_log_tank").empty()) { // SRS_SRS_LOG_TANK
        return srs_getenv("srs.srs_log_tank") != "console";
    }
    if (!srs_getenv("srs.log_tank").empty()) { // SRS_LOG_TANK
        return srs_getenv("srs.log_tank") != "console";
    }

    static bool DEFAULT = true;
    
    SrsConfDirective* conf = root->get("srs_log_tank");
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }
    
    return conf->arg0() != "console";
}

string SrsConfig::get_log_level()
{
    SRS_OVERWRITE_BY_ENV_STRING("srs.srs_log_level"); // SRS_SRS_LOG_LEVEL
    SRS_OVERWRITE_BY_ENV_STRING("srs.log_level"); // SRS_LOG_LEVEL

    static string DEFAULT = "trace";

    SrsConfDirective* conf = root->get("srs_log_level");
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }

    return conf->arg0();
}

string SrsConfig::get_log_level_v2()
{
    SRS_OVERWRITE_BY_ENV_STRING("srs.srs_log_level_v2"); // SRS_SRS_LOG_LEVEL_V2
    SRS_OVERWRITE_BY_ENV_STRING("srs.log_level_v2"); // SRS_LOG_LEVEL_V2

    static string DEFAULT = "";

    SrsConfDirective* conf = root->get("srs_log_level_v2");
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }

    return conf->arg0();
}

string SrsConfig::get_log_file()
{
    SRS_OVERWRITE_BY_ENV_STRING("srs.srs_log_file"); // SRS_SRS_LOG_FILE
    SRS_OVERWRITE_BY_ENV_STRING("srs.log_file"); // SRS_LOG_FILE

    static string DEFAULT = "./objs/srs.log";
    
    SrsConfDirective* conf = root->get("srs_log_file");
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }
    
    return conf->arg0();
}

bool SrsConfig::get_ff_log_enabled()
{
    string log = get_ff_log_dir();
    return log != SRS_CONSTS_NULL_FILE;
}

string SrsConfig::get_ff_log_dir()
{
    SRS_OVERWRITE_BY_ENV_STRING("srs.ff_log_dir"); // SRS_FF_LOG_DIR

    static string DEFAULT = "./objs";
    
    SrsConfDirective* conf = root->get("ff_log_dir");
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }
    
    return conf->arg0();
}

string SrsConfig::get_ff_log_level()
{
    SRS_OVERWRITE_BY_ENV_STRING("srs.ff_log_level"); // SRS_FF_LOG_LEVEL

    static string DEFAULT = "info";

    SrsConfDirective* conf = root->get("ff_log_level");
    if (!conf || conf->arg0().empty()) {
        return DEFAULT;
    }

    return conf->arg0();
}

string SrsConfig::get_http_server_listen()
{
    static string DEFAULT = "9090";//找不到配置项时使用默认值

    SrsConfDirective* conf = root->get("http_server");//先从根指令获取子指令
    for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
        //再遍历子指令里面的子指令
        string n = conf->at(i)->name;
        if(n == "listen" && !conf->at(i)->arg0().empty())
        {
            //如果这一级子指令名称符合,返回子指令的第一个参数
            printf("http_server,listen=%s\n",conf->at(i)->arg0().c_str());
            return conf->at(i)->arg0();
        }
    }

    return DEFAULT;
}


chw_adapt.h

#ifndef CHW_ADAPT_H
#define CHW_ADAPT_H

#include 
#include 
#include 
#include 
#include 
typedef int64_t srs_utime_t;
// The time unit in ms, for example 100 * SRS_UTIME_MILLISECONDS means 100ms.
#define SRS_UTIME_MILLISECONDS 1000

// Convert srs_utime_t as ms.
#define srsu2ms(us) ((us) / SRS_UTIME_MILLISECONDS)
#define srsu2msi(us) int((us) / SRS_UTIME_MILLISECONDS)

// Convert srs_utime_t as second.
#define srsu2s(us) ((us) / SRS_UTIME_SECONDS)
#define srsu2si(us) ((us) / SRS_UTIME_SECONDS)

// Them time duration = end - start. return 0, if start or end is 0.
srs_utime_t srs_duration(srs_utime_t start, srs_utime_t end);

// The time unit in ms, for example 120 * SRS_UTIME_SECONDS means 120s.
#define SRS_UTIME_SECONDS 1000000LL

// The time unit in minutes, for example 3 * SRS_UTIME_MINUTES means 3m.
#define SRS_UTIME_MINUTES 60000000LL

// The time unit in hours, for example 2 * SRS_UTIME_HOURS means 2h.
#define SRS_UTIME_HOURS 3600000000LL

#define SRS_PERF_MERGED_READ
// the default config of mr.
#define SRS_PERF_MR_ENABLED false
#define SRS_PERF_MR_SLEEP (350 * SRS_UTIME_MILLISECONDS)

// For tcmalloc, set the default release rate.
// @see https://gperftools.github.io/gperftools/tcmalloc.html
#define SRS_PERF_TCMALLOC_RELEASE_RATE 0.8
#define SRS_CONSTS_NULL_FILE "/dev/null"
#define SRS_CONSTS_LOCALHOST "127.0.0.1"
#define SRS_CONSTS_LOCALHOST_NAME "localhost"

#define srs_freep(p) \
    delete p; \
    p = NULL; \
    (void)0
// Please use the freepa(T[]) to free an array, otherwise the behavior is undefined.
#define srs_freepa(pa) \
    delete[] pa; \
    pa = NULL; \
    (void)0

// CR             = 
#define SRS_CONSTS_CR '\r' // 0x0D
// LF             = 
#define SRS_CONSTS_LF '\n' // 0x0A
// SP             = 
#define SRS_CONSTS_SP ' ' // 0x20
// SE             = 
#define SRS_CONSTS_SE ';' // 0x3b
// LB             = 
#define SRS_CONSTS_LB '{' // 0x7b
// RB             = 
#define SRS_CONSTS_RB '}' // 0x7d

#define VERSION_MAJOR       5
#define VERSION_MINOR       0
#define VERSION_REVISION    168

#define SRS_INTERNAL_STR(v) #v
#define SRS_XSTR(v) SRS_INTERNAL_STR(v)

#define SRS_CONSTS_RTMP_MIN_CHUNK_SIZE 128
#define SRS_CONSTS_RTMP_MAX_CHUNK_SIZE 65536

#define RTMP_SIG_SRS_KEY "SRS"
#define RTMP_SIG_SRS_CODE "Bee"
#define RTMP_SIG_SRS_URL "https://github.com/ossrs/srs"
#define RTMP_SIG_SRS_LICENSE "MIT"
#define SRS_CONSTRIBUTORS "https://github.com/ossrs/srs/blob/develop/trunk/AUTHORS.md#contributors"
#define RTMP_SIG_SRS_VERSION SRS_XSTR(VERSION_MAJOR) "." SRS_XSTR(VERSION_MINOR) "." SRS_XSTR(VERSION_REVISION)
#define RTMP_SIG_SRS_SERVER RTMP_SIG_SRS_KEY "/" RTMP_SIG_SRS_VERSION "(" RTMP_SIG_SRS_CODE ")"
#define RTMP_SIG_SRS_DOMAIN "ossrs.net"
#define RTMP_SIG_SRS_AUTHORS    "dev"
#define SRS_CONSTS_LOOPBACK "0.0.0.0"
#define SRS_CONSTS_LOOPBACK6 "::"
using namespace std;
#include 
#include 
#include 
#include 
#include 
#include "ifaddrs.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

bool srs_string_ends_with(string str, string flag);
// Get local ip, fill to @param ips
struct SrsIPAddress
{
    // The network interface name, such as eth0, en0, eth1.
    std::string ifname;
    // The IP v4 or v6 address.
    std::string ip;
    // Whether the ip is IPv4 address.
    bool is_ipv4;
    // Whether the ip is internet public IP address.
    bool is_internet;
    // Whether the ip is loopback, such as 127.0.0.1
    bool is_loopback;
};
extern std::vector<SrsIPAddress*>& srs_get_local_ips();
extern vector<SrsIPAddress*> _srs_system_ips;
void srs_free_global_system_ips();

// we detect all network device as internet or intranet device, by its ip address.
//      key is device name, for instance, eth0
//      value is whether internet, for instance, true.
static std::map<std::string, bool> _srs_device_ifs;
bool srs_net_device_is_internet(const sockaddr* addr);

void discover_network_iface(ifaddrs* cur, vector<SrsIPAddress*>& ips, stringstream& ss0, stringstream& ss1, bool ipv6, bool loopback);

void retrieve_local_ips();

vector<SrsIPAddress*>& srs_get_local_ips();

bool srs_check_ip_addr_valid(string ip);

string srs_any_address_for_listener();

void srs_parse_endpoint(string hostport, string& ip, int& port);

std::string srs_fmt(const char* fmt, ...);

string srs_int2str(int64_t value);

// 自动释放对象
// To delete object.
#define SrsAutoFree(className, instance) \
    impl_SrsAutoFree<className> _auto_free_##instance(&instance, false, false, NULL)
// To delete array.
#define SrsAutoFreeA(className, instance) \
    impl_SrsAutoFree<className> _auto_free_array_##instance(&instance, true, false, NULL)
// Use free instead of delete.
#define SrsAutoFreeF(className, instance) \
    impl_SrsAutoFree<className> _auto_free_##instance(&instance, false, true, NULL)
// Use hook instead of delete.
#define SrsAutoFreeH(className, instance, hook) \
    impl_SrsAutoFree<className> _auto_free_##instance(&instance, false, false, hook)
// The template implementation.
template<class T>
class impl_SrsAutoFree
{
private:
    T** ptr;
    bool is_array;
    bool _use_free;
    void (*_hook)(T*);
public:
    // If use_free, use free(void*) to release the p.
    // If specified hook, use hook(p) to release it.
    // Use delete to release p, or delete[] if p is an array.
    impl_SrsAutoFree(T** p, bool array, bool use_free, void (*hook)(T*)) {
        ptr = p;
        is_array = array;
        _use_free = use_free;
        _hook = hook;
    }

    virtual ~impl_SrsAutoFree() {
        if (ptr == NULL || *ptr == NULL) {
            return;
        }

        if (_use_free) {
            free(*ptr);
        } else if (_hook) {
            _hook(*ptr);
        } else {
            if (is_array) {
                delete[] *ptr;
            } else {
                delete *ptr;
            }
        }

        *ptr = NULL;
    }
};

bool srs_path_exists(std::string path);

vector<string> srs_string_split(string s, string seperator);

string srs_string_replace(string str, string old_str, string new_str);

bool srs_string_starts_with(string str, string flag);
string srs_getenv(const string& key);


#endif // CHW_ADAPT_H

chw_adapt.cpp

#include "chw_adapt.h"

vector<SrsIPAddress*> _srs_system_ips;
bool srs_string_ends_with(string str, string flag)
{
    const size_t pos = str.rfind(flag);
    return (pos != string::npos) && (pos == str.length() - flag.length());
}

extern std::vector<SrsIPAddress*>& srs_get_local_ips();

void srs_free_global_system_ips()
{
    vector<SrsIPAddress*>& ips = _srs_system_ips;

    // Release previous IPs.
    for (int i = 0; i < (int)ips.size(); i++) {
        SrsIPAddress* ip = ips[i];
        srs_freep(ip);
    }
    ips.clear();
}

// we detect all network device as internet or intranet device, by its ip address.
//      key is device name, for instance, eth0
//      value is whether internet, for instance, true.

bool srs_net_device_is_internet(const sockaddr* addr)
{
    if(addr->sa_family == AF_INET) {
        const in_addr inaddr = ((sockaddr_in*)addr)->sin_addr;
        const uint32_t addr_h = ntohl(inaddr.s_addr);

        // lo, 127.0.0.0-127.0.0.1
        if (addr_h >= 0x7f000000 && addr_h <= 0x7f000001) {
            return false;
        }

        // Class A 10.0.0.0-10.255.255.255
        if (addr_h >= 0x0a000000 && addr_h <= 0x0affffff) {
            return false;
        }

        // Class B 172.16.0.0-172.31.255.255
        if (addr_h >= 0xac100000 && addr_h <= 0xac1fffff) {
            return false;
        }

        // Class C 192.168.0.0-192.168.255.255
        if (addr_h >= 0xc0a80000 && addr_h <= 0xc0a8ffff) {
            return false;
        }
    } else if(addr->sa_family == AF_INET6) {
        const sockaddr_in6* a6 = (const sockaddr_in6*)addr;

        // IPv6 loopback is ::1
        if (IN6_IS_ADDR_LOOPBACK(&a6->sin6_addr)) {
            return false;
        }

        // IPv6 unspecified is ::
        if (IN6_IS_ADDR_UNSPECIFIED(&a6->sin6_addr)) {
            return false;
        }

        // From IPv4, you might know APIPA (Automatic Private IP Addressing) or AutoNet.
        // Whenever automatic IP configuration through DHCP fails.
        // The prefix of a site-local address is FE80::/10.
        if (IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr)) {
            return false;
        }

        // Site-local addresses are equivalent to private IP addresses in IPv4.
        // The prefix of a site-local address is FEC0::/10.
        // https://4sysops.com/archives/ipv6-tutorial-part-6-site-local-addresses-and-link-local-addresses/
        if (IN6_IS_ADDR_SITELOCAL(&a6->sin6_addr)) {
            return false;
        }

        // Others.
        if (IN6_IS_ADDR_MULTICAST(&a6->sin6_addr)) {
            return false;
        }
        if (IN6_IS_ADDR_MC_NODELOCAL(&a6->sin6_addr)) {
            return false;
        }
        if (IN6_IS_ADDR_MC_LINKLOCAL(&a6->sin6_addr)) {
            return false;
        }
        if (IN6_IS_ADDR_MC_SITELOCAL(&a6->sin6_addr)) {
            return false;
        }
        if (IN6_IS_ADDR_MC_ORGLOCAL(&a6->sin6_addr)) {
            return false;
        }
        if (IN6_IS_ADDR_MC_GLOBAL(&a6->sin6_addr)) {
            return false;
        }
    }

    return true;
}
void discover_network_iface(ifaddrs* cur, vector<SrsIPAddress*>& ips, stringstream& ss0, stringstream& ss1, bool ipv6, bool loopback)
{
    char saddr[64];
    char* h = (char*)saddr;
    socklen_t nbh = (socklen_t)sizeof(saddr);
    const int r0 = getnameinfo(cur->ifa_addr, sizeof(sockaddr_storage), h, nbh, NULL, 0, NI_NUMERICHOST);
    if(r0) {
        printf("convert local ip failed: %s", gai_strerror(r0));
        return;
    }

    std::string ip(saddr, strlen(saddr));
    ss0 << ", iface[" << (int)ips.size() << "] " << cur->ifa_name << " " << (ipv6? "ipv6":"ipv4")
        << " 0x" << std::hex << cur->ifa_flags  << std::dec << " " << ip;

    SrsIPAddress* ip_address = new SrsIPAddress();
    ip_address->ip = ip;
    ip_address->is_ipv4 = !ipv6;
    ip_address->is_loopback = loopback;
    ip_address->ifname = cur->ifa_name;
    ip_address->is_internet = srs_net_device_is_internet(cur->ifa_addr);
    ips.push_back(ip_address);

    // set the device internet status.
    if (!ip_address->is_internet) {
        ss1 << ", intranet ";
        _srs_device_ifs[cur->ifa_name] = false;
    } else {
        ss1 << ", internet ";
        _srs_device_ifs[cur->ifa_name] = true;
    }
    ss1 << cur->ifa_name << " " << ip;
}
void retrieve_local_ips()
{
    // Release previous IPs.
    srs_free_global_system_ips();

    vector<SrsIPAddress*>& ips = _srs_system_ips;

    // Get the addresses.
    ifaddrs* ifap;
    if (getifaddrs(&ifap) == -1) {
        printf("retrieve local ips, getifaddrs failed.");
        return;
    }

    stringstream ss0;
    ss0 << "ips";

    stringstream ss1;
    ss1 << "devices";

    // Discover IPv4 first.
    for (ifaddrs* p = ifap; p ; p = p->ifa_next) {
        ifaddrs* cur = p;

        // Ignore if no address for this interface.
        // @see https://github.com/ossrs/srs/issues/1087#issuecomment-408847115
        if (!cur->ifa_addr) {
            continue;
        }

        // retrieve IP address, ignore the tun0 network device, whose addr is NULL.
        // @see: https://github.com/ossrs/srs/issues/141
        bool ipv4 = (cur->ifa_addr->sa_family == AF_INET);
        bool ready = (cur->ifa_flags & IFF_UP) && (cur->ifa_flags & IFF_RUNNING);
        // Ignore IFF_PROMISC(Interface is in promiscuous mode), which may be set by Wireshark.
        bool ignored = (!cur->ifa_addr) || (cur->ifa_flags & IFF_LOOPBACK) || (cur->ifa_flags & IFF_POINTOPOINT);
        bool loopback = (cur->ifa_flags & IFF_LOOPBACK);
        if (ipv4 && ready && !ignored) {
            discover_network_iface(cur, ips, ss0, ss1, false, loopback);
        }
    }

    // Then, discover IPv6 addresses.
    for (ifaddrs* p = ifap; p ; p = p->ifa_next) {
        ifaddrs* cur = p;

        // Ignore if no address for this interface.
        // @see https://github.com/ossrs/srs/issues/1087#issuecomment-408847115
        if (!cur->ifa_addr) {
            continue;
        }

        // retrieve IP address, ignore the tun0 network device, whose addr is NULL.
        // @see: https://github.com/ossrs/srs/issues/141
        bool ipv6 = (cur->ifa_addr->sa_family == AF_INET6);
        bool ready = (cur->ifa_flags & IFF_UP) && (cur->ifa_flags & IFF_RUNNING);
        bool ignored = (!cur->ifa_addr) || (cur->ifa_flags & IFF_POINTOPOINT) || (cur->ifa_flags & IFF_PROMISC) || (cur->ifa_flags & IFF_LOOPBACK);
        bool loopback = (cur->ifa_flags & IFF_LOOPBACK);
        if (ipv6 && ready && !ignored) {
            discover_network_iface(cur, ips, ss0, ss1, true, loopback);
        }
    }

    // If empty, disover IPv4 loopback.
    if (ips.empty()) {
        for (ifaddrs* p = ifap; p ; p = p->ifa_next) {
            ifaddrs* cur = p;

            // Ignore if no address for this interface.
            // @see https://github.com/ossrs/srs/issues/1087#issuecomment-408847115
            if (!cur->ifa_addr) {
                continue;
            }

            // retrieve IP address, ignore the tun0 network device, whose addr is NULL.
            // @see: https://github.com/ossrs/srs/issues/141
            bool ipv4 = (cur->ifa_addr->sa_family == AF_INET);
            bool ready = (cur->ifa_flags & IFF_UP) && (cur->ifa_flags & IFF_RUNNING);
            bool ignored = (!cur->ifa_addr) || (cur->ifa_flags & IFF_POINTOPOINT) || (cur->ifa_flags & IFF_PROMISC);
            bool loopback = (cur->ifa_flags & IFF_LOOPBACK);
            if (ipv4 && ready && !ignored) {
                discover_network_iface(cur, ips, ss0, ss1, false, loopback);
            }
        }
    }

    printf("%s", ss0.str().c_str());
    printf("%s", ss1.str().c_str());

    freeifaddrs(ifap);
}
vector<SrsIPAddress*>& srs_get_local_ips()
{
    if (_srs_system_ips.empty()) {
        retrieve_local_ips();
    }

    return _srs_system_ips;
}

bool srs_check_ip_addr_valid(string ip)
{
    unsigned char buf[sizeof(struct in6_addr)];

    // check ipv4
    int ret = inet_pton(AF_INET, ip.data(), buf);
    if (ret > 0) {
        return true;
    }

    ret = inet_pton(AF_INET6, ip.data(), buf);
    if (ret > 0) {
        return true;
    }

    return false;
}

string srs_any_address_for_listener()
{
    bool ipv4_active = false;
    bool ipv6_active = false;

    if (true) {
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
        if(fd != -1) {
            ipv4_active = true;
            close(fd);
        }
    }
    if (true) {
        int fd = socket(AF_INET6, SOCK_DGRAM, 0);
        if(fd != -1) {
            ipv6_active = true;
            close(fd);
        }
    }

    if (ipv6_active && !ipv4_active) {
        return SRS_CONSTS_LOOPBACK6;
    }
    return SRS_CONSTS_LOOPBACK;
}
void srs_parse_endpoint(string hostport, string& ip, int& port)
{
    const size_t pos = hostport.rfind(":");   // Look for ":" from the end, to work with IPv6.
    if (pos != std::string::npos) {
        if ((pos >= 1) && (hostport[0] == '[') && (hostport[pos - 1] == ']')) {
            // Handle IPv6 in RFC 2732 format, e.g. [3ffe:dead:beef::1]:1935
            ip = hostport.substr(1, pos - 2);
        } else {
            // Handle IP address
            ip = hostport.substr(0, pos);
        }

        const string sport = hostport.substr(pos + 1);
        port = ::atoi(sport.c_str());
    } else {
        ip = srs_any_address_for_listener();
        port = ::atoi(hostport.c_str());
    }
}

std::string srs_fmt(const char* fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);

    static char buf[8192];
    int r0 = vsnprintf(buf, sizeof(buf), fmt, ap);
    va_end(ap);

    string v;
    if (r0 > 0 && r0 < (int)sizeof(buf)) {
        v.append(buf, r0);
    }

    return v;
}

string srs_int2str(int64_t value)
{
    return srs_fmt("%" PRId64, value);
}


bool srs_path_exists(std::string path)
{
    struct stat st;

    // stat current dir, if exists, return error.
    if (stat(path.c_str(), &st) == 0) {
        return true;
    }

    return false;
}

vector<string> srs_string_split(string s, string seperator)
{
    vector<string> result;
    if(seperator.empty()){
        result.push_back(s);
        return result;
    }

    size_t posBegin = 0;
    size_t posSeperator = s.find(seperator);
    while (posSeperator != string::npos) {
        result.push_back(s.substr(posBegin, posSeperator - posBegin));
        posBegin = posSeperator + seperator.length(); // next byte of seperator
        posSeperator = s.find(seperator, posBegin);
    }
    // push the last element
    result.push_back(s.substr(posBegin));
    return result;
}

string srs_string_replace(string str, string old_str, string new_str)
{
    std::string ret = str;

    if (old_str == new_str) {
        return ret;
    }

    size_t pos = 0;
    while ((pos = ret.find(old_str, pos)) != std::string::npos) {
        ret = ret.replace(pos, old_str.length(), new_str);
        pos += new_str.length();
    }

    return ret;
}

bool srs_string_starts_with(string str, string flag)
{
    return str.find(flag) == 0;
}
string srs_getenv(const string& key)
{
    string ekey = key;
    if (srs_string_starts_with(key, "$")) {
        ekey = key.substr(1);
    }

    if (ekey.empty()) {
        return "";
    }

    std::string::iterator it;
    for (it = ekey.begin(); it != ekey.end(); ++it) {
        if (*it >= 'a' && *it <= 'z') {
            *it += ('A' - 'a');
        } else if (*it == '.') {
            *it = '_';
        }
    }

    char* value = ::getenv(ekey.c_str());
    if (value) {
        return value;
    }

    return "";
}

srs.conf

# main config for srs.
# @see full.conf for detail config.

listen              1935;
max_connections     500;
#srs_log_tank        file;
#srs_log_file        ./objs/srs.log;
daemon              on;
http_api {
    enabled         on;
    listen          1985;
}
http_server {
    enabled         on;
    listen          8080;
    dir             ./objs/nginx/html;
}
rtc_server {
    enabled on;
    listen 8000; # UDP port
    # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
    candidate $CANDIDATE;
}
vhost __defaultVhost__ {
    hls {
        enabled         on;
    }
    http_remux {
        enabled     on;
        mount       [vhost]/[app]/[stream].flv;
    }
    rtc {
        enabled     on;
        # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
        rtmp_to_rtc off;
        # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
        rtc_to_rtmp off;
    }

    play{
        gop_cache_max_frames 2500;
    }
}

Test_ReloadWork.h

#ifndef TEST_RELOADWORK_H
#define TEST_RELOADWORK_H

#include "srs_app_reload.hpp"
class Test_ReloadWork : public ISrsReloadHandler
{
public:
    Test_ReloadWork();

    virtual srs_error_t on_reload_testwork();
};

#endif // TEST_RELOADWORK_H

Test_ReloadWork.cpp

#include "Test_ReloadWork.h"

Test_ReloadWork::Test_ReloadWork()
{

}

srs_error_t Test_ReloadWork::on_reload_testwork()
{
    printf("\non_reload_testwork\n");
    return srs_success;
}

源码测试

main.cpp

#include "srs_app_config.hpp"
#include "Test_ReloadWork.h"

SrsConfig* _srs_config = NULL;

int main(int argc, char *argv[])
{
    setbuf(stdout,NULL);

    _srs_config = new SrsConfig();
    _srs_config->parse_options(argc, argv);
    _srs_config->check_config();

    _srs_config->get_http_server_listen();

    Test_ReloadWork* pTest_ReloadWork = new Test_ReloadWork;
    _srs_config->subscribe(pTest_ReloadWork);
    _srs_config->reload();

    while(1){}
}

打印:

XCORE-SRS/5.0.168(Bee)config parse complete
you can check log by: tail -n 30 -f ./objs/srs.log
please check SRS by: ./etc/init.d/srs status
srs checking config...
you can check log by: tail -n 30 -f ./objs/srs.log
please check SRS by: ./etc/init.d/srs status
http_server,listen=8080
config parse complete
config reloader parse file success.
srs checking config...
you can check log by: tail -n 30 -f ./objs/srs.log
please check SRS by: ./etc/init.d/srs status

on_reload_testwork
reload testwork success.

你可能感兴趣的:(C/C++,c++,linux,服务器,c语言)