转载

Jump The Great Firewall【step14 嵌入lua】

首先这里需要宣传一下,qtun的 网站 已经开张了,功能正在逐步添加中。

一、嵌入lua的原因

由于qtun的可配置参数不断增多,因此有必要将参数写入到配置文件之中。又由于C语言并不擅长做字符串的处理,因此加入了轻量级的lua脚本语言,同时嵌入lua更有助于加入第三方插件的支持。

二、代码的修改

  1. 包装初始化函数
    extern int init_path(char* cmd); extern int init_lua(); extern void show_logo(); extern void conf_init(library_conf_t* conf);
    1. init_path中传入qtun程序所在路径,将其转换为realpath后记录到qtun->this_path中
    2. init_lua中创建出一个lua_State对象,并调用scripts/qtun.lua脚本进行初始化,最后调用script_global_init函数初始化lua环境
    3. show_logo中调用scripts/logo.lua脚本打印出qtun的logo ^o^
    4. conf_init中对传入的conf变量设置初值如默认端口为6687等
  2. script_global_init函数 script_global_init函数在init_lua过程中被调用,用于初始化lua环境
    int script_global_init(lua_State* lua) {  load_lib(lua, "_G", luaopen_base);  load_lib(lua, LUA_TABLIBNAME, luaopen_table);  load_lib(lua, LUA_STRLIBNAME, luaopen_string);  load_lib(lua, LUA_IOLIBNAME, luaopen_io);  lua_pushcfunction(lua, _syslog);  lua_setglobal(lua, "_syslog");  init_qtun_state(lua);  init_qtun_conf(lua);  init_qtun_log(lua);  return 1; } 
      1. 这里首先载入基础的lua库
      2. 然后加入自定义函数_syslog
      3. 创建qtun.state、qtun.conf和qtun.log对象,通过lua.md文档可知qtun.state中所有的属性均是只读的,qtun.conf.conf_file属性是只读的,其他均为可读写的,qtun.log中包含了syslog的一些log等级定义。
      4. 下面我们来看一下这三个函数的实现
        static void init_qtun_state(lua_State* lua) {  get_qtun_obj(lua);  lua_pushstring(lua, "state");  lua_newtable(lua);  lua_newtable(lua);  lua_pushstring(lua, "__index");  lua_pushcfunction(lua, state_get);  lua_settable(lua, -3);  lua_setmetatable(lua, -2);  lua_settable(lua, -3);  lua_pop(lua, 1); } static void init_qtun_conf(lua_State* lua) {  get_qtun_obj(lua);  lua_pushstring(lua, "conf");  lua_newtable(lua);  lua_newtable(lua);  lua_pushstring(lua, "__index");  lua_pushcfunction(lua, conf_get);  lua_settable(lua, -3);  lua_pushstring(lua, "__newindex");  lua_pushcfunction(lua, conf_set);  lua_settable(lua, -3);  lua_setmetatable(lua, -2);  lua_settable(lua, -3);  lua_pop(lua, 1); } static void init_qtun_log(lua_State* lua) {  int new_log = 0;  get_qtun_obj(lua);  lua_pushstring(lua, "log");  lua_gettable(lua, -2);  if (lua_isnil(lua, -1))  {   lua_pop(lua, 1);   lua_pushstring(lua, "log");   lua_newtable(lua);   new_log = 1;  }  push_log_level(lua, "LOG_EMERG"  , 0);  push_log_level(lua, "LOG_ALERT"  , 1);  push_log_level(lua, "LOG_CRIT"   , 2);  push_log_level(lua, "LOG_ERR" , 3);  push_log_level(lua, "LOG_WARNING", 4);  push_log_level(lua, "LOG_NOTICE" , 5);  push_log_level(lua, "LOG_INFO"   , 6);  push_log_level(lua, "LOG_DEBUG"  , 7);  if (new_log)   lua_settable(lua, -3);  lua_pop(lua, 1); } 
    从代码中可知:qtun.state和qtun.conf通过metatable将C对象映射到lua中,qtun.log中只定义了log等级的常量
  3. script_load_config函数 script_load_config函数通过调用scripts/load_config.lua脚本来载入配置文件
    int script_load_config(lua_State* lua, library_conf_t* conf, const char* file_path) {  char path[MAX_PATH];  lua_pushlightuserdata(lua, conf);  lua_setglobal(lua, "__conf__");  strcpy(path, qtun->this_path);  strcat(path, "scripts/load_config.lua");  if (luaL_dofile(lua, path) != 0)  {   fprintf(stderr, "%s/n", lua_tostring(qtun->lua, -1));   lua_close(qtun->lua);   exit(1);  }  return 1; } 
  4. 下面我们来看一下main函数的整个流程

Jump The Great Firewall【step14 嵌入lua】

三、lua脚本

最后让我们来分别看一下这三个lua脚本长啥样

  1. logo.lua
    print('┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓') print('┃   ...   .....  .   .  .  .  ┃') print('┃  .   .    .    .   .  .. .  ┃') print('┃  .   .    .    .   .  . ..  ┃') print('┃   ... .   .     ...   .  .  ┃') print('┃                             ┃') print('┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛')
    这个logo是不是很有爱^_^
  2. qtun.lua
    qtun = {  log = {   syslog = function (level, fmt, ...)    _syslog(level, string.format(fmt, ...))   end  } } function trim(s)  return s:gsub('^%s*(.-)%s*$', '%1') end function is_numeric(s)  return tonumber(s) ~= nil end function is_string(s)  return not is_numeric(s) end 
    这里包装了一些公共函数
  3. load_config.lua
    local function append_key_value(key, value)  if value == 'true' then   value = true  elseif value == 'false' then   value = false  elseif is_numeric(value) then   value = tonumber(value)  elseif key == 'log_level' then   if is_string(value) then    value = qtun.log[value]   end  end  qtun.conf[key] = value end local function set_true(key)  qtun.conf[key] = true end local file = io.open(qtun.conf.conf_file, 'r') while true do ::start::  local line = file:read('*line')  if line == nil then break end  local len = #line  local key = ''  local first = ''  local start = 0  local have_key = false  local have_value = false  local added = false  local j = 0  for i=0, len do   j = i   local ch = line:sub(i + 1, i + 1)   if first == '' and ch ~= ' ' then    first = ch   end   if ch == '#' then    if trim(line:sub(start, i)) == '' then goto start end    have_value = true    if have_key then     append_key_value(key, trim(line:sub(start, i)))    else     set_true(trim(line:sub(start, i)))    end    added = true    break   elseif ch == '=' then    key = trim(line:sub(start, i))    if key == '' then     error('missing key')    end    have_key = true    start = i + 2   end  end  if first == '#' then goto start end  if not added then   if not have_key then    local key = trim(line:sub(start, j))    if key == '' then goto start end    set_true(key)   else    append_key_value(key, trim(line:sub(start, j)))   end  end end file:close() 
    解析配置文件的主要逻辑都在这里了

四、完整代码

完整代码可到 step14 中查看

正文到此结束
Loading...