|  | // Copyright 2015 The Weave Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "examples/provider/wifi_manager.h" | 
|  |  | 
|  | #include <arpa/inet.h> | 
|  | #include <linux/wireless.h> | 
|  | #include <sys/ioctl.h> | 
|  | #include <sys/wait.h> | 
|  |  | 
|  | #include <fstream> | 
|  |  | 
|  | #include <base/bind.h> | 
|  | #include <weave/provider/task_runner.h> | 
|  |  | 
|  | #include "examples/provider/event_network.h" | 
|  | #include "examples/provider/ssl_stream.h" | 
|  |  | 
|  | namespace weave { | 
|  | namespace examples { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | int ForkCmd(const std::string& path, const std::vector<std::string>& args) { | 
|  | int pid = fork(); | 
|  | if (pid != 0) | 
|  | return pid; | 
|  |  | 
|  | std::vector<const char*> args_vector; | 
|  | args_vector.push_back(path.c_str()); | 
|  | for (auto& i : args) | 
|  | args_vector.push_back(i.c_str()); | 
|  | args_vector.push_back(nullptr); | 
|  |  | 
|  | execvp(path.c_str(), const_cast<char**>(args_vector.data())); | 
|  | NOTREACHED(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | WifiImpl::WifiImpl(provider::TaskRunner* task_runner, EventNetworkImpl* network) | 
|  | : task_runner_{task_runner}, network_{network} { | 
|  | CHECK_EQ(0u, getuid()) | 
|  | << "WiFi manager expects root access to control WiFi capabilities"; | 
|  | StopAccessPoint(); | 
|  | } | 
|  | WifiImpl::~WifiImpl() { | 
|  | StopAccessPoint(); | 
|  | } | 
|  |  | 
|  | void WifiImpl::TryToConnect(const std::string& ssid, | 
|  | const std::string& passphrase, | 
|  | int pid, | 
|  | base::Time until, | 
|  | const DoneCallback& callback) { | 
|  | if (pid) { | 
|  | int status = 0; | 
|  | if (pid == waitpid(pid, &status, WNOWAIT)) { | 
|  | int sockf_d = socket(AF_INET, SOCK_DGRAM, 0); | 
|  | CHECK_GE(sockf_d, 0) << strerror(errno); | 
|  |  | 
|  | iwreq wreq = {}; | 
|  | snprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "wlan0"); | 
|  | std::string essid(' ', IW_ESSID_MAX_SIZE + 1); | 
|  | wreq.u.essid.pointer = &essid[0]; | 
|  | wreq.u.essid.length = essid.size(); | 
|  | CHECK_GE(ioctl(sockf_d, SIOCGIWESSID, &wreq), 0) << strerror(errno); | 
|  | essid.resize(wreq.u.essid.length); | 
|  | close(sockf_d); | 
|  |  | 
|  | if (ssid == essid) | 
|  | return task_runner_->PostDelayedTask(FROM_HERE, | 
|  | base::Bind(callback, nullptr), {}); | 
|  | pid = 0;  // Try again. | 
|  | } | 
|  | } | 
|  |  | 
|  | if (pid == 0) { | 
|  | pid = ForkCmd("nmcli", | 
|  | {"dev", "wifi", "connect", ssid, "password", passphrase}); | 
|  | } | 
|  |  | 
|  | if (base::Time::Now() >= until) { | 
|  | ErrorPtr error; | 
|  | Error::AddTo(&error, FROM_HERE, "wifi", "timeout", | 
|  | "Timeout connecting to WiFI network."); | 
|  | task_runner_->PostDelayedTask( | 
|  | FROM_HERE, base::Bind(callback, base::Passed(&error)), {}); | 
|  | return; | 
|  | } | 
|  |  | 
|  | task_runner_->PostDelayedTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&WifiImpl::TryToConnect, weak_ptr_factory_.GetWeakPtr(), ssid, | 
|  | passphrase, pid, until, callback), | 
|  | base::TimeDelta::FromSeconds(1)); | 
|  | } | 
|  |  | 
|  | void WifiImpl::Connect(const std::string& ssid, | 
|  | const std::string& passphrase, | 
|  | const DoneCallback& callback) { | 
|  | network_->SetSimulateOffline(false); | 
|  | CHECK(!hostapd_started_); | 
|  | if (hostapd_started_) { | 
|  | ErrorPtr error; | 
|  | Error::AddTo(&error, FROM_HERE, "wifi", "busy", "Running Access Point."); | 
|  | task_runner_->PostDelayedTask( | 
|  | FROM_HERE, base::Bind(callback, base::Passed(&error)), {}); | 
|  | return; | 
|  | } | 
|  |  | 
|  | TryToConnect(ssid, passphrase, 0, | 
|  | base::Time::Now() + base::TimeDelta::FromMinutes(1), callback); | 
|  | } | 
|  |  | 
|  | void WifiImpl::StartAccessPoint(const std::string& ssid) { | 
|  | if (hostapd_started_) | 
|  | return; | 
|  |  | 
|  | // Release wlan0 interface. | 
|  | CHECK_EQ(0, std::system("nmcli nm wifi off")); | 
|  | CHECK_EQ(0, std::system("rfkill unblock wlan")); | 
|  | sleep(1); | 
|  |  | 
|  | std::string hostapd_conf = "/tmp/weave_hostapd.conf"; | 
|  | { | 
|  | std::ofstream ofs(hostapd_conf); | 
|  | ofs << "interface=wlan0" << std::endl; | 
|  | ofs << "channel=1" << std::endl; | 
|  | ofs << "ssid=" << ssid << std::endl; | 
|  | } | 
|  |  | 
|  | CHECK_EQ(0, std::system(("hostapd -B -K " + hostapd_conf).c_str())); | 
|  | hostapd_started_ = true; | 
|  |  | 
|  | for (size_t i = 0; i < 10; ++i) { | 
|  | if (0 == std::system("ifconfig wlan0 192.168.76.1/24")) | 
|  | break; | 
|  | sleep(1); | 
|  | } | 
|  |  | 
|  | std::string dnsmasq_conf = "/tmp/weave_dnsmasq.conf"; | 
|  | { | 
|  | std::ofstream ofs(dnsmasq_conf.c_str()); | 
|  | ofs << "port=0" << std::endl; | 
|  | ofs << "bind-interfaces" << std::endl; | 
|  | ofs << "log-dhcp" << std::endl; | 
|  | ofs << "dhcp-range=192.168.76.10,192.168.76.100" << std::endl; | 
|  | ofs << "interface=wlan0" << std::endl; | 
|  | ofs << "dhcp-leasefile=" << dnsmasq_conf << ".leases" << std::endl; | 
|  | } | 
|  |  | 
|  | CHECK_EQ(0, std::system(("dnsmasq --conf-file=" + dnsmasq_conf).c_str())); | 
|  | } | 
|  |  | 
|  | void WifiImpl::StopAccessPoint() { | 
|  | base::IgnoreResult(std::system("pkill -f dnsmasq.*/tmp/weave")); | 
|  | base::IgnoreResult(std::system("pkill -f hostapd.*/tmp/weave")); | 
|  | CHECK_EQ(0, std::system("nmcli nm wifi on")); | 
|  | hostapd_started_ = false; | 
|  | } | 
|  |  | 
|  | bool WifiImpl::HasWifiCapability() { | 
|  | return std::system("nmcli dev | grep ^wlan0") == 0; | 
|  | } | 
|  |  | 
|  | }  // namespace examples | 
|  | }  // namespace weave |