blob: e78febc6b38738226fba322a459780b964e8cf8e [file] [log] [blame]
Peter Qiufda548c2015-01-13 14:39:19 -08001// Copyright 2015 The Chromium OS 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 "apmanager/hostapd_monitor.h"
6
7#include <sys/socket.h>
8#include <sys/stat.h>
9#include <sys/un.h>
10
11#include <base/bind.h>
12#include <base/logging.h>
13#include <base/strings/stringprintf.h>
14#include <shill/net/io_handler_factory_container.h>
15#include <shill/net/sockets.h>
16
17using base::Bind;
18using base::Unretained;
19using shill::IOHandlerFactoryContainer;
20using std::string;
21
22namespace apmanager {
23
24// static.
25const char HostapdMonitor::kLocalPathFormat[] =
26 "/var/run/apmanager/hostapd/hostapd_ctrl_%s";
27const char HostapdMonitor::kHostapdCmdAttach[] = "ATTACH";
28const char HostapdMonitor::kHostapdRespOk[] = "OK\n";
29const char HostapdMonitor::kHostapdEventStationConnected[] = "AP-STA-CONNECTED";
30const char HostapdMonitor::kHostapdEventStationDisconnected[] =
31 "AP-STA-DISCONNECTED";
32const int HostapdMonitor::kHostapdCtrlIfaceCheckIntervalMs = 500;
33const int HostapdMonitor::kHostapdCtrlIfaceCheckMaxAttempts = 5;
34const int HostapdMonitor::kHostapdAttachTimeoutMs = 1000;
35const int HostapdMonitor::kInvalidSocket = -1;
36
37HostapdMonitor::HostapdMonitor(const EventCallback& callback,
38 const string& control_interface_path,
39 const string& network_interface_name)
40 : sockets_(new shill::Sockets()),
41 event_callback_(callback),
42 dest_path_(base::StringPrintf("%s/%s",
43 control_interface_path.c_str(),
44 network_interface_name.c_str())),
45 local_path_(base::StringPrintf(kLocalPathFormat,
46 network_interface_name.c_str())),
47 hostapd_socket_(kInvalidSocket),
48 io_handler_factory_(
49 IOHandlerFactoryContainer::GetInstance()->GetIOHandlerFactory()),
50 event_dispatcher_(EventDispatcher::GetInstance()),
Peter Qiue2657852015-02-03 11:33:11 -080051 weak_ptr_factory_(this),
Peter Qiufda548c2015-01-13 14:39:19 -080052 started_(false) {}
53
54HostapdMonitor::~HostapdMonitor() {
55 if (hostapd_socket_ != kInvalidSocket) {
56 unlink(local_path_.c_str());
57 sockets_->Close(hostapd_socket_);
58 }
59}
60
61void HostapdMonitor::Start() {
62 if (started_) {
63 LOG(ERROR) << "HostapdMonitor already started";
64 return;
65 }
66
67 hostapd_ctrl_iface_check_count_ = 0;
68 // Start off by checking the control interface file for the hostapd process.
69 event_dispatcher_->PostTask(
Peter Qiue2657852015-02-03 11:33:11 -080070 Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask,
71 weak_ptr_factory_.GetWeakPtr()));
Peter Qiufda548c2015-01-13 14:39:19 -080072 started_ = true;
73}
74
75void HostapdMonitor::HostapdCtrlIfaceCheckTask() {
76 struct stat buf;
77 if (stat(dest_path_.c_str(), &buf) != 0) {
78 if (hostapd_ctrl_iface_check_count_ >= kHostapdCtrlIfaceCheckMaxAttempts) {
Peter Qiue2657852015-02-03 11:33:11 -080079 // This indicates the hostapd failed to start. Invoke callback indicating
80 // hostapd start failed.
Peter Qiufda548c2015-01-13 14:39:19 -080081 LOG(ERROR) << "Timeout waiting for hostapd control interface";
Peter Qiue2657852015-02-03 11:33:11 -080082 event_callback_.Run(kHostapdFailed, "");
Peter Qiufda548c2015-01-13 14:39:19 -080083 } else {
84 hostapd_ctrl_iface_check_count_++;
85 event_dispatcher_->PostDelayedTask(
86 base::Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask,
Peter Qiue2657852015-02-03 11:33:11 -080087 weak_ptr_factory_.GetWeakPtr()),
Peter Qiufda548c2015-01-13 14:39:19 -080088 kHostapdCtrlIfaceCheckIntervalMs);
89 }
90 return;
91 }
92
Peter Qiue2657852015-02-03 11:33:11 -080093 // Control interface is up, meaning hostapd started successfully.
94 event_callback_.Run(kHostapdStarted, "");
95
Peter Qiufda548c2015-01-13 14:39:19 -080096 // Attach to the control interface to receive unsolicited event notifications.
97 AttachToHostapd();
98}
99
100void HostapdMonitor::AttachToHostapd() {
101 if (hostapd_socket_ != kInvalidSocket) {
102 LOG(ERROR) << "Socket already initialized";
103 return;
104 }
105
106 // Setup socket address for local file and remote file.
107 struct sockaddr_un local;
108 local.sun_family = AF_UNIX;
109 snprintf(local.sun_path, sizeof(local.sun_path), "%s", local_path_.c_str());
110 struct sockaddr_un dest;
111 dest.sun_family = AF_UNIX;
112 snprintf(dest.sun_path, sizeof(dest.sun_path), "%s", dest_path_.c_str());
113
114 // Setup socket for interprocess communication.
115 hostapd_socket_ = sockets_->Socket(PF_UNIX, SOCK_DGRAM, 0);
116 if (hostapd_socket_ < 0) {
117 LOG(ERROR) << "Failed to open hostapd socket";
118 return;
119 }
120 if (sockets_->Bind(hostapd_socket_,
121 reinterpret_cast<struct sockaddr*>(&local),
122 sizeof(local)) < 0) {
Alex Deymo1883cf22015-04-09 10:06:31 -0700123 PLOG(ERROR) << "Failed to bind to local socket";
Peter Qiufda548c2015-01-13 14:39:19 -0800124 return;
125 }
126 if (sockets_->Connect(hostapd_socket_,
127 reinterpret_cast<struct sockaddr*>(&dest),
128 sizeof(dest)) < 0) {
Alex Deymo1883cf22015-04-09 10:06:31 -0700129 PLOG(ERROR) << "Failed to connect";
Peter Qiufda548c2015-01-13 14:39:19 -0800130 return;
131 }
132
133 // Setup IO Input handler.
134 hostapd_input_handler_.reset(io_handler_factory_->CreateIOInputHandler(
135 hostapd_socket_,
136 Bind(&HostapdMonitor::ParseMessage, Unretained(this)),
137 Bind(&HostapdMonitor::OnReadError, Unretained(this))));
138
139 if (!SendMessage(kHostapdCmdAttach, strlen(kHostapdCmdAttach))) {
140 LOG(ERROR) << "Failed to attach to hostapd";
141 return;
142 }
143
144 // Start a timer for ATTACH response.
145 attach_timeout_callback_.Reset(
Peter Qiue2657852015-02-03 11:33:11 -0800146 Bind(&HostapdMonitor::AttachTimeoutHandler,
147 weak_ptr_factory_.GetWeakPtr()));
Peter Qiufda548c2015-01-13 14:39:19 -0800148 event_dispatcher_->PostDelayedTask(attach_timeout_callback_.callback(),
149 kHostapdAttachTimeoutMs);
150 return;
151}
152
153void HostapdMonitor::AttachTimeoutHandler() {
154 LOG(ERROR) << "Timeout waiting for attach response";
155}
156
157// Method for sending message to hostapd control interface.
158bool HostapdMonitor::SendMessage(const char* message, size_t length) {
159 if (sockets_->Send(hostapd_socket_, message, length, 0) < 0) {
Alex Deymo1883cf22015-04-09 10:06:31 -0700160 PLOG(ERROR) << "Send to hostapd failed";
Peter Qiufda548c2015-01-13 14:39:19 -0800161 return false;
162 }
163
164 return true;
165}
166
167void HostapdMonitor::ParseMessage(shill::InputData* data) {
168 string str(reinterpret_cast<const char*>(data->buf), data->len);
169 // "OK" response for the "ATTACH" command.
170 if (str == kHostapdRespOk) {
171 attach_timeout_callback_.Cancel();
172 return;
173 }
174
175 // Event messages are in format of <[Level]>[Event] [Detail message].
176 // For example: <2>AP-STA-CONNECTED 00:11:22:33:44:55
177 // Refer to wpa_ctrl.h for complete list of possible events.
178 if (str.find_first_of('<', 0) == 0 && str.find_first_of('>', 0) == 2) {
179 // Remove the log level.
180 string msg = str.substr(3);
181 string event;
182 string data;
183 size_t pos = msg.find_first_of(' ', 0);
184 if (pos == string::npos) {
185 event = msg;
186 } else {
187 event = msg.substr(0, pos);
188 data = msg.substr(pos + 1);
189 }
190
191 Event event_code;
192 if (event == kHostapdEventStationConnected) {
193 event_code = kStationConnected;
194 } else if (event == kHostapdEventStationDisconnected) {
195 event_code = kStationDisconnected;
196 } else {
197 LOG(INFO) << "Received unknown event: " << event;
198 return;
199 }
200 event_callback_.Run(event_code, data);
201 return;
202 }
203
204 LOG(INFO) << "Received unknown message: " << str;
205}
206
207void HostapdMonitor::OnReadError(const string& error_msg) {
208 LOG(FATAL) << "Hostapd Socket read returns error: "
209 << error_msg;
210}
211
212} // namespace apmanager