Home | History | Annotate | Download | only in provider
      1 // Copyright 2015 The Weave Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "examples/provider/wifi_manager.h"
      6 
      7 #include <arpa/inet.h>
      8 #include <dirent.h>
      9 #include <linux/wireless.h>
     10 #include <sys/ioctl.h>
     11 #include <sys/wait.h>
     12 
     13 #include <fstream>
     14 
     15 #include <base/bind.h>
     16 #include <weave/provider/task_runner.h>
     17 
     18 #include "examples/provider/event_network.h"
     19 #include "examples/provider/ssl_stream.h"
     20 
     21 namespace weave {
     22 namespace examples {
     23 
     24 namespace {
     25 
     26 int ForkCmd(const std::string& path, const std::vector<std::string>& args) {
     27   int pid = fork();
     28   if (pid != 0)
     29     return pid;
     30 
     31   std::vector<const char*> args_vector;
     32   args_vector.push_back(path.c_str());
     33   for (auto& i : args)
     34     args_vector.push_back(i.c_str());
     35   args_vector.push_back(nullptr);
     36   execvp(path.c_str(), const_cast<char**>(args_vector.data()));
     37   NOTREACHED();
     38   return 0;
     39 }
     40 
     41 int ForkCmdAndWait(const std::string& path,
     42                    const std::vector<std::string>& args) {
     43   int pid = ForkCmd(path, args);
     44   int status = 0;
     45   CHECK_EQ(pid, waitpid(pid, &status, 0));
     46   return status;
     47 }
     48 
     49 std::string FindWirelessInterface() {
     50   std::string sysfs_net{"/sys/class/net"};
     51   DIR* net_dir = opendir(sysfs_net.c_str());
     52   dirent* iface;
     53   while ((iface = readdir(net_dir))) {
     54     auto path = sysfs_net + "/" + iface->d_name + "/wireless";
     55     DIR* wireless_dir = opendir(path.c_str());
     56     if (wireless_dir != nullptr) {
     57       closedir(net_dir);
     58       closedir(wireless_dir);
     59       return iface->d_name;
     60     }
     61   }
     62   closedir(net_dir);
     63   return "";
     64 }
     65 
     66 }  // namespace
     67 
     68 WifiImpl::WifiImpl(provider::TaskRunner* task_runner, EventNetworkImpl* network)
     69   : task_runner_{task_runner}, network_{network}, iface_{FindWirelessInterface()} {
     70   CHECK(!iface_.empty()) <<  "WiFi interface not found";
     71   CHECK_EQ(0u, getuid())
     72       << "\nWiFi manager expects root access to control WiFi capabilities";
     73   StopAccessPoint();
     74 }
     75 WifiImpl::~WifiImpl() {
     76   StopAccessPoint();
     77 }
     78 
     79 void WifiImpl::TryToConnect(const std::string& ssid,
     80                             const std::string& passphrase,
     81                             int pid,
     82                             base::Time until,
     83                             const DoneCallback& callback) {
     84   if (pid) {
     85     int status = 0;
     86     if (pid == waitpid(pid, &status, WNOWAIT)) {
     87       int sockf_d = socket(AF_INET, SOCK_DGRAM, 0);
     88       CHECK_GE(sockf_d, 0) << strerror(errno);
     89 
     90       iwreq wreq = {};
     91       strncpy(wreq.ifr_name, iface_.c_str(), sizeof(wreq.ifr_name));
     92       std::string essid(' ', IW_ESSID_MAX_SIZE + 1);
     93       wreq.u.essid.pointer = &essid[0];
     94       wreq.u.essid.length = essid.size();
     95       CHECK_GE(ioctl(sockf_d, SIOCGIWESSID, &wreq), 0) << strerror(errno);
     96       essid.resize(wreq.u.essid.length);
     97       close(sockf_d);
     98 
     99       if (ssid == essid)
    100         return task_runner_->PostDelayedTask(FROM_HERE,
    101                                              base::Bind(callback, nullptr), {});
    102       pid = 0;  // Try again.
    103     }
    104   }
    105 
    106   if (pid == 0) {
    107     pid = ForkCmd("nmcli",
    108                   {"dev", "wifi", "connect", ssid, "password", passphrase});
    109   }
    110 
    111   if (base::Time::Now() >= until) {
    112     ErrorPtr error;
    113     Error::AddTo(&error, FROM_HERE, "timeout",
    114                  "Timeout connecting to WiFI network.");
    115     task_runner_->PostDelayedTask(
    116         FROM_HERE, base::Bind(callback, base::Passed(&error)), {});
    117     return;
    118   }
    119 
    120   task_runner_->PostDelayedTask(
    121       FROM_HERE,
    122       base::Bind(&WifiImpl::TryToConnect, weak_ptr_factory_.GetWeakPtr(), ssid,
    123                  passphrase, pid, until, callback),
    124       base::TimeDelta::FromSeconds(1));
    125 }
    126 
    127 void WifiImpl::Connect(const std::string& ssid,
    128                        const std::string& passphrase,
    129                        const DoneCallback& callback) {
    130   network_->SetSimulateOffline(false);
    131   CHECK(!hostapd_started_);
    132   if (hostapd_started_) {
    133     ErrorPtr error;
    134     Error::AddTo(&error, FROM_HERE, "busy", "Running Access Point.");
    135     task_runner_->PostDelayedTask(
    136         FROM_HERE, base::Bind(callback, base::Passed(&error)), {});
    137     return;
    138   }
    139 
    140   TryToConnect(ssid, passphrase, 0,
    141                base::Time::Now() + base::TimeDelta::FromMinutes(1), callback);
    142 }
    143 
    144 void WifiImpl::StartAccessPoint(const std::string& ssid) {
    145   if (hostapd_started_)
    146     return;
    147 
    148   // Release wifi interface.
    149   CHECK_EQ(0, ForkCmdAndWait("nmcli", {"nm", "wifi",  "off"}));
    150   CHECK_EQ(0, ForkCmdAndWait("rfkill", {"unblock", "wlan"}));
    151   sleep(1);
    152 
    153   std::string hostapd_conf = "/tmp/weave_hostapd.conf";
    154   {
    155     std::ofstream ofs(hostapd_conf);
    156     ofs << "interface=" << iface_ << std::endl;
    157     ofs << "channel=1" << std::endl;
    158     ofs << "ssid=" << ssid << std::endl;
    159   }
    160 
    161   CHECK_EQ(0, ForkCmdAndWait("hostapd", {"-B", "-K", hostapd_conf}));
    162   hostapd_started_ = true;
    163 
    164   for (size_t i = 0; i < 10; ++i) {
    165     if (0 == ForkCmdAndWait("ifconfig", {iface_, "192.168.76.1/24"}))
    166       break;
    167     sleep(1);
    168   }
    169 
    170   std::string dnsmasq_conf = "/tmp/weave_dnsmasq.conf";
    171   {
    172     std::ofstream ofs(dnsmasq_conf.c_str());
    173     ofs << "port=0" << std::endl;
    174     ofs << "bind-interfaces" << std::endl;
    175     ofs << "log-dhcp" << std::endl;
    176     ofs << "dhcp-range=192.168.76.10,192.168.76.100" << std::endl;
    177     ofs << "interface=" << iface_ << std::endl;
    178     ofs << "dhcp-leasefile=" << dnsmasq_conf << ".leases" << std::endl;
    179   }
    180 
    181   CHECK_EQ(0, ForkCmdAndWait("dnsmasq", {"--conf-file=" + dnsmasq_conf}));
    182 }
    183 
    184 void WifiImpl::StopAccessPoint() {
    185   base::IgnoreResult(ForkCmdAndWait("pkill", {"-f", "dnsmasq.*/tmp/weave"}));
    186   base::IgnoreResult(ForkCmdAndWait("pkill", {"-f", "hostapd.*/tmp/weave"}));
    187   CHECK_EQ(0, ForkCmdAndWait("nmcli", {"nm", "wifi", "on"}));
    188   hostapd_started_ = false;
    189 }
    190 
    191 bool WifiImpl::HasWifiCapability() {
    192   return !FindWirelessInterface().empty();
    193 }
    194 
    195 }  // namespace examples
    196 }  // namespace weave
    197