blob: b460174c9d30fbf7dcf5245434545b30e63471ac [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// ICMP helper - emits info about ICMP connectivity to a specified host as json.
// Example output:
// { "4.2.2.1":
// { "sent": 4,
// "recvd": 4,
// "time": 3005,
// "min": 5.789000,
// "avg": 5.913000,
// "max": 6.227000,
// "dev": 0.197000
// }
// }
// All times are in milliseconds. "time" is the total time taken by ping(1).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <base/command_line.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
using base::StringPrintf;
using std::string;
namespace {
static const char kHelpMessage[] =
"Usage: icmp [<switches>] <ip>\n\n"
"Available switches:\n"
" --count=<number of packets> (default: 4)\n"
" --size=<packet size>\n"
" --ttl=<IP Time to Live>\n"
" --timeout=<time to wait for response>";
static const char kIPv4v6Chars[] = "ABCDEFabcdef0123456789.:";
static void Die(const string& why) {
printf("<%s>\n", why.c_str());
exit(1);
}
static int GetIntSwitch(const base::CommandLine* cl,
const string& name,
int default_value) {
int val = default_value;
if (cl->HasSwitch(name)) {
string switch_value = cl->GetSwitchValueASCII(name);
if (!base::StringToInt(switch_value, &val) || val <= 0) {
Die(StringPrintf("Invalid %s switch: %s", name.c_str(),
switch_value.c_str()));
}
}
return val;
}
} // namespace
int main(int argc, char* argv[]) {
char outbuf[1024];
char ipbuf[128] = {0};
FILE* out;
int sent = -1, recvd = -1, loss = -1, errors = -1, time = -1;
float min = 0.0, avg = 0.0, max = 0.0, mdev = 0.0;
// Parse commandline switches.
base::CommandLine::Init(argc, argv);
base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
int count = GetIntSwitch(cl, "count", 4);
int size = GetIntSwitch(cl, "size", 0);
int ttl = GetIntSwitch(cl, "ttl", 0);
int timeout = GetIntSwitch(cl, "timeout", 0);
// Parse out the IP address.
base::CommandLine::StringVector args = cl->GetArgs();
if (args.size() != 1)
Die(kHelpMessage);
string ip_addr = args[0];
if (!base::ContainsOnlyChars(ip_addr, kIPv4v6Chars))
Die("not ip address");
// Construct command.
string size_out = size ? StringPrintf("-s %d", size) : "";
string ttl_out = ttl ? StringPrintf("-t %d", ttl) : "";
string timeout_out = timeout ? StringPrintf("-W %d", timeout) : "";
string command = StringPrintf("/bin/ping -c %d -w 10 -n %s %s %s %s", count,
ttl_out.c_str(), size_out.c_str(),
timeout_out.c_str(), ip_addr.c_str());
// Execute!
out = popen(command.c_str(), "r");
if (!out)
Die("can't create subprocess");
// Parse output.
while (fgets(outbuf, sizeof(outbuf), out)) {
if (sscanf(outbuf, "From %127s icmp_seq=", ipbuf) == 1) {
// If there is a colon after the ip, strip it.
char* last_char = ipbuf + strlen(ipbuf) - 1;
if (*last_char == ':')
*last_char = '\0';
continue;
} else if (sscanf(outbuf,
"%d packets transmitted, %d received, %d%% packet loss,"
" time %dms",
&sent, &recvd, &loss, &time) == 4) {
continue;
} else if (sscanf(outbuf,
"%d packets transmitted, %d received, +%d errors,"
" %d%% packet loss, time %dms",
&sent, &recvd, &errors, &loss, &time) == 5) {
continue;
} else if (sscanf(outbuf, "rtt min/avg/max/mdev = %f/%f/%f/%f ms", &min,
&avg, &max, &mdev) == 4) {
continue;
}
}
pclose(out);
if (time == -1)
Die("didn't get all output");
string ip_out = ipbuf[0] ? ipbuf : ip_addr;
printf("{ \"%s\":\n", ip_out.c_str());
printf(" { \"sent\": %d,\n", sent);
printf(" \"recvd\": %d,\n", recvd);
printf(" \"time\": %d,\n", time);
printf(" \"min\": %f,\n", min);
printf(" \"avg\": %f,\n", avg);
printf(" \"max\": %f,\n", max);
printf(" \"dev\": %f\n", mdev);
printf(" }\n");
printf("}\n");
return 0;
}