uhttp luci cgi-bin 自定义输出内容

发布时间 2023-11-28 22:33:41作者: lsgxeva

uhttp luci cgi-bin 自定义输出内容

来源  https://www.cnblogs.com/osnosn/p/17131543.html

 

参考

修改openwrt uhttpd 使用的 ssl 证书

  • op21, op22 测试OK。
  • 如果使用 https 访问,对默认证书的信息不满意。
  • 修改 /etc/config/uhttpd 中 config cert 'defaults' 部分的内容。
    uci -q batch << EOF
    set uhttpd.defaults.organization='my organization'  #组织
    set uhttpd.defaults.commonname='my common name'     #通用名称
    set uhttpd.defaults.location='my location'   #地址
    set uhttpd.defaults.state='my state'    #省/州
    set uhttpd.defaults.country='CN'        #国家代码
    set uhttpd.defaults.days='730'          #有效期,两年
    commit uhttpd
    EOF
    
  • 删除旧证书 (.crt和.key)。rm /etc/uhttpd.*
  • 重启 uhttpd,会自动重新生成证书。/etc/init.d/uhttpd restart

我的测试

  • 不想安装 php-fpm 或者 python3,它们的体积都比较大,小路由器空间不太够。
  • 不修改 openwrt的 uhttpd的配置。因为不想改变 luci的页面。
    • uhttpd 的 root 目录是 /www/
    • 在 /www/中,只支持静态html文件。不支持lua脚本。
    • 在 /www/cgi-bin/中,支持各种可执行文件,包括lua脚本,包括shell脚本。
      脚本文件无后缀要求,需要设置执行权限 chmod +x
  • 最简单的办法就是,创建 /www/cgi-bin/test
    • chmod +x test
    • print() 和 io.write() 尽量不要混用,防止因缓存,导致输出顺序不正确。
      test 内容如下。op22测试可用。
      #!/usr/bin/lua
      io.write("Content-type: text/html\n\n")
      io.write('test\n')
      -- print('test')
      io.write(os.date("%x", os.time()).."\n")
      
      注意:CGI脚本必须要输出"Content-type: text/html\n\n",uhttpd才会认为脚本正确进行了响应,才会输出内容到浏览器。​

如果要操作 sqlite3文件。试试这两个包。

  • 二选一。
    opkg install lsqlite3      #lsqlite3(64kB) 依赖 libsqlite3(840kB)
    opkg install luasql-sqlite3   #luasql-sqlite3(64kB) 依赖 libsqlite3(840kB)
    
  • 在 lua中 可以用 require "lsqlite3" 或者 require "luasql.sqlite3" 引入。
  • sqlite3 命令行工具
    opkg install sqlite3-cli   #sqlite3-cli(193kB) 依赖 libsqlite3(840kB),libedit(194kB)
    

lua 读取 sqlite3 测试,TEST.db 是有内容的数据库文件。

  • op22测试可用。
    #!/usr/bin/lua
    io.write("Content-type: text/html;charset=utf-8\n\n")
    
    local sql3=require "luasql.sqlite3"
    local env=sql3.sqlite3()
    local conn=env:connect("/tmp/tmp/TEST.db")
    name="tom"
    sql_str=string.format("select * from tasktab where id='%s' order by rowid desc limit 10",name)
    cur,err=conn:execute(sql_str)
    print(cur,err,"<br>")
    row=cur:fetch({},"a")
    while row do
      for k,v in pairs(row) do  -- 打印所有字段
        print(k,v,"<br>")
      end
      row=cur:fetch(row,"a")
    end
    cur:close()
    conn:close()
    env:close()
    

openwrt中cgi脚本获取GET提交的内容,是通过环境变量获取。

  • op22测试可用。
    os.getenv("QUERY_STRING")
    

lua 遍历全局变量,环境变量

  • 遍历全局变量。
    for k,v in ipairs(_G) do print(k,v) end
    
  • lua中,没找到遍历 局部变量 的方法。
  • 可以使用 shell脚本打印出所有的环境变量。op22测试可用。
    #!/bin/sh
    echo Content-type: text/html;charset=utf8
    echo
    echo 'test <pre>'
    set
    
  • 或者,用 openwrt中的 NIXIO POSIX library,遍历环境变量。op22测试可用。
    local nixio=require "nixio"
    local env=nixio.getenv()
    for k,v in ipairs(env) do print(k,v) end
    

openwrt中cgi脚本获取POST提交的内容。

  • op22测试可用。
    local POST_DATA = nil
    local POSTLength = tonumber(os.getenv("CONTENT_LENGTH")) or 0
    if (POSTLength > 0) then
       POST_DATA = io.read(POSTLength)
       --POST_DATA = io.read("*a")
    end
    -- POST_DATA -> "test1=1234&test2=abcd%2B"
    
  • urldecode
    local http=require "luci.http"
    posted_str=http.urldecode(posted_str, true)
    

判断文件是否存在。

  • op22测试可用。
    function file_exists(name)
      local f=io.open(name,"rb")
      if f~=nil then io.close(f) return true else return false end
    end
    
  • 或者,用 openwrt中的 NIXIO POSIX library。op22测试可用。
    local fs=require "nixio.fs"
    function file_exists(name)
      local f=fs.stat(name)
      if f~=nil then return true else return false end
    end
    

获取文件大小,不引入其他库。用"rb"二进制读方式,不容易出错。

  • op22测试可用。
    function length_of_file(filename)
      local fh = assert(io.open(filename, "rb"))
      local len = assert(fh:seek("end"))
      fh:close()
      return len
    end
    -- file:seek([whence][,offset])
    -- whence=set, cur, end 文件头,当前位置,文件尾。offset可为负数。
    

文件的写锁。

  • lua 本身没找到 文件读写锁 的操作函数,无论是咨询锁,还是强制锁,都没有。
  • openwrt中的 nixio.open() 提供 文件锁 的操作。 看 nixio.File 的文档。op22测试可用。
    local nixio=require "nixio"
    fp=nixio.open("test-file.txt","w")
    fp:lock('lock')
    fp:write('abcdefghij\n')
    fp:lock('ulock')
    fp:close()
    
    • nixio.open() 打开后,没有 lines() 函数。
    • nixio.open() 以只读打开,不能加锁 lock("lock"),lock("tlock")。

Basic WEB 认证。

  • op22测试可用。
    #!/bin/sh
    echo Content-type: text/html;charset=utf8;
    if [ -z $HTTP_AUTHORIZATION ]; then
      echo Status: 401 Unauthorized
      echo WWW-Authenticate: Basic realm="My Realm"
    fi
    echo
    echo HTTP_AUTHORIZATION=$HTTP_AUTHORIZATION
    # 如:user=test, pwd=test, HTTP_AUTHORIZATION="Basic dGVzdDp0ZXN0"
    
  • 纯lua实现Base64加密与解密
  • openwrt 的 nixio包,有base64decode函数。op22测试可用。
    #!/usr/bin/lua
    require "os"
    local nixio=require "nixio"
    -- "test:test" -> "Basic dGVzdDp0ZXN0"
    local HTTP_AUTHORIZATION=os.getenv("HTTP_AUTHORIZATION")
    local auth_ok=false
    if HTTP_AUTHORIZATION ~= nil then
      local user_pwd=HTTP_AUTHORIZATION:sub(7)
      user_pwd=nixio.bin.b64decode(user_pwd)
      if user_pwd == 'test:test' then
        auth_ok=true
      end
    end
    if auth_ok == false then
      io.write("Status: 401 Unauthorized\n")
      io.write('WWW-Authenticate: Basic realm="My Realm2"\n')
    end
    io.write("Content-type: text/html;charset=utf8\n\n")
    
    print(HTTP_AUTHORIZATION, '<br>')
    print('auth_ok=',auth_ok, '<br>')
    

session管理。

  • 暂时只有思路,未实测。// 假设,保存session文件的目录是/tmp/myweb/。目录路径自己定义。
  • 检查 header 有没有 sessionid 的 cookie。
    • 。就从/tmp/myweb/中吧session文件读入table中。
      如果session文件找不到。则,同下面的"无"。重新生成 sessionid。
    • 。通过 /dev/urandom 设备获取随机数,加上当前时间,混合生成 sessionid。
      通过 cookie把 sessionid写入返回的 header中。
      用 sessionid创建 lua的table,用于保存session数据。
  • 通过 读,改,增,删 这个table中的成员变量。实现对 session变量的读写。
  • 在cgi结束前,把这个 session 的 table写入文件,在/tmp/myweb/目录中。
  • 另外跑一个 cron任务,定时把长时间未修改的 session文件删除了。

openwrt中其他的 lua可用包

  • local jsonc=require "luci.jsonc"
  • local sys= require "luci.sys"
  • local http=require "luci.http"
  • local ip= require "luci.ip"
  • local xml= require "luci.xml"
  • local util=require "luci.util"
  • 这些包的使用文档【luci api Reference

lua 引用其他文件

  • 把其他文件写成模块,放入系统目录,用 xx=require 引入
  • 用 xx=assert(dofile()) 引入,不限位置。
  • 用 xx=assert(loadfile()) 引入,

读写ini配置文件

openwrt的shell字符串通配符比较

  • openwrt 的 shell支持这个写法 [[ ]]。op22测试可用。
    CHECK="abc 123 456 789"
    fnd="456"
    if [[ "$CHECK" == abc* ]]; then
       echo found abc
    fi
    if [[ "$CHECK" == *"$fnd"* ]]; then
       echo found 456
    fi
    

openwrt中ms毫秒级的sleep

三个方案,都不会占用 CPU时间。op22测试可用。

  • 方案一,安装opkg install coreutils-sleep,约占32k的rom空间。
    支持毫秒级精度的 sleep。定时误差就是启动 sleep进程的时间。
  • 方案二,使用 lua的nixio.nanosleep(seconds, nanoseconds)函数,接受纳秒参数。
    毫秒级精度应该 OK,纳秒级精度估计不行。定时误差就是启动 lua进程的时间。mt7620的小路由,启动 lua要20ms。
    下面的例子,可以在 shell中执行./luasleep 2.5延时 2s+500ms。
    #!/usr/bin/lua
    -- filename: luasleep
    if arg[1] == nil then
      local usage="\nUsage: %s s[.ms]\n   s; seconds\n   ms: microSecond 0-999\n"
      print(usage:format(arg[0]) )
      os.exit()
    end
    local nixio=require "nixio"
    local ss,ms=math.modf(arg[1])
    ms=ms *1000
    --nixio.nanosleep(ss, ms*1000000)
    --因精度原因,有的数相乘后的结果值带小数,就会出错。例如xxx.99999,xxx.000001
    nixio.nanosleep(ss, math.floor(ms*1000000))
    
  • 方案三,使用 lua的nixio.poll(fds, microseconds)函数,接受毫秒参数。
    local nixio=require "nixio"
    nixio.poll({},6000)  -- 6 sec
    

lua获取自身脚本所在的目录

  • 比如文件 /root/abc/def/test.lua。op22测试可用。
    local selfpath=debug.getinfo(1,'S').source:sub(2):match('^.*/')
    print(selfpath) -- "/root/abc/def/"或"def/"或"./" 相对路径,有"/"结尾
    
    local selfname=debug.getinfo(1,'S').source:sub(2)
    local fs=require "nixio.fs"
    print(fs.dirname(selfname)) --相对路径,无"/"结尾
    print(fs.realpath(fs.dirname(selfname))) --"/root/abc/def" 绝对路径,无"/"结尾
    
    local nixio=require "nixio"
    print(nixio.getcwd())  -- 工作目录 current working directory

 

========== End