Added support for shadow passwords
[project/luci.git] / libs / sys / luasrc / sys.lua
index 6b6ea70..4bf2946 100644 (file)
@@ -24,14 +24,41 @@ limitations under the License.
 
 ]]--
 
+
+local io    = require "io"
+local os    = require "os"
+local posix = require "posix"
+local table = require "table"
+
+local luci  = {}
+luci.util   = require "luci.util"
+luci.fs     = require "luci.fs"
+luci.ip     = require "luci.ip"
+
+local tonumber, ipairs, pairs = tonumber, ipairs, pairs
+
+
 --- LuCI Linux and POSIX system utilities.
-module("luci.sys", package.seeall)
-require("posix")
-require("luci.util")
-require("luci.fs")
-require("luci.ip")
+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
 -- @param kpattern     Pattern of files to keep over flash process
 -- @return                     Return value of os.execute()
 function flash(image, kpattern)
@@ -50,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
 
@@ -94,10 +138,16 @@ end
 --- Returns the contents of a documented referred by an URL.
 -- @param url   The URL to retrieve
 -- @param stream Return a stream instead of a buffer
+-- @param target Directly write to target file name
 -- @return             String containing the contents of given the URL
-function httpget(url, stream)
-       local source = stream and io.open or luci.util.exec
-       return source("wget -qO- '"..url:gsub("'", "").."'")
+function httpget(url, stream, target)
+       if not target then
+               local source = stream and io.open or luci.util.exec
+               return source("wget -qO- '"..url:gsub("'", "").."'")
+       else
+               return os.execute("wget -qO '%s' '%s'" %
+                       {target:gsub("'", ""), url:gsub("'", "")})
+       end
 end
 
 --- Returns the system load average values.
@@ -124,9 +174,6 @@ end
 -- @return     String containing the memory used for caching in kB
 -- @return     String containing the memory used for buffering in kB
 -- @return     String containing the free memory amount in kB
--- @return     Number containing free memory in percent
--- @return     Number containing buffer memory in percent
--- @return     Number containing cache memory in percent
 function sysinfo()
        local c1 = "cat /proc/cpuinfo|grep system\\ typ|cut -d: -f2 2>/dev/null"
        local c2 = "uname -m 2>/dev/null"
@@ -139,13 +186,10 @@ function sysinfo()
 
        local system = luci.util.trim(luci.util.exec(c1))
        local model = ""
-       local memtotal = luci.util.trim(luci.util.exec(c5))
-       local memcached = luci.util.trim(luci.util.exec(c6))
-       local memfree = luci.util.trim(luci.util.exec(c7))
-       local membuffers = luci.util.trim(luci.util.exec(c8))
-       local perc_memfree = math.floor((memfree/memtotal)*100)
-       local perc_membuffers = math.floor((membuffers/memtotal)*100)
-       local perc_memcached = math.floor((memcached/memtotal)*100)
+       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))
@@ -154,7 +198,7 @@ function sysinfo()
                model = luci.util.trim(luci.util.exec(c4))
        end
 
-       return system, model, memtotal, memcached, membuffers, memfree, perc_memfree, perc_membuffers, perc_memcached
+       return system, model, memtotal, memcached, membuffers, memfree
 end
 
 --- Retrieves the output of the "logread" command.
@@ -195,6 +239,8 @@ end
 group = {}
 
 --- Returns information about a POSIX user group.
+-- @class function
+-- @name               getgroup
 -- @param group Group ID or name of a system user group
 -- @return     Table with information about the requested group
 group.getgroup = posix.getgroup
@@ -295,6 +341,8 @@ end
 process = {}
 
 --- Get the current process id.
+-- @class function
+-- @name  process.info
 -- @return     Number containing the current pid
 process.info = posix.getpid
 
@@ -304,37 +352,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
 
@@ -359,6 +407,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
@@ -387,11 +437,23 @@ function user.checkpasswd(username, password)
        local account = user.getuser(username)
 
        if account then
-               if account.passwd == "!" then
+               local pwd = account.passwd
+               if pwd == "!" then
                        return true
-               else
-                       return (account.passwd == posix.crypt(password, account.passwd))
+               elseif pwd == "x" then
+                       pwd = nil
+                       for l in io.lines("/etc/shadow") do
+                               pwd = l:match("^%s:([^:]+)" % username)
+                               if pwd then
+                                       break
+                               end
+                       end
+                       if not pwd then
+                               return nil, "No shadow password for " .. username
+                       end
                end
+
+               return (pwd == posix.crypt(password, pwd))
        end
 end
 
@@ -438,8 +500,9 @@ end
 
 --- Get iwlist scan output from all wireless devices.
 -- @return     Table of tables contaiing all scan results
-function wifi.iwscan()
-       local cnt = luci.util.exec("iwlist scan 2>/dev/null")
+function wifi.iwscan(iface)
+       local siface = iface or ""
+       local cnt = luci.util.exec("iwlist "..siface.." scan 2>/dev/null")
        local iws = {}
 
        for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n\n")) do
@@ -457,7 +520,7 @@ function wifi.iwscan()
                end
        end
 
-       return iws
+       return iface and (iws[iface] or {}) or iws
 end