Detect IXP4xx processors correctly
[project/luci.git] / libs / sys / luasrc / sys.lua
index d9f236e..413aa4c 100644 (file)
@@ -35,12 +35,27 @@ luci.util   = require "luci.util"
 luci.fs     = require "luci.fs"
 luci.ip     = require "luci.ip"
 
-local tonumber, ipairs, pairs = tonumber, ipairs, pairs
+local tonumber, ipairs, pairs, pcall = tonumber, ipairs, pairs, pcall
 
 
 --- LuCI Linux and POSIX system utilities.
 module "luci.sys"
 
+--- Execute a given shell command and return the error code
+-- @class              function
+-- @name               call
+-- @param              ...             Command to call
+-- @return             Error code of the command
+function call(...)
+       return os.execute(...) / 256
+end
+
+--- Execute a given shell command and capture its standard output
+-- @class              function
+-- @name               exec
+-- @param command      Command to call
+-- @return                     String containg the return the output of the command
+exec = luci.util.exec
 
 --- Invoke the luci-flash executable to write an image to the flash memory.
 -- @param image                Local path or URL to image file
@@ -62,27 +77,44 @@ function mounts()
        local data = {}
        local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"}
        local ps = luci.util.execi("df")
-       
+
        if not ps then
                return
        else
                ps()
        end
-       
+
        for line in ps do
                local row = {}
-               
+
                local j = 1
                for value in line:gmatch("[^%s]+") do
                        row[k[j]] = value
                        j = j + 1
                end
-               
+
                if row[k[1]] then
+
+                       -- this is a rather ugly workaround to cope with wrapped lines in
+                       -- the df output:
+                       --
+                       --      /dev/scsi/host0/bus0/target0/lun0/part3
+                       --                   114382024  93566472  15005244  86% /mnt/usb
+                       --
+
+                       if not row[k[2]] then
+                               j = 2
+                               line = ps()
+                               for value in line:gmatch("[^%s]+") do
+                                       row[k[j]] = value
+                                       j = j + 1
+                               end
+                       end
+
                        table.insert(data, row)
                end
        end
-       
+
        return data
 end
 
@@ -100,7 +132,7 @@ getenv = posix.getenv
 --- Determine the current hostname.
 -- @return             String containing the system hostname
 function hostname()
-       return io.lines("/proc/sys/kernel/hostname")()
+       return posix.uname("%n")
 end
 
 --- Returns the contents of a documented referred by an URL.
@@ -110,7 +142,7 @@ end
 -- @return             String containing the contents of given the URL
 function httpget(url, stream, target)
        if not target then
-               local source = stream and io.open or luci.util.exec
+               local source = stream and io.popen or luci.util.exec
                return source("wget -qO- '"..url:gsub("'", "").."'")
        else
                return os.execute("wget -qO '%s' '%s'" %
@@ -143,27 +175,24 @@ end
 -- @return     String containing the memory used for buffering in kB
 -- @return     String containing the free memory amount in kB
 function sysinfo()
-       local c1 = "cat /proc/cpuinfo|grep system\\ typ|cut -d: -f2 2>/dev/null"
-       local c2 = "uname -m 2>/dev/null"
-       local c3 = "cat /proc/cpuinfo|grep model\\ name|cut -d: -f2 2>/dev/null"
-       local c4 = "cat /proc/cpuinfo|grep cpu\\ model|cut -d: -f2 2>/dev/null"
-       local c5 = "cat /proc/meminfo|grep MemTotal|awk {' print $2 '} 2>/dev/null"
-       local c6 = "cat /proc/meminfo|grep ^Cached|awk {' print $2 '} 2>/dev/null"
-       local c7 = "cat /proc/meminfo|grep MemFree|awk {' print $2 '} 2>/dev/null"
-       local c8 = "cat /proc/meminfo|grep Buffers|awk {' print $2 '} 2>/dev/null"
-
-       local system = luci.util.trim(luci.util.exec(c1))
+       local cpuinfo = luci.fs.readfile("/proc/cpuinfo")
+       local meminfo = luci.fs.readfile("/proc/meminfo")
+
+       local system = cpuinfo:match("system typ.-:%s*([^\n]+)")
        local model = ""
-       local memtotal = tonumber(luci.util.trim(luci.util.exec(c5)))
-       local memcached = tonumber(luci.util.trim(luci.util.exec(c6)))
-       local memfree = tonumber(luci.util.trim(luci.util.exec(c7)))
-       local membuffers = tonumber(luci.util.trim(luci.util.exec(c8)))
-
-       if system == "" then
-               system = luci.util.trim(luci.util.exec(c2))
-               model = luci.util.trim(luci.util.exec(c3))
+       local memtotal = tonumber(meminfo:match("MemTotal:%s*(%d+)"))
+       local memcached = tonumber(meminfo:match("\nCached:%s*(%d+)"))
+       local memfree = tonumber(meminfo:match("MemFree:%s*(%d+)"))
+       local membuffers = tonumber(meminfo:match("Buffers:%s*(%d+)"))
+
+       if not system then
+               system = posix.uname("%m")
+               model = cpuinfo:match("model name.-:%s*([^\n]+)")
+               if not model then
+                       model = cpuinfo:match("Processor.-:%s*([^\n]+)")
+               end
        else
-               model = luci.util.trim(luci.util.exec(c4))
+               model = cpuinfo:match("cpu model.-:%s*([^\n]+)")
        end
 
        return system, model, memtotal, memcached, membuffers, memfree
@@ -227,6 +256,38 @@ function net.arptable()
        return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+")
 end
 
+--- Returns conntrack information
+-- @return     Table with the currently tracked IP connections
+function net.conntrack()
+       local connt = {}
+       if luci.fs.access("/proc/net/nf_conntrack") then
+               for line in io.lines("/proc/net/nf_conntrack") do
+                       local entry, flags = _parse_mixed_record(line, " +")
+                       entry.layer3 = flags[1]
+                       entry.layer4 = flags[2]
+                       for i=1, #entry do
+                               entry[i] = nil
+                       end
+
+                       connt[#connt+1] = entry
+               end
+       elseif luci.fs.access("/proc/net/ip_conntrack") then
+               for line in io.lines("/proc/net/ip_conntrack") do
+                       local entry, flags = _parse_mixed_record(line, " +")
+                       entry.layer3 = "ipv4"
+                       entry.layer4 = flags[1]
+                       for i=1, #entry do
+                               entry[i] = nil
+                       end
+
+                       connt[#connt+1] = entry
+               end
+       else
+               return nil
+       end
+       return connt
+end
+
 --- Determine the current default route.
 -- @return     Table with the properties of the current default route.
 --                     The following fields are defined:
@@ -309,6 +370,8 @@ end
 process = {}
 
 --- Get the current process id.
+-- @class function
+-- @name  process.info
 -- @return     Number containing the current pid
 process.info = posix.getpid
 
@@ -318,37 +381,37 @@ function process.list()
        local data = {}
        local k
        local ps = luci.util.execi("top -bn1")
-       
+
        if not ps then
                return
        end
-       
+
        while true do
                local line = ps()
                if not line then
                        return
                end
-               
+
                k = luci.util.split(luci.util.trim(line), "%s+", nil, true)
                if k[1] == "PID" then
                        break
                end
        end
-       
+
        for line in ps do
                local row = {}
-               
+
                line = luci.util.trim(line)
                for i, value in ipairs(luci.util.split(line, "%s+", #k-1, true)) do
                        row[k[i]] = value
                end
-               
+
                local pid = tonumber(row[k[1]])
                if pid then
                        data[pid] = row
                end
        end
-       
+
        return data
 end
 
@@ -373,6 +436,8 @@ function process.setuser(pid, uid)
 end
 
 --- Send a signal to a process identified by given pid.
+-- @class function
+-- @name  process.signal
 -- @param pid  Number containing the process id
 -- @param sig  Signal to send (default: 15 [SIGTERM])
 -- @return             Boolean indicating successful operation
@@ -401,12 +466,34 @@ function user.checkpasswd(username, password)
        local account = user.getuser(username)
 
        if account then
-               if account.passwd == "!" then
-                       return true
-               else
-                       return (account.passwd == posix.crypt(password, account.passwd))
+               local pwd = account.passwd
+               local shadowpw
+               if #pwd == 1 then
+                       if luci.fs.stat("/etc/shadow") then
+                               if not pcall(function()
+                                       for l in io.lines("/etc/shadow") do
+                                               shadowpw = l:match("^%s:([^:]+)" % username)
+                                               if shadowpw then
+                                                       pwd = shadowpw
+                                                       break
+                                               end
+                                       end
+                               end) then
+                                       return nil, "Unable to access shadow-file"
+                               end
+                       end
+
+                       if pwd == "!" then
+                               return true
+                       end
+               end
+
+               if pwd and #pwd > 0 and password and #password > 0 then
+                       return (pwd == posix.crypt(password, pwd))
                end
        end
+
+       return false
 end
 
 --- Change the password of given user.
@@ -443,7 +530,11 @@ function wifi.getiwconfig()
                local k = l:match("^(.-) ")
                l = l:gsub("^(.-) +", "", 1)
                if k then
-                       iwc[k] = _parse_mixed_record(l)
+                       local entry, flags = _parse_mixed_record(l)
+                       if entry then
+                               entry.flags = flags
+                       end
+                       iwc[k] = entry
                end
        end
 
@@ -467,7 +558,11 @@ function wifi.iwscan(iface)
                                c = c:gsub("^(.-)- ", "", 1)
                                c = luci.util.split(c, "\n", 7)
                                c = table.concat(c, "\n", 1)
-                               table.insert(iws[k], _parse_mixed_record(c))
+                               local entry, flags = _parse_mixed_record(c)
+                               if entry then
+                                       entry.flags = flags
+                               end
+                               table.insert(iws[k], entry)
                        end
                end
        end
@@ -476,6 +571,60 @@ function wifi.iwscan(iface)
 end
 
 
+--- LuCI system utilities / init related functions.
+-- @class      module
+-- @name       luci.sys.init
+init = {}
+init.dir = "/etc/init.d/"
+
+--- Get the names of all installed init scripts
+-- @return     Table containing the names of all inistalled init scripts
+function init.names()
+       local names = { }
+       for _, name in ipairs(luci.fs.glob(init.dir.."*")) do
+               names[#names+1] = luci.fs.basename(name)
+       end
+       return names
+end
+
+--- Test whether the given init script is enabled
+-- @param name Name of the init script
+-- @return             Boolean indicating whether init is enabled
+function init.enabled(name)
+       if luci.fs.access(init.dir..name) then
+               return ( call(init.dir..name.." enabled") == 0 )
+       end
+       return false
+end
+
+--- Get the index of he given init script
+-- @param name Name of the init script
+-- @return             Numeric index value
+function init.index(name)
+       if luci.fs.access(init.dir..name) then
+               return call("source "..init.dir..name.."; exit $START")
+       end
+end
+
+--- Enable the given init script
+-- @param name Name of the init script
+-- @return             Boolean indicating success
+function init.enable(name)
+       if luci.fs.access(init.dir..name) then
+               return ( call(init.dir..name.." enable") == 1 )
+       end
+end
+
+--- Disable the given init script
+-- @param name Name of the init script
+-- @return             Boolean indicating success
+function init.disable(name)
+       if luci.fs.access(init.dir..name) then
+               return ( call(init.dir..name.." disable") == 0 )
+       end
+end
+
+
 -- Internal functions
 
 function _parse_delimited_table(iter, delimiter)
@@ -506,16 +655,18 @@ function _parse_delimited_table(iter, delimiter)
        return data
 end
 
-function _parse_mixed_record(cnt)
+function _parse_mixed_record(cnt, delimiter)
+       delimiter = delimiter or "  "
        local data = {}
+       local flags = {}
 
        for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
-       for j, f in pairs(luci.util.split(luci.util.trim(l), "  ")) do
+               for j, f in pairs(luci.util.split(luci.util.trim(l), delimiter, nil, true)) do
                local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*')
 
             if k then
                                if x == "" then
-                                       table.insert(data, k)
+                                       table.insert(flags, k)
                                else
                        data[k] = v
                                end
@@ -523,5 +674,5 @@ function _parse_mixed_record(cnt)
        end
        end
 
-    return data
+    return data, flags
 end