配置文件如下
listen 1935;
chunk_size 65000;
log_dir ./objs/logs;
max_connections 2000;
vhost __defaultVhost__ {
enabled on;
gop_cache on;
queue_length 30;
forward 127.0.0.1:19350;
hls {
enabled on;
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 30;
}
}
vhost aaa {
enabled on;
gop_cache on;
queue_length 30;
forward 127.0.0.1:19360;
hls {
enabled on;
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 30;
}
http_hooks {
enabled on;
on_connect http://127.0.0.1:8085/api/v1/clients;
on_close http://127.0.0.1:8085/api/v1/clients;
on_publish http://127.0.0.1:8085/api/v1/streams;
on_unpublish http://127.0.0.1:8085/api/v1/streams;
on_play http://127.0.0.1:8085/api/v1/sessions;
on_stop http://127.0.0.1:8085/api/v1/sessions;
}
}
main.cpp
int main(int argc, char** argv){
config->parse_options(argc,argv);
return ret;
}
config.hpp
#ifndef CONIFG_HPP
#define CONIFG_HPP
#include "core.hpp"
#include <vector>
#include <string>
//default vhost for rtmp
class FileBuffer;
class ConfDirective{
public:
int conf_line;
std::string name;
std::vector<std::string> args;
std::vector<ConfDirective*> directives;
public:
ConfDirective();
virtual ~ConfDirective();
public:
virtual int parse(const char* filename);
public:
enum DirectiveType{parse_file,parse_block};
virtual int parse_conf(FileBuffer* buffer,DirectiveType type);
virtual int read_token(FileBuffer* buffer,std::vector<std::string>& args);
};
class Config
{
private:
bool show_help;
bool show_version;
std::string config_file;
ConfDirective* root;
std::vector<IReloadHandler*> subscribes;
public:
Config();
virtual ~Config();
public:
virtual int reload();
virtual void subscribe(IReloadHandler* handler);
virtual void unsubsribe(IReloadHandler* handler);
public:
virtual int parse_options(int argc,char** argv);
virtual void printtree(ConfDirective *root);
private:
virtual int parse_file();
virtual int parse_argv(int& i,char** argv);
virtual void print_help(char** argv);
};
bool dective_equals(ConfDirective* a,ConfDirective* b);
extern Config *config;
#endif
config.cpp
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// file operations.
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define FILE_OFFSET(fd) lseek(fd,0,SEEK_CUR)
int64_t FILE_SIZE(int fd)
{
int64_t pre = FILE_OFFSET(fd);
int64_t pos = lseek(fd,0,SEEK_END);
lseek(fd,pre,SEEK_SET);
return pos;
}
#define LF (char)0x0a
#define CR (char)0x0d
bool is_common_space(char ch)
{
return (ch==' '||ch=='\t'||ch==CR||ch==LF);
}
class FileBuffer
{
private:
// last available position
char* last;
// end of buffer
char* end;
// start of buffer
char* start;
public:
//current consumed position.
char* pos;
//current parsed line.
int line;
FileBuffer();
virtual ~FileBuffer();
virtual int fullfill(const char* filename);
virtual bool empty();
};
FileBuffer::FileBuffer()
{
line = 0;
pos = last = start = NULL;
end = start;
}
FileBuffer::~FileBuffer()
{
sfreepa(start);
}
int FileBuffer::fullfill(const char* filename)
{
int ret = ERROR_SUCCESS;
int fd = -1;
int nread = 0;
int filesize = 0;
if((fd=::open(filename,O_RDONLY,0))<0){
ret = ERROR_SYSTEM_CONFIG_INVALID;
goto finish;
}
if((filesize = FILE_SIZE(fd)-FILE_OFFSET(fd))<=0){
ret = ERROR_SYSTEM_CONFIG_EOF;
goto finish;
}
freepa(start);
pos = last = start = new char[filesize];
end = start + filesize;
if((nread = read(fd, start, filesize)) != filesize)
{
ret = ERROR_SYSTEM_CONFIG_INVALID;
goto finish;
}
finish:
if(fd>0){
::close(fd);
}
return ret;
}
bool FileBuffer::empty(){
return pos >= end;
}
ConfDirective::ConfDirective()
{
}
ConfDirective::~ConfDirective()
{
std::vector<ConfDirective*>::iterator it;
for(it = directives.begin();it!=directives.end();++it){
ConfDirective* directive = * it;
freep(directive);
}
directives.clear();
}
void Config::printtree(ConfDirective *root){
std::vector<ConfDirective*>::iterator it;
std::vector<std::string>::iterator subit;
cout << "name: "<< root->name << " value:";
for(subit=root->args.begin();subit!=root->args.end();++subit){
cout << " " <<*subit <<" ";
}
cout << endl;
if(!root->directives.empty()){
cout << "======" << root->name <<" sub =======" <<endl;
for(it = root->directives.begin();it!=root->directives.end();++it){
ConfDirective* directive = *it;
printtree(directive);
}
cout << "======" << root->name <<" sub end =======" <<endl;
}
}
int ConfDirective::parse_conf(FileBuffer* buffer, DirectiveType type)
{
int ret = ERROR_SUCCESS;
while(true){
std::vector<string> args;
ret = read_token(buffer,args);
/**
ret maybe:
ERROR_SYSTEM_CONFIG_INVALID error
ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found
ERROR_SYSTEM_CONFIG_BLOCK_START toket terminated by '{' found
ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found
ERROR_SYSTEM_CONFIG_EOF the config file is done
*/
if(ret == ERROR_SYSTEM_CONFIG_INVALID){
return ret;
}
if(ret == ERROR_SYSTEM_CONFIG_BLOCK_END){
if(type!=parse_block){
return ret;
}
return ERROR_SUCCESS;
}
if(ret == ERROR_SYSTEM_CONFIG_EOF){
if(type == parse_block){
return ret;
}
return ERROR_SUCCESS;
}
if(args.empty()){
return ret;
}
ConfDirective* directive = new ConfDirective();
directive->conf_line = buffer->line;
directive->name = args[0];
args.erase(args.begin());
directive->args.swap(args);
directives.push_back(directive);
if(ret == ERROR_SYSTEM_CONFIG_BLOCK_START){
if((ret=directive->parse_conf(buffer,parse_block))!=ERROR_SUCCESS){
return ret;
}
}
}
return ret;
}
int ConfDirective::read_token(FileBuffer* buffer, std::vector<string>& args)
{
int ret = ERROR_SUCCESS;
char* pstart = buffer->pos;
int startline = buffer->line;
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()){
ret = ERROR_SYSTEM_CONFIG_EOF;
if(!args.empty() || !last_space){
return ERROR_SYSTEM_CONFIG_INVALID;
}
return ret;
}
char ch = *buffer->pos++;
if(ch == 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==';'){
return ERROR_SYSTEM_CONFIG_DIRECTIVE;
}
if(ch=='{'){
return ERROR_SYSTEM_CONFIG_BLOCK_START;
}
return ERROR_SYSTEM_CONFIG_INVALID;
}
// last charecter is space
if(last_space){
if(is_common_space(ch)){
continue;
}
pstart = buffer->pos - 1;
startline = buffer->line;
switch(ch){
case ';':
if(args.size()==0){
return ERROR_SYSTEM_CONFIG_INVALID;
}
return ERROR_SYSTEM_CONFIG_DIRECTIVE;
case '{':
if(args.size()==0){
return ERROR_SYSTEM_CONFIG_INVALID;
}
return ERROR_SYSTEM_CONFIG_BLOCK_START;
case '}':
if(args.size()!=0){
return ERROR_SYSTEM_CONFIG_INVALID;
}
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 {
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 = buffer->pos - pstart;
char* word = new char[len];
memcpy(word,pstart,len);
word[len-1]=0;
string word_str = word;
if(!word_str.empty()){
args.push_back(word_str);
}
free(word);
if(ch==';'){
return ERROR_SYSTEM_CONFIG_DIRECTIVE;
}
if(ch=='{'){
return ERROR_SYSTEM_CONFIG_BLOCK_START;
}
}
}
}
return ret;
}
int ConfDirective::parse(const char* filename)
{
int ret = ERROR_SUCCESS;
FileBuffer buffer;
if((ret=buffer.fullfill(filename))!=ERROR_SUCCESS){
return ret;
}
return parse_conf(&buffer,parse_file);
}
Config* config = new Config();
Config::Config()
{
show_help = false;
show_version = false;
root = new ConfDirective();
root->conf_line = 0;
root->name = "root";
}
Config::~Config()
{
delete root;
}
int Config::parse_options(int argc,char** argv)
{
int ret = ERROR_SUCCESS;
for(int i=1;i<argc;i++){
if((ret=parse_argv(i,argv))!=ERROR_SUCCESS){
return ret;
}
}
if(show_help){
print_help(argv);
}
if(show_version){
}
if(show_help || show_version){
exit(0);
}
return parse_file();
}
int Config::parse_file()
{
int ret = ERROR_SUCCESS;
if(config_file.empty()){
//return ERROR_SYSTEM_CONFIG_INVALID;
}
if(config_file.empty()){
return ERROR_SYSTEM_CONFIG_INVALID;
}
config_file = CONFIG_DIRECTORY + config_file;
//cout << config_file << endl;
if((ret=root->parse(config_file.c_str()))!=ERROR_SUCCESS){
return ret;
}
return ret;
}
void Config::print_help(char** argv){
printf(
"Usage: %s [-h?Vv] [-c <filename>]\n"
"\n"
"Options:\n"
" -?-h : show help\n"
" -v-V : show version and exit\n"
" -c filename : set configuration file\n"
"\n",
argv[0]);
}
int Config::parse_argv(int& i, char** argv){
int ret = ERROR_SUCCESS;
char* p = argv[i];
if(p==NULL)
return ERROR_SYSTEM_CONFIG_INVALID;
if(*p++!='-'){
ret = ERROR_SYSTEM_CONFIG_INVALID;
return ret;
}
while(*p){
switch(*p++){
case '?':
case 'h':
show_help = true;
break;
case 'v':
case 'V':
show_version = true;
break;
case 'c':
if(*p){
config_file = p;
return ret;
}
if(argv[++i]){
config_file = argv[i];
return ret;
}
ret = ERROR_SYSTEM_CONFIG_INVALID;
return ret;
default:
ret = ERROR_SYSTEM_CONFIG_INVALID;
return ret;
}
}
return ret;
}
int Config::reload()
{
return 0;
}