summaryrefslogtreecommitdiff
path: root/xmake/modules/net/ping.lua
blob: b711da60b42ad480db943d0a9c22ef8c14609443 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
--!A cross-platform build utility based on Lua
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author      ruki
-- @file        ping.lua
--

-- imports
import("core.cache.detectcache")
import("lib.detect.find_tool")
import("async.runjobs")

-- using ping to ping host
function _ping_via_ping(ping, host)
    local data = nil
    if is_host("windows") then
        data = try { function () return os.iorun("%s -n 1 -w 1000 %s", ping.program, host) end }
    elseif is_host("macosx") then
        data = try { function () return os.iorun("%s -c 1 -t 1 %s", ping.program, host) end }
    else
        -- @see https://github.com/xmake-io/xmake/issues/4470#issuecomment-1840338777
        data = try { function () return os.iorun("%s -c 1 -W 1 -n %s", ping.program, host) end }
        if not data then
            data = try { function () return os.iorun("%s -c 1 -W 1 %s", ping.program, host) end }
        end
    end
    local timeval = "65535"
    if data then
        timeval = data:match("= [^/]+/([^/]+)/", 1, true) or data:match("[=<]([%d%s%.]-)ms TTL", 1, true) or "65535"
    end
    if timeval then
        timeval = tonumber(timeval:trim())
    end
    return timeval
end

-- using curl to ping host
function _ping_via_curl(curl, host)
    local data, dt = try { function ()
        local t = os.mclock()
        local tmpfile = os.tmpfile()
        local outdata = os.iorunv(curl.program, {"-o", tmpfile, "-s", "-w", "%{time_total}", "--max-time", "1", host})
        t = os.mclock() - t
        os.tryrm(tmpfile)
        return outdata, t
    end }
    local timeval = 65535
    if data then
        local t = tonumber(data:trim())
        if t then
            timeval = t * 1000
        else
            timeval = dt
        end
    end
    return timeval
end

-- using wget to ping host
function _ping_via_wget(wget, host)
    local data = try { function ()
        local t = os.mclock()
        os.runv(wget.program, {"-O", os.nuldev(), "--tries=1", "--timeout=1", host})
        t = os.mclock() - t
        return t
    end }
    local timeval = 65535
    if data then
        timeval = data
    end
    return timeval
end

-- ping host
-- @see https://github.com/xmake-io/xmake/issues/6579
function _ping(ping, host)
    local routers = {
        ping = _ping_via_ping,
        curl = _ping_via_curl,
        wget = _ping_via_wget
    }
    local router = routers[ping.name] or _ping_via_ping
    if router then
        return router(ping, host)
    end
end

-- send ping to hosts
--
-- @param hosts     the hosts
-- @param opt       the options
--
-- @return          the time or -1
--
function main(hosts, opt)
    opt = opt or {}

    local ping = find_tool("curl", opt) or find_tool("wget") or find_tool("ping", opt)
    if not ping then
        return {}
    end

    local cacheinfo = nil
    if not opt.force then
        cacheinfo = detectcache:get("net.ping")
    end

    local results = {}
    hosts = table.wrap(hosts)
    runjobs("ping", function (index)
        local host = hosts[index]
        if host then
            local timeval = nil
            if cacheinfo then
                timeval = cacheinfo[host]
            end
            if timeval then
                results[host] = timeval
            else
                timeval = _ping(ping, host)
                results[host] = timeval
                if cacheinfo then
                    cacheinfo[host] = timeval
                end
                vprint("pinging the host(%s) ... %d ms", host, math.floor(timeval))
            end
        end
    end, {total = #hosts})

    if cacheinfo then
        detectcache:set("net.ping", cacheinfo)
    end
    return results
end