From 62e9a757dfd3b88c45f60407671db26987c8f088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Wed, 29 May 2019 21:45:28 +0700 Subject: [PATCH] [{wifi,wifiiw}_linux] Deprecate io.popen --- Changes.md | 6 ++- README.md | 12 +++-- widgets/wifi_linux.lua | 109 +++++++++++++-------------------------- widgets/wifiiw_linux.lua | 80 +++++++++++++--------------- 4 files changed, 84 insertions(+), 123 deletions(-) diff --git a/Changes.md b/Changes.md index c277149..be479f5 100644 --- a/Changes.md +++ b/Changes.md @@ -6,6 +6,7 @@ IMPORTANT: Added: +- [wifi_linux] Expose frequency and transmission power - `spawn` as a fallback for `awful.spawn` in case Vicious is used as a stand-alone library. This wrapper, however, does NOT provide the facilities to asynchronously spawn new processes. It also lacks a few features such as @@ -16,7 +17,10 @@ Added: Fixed: -- [volume,gmail,bat_freebsd,mem_freebsd,net_freebsd,mdir] Deprecate `io.popen` +- Deprecate the use of `io.popen` in following widgets: + * wifi_linux, wifiiw_linux + * bat_freebsd, mem_freebsd, net_freebsd + * volume, gmail, mdir - [mpd] Lua 5.3 compatibility (for real this time); also correct a typo - [pkg,weather,contrib/btc] Allow function call without Awesome - [pkg] Use more updated front-ends for Debian/Ubuntu (apt) and Fedora (dnf) diff --git a/README.md b/README.md index d8ff255..4e9b50e 100644 --- a/README.md +++ b/README.md @@ -514,8 +514,10 @@ Provides wireless information for a requested interface. Supported platforms: GNU/Linux. * Argument: the network interface, e.g. `"wlan0"` -* Returns a table with string keys: `${ssid}`, `${mode}`, `${chan}`, `${rate}`, - `${link}`, `${linp}` (link quality in percent) and `${sign}` (signal level) +* Returns a table with string keys: `${ssid}`, `${mode}`, `${chan}`, + `${rate}` (Mb/s), `${freq}` (MHz), `${txpw}` (transmission power, in dBm), + `${sign}` (signal level), `${link}` and `${linp}` (link quality + per 70 and per cent) ### vicious.widgets.wifiiw @@ -525,9 +527,9 @@ vicious.widgets.wifi, but uses `iw` instead of `iwconfig`). Supported platforms: GNU/Linux. * Argument: the network interface, e.g. `"wlan0"` -* Returns a table with string keys: `${ssid}`, `${mode}`, `${chan}`, `${rate}`, - `${freq}`, `${linp}` (link quality in percent), `${txpw}` (tx power) and - `${sign}` (signal level) +* Returns a table with string keys: `${ssid}`, `${mode}`, `${chan}`, + `${rate}` (Mb/s), `${freq}` (MHz), `${linp}` (link quality in percent), + `${txpw}` (transmission power, in dBm) and `${sign}` (signal level, in dBm) ## Custom widget types diff --git a/widgets/wifi_linux.lua b/widgets/wifi_linux.lua index 423765a..b7a3fd8 100644 --- a/widgets/wifi_linux.lua +++ b/widgets/wifi_linux.lua @@ -4,89 +4,50 @@ --------------------------------------------------- -- {{{ Grab environment +local type = type local tonumber = tonumber -local math = { ceil = math.ceil } -local setmetatable = setmetatable -local helpers = require("vicious.helpers") -local io = { - open = io.open, - popen = io.popen -} -local string = { - find = string.find, - match = string.match -} +local math = { floor = math.floor } + +local helpers = require"vicious.helpers" +local spawn = require"vicious.spawn" -- }}} --- Wifi: provides wireless information for a requested interface +-- Wifi: provides wireless information for a requested interface using iwconfig -- vicious.widgets.wifi local wifi_linux = {} - --- {{{ Variable definitions -local iwconfig = "iwconfig" -local iwcpaths = { "/sbin", "/usr/sbin", "/usr/local/sbin", "/usr/bin" } --- }}} - - -- {{{ Wireless widget type -local function worker(format, warg) - if not warg then return end - - -- Default values - local winfo = { - ["{ssid}"] = "N/A", - ["{mode}"] = "N/A", - ["{chan}"] = 0, - ["{rate}"] = 0, - ["{link}"] = 0, - ["{linp}"] = 0, - ["{sign}"] = 0 - } - - -- Sbin paths aren't in user PATH, search for the binary - if iwconfig == "iwconfig" then - for _, p in ipairs(iwcpaths) do - local f = io.open(p .. "/iwconfig", "rb") - if f then - iwconfig = p .. "/iwconfig" - f:close() - break - end - end - end - - -- Get data from iwconfig where available - local f = io.popen(iwconfig .." ".. helpers.shellquote(warg) .. " 2>&1") - local iw = f:read("*all") - f:close() - - -- iwconfig wasn't found, isn't executable, or non-wireless interface - if iw == nil or string.find(iw, "No such device") then - return winfo - end - - -- Output differs from system to system, some stats can be - -- separated by =, and not all drivers report all stats - -- SSID can have almost anything in it - winfo["{ssid}"] = string.match(iw, 'ESSID[=:]"(.-)"') or winfo["{ssid}"] - winfo["{mode}"] = -- Modes are simple, but also match the "-" in Ad-Hoc - string.match(iw, "Mode[=:]([%w%-]*)") or winfo["{mode}"] - winfo["{chan}"] = -- Channels are plain digits - tonumber(string.match(iw, "Channel[=:]([%d]+)") or winfo["{chan}"]) - winfo["{rate}"] = -- Bitrate can start with a space, we don't want to display Mb/s - tonumber(string.match(iw, "Bit Rate[=:]([%s]?[%d%.]*)") or winfo["{rate}"]) - winfo["{link}"] = -- Link quality can contain a slash (32/70), match only the first number - tonumber(string.match(iw, "Link Quality[=:]([%d]+)") or winfo["{link}"]) - winfo["{sign}"] = -- Signal level can be a negative value, don't display decibel notation - tonumber(string.match(iw, "Signal level[=:]([%-]?[%d]+)") or winfo["{sign}"]) - - -- Link quality percentage if quality was available - if winfo["{link}"] ~= 0 then winfo["{linp}"] = math.ceil(winfo["{link}"] / 0.7) end - +local function parser(stdout, stderr, exitreason, exitcode) + local winfo = {} + -- Output differs from system to system, stats can be separated by + -- either = or :. Some stats may not be supported by driver. + -- SSID can have almost anything in it. + winfo["{ssid}"] = stdout:match'ESSID[=:]"(.-)"' or "N/A" + -- Modes are simple, but also match the "-" in Ad-Hoc + winfo["{mode}"] = stdout:match"Mode[=:]([%w%-]+)" or "N/A" + winfo["{chan}"] = tonumber(stdout:match"Channel[=:](%d+)" or 0) + winfo["{rate}"] = -- Bitrate without unit (Mb/s) + tonumber(stdout:match"Bit Rate[=:]%s?([%d%.]+)" or 0) + winfo["{freq}"] = -- Frequency in MHz (is output always in GHz?) + tonumber(stdout:match"Frequency[=:]%s?([%d%.]+)" or 0) * 1000 + winfo["{txpw}"] = -- Transmission power in dBm + tonumber(stdout:match"Tx%-Power[=:](%d+)" or 0) + winfo["{link}"] = -- Link quality over 70 + tonumber(stdout:match"Link Quality[=:](%d+)" or 0) + winfo["{linp}"] = -- Link quality percentage if quality was available + winfo["{link}"] ~= 0 and math.floor(winfo["{link}"]/0.7 + 0.5) or 0 + -- Signal level without unit (dBm), can be negative value + winfo["{sign}"] = tonumber(stdout:match"Signal level[=:](%-?%d+)" or 0) return winfo end + +function wifi_linux.async(format, warg, callback) + if type(warg) ~= "string" then return callback{} end + spawn.easy_async_with_shell( + "PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin iwconfig " .. warg, + function (...) callback(parser(...)) end) +end -- }}} -return setmetatable(wifi_linux, { __call = function(_, ...) return worker(...) end }) +return helpers.setasyncall(wifi_linux) diff --git a/widgets/wifiiw_linux.lua b/widgets/wifiiw_linux.lua index 82d4e3f..25aebe1 100644 --- a/widgets/wifiiw_linux.lua +++ b/widgets/wifiiw_linux.lua @@ -4,63 +4,57 @@ --------------------------------------------------- -- {{{ Grab environment +local type = type local tonumber = tonumber -local setmetatable = setmetatable + local helpers = require("vicious.helpers") -local io = { - open = io.open, - popen = io.popen -} -local string = { - find = string.find, - match = string.match -} +local spawn = require("vicious.spawn") -- }}} --- Wifiiw: provides wireless information for a requested interface using iw instead of deprecated iwconfig +-- Wifiiw: provides wireless information for a requested interface +-- using iw instead of deprecated iwconfig -- vicious.widgets.wifiiw local wifiiw_linux = {} +local LINK = "PATH=$PATH:/sbin/:/usr/sbin:/usr/local/sbin iw dev %s link" +local INFO = "PATH=$PATH:/sbin/:/usr/sbin:/usr/local/sbin iw dev %s info" -- {{{ Wireless widget type -local function worker(format, warg) - if not warg then return end - - -- Default values +function wifiiw_linux.async(format, warg, callback) + if type(warg) ~= "string" then return callback{} end local winfo = {} - -- Get data from iw where available - local f = io.popen("export PATH=$PATH:/sbin/:/usr/sbin:/usr/local/sbin;" .. - "iw dev ".. helpers.shellquote(tostring(warg)) .. " link 2>&1;" .. - "iw dev ".. helpers.shellquote(tostring(warg)) .. " info 2>&1") - local iwresult = f:read("*all") - f:close() - - -- iw wasn't found, isn't executable, or non-wireless interface - if iwresult == nil or string.find(iwresult, "No such device") then - return winfo + local function parse_link(stdout) + winfo["{ssid}"] = stdout:match"SSID: ([^\n]*)" or "N/A" + winfo["{freq}"] = tonumber(stdout:match"freq: (%d+)" or 0) + winfo["{sign}"] = -- Signal level can be negative; w/o unit (dBm) + tonumber(stdout:match"signal: (%-?%d+)" or 0) + winfo["{linp}"] = -- Link Quality (-100dBm->0%, -50dBm->100%) + winfo["{sign}"] ~= 0 and 200 + winfo["{sign}"]*2 or 0 + winfo["{rate}"] = -- Transmission rate, without unit (Mb/s) + tonumber(stdout:match"tx bitrate: ([%d%.]+)" or 0) end - -- string match is simple in most cases, because iw uses a new line for every info - winfo["{ssid}"] = -- SSID can have almost anything in it until new line - string.match(iwresult, "SSID: ([^\n]*)") or "N/A" - winfo["{mode}"] = -- everything after 'type ' until new line - string.match(iwresult, "type ([^\n]*)") or "N/A" - winfo["{chan}"] = -- Channels are plain digits - tonumber(string.match(iwresult, "channel ([%d]+)") or 0) - winfo["{rate}"] = -- We don't want to display Mb/s - tonumber(string.match(iwresult, "tx bitrate: ([%d%.]*)") or 0) - winfo["{freq}"] = -- Frequency are plain digits - tonumber(string.match(iwresult, "freq: ([%d]+)") or 0) - winfo["{sign}"] = -- Signal level can be a negative value, don't display decibel notation - tonumber(string.match(iwresult, "signal: (%-[%d]+)") or 0) - winfo["{linp}"] = -- Link Quality using the Windows definition (-50dBm->100%, -100dBm->0%) - (winfo["{sign}"] ~= 0 and 100 - ((winfo["{sign}"] * -2) - 100) or 0) - winfo["{txpw}"] = -- TX Power can be a negative value, don't display decibel notation - tonumber(string.match(iwresult, "txpower ([%-]?[%d]+)") or 0) - return winfo + local function parse_info(stdout) + winfo["{mode}"] = stdout:match"type ([^\n]*)" or "N/A" + winfo["{chan}"] = tonumber(stdout:match"channel (%d+)" or 0) + -- Transmission power, without unit (dBm) + winfo["{txpw}"] = tonumber(stdout:match"txpower (%-?%d+)" or 0) + end + + spawn.easy_async_with_shell( + LINK:format(warg), + function (stdout, stderr, exitreason, exitcode) + parse_link(stdout) + spawn.easy_async_with_shell( + INFO:format(warg), + function (stdout, stderr, exitreason, exitcode) + parse_info(stdout) + callback(winfo) + end) + end) end -- }}} -return setmetatable(wifiiw_linux, { __call = function(_, ...) return worker(...) end }) +return helpers.setasyncall(wifiiw_linux)