|
|
|
@ -7,18 +7,20 @@ This module defines stubs for common methods.
|
|
|
|
|
local DataStorage = require("datastorage")
|
|
|
|
|
local Geom = require("ui/geometry")
|
|
|
|
|
local logger = require("logger")
|
|
|
|
|
local ffi = require("ffi")
|
|
|
|
|
local time = require("ui/time")
|
|
|
|
|
local util = require("util")
|
|
|
|
|
local _ = require("gettext")
|
|
|
|
|
local ffiUtil = require("ffi/util")
|
|
|
|
|
local C = ffi.C
|
|
|
|
|
local T = ffiUtil.template
|
|
|
|
|
|
|
|
|
|
-- We'll need a bunch of stuff for getifaddrs & co in Device:retrieveNetworkInfo
|
|
|
|
|
require("ffi/posix_h")
|
|
|
|
|
|
|
|
|
|
local function yes() return true end
|
|
|
|
|
local function no() return false end
|
|
|
|
|
|
|
|
|
|
local function isCommand(s)
|
|
|
|
|
return os.execute("command -v "..s.." >/dev/null") == 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local Device = {
|
|
|
|
|
screen_saver_mode = false,
|
|
|
|
|
screen_saver_lock = false,
|
|
|
|
@ -500,57 +502,360 @@ function Device:exit()
|
|
|
|
|
require("ffi/input"):closeAll()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Device:retrieveNetworkInfo()
|
|
|
|
|
-- NOTE: This sed monstrosity is tailored for the busybox implementation of ifconfig
|
|
|
|
|
local std_out = io.popen("ifconfig | " ..
|
|
|
|
|
"sed -n " ..
|
|
|
|
|
"-e 's/ \\+$//g' " ..
|
|
|
|
|
"-e 's/ \\+/ /g' " ..
|
|
|
|
|
"-e 's/ \\?inet6\\? addr: \\?\\([^ ]\\+\\) .*$/IP: \\1/p' " ..
|
|
|
|
|
"-e 's/Link encap:Ethernet[[:blank:]]*HWaddr \\(.*\\)/\\1/p'",
|
|
|
|
|
"r")
|
|
|
|
|
if std_out then
|
|
|
|
|
local result = std_out:read("*all")
|
|
|
|
|
std_out:close()
|
|
|
|
|
std_out = io.popen('2>/dev/null iwconfig | grep ESSID | cut -d\\" -f2')
|
|
|
|
|
if std_out then
|
|
|
|
|
local ssid = std_out:read("*l")
|
|
|
|
|
std_out:close()
|
|
|
|
|
result = result .. "SSID: " .. ssid .. "\n"
|
|
|
|
|
end
|
|
|
|
|
-- iproute2 tools may not always be available, fall back to net-tools if necessary
|
|
|
|
|
if isCommand("ip") then
|
|
|
|
|
std_out = io.popen([[ip r | grep default | tail -n 1 | cut -d ' ' -f 3]], "r")
|
|
|
|
|
else
|
|
|
|
|
std_out = io.popen([[route -n | awk '$4 == "UG" {print $2}' | tail -n 1]], "r")
|
|
|
|
|
end
|
|
|
|
|
local default_gw
|
|
|
|
|
if std_out then
|
|
|
|
|
default_gw = std_out:read("*l")
|
|
|
|
|
std_out:close()
|
|
|
|
|
if default_gw == "" then
|
|
|
|
|
default_gw = nil
|
|
|
|
|
-- Lifted from busybox's libbb/inet_cksum.c
|
|
|
|
|
local function inet_cksum(ptr, nleft)
|
|
|
|
|
local addr = ffi.new("const uint16_t *", ptr)
|
|
|
|
|
|
|
|
|
|
local sum = ffi.new("unsigned int", 0)
|
|
|
|
|
while nleft > 1 do
|
|
|
|
|
sum = sum + addr[0]
|
|
|
|
|
addr = addr + 1
|
|
|
|
|
nleft = nleft - 2
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if nleft == 1 then
|
|
|
|
|
local u8p = ffi.cast("uint8_t *", addr)
|
|
|
|
|
sum = sum + u8p[0]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
sum = bit.rshift(sum, 16) + bit.band(sum, 0xFFFF)
|
|
|
|
|
sum = sum + bit.rshift(sum, 16)
|
|
|
|
|
|
|
|
|
|
sum = bit.bnot(sum)
|
|
|
|
|
return ffi.cast("uint16_t", sum)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Device:ping4(ip)
|
|
|
|
|
-- Try an unprivileged ICMP socket first
|
|
|
|
|
-- NOTE: This is disabled by default, barring custom distro setup during init, c.f., sysctl net.ipv4.ping_group_range
|
|
|
|
|
-- It also requires Linux 3.0+ (https://github.com/torvalds/linux/commit/c319b4d76b9e583a5d88d6bf190e079c4e43213d)
|
|
|
|
|
local socket, socket_type
|
|
|
|
|
socket = C.socket(C.AF_INET, bit.bor(C.SOCK_DGRAM, C.SOCK_NONBLOCK, C.SOCK_CLOEXEC), C.IPPROTO_ICMP)
|
|
|
|
|
if socket == -1 then
|
|
|
|
|
local errno = ffi.errno()
|
|
|
|
|
logger.dbg("Device:ping4: unprivileged ICMP socket:", ffi.string(C.strerror(errno)))
|
|
|
|
|
|
|
|
|
|
-- Try a raw socket
|
|
|
|
|
socket = C.socket(C.AF_INET, bit.bor(C.SOCK_RAW, C.SOCK_NONBLOCK, C.SOCK_CLOEXEC), C.IPPROTO_ICMP)
|
|
|
|
|
if socket == -1 then
|
|
|
|
|
errno = ffi.errno()
|
|
|
|
|
if errno == C.EPERM then
|
|
|
|
|
logger.dbg("Device:ping4: Opening a RAW ICMP socket requires CAP_NET_RAW capabilities!")
|
|
|
|
|
else
|
|
|
|
|
logger.dbg("Device:ping4: Raw ICMP socket:", ffi.string(C.strerror(errno)))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if default_gw then
|
|
|
|
|
result = result .. T(_("Default gateway: %1"), default_gw) .. "\n"
|
|
|
|
|
-- NOTE: No -w flag available in the old busybox build used on Legacy Kindles (K4 included)...
|
|
|
|
|
local pingok
|
|
|
|
|
--- Fall-back to the ping CLI tool, in the hope that it's setuid...
|
|
|
|
|
if self:isKindle() and self:hasDPad() then
|
|
|
|
|
pingok = os.execute("ping -q -c1 " .. default_gw .. " > /dev/null")
|
|
|
|
|
-- NOTE: No -w flag available in the old busybox build used on Legacy Kindles (K4 included)...
|
|
|
|
|
return os.execute("ping -q -c1 " .. ip .. " > /dev/null") == 0
|
|
|
|
|
else
|
|
|
|
|
pingok = os.execute("ping -q -c1 -w2 " .. default_gw .. " > /dev/null")
|
|
|
|
|
return os.execute("ping -q -c1 -w2 " .. ip .. " > /dev/null") == 0
|
|
|
|
|
end
|
|
|
|
|
if pingok == 0 then
|
|
|
|
|
result = result .. _("Gateway ping successful")
|
|
|
|
|
else
|
|
|
|
|
socket_type = C.SOCK_RAW
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
socket_type = C.SOCK_DGRAM
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- c.f., busybox's networking/ping.c
|
|
|
|
|
local DEFDATALEN = 56 -- 64 - 8
|
|
|
|
|
local MAXIPLEN = 60
|
|
|
|
|
local MAXICMPLEN = 76
|
|
|
|
|
|
|
|
|
|
-- Base the id on our PID (like busybox)
|
|
|
|
|
local myid = ffi.cast("uint16_t", C.getpid())
|
|
|
|
|
myid = C.htons(myid)
|
|
|
|
|
|
|
|
|
|
-- Setup the packet
|
|
|
|
|
local packet = ffi.new("char[?]", DEFDATALEN + MAXIPLEN + MAXICMPLEN)
|
|
|
|
|
local pkt = ffi.cast("struct icmphdr *", packet)
|
|
|
|
|
pkt.type = C.ICMP_ECHO
|
|
|
|
|
pkt.un.echo.id = myid
|
|
|
|
|
pkt.un.echo.sequence = C.htons(1)
|
|
|
|
|
pkt.checksum = inet_cksum(ffi.cast("const void *", pkt), ffi.sizeof(packet))
|
|
|
|
|
|
|
|
|
|
-- Set the destination address
|
|
|
|
|
local addr = ffi.new("struct sockaddr_in")
|
|
|
|
|
addr.sin_family = C.AF_INET
|
|
|
|
|
local in_addr = ffi.new("struct in_addr")
|
|
|
|
|
if C.inet_aton(ip, in_addr) == 0 then
|
|
|
|
|
logger.err("Device:ping4: Invalid address:", ip)
|
|
|
|
|
C.close(socket)
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
addr.sin_addr = in_addr
|
|
|
|
|
addr.sin_port = 0
|
|
|
|
|
|
|
|
|
|
-- Send the ping
|
|
|
|
|
local start_time = time.now()
|
|
|
|
|
if C.sendto(socket, packet, DEFDATALEN + C.ICMP_MINLEN, 0, ffi.cast("struct sockaddr*", addr), ffi.sizeof(addr)) == - 1 then
|
|
|
|
|
local errno = ffi.errno()
|
|
|
|
|
logger.err("Device:ping4: sendto:", ffi.string(C.strerror(errno)))
|
|
|
|
|
C.close(socket)
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- We'll poll to make timing out easier on us (busybox uses a SIGALRM :s)
|
|
|
|
|
local pfd = ffi.new("struct pollfd")
|
|
|
|
|
pfd.fd = socket
|
|
|
|
|
pfd.events = C.POLLIN
|
|
|
|
|
local timeout = 2000
|
|
|
|
|
|
|
|
|
|
-- Wait for a response
|
|
|
|
|
while true do
|
|
|
|
|
local poll_num = C.poll(pfd, 1, timeout)
|
|
|
|
|
-- Slice the timeout in two on every retry, ensuring we'll bail definitively after 4s...
|
|
|
|
|
timeout = bit.rshift(timeout, 1)
|
|
|
|
|
if poll_num == -1 then
|
|
|
|
|
local errno = ffi.errno()
|
|
|
|
|
if errno ~= C.EINTR then
|
|
|
|
|
logger.err("Device:ping4: poll:", ffi.string(C.strerror(errno)))
|
|
|
|
|
C.close(socket)
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
elseif poll_num > 0 then
|
|
|
|
|
local c = C.recv(socket, packet, ffi.sizeof(packet), 0)
|
|
|
|
|
if c == -1 then
|
|
|
|
|
local errno = ffi.errno()
|
|
|
|
|
if errno ~= C.EINTR then
|
|
|
|
|
logger.err("Device:ping4: recv:", ffi.string(C.strerror(errno)))
|
|
|
|
|
C.close(socket)
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
result = result .. _("Gateway ping FAILED")
|
|
|
|
|
-- Do some minimal verification of the reply's validity.
|
|
|
|
|
-- This is mostly based on busybox's ping,
|
|
|
|
|
-- with some extra inspiration from iputils's ping, especially as far as SOCK_DGRAM is concerned.
|
|
|
|
|
local iphdr = ffi.cast("struct iphdr *", packet) -- ip + icmp
|
|
|
|
|
local hlen
|
|
|
|
|
if socket_type == C.SOCK_RAW then
|
|
|
|
|
hlen = bit.lshift(iphdr.ihl, 2)
|
|
|
|
|
if c < (hlen + 8) or iphdr.ihl < 5 then
|
|
|
|
|
-- Packet too short (we don't use recvfrom, so we can't log where it's from ;o))
|
|
|
|
|
logger.dbg("Device:ping4: received a short packet")
|
|
|
|
|
goto continue
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
hlen = 0
|
|
|
|
|
end
|
|
|
|
|
-- Skip ip hdr to get at the ICMP part
|
|
|
|
|
local icp = ffi.cast("struct icmphdr *", packet + hlen)
|
|
|
|
|
-- Check that we got a *reply* to *our* ping
|
|
|
|
|
-- NOTE: The reply's ident is defined by the kernel for SOCK_DGRAM, so we can't do anything with it!
|
|
|
|
|
if icp.type == C.ICMP_ECHOREPLY and
|
|
|
|
|
(socket_type == C.SOCK_DGRAM or icp.un.echo.id == myid) then
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
local end_time = time.now()
|
|
|
|
|
logger.info("Device:ping4: timed out waiting for a response from", ip)
|
|
|
|
|
C.close(socket)
|
|
|
|
|
return false, end_time - start_time
|
|
|
|
|
end
|
|
|
|
|
::continue::
|
|
|
|
|
end
|
|
|
|
|
local end_time = time.now()
|
|
|
|
|
|
|
|
|
|
-- If we got this far, we've got a reply to our ping in time!
|
|
|
|
|
C.close(socket)
|
|
|
|
|
return true, end_time - start_time
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Device:getDefaultRoute(interface)
|
|
|
|
|
local fd = io.open("/proc/net/route", "re")
|
|
|
|
|
if not fd then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local gateway
|
|
|
|
|
local l = 1
|
|
|
|
|
for line in fd:lines() do
|
|
|
|
|
-- Skip the first line (header)
|
|
|
|
|
if l > 1 then
|
|
|
|
|
local fields = {}
|
|
|
|
|
for field in line:gmatch("%S+") do
|
|
|
|
|
table.insert(fields, field)
|
|
|
|
|
end
|
|
|
|
|
-- Check the requested interface or anything that isn't lo
|
|
|
|
|
if (interface and fields[1] == interface) or (not interface and fields[1] ~= "lo") then
|
|
|
|
|
-- We're looking for something that's up & a gateway
|
|
|
|
|
if bit.band(fields[4], C.RTF_UP) ~= 0 and bit.band(fields[4], C.RTF_GATEWAY) ~= 0 then
|
|
|
|
|
-- Handle the conversion from network endianness hex string into a human-readable numeric form
|
|
|
|
|
local sockaddr_in = ffi.new("struct sockaddr_in")
|
|
|
|
|
sockaddr_in.sin_family = C.AF_INET
|
|
|
|
|
sockaddr_in.sin_addr.s_addr = tonumber(fields[3], 16)
|
|
|
|
|
local host = ffi.new("char[?]", C.NI_MAXHOST)
|
|
|
|
|
local s = C.getnameinfo(ffi.cast("struct sockaddr *", sockaddr_in),
|
|
|
|
|
ffi.sizeof("struct sockaddr_in"),
|
|
|
|
|
host, C.NI_MAXHOST,
|
|
|
|
|
nil, 0,
|
|
|
|
|
C.NI_NUMERICHOST)
|
|
|
|
|
if s ~= 0 then
|
|
|
|
|
logger.err("Device:getDefaultRoute: getnameinfo:", ffi.string(C.gai_strerror(s)))
|
|
|
|
|
break
|
|
|
|
|
else
|
|
|
|
|
gateway = ffi.string(host)
|
|
|
|
|
-- If we specified an interface, we're done.
|
|
|
|
|
-- If we didn't, we'll just keep the last gateway in the routing table...
|
|
|
|
|
if interface then
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
l = l + 1
|
|
|
|
|
end
|
|
|
|
|
fd:close()
|
|
|
|
|
|
|
|
|
|
return gateway
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Device:retrieveNetworkInfo()
|
|
|
|
|
-- We're going to need a random socket for the network & wireless ioctls...
|
|
|
|
|
local socket = C.socket(C.PF_INET, C.SOCK_DGRAM, C.IPPROTO_IP);
|
|
|
|
|
if socket == -1 then
|
|
|
|
|
local errno = ffi.errno()
|
|
|
|
|
logger.err("Device:retrieveNetworkInfo: socket:", ffi.string(C.strerror(errno)))
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local ifaddr = ffi.new("struct ifaddrs *[1]")
|
|
|
|
|
if C.getifaddrs(ifaddr) == -1 then
|
|
|
|
|
local errno = ffi.errno()
|
|
|
|
|
logger.err("Device:retrieveNetworkInfo: getifaddrs:", ffi.string(C.strerror(errno)))
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Build a string rope to format the results
|
|
|
|
|
local results = {}
|
|
|
|
|
local interfaces = {}
|
|
|
|
|
local prev_ifname, default_gw
|
|
|
|
|
|
|
|
|
|
-- Loop over all the network interfaces
|
|
|
|
|
local ifa = ifaddr[0]
|
|
|
|
|
while ifa ~= nil do
|
|
|
|
|
-- Skip over loopback or downed interfaces
|
|
|
|
|
if ifa.ifa_addr ~= nil and
|
|
|
|
|
bit.band(ifa.ifa_flags, C.IFF_UP) ~= 0 and
|
|
|
|
|
bit.band(ifa.ifa_flags, C.IFF_LOOPBACK) == 0 then
|
|
|
|
|
local family = ifa.ifa_addr.sa_family
|
|
|
|
|
if family == C.AF_INET or family == C.AF_INET6 then
|
|
|
|
|
local host = ffi.new("char[?]", C.NI_MAXHOST)
|
|
|
|
|
local s = C.getnameinfo(ifa.ifa_addr,
|
|
|
|
|
family == C.AF_INET and ffi.sizeof("struct sockaddr_in") or ffi.sizeof("struct sockaddr_in6"),
|
|
|
|
|
host, C.NI_MAXHOST,
|
|
|
|
|
nil, 0,
|
|
|
|
|
C.NI_NUMERICHOST)
|
|
|
|
|
if s ~= 0 then
|
|
|
|
|
logger.err("Device:retrieveNetworkInfo: getnameinfo:", ffi.string(C.gai_strerror(s)))
|
|
|
|
|
else
|
|
|
|
|
-- Only print the ifname once
|
|
|
|
|
local ifname = ffi.string(ifa.ifa_name)
|
|
|
|
|
if not interfaces[ifname] then
|
|
|
|
|
if prev_ifname and ifname ~= prev_ifname then
|
|
|
|
|
-- Add a linebreak between interfaces
|
|
|
|
|
table.insert(results, "")
|
|
|
|
|
end
|
|
|
|
|
prev_ifname = ifname
|
|
|
|
|
table.insert(results, T(_("Interface: %1"), ifname))
|
|
|
|
|
interfaces[ifname] = true
|
|
|
|
|
-- Get its MAC address
|
|
|
|
|
local ifr = ffi.new("struct ifreq")
|
|
|
|
|
ffi.copy(ifr.ifr_ifrn.ifrn_name, ifa.ifa_name, C.IFNAMSIZ)
|
|
|
|
|
if C.ioctl(socket, C.SIOCGIFHWADDR, ifr) == -1 then
|
|
|
|
|
local errno = ffi.errno()
|
|
|
|
|
logger.err("Device:retrieveNetworkInfo: SIOCGIFHWADDR ioctl:", ffi.string(C.strerror(errno)))
|
|
|
|
|
else
|
|
|
|
|
local mac = string.format("%02X:%02X:%02X:%02X:%02X:%02X",
|
|
|
|
|
bit.band(ifr.ifr_ifru.ifru_hwaddr.sa_data[0], 0xFF),
|
|
|
|
|
bit.band(ifr.ifr_ifru.ifru_hwaddr.sa_data[1], 0xFF),
|
|
|
|
|
bit.band(ifr.ifr_ifru.ifru_hwaddr.sa_data[2], 0xFF),
|
|
|
|
|
bit.band(ifr.ifr_ifru.ifru_hwaddr.sa_data[3], 0xFF),
|
|
|
|
|
bit.band(ifr.ifr_ifru.ifru_hwaddr.sa_data[4], 0xFF),
|
|
|
|
|
bit.band(ifr.ifr_ifru.ifru_hwaddr.sa_data[5], 0xFF))
|
|
|
|
|
table.insert(results, T(_("MAC: %1"), mac))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Check if it's a wireless interface (c.f., wireless-tools)
|
|
|
|
|
local iwr = ffi.new("struct iwreq")
|
|
|
|
|
ffi.copy(iwr.ifr_ifrn.ifrn_name, ifa.ifa_name, C.IFNAMSIZ)
|
|
|
|
|
if C.ioctl(socket, C.SIOCGIWNAME, iwr) ~= -1 then
|
|
|
|
|
interfaces[ifname] = "wireless"
|
|
|
|
|
-- Get its ESSID
|
|
|
|
|
local essid = ffi.new("char[?]", C.IW_ESSID_MAX_SIZE + 1)
|
|
|
|
|
iwr.u.essid.pointer = ffi.cast("caddr_t", essid)
|
|
|
|
|
iwr.u.essid.length = C.IW_ESSID_MAX_SIZE + 1
|
|
|
|
|
iwr.u.essid.flags = 0
|
|
|
|
|
if C.ioctl(socket, C.SIOCGIWESSID, iwr) == -1 then
|
|
|
|
|
local errno = ffi.errno()
|
|
|
|
|
logger.err("Device:retrieveNetworkInfo: SIOCGIWESSID ioctl:", ffi.string(C.strerror(errno)))
|
|
|
|
|
else
|
|
|
|
|
local essid_on = iwr.u.data.flags
|
|
|
|
|
if essid_on ~= 0 then
|
|
|
|
|
local token_index = bit.band(essid_on, C.IW_ENCODE_INDEX)
|
|
|
|
|
if token_index > 1 then
|
|
|
|
|
table.insert(results, T(_("SSID: \"%1\" [%2]"), ffi.string(essid), token_index))
|
|
|
|
|
else
|
|
|
|
|
table.insert(results, T(_("SSID: \"%1\""), ffi.string(essid)))
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
table.insert(results, _("SSID: off/any"))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if family == C.AF_INET then
|
|
|
|
|
table.insert(results, T(_("IP: %1"), ffi.string(host)))
|
|
|
|
|
local gw = self:getDefaultRoute(ifname)
|
|
|
|
|
if gw then
|
|
|
|
|
table.insert(results, T(_("Default gateway: %1"), gw))
|
|
|
|
|
-- If that's a wireless interface, use *that* one for the ping test
|
|
|
|
|
if interfaces[ifname] == "wireless" then
|
|
|
|
|
default_gw = gw
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
table.insert(results, T(_("IPv6: %1"), ffi.string(host)))
|
|
|
|
|
--- @todo: Build an IPv6 variant of getDefaultRoute that parses /proc/net/ipv6_route
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
ifa = ifa.ifa_next
|
|
|
|
|
end
|
|
|
|
|
C.freeifaddrs(ifaddr[0])
|
|
|
|
|
C.close(socket)
|
|
|
|
|
|
|
|
|
|
if prev_ifname then
|
|
|
|
|
table.insert(results, "")
|
|
|
|
|
end
|
|
|
|
|
-- Only ping a single gateway (if we found a wireless interface earlier, we've kept its gateway address around)
|
|
|
|
|
if not default_gw then
|
|
|
|
|
-- If not, we'll simply use the last one in the list...
|
|
|
|
|
default_gw = self:getDefaultRoute()
|
|
|
|
|
end
|
|
|
|
|
if default_gw then
|
|
|
|
|
local ok, rtt = self:ping4(default_gw)
|
|
|
|
|
if ok then
|
|
|
|
|
rtt = string.format("%.3f", rtt * 1/1000) -- i.e., time.to_ms w/o flooring
|
|
|
|
|
table.insert(results, _("Gateway ping successful"))
|
|
|
|
|
table.insert(results, T(_("RTT: %1 ms"), rtt))
|
|
|
|
|
else
|
|
|
|
|
result = result .. _("No default gateway to ping")
|
|
|
|
|
table.insert(results, _("Gateway ping FAILED"))
|
|
|
|
|
if rtt then
|
|
|
|
|
rtt = string.format("%.1f", time.to_s(rtt))
|
|
|
|
|
table.insert(results, T(_("Timed out after %1 s"), rtt))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return result
|
|
|
|
|
else
|
|
|
|
|
table.insert(results, _("No default gateway to ping"))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return table.concat(results, "\n")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function Device:setTime(hour, min)
|
|
|
|
|