blob: 997ec546291ff8e985dccbfee4a5241a288a59fb [file] [log] [blame]
Peter Qiu326b6cf2015-09-02 11:11:42 -07001//
2// Copyright (C) 2015 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
Peter Qiufda548c2015-01-13 14:39:19 -080016
17#include "apmanager/hostapd_monitor.h"
18
19#include <sys/socket.h>
20#include <sys/stat.h>
21#include <sys/un.h>
22
23#include <base/bind.h>
24#include <base/logging.h>
25#include <base/strings/stringprintf.h>
26#include <shill/net/io_handler_factory_container.h>
27#include <shill/net/sockets.h>
28
29using base::Bind;
30using base::Unretained;
31using shill::IOHandlerFactoryContainer;
32using std::string;
33
34namespace apmanager {
35
36// static.
37const char HostapdMonitor::kLocalPathFormat[] =
38 "/var/run/apmanager/hostapd/hostapd_ctrl_%s";
39const char HostapdMonitor::kHostapdCmdAttach[] = "ATTACH";
40const char HostapdMonitor::kHostapdRespOk[] = "OK\n";
41const char HostapdMonitor::kHostapdEventStationConnected[] = "AP-STA-CONNECTED";
42const char HostapdMonitor::kHostapdEventStationDisconnected[] =
43 "AP-STA-DISCONNECTED";
44const int HostapdMonitor::kHostapdCtrlIfaceCheckIntervalMs = 500;
45const int HostapdMonitor::kHostapdCtrlIfaceCheckMaxAttempts = 5;
46const int HostapdMonitor::kHostapdAttachTimeoutMs = 1000;
47const int HostapdMonitor::kInvalidSocket = -1;
48
49HostapdMonitor::HostapdMonitor(const EventCallback& callback,
50 const string& control_interface_path,
51 const string& network_interface_name)
52 : sockets_(new shill::Sockets()),
53 event_callback_(callback),
54 dest_path_(base::StringPrintf("%s/%s",
55 control_interface_path.c_str(),
56 network_interface_name.c_str())),
57 local_path_(base::StringPrintf(kLocalPathFormat,
58 network_interface_name.c_str())),
59 hostapd_socket_(kInvalidSocket),
60 io_handler_factory_(
61 IOHandlerFactoryContainer::GetInstance()->GetIOHandlerFactory()),
62 event_dispatcher_(EventDispatcher::GetInstance()),
Peter Qiue2657852015-02-03 11:33:11 -080063 weak_ptr_factory_(this),
Peter Qiufda548c2015-01-13 14:39:19 -080064 started_(false) {}
65
66HostapdMonitor::~HostapdMonitor() {
67 if (hostapd_socket_ != kInvalidSocket) {
68 unlink(local_path_.c_str());
69 sockets_->Close(hostapd_socket_);
70 }
71}
72
73void HostapdMonitor::Start() {
74 if (started_) {
75 LOG(ERROR) << "HostapdMonitor already started";
76 return;
77 }
78
79 hostapd_ctrl_iface_check_count_ = 0;
80 // Start off by checking the control interface file for the hostapd process.
81 event_dispatcher_->PostTask(
Peter Qiue2657852015-02-03 11:33:11 -080082 Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask,
83 weak_ptr_factory_.GetWeakPtr()));
Peter Qiufda548c2015-01-13 14:39:19 -080084 started_ = true;
85}
86
87void HostapdMonitor::HostapdCtrlIfaceCheckTask() {
88 struct stat buf;
89 if (stat(dest_path_.c_str(), &buf) != 0) {
90 if (hostapd_ctrl_iface_check_count_ >= kHostapdCtrlIfaceCheckMaxAttempts) {
Peter Qiue2657852015-02-03 11:33:11 -080091 // This indicates the hostapd failed to start. Invoke callback indicating
92 // hostapd start failed.
Peter Qiufda548c2015-01-13 14:39:19 -080093 LOG(ERROR) << "Timeout waiting for hostapd control interface";
Peter Qiue2657852015-02-03 11:33:11 -080094 event_callback_.Run(kHostapdFailed, "");
Peter Qiufda548c2015-01-13 14:39:19 -080095 } else {
96 hostapd_ctrl_iface_check_count_++;
97 event_dispatcher_->PostDelayedTask(
98 base::Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask,
Peter Qiue2657852015-02-03 11:33:11 -080099 weak_ptr_factory_.GetWeakPtr()),
Peter Qiufda548c2015-01-13 14:39:19 -0800100 kHostapdCtrlIfaceCheckIntervalMs);
101 }
102 return;
103 }
104
Peter Qiue2657852015-02-03 11:33:11 -0800105 // Control interface is up, meaning hostapd started successfully.
106 event_callback_.Run(kHostapdStarted, "");
107
Peter Qiufda548c2015-01-13 14:39:19 -0800108 // Attach to the control interface to receive unsolicited event notifications.
109 AttachToHostapd();
110}
111
112void HostapdMonitor::AttachToHostapd() {
113 if (hostapd_socket_ != kInvalidSocket) {
114 LOG(ERROR) << "Socket already initialized";
115 return;
116 }
117
118 // Setup socket address for local file and remote file.
119 struct sockaddr_un local;
120 local.sun_family = AF_UNIX;
121 snprintf(local.sun_path, sizeof(local.sun_path), "%s", local_path_.c_str());
122 struct sockaddr_un dest;
123 dest.sun_family = AF_UNIX;
124 snprintf(dest.sun_path, sizeof(dest.sun_path), "%s", dest_path_.c_str());
125
126 // Setup socket for interprocess communication.
127 hostapd_socket_ = sockets_->Socket(PF_UNIX, SOCK_DGRAM, 0);
128 if (hostapd_socket_ < 0) {
129 LOG(ERROR) << "Failed to open hostapd socket";
130 return;
131 }
132 if (sockets_->Bind(hostapd_socket_,
133 reinterpret_cast<struct sockaddr*>(&local),
134 sizeof(local)) < 0) {
Alex Deymo1883cf22015-04-09 10:06:31 -0700135 PLOG(ERROR) << "Failed to bind to local socket";
Peter Qiufda548c2015-01-13 14:39:19 -0800136 return;
137 }
138 if (sockets_->Connect(hostapd_socket_,
139 reinterpret_cast<struct sockaddr*>(&dest),
140 sizeof(dest)) < 0) {
Alex Deymo1883cf22015-04-09 10:06:31 -0700141 PLOG(ERROR) << "Failed to connect";
Peter Qiufda548c2015-01-13 14:39:19 -0800142 return;
143 }
144
145 // Setup IO Input handler.
146 hostapd_input_handler_.reset(io_handler_factory_->CreateIOInputHandler(
147 hostapd_socket_,
148 Bind(&HostapdMonitor::ParseMessage, Unretained(this)),
149 Bind(&HostapdMonitor::OnReadError, Unretained(this))));
150
151 if (!SendMessage(kHostapdCmdAttach, strlen(kHostapdCmdAttach))) {
152 LOG(ERROR) << "Failed to attach to hostapd";
153 return;
154 }
155
156 // Start a timer for ATTACH response.
157 attach_timeout_callback_.Reset(
Peter Qiue2657852015-02-03 11:33:11 -0800158 Bind(&HostapdMonitor::AttachTimeoutHandler,
159 weak_ptr_factory_.GetWeakPtr()));
Peter Qiufda548c2015-01-13 14:39:19 -0800160 event_dispatcher_->PostDelayedTask(attach_timeout_callback_.callback(),
161 kHostapdAttachTimeoutMs);
162 return;
163}
164
165void HostapdMonitor::AttachTimeoutHandler() {
166 LOG(ERROR) << "Timeout waiting for attach response";
167}
168
169// Method for sending message to hostapd control interface.
170bool HostapdMonitor::SendMessage(const char* message, size_t length) {
171 if (sockets_->Send(hostapd_socket_, message, length, 0) < 0) {
Alex Deymo1883cf22015-04-09 10:06:31 -0700172 PLOG(ERROR) << "Send to hostapd failed";
Peter Qiufda548c2015-01-13 14:39:19 -0800173 return false;
174 }
175
176 return true;
177}
178
179void HostapdMonitor::ParseMessage(shill::InputData* data) {
180 string str(reinterpret_cast<const char*>(data->buf), data->len);
181 // "OK" response for the "ATTACH" command.
182 if (str == kHostapdRespOk) {
183 attach_timeout_callback_.Cancel();
184 return;
185 }
186
187 // Event messages are in format of <[Level]>[Event] [Detail message].
188 // For example: <2>AP-STA-CONNECTED 00:11:22:33:44:55
189 // Refer to wpa_ctrl.h for complete list of possible events.
190 if (str.find_first_of('<', 0) == 0 && str.find_first_of('>', 0) == 2) {
191 // Remove the log level.
192 string msg = str.substr(3);
193 string event;
194 string data;
195 size_t pos = msg.find_first_of(' ', 0);
196 if (pos == string::npos) {
197 event = msg;
198 } else {
199 event = msg.substr(0, pos);
200 data = msg.substr(pos + 1);
201 }
202
203 Event event_code;
204 if (event == kHostapdEventStationConnected) {
205 event_code = kStationConnected;
206 } else if (event == kHostapdEventStationDisconnected) {
207 event_code = kStationDisconnected;
208 } else {
209 LOG(INFO) << "Received unknown event: " << event;
210 return;
211 }
212 event_callback_.Run(event_code, data);
213 return;
214 }
215
216 LOG(INFO) << "Received unknown message: " << str;
217}
218
219void HostapdMonitor::OnReadError(const string& error_msg) {
220 LOG(FATAL) << "Hostapd Socket read returns error: "
221 << error_msg;
222}
223
224} // namespace apmanager