blob: ffb94aa3efa31ca855335310b35175f414118995 [file] [log] [blame]
Peter Qiu376e4042014-11-13 09:40:28 -08001// Copyright 2014 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/service.h"
6
7#include <signal.h>
8
Peter Qiu376e4042014-11-13 09:40:28 -08009#include <base/strings/stringprintf.h>
10#include <chromeos/dbus/service_constants.h>
11#include <chromeos/errors/error.h>
12
Peter Qiufb39ba42014-11-21 09:09:59 -080013#include "apmanager/manager.h"
Peter Qiu376e4042014-11-13 09:40:28 -080014
15using chromeos::dbus_utils::AsyncEventSequencer;
16using chromeos::dbus_utils::ExportedObjectManager;
Vitaly Bukaa4630922014-12-11 18:46:46 -080017using org::chromium::apmanager::ManagerAdaptor;
Peter Qiu376e4042014-11-13 09:40:28 -080018using std::string;
19
20namespace apmanager {
21
22// static.
Peter Qiufb39ba42014-11-21 09:09:59 -080023const char Service::kHostapdPath[] = "/usr/sbin/hostapd";
Peter Qiu77517302015-01-08 16:22:16 -080024const char Service::kHostapdConfigPathFormat[] =
25 "/var/run/apmanager/hostapd/hostapd-%d.conf";
Peter Qiu1dbf9fd2015-01-09 13:36:55 -080026const char Service::kHostapdControlInterfacePath[] =
27 "/var/run/apmanager/hostapd/ctrl_iface";
Peter Qiu376e4042014-11-13 09:40:28 -080028const int Service::kTerminationTimeoutSeconds = 2;
29
Peter Qiufb39ba42014-11-21 09:09:59 -080030Service::Service(Manager* manager, int service_identifier)
Peter Qiu376e4042014-11-13 09:40:28 -080031 : org::chromium::apmanager::ServiceAdaptor(this),
Peter Qiufb39ba42014-11-21 09:09:59 -080032 manager_(manager),
Peter Qiu376e4042014-11-13 09:40:28 -080033 service_identifier_(service_identifier),
Vitaly Bukaa4630922014-12-11 18:46:46 -080034 service_path_(
35 base::StringPrintf("%s/services/%d",
36 ManagerAdaptor::GetObjectPath().value().c_str(),
37 service_identifier)),
Peter Qiu376e4042014-11-13 09:40:28 -080038 dbus_path_(dbus::ObjectPath(service_path_)),
Peter Qiubf8e36c2014-12-03 22:59:45 -080039 config_(new Config(manager, service_path_)),
Peter Qiu77517302015-01-08 16:22:16 -080040 dhcp_server_factory_(DHCPServerFactory::GetInstance()),
Peter Qiu1dbf9fd2015-01-09 13:36:55 -080041 file_writer_(FileWriter::GetInstance()),
42 process_factory_(ProcessFactory::GetInstance()) {
Peter Qiu376e4042014-11-13 09:40:28 -080043 SetConfig(config_->dbus_path());
Peter Qiubf8e36c2014-12-03 22:59:45 -080044 // TODO(zqiu): come up with better server address management. This is good
45 // enough for now.
46 config_->SetServerAddressIndex(service_identifier_ & 0xFF);
Peter Qiu376e4042014-11-13 09:40:28 -080047}
48
49Service::~Service() {
50 // Stop hostapd process if still running.
51 if (IsHostapdRunning()) {
Peter Qiubf8e36c2014-12-03 22:59:45 -080052 ReleaseResources();
Peter Qiu376e4042014-11-13 09:40:28 -080053 }
54}
55
56void Service::RegisterAsync(ExportedObjectManager* object_manager,
Peter Qiuc9ce1f12014-12-05 11:14:29 -080057 const scoped_refptr<dbus::Bus>& bus,
Peter Qiu376e4042014-11-13 09:40:28 -080058 AsyncEventSequencer* sequencer) {
59 CHECK(!dbus_object_) << "Already registered";
60 dbus_object_.reset(
61 new chromeos::dbus_utils::DBusObject(
62 object_manager,
Peter Qiuc9ce1f12014-12-05 11:14:29 -080063 bus,
Peter Qiu376e4042014-11-13 09:40:28 -080064 dbus_path_));
65 RegisterWithDBusObject(dbus_object_.get());
66 dbus_object_->RegisterAsync(
67 sequencer->GetHandler("Service.RegisterAsync() failed.", true));
68
69 // Register Config DBus object.
Peter Qiuc9ce1f12014-12-05 11:14:29 -080070 config_->RegisterAsync(object_manager, bus, sequencer);
Peter Qiu376e4042014-11-13 09:40:28 -080071}
72
73bool Service::Start(chromeos::ErrorPtr* error) {
74 if (IsHostapdRunning()) {
75 chromeos::Error::AddTo(
76 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
77 "Service already running");
78 return false;
79 }
80
Peter Qiubfd410e2015-01-09 15:14:20 -080081 // Setup hostapd control interface path.
82 config_->set_control_interface(kHostapdControlInterfacePath);
83
Peter Qiu376e4042014-11-13 09:40:28 -080084 // Generate hostapd configuration content.
85 string config_str;
86 if (!config_->GenerateConfigFile(error, &config_str)) {
87 chromeos::Error::AddTo(
88 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
89 "Failed to generate config file");
90 return false;
91 }
92
93 // Write configuration to a file.
Peter Qiu77517302015-01-08 16:22:16 -080094 string config_file_name = base::StringPrintf(kHostapdConfigPathFormat,
95 service_identifier_);
96 if (!file_writer_->Write(config_file_name, config_str)) {
Peter Qiu376e4042014-11-13 09:40:28 -080097 chromeos::Error::AddTo(
98 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
99 "Failed to write configuration to a file");
100 return false;
101 }
102
Peter Qiufb39ba42014-11-21 09:09:59 -0800103 // Claim the device needed for this ap service.
104 if (!config_->ClaimDevice()) {
105 chromeos::Error::AddTo(
106 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
107 "Failed to claim the device for this service");
108 return false;
109 }
110
Peter Qiu376e4042014-11-13 09:40:28 -0800111 // Start hostapd process.
Peter Qiu77517302015-01-08 16:22:16 -0800112 if (!StartHostapdProcess(config_file_name)) {
Peter Qiu376e4042014-11-13 09:40:28 -0800113 chromeos::Error::AddTo(
114 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
115 "Failed to start hostapd");
Peter Qiufb39ba42014-11-21 09:09:59 -0800116 // Release the device claimed for this service.
117 config_->ReleaseDevice();
Peter Qiu376e4042014-11-13 09:40:28 -0800118 return false;
119 }
120
Peter Qiubf8e36c2014-12-03 22:59:45 -0800121 // Start DHCP server if in server mode.
122 if (config_->GetOperationMode() == kOperationModeServer) {
123 dhcp_server_.reset(
124 dhcp_server_factory_->CreateDHCPServer(config_->GetServerAddressIndex(),
125 config_->selected_interface()));
126 if (!dhcp_server_->Start()) {
127 chromeos::Error::AddTo(
128 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
129 "Failed to start DHCP server");
130 ReleaseResources();
131 return false;
132 }
133 }
134
Peter Qiufda548c2015-01-13 14:39:19 -0800135 // Start monitoring hostapd.
136 if (!hostapd_monitor_) {
137 hostapd_monitor_.reset(
138 new HostapdMonitor(base::Bind(&Service::HostapdEventCallback,
139 base::Unretained(this)),
140 config_->control_interface(),
141 config_->selected_interface()));
142 }
143 hostapd_monitor_->Start();
144
Peter Qiu376e4042014-11-13 09:40:28 -0800145 return true;
146}
147
148bool Service::Stop(chromeos::ErrorPtr* error) {
149 if (!IsHostapdRunning()) {
150 chromeos::Error::AddTo(
151 error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
152 "Service is not currently running");
153 return false;
154 }
155
Peter Qiubf8e36c2014-12-03 22:59:45 -0800156 ReleaseResources();
Peter Qiu376e4042014-11-13 09:40:28 -0800157 return true;
158}
159
160bool Service::IsHostapdRunning() {
161 return hostapd_process_ && hostapd_process_->pid() != 0 &&
162 chromeos::Process::ProcessExists(hostapd_process_->pid());
163}
164
165bool Service::StartHostapdProcess(const string& config_file_path) {
Peter Qiu1dbf9fd2015-01-09 13:36:55 -0800166 hostapd_process_.reset(process_factory_->CreateProcess());
Peter Qiufb39ba42014-11-21 09:09:59 -0800167 hostapd_process_->AddArg(kHostapdPath);
Peter Qiu376e4042014-11-13 09:40:28 -0800168 hostapd_process_->AddArg(config_file_path);
169 if (!hostapd_process_->Start()) {
170 hostapd_process_.reset();
171 return false;
172 }
173 return true;
174}
175
176void Service::StopHostapdProcess() {
177 if (!hostapd_process_->Kill(SIGTERM, kTerminationTimeoutSeconds)) {
178 hostapd_process_->Kill(SIGKILL, kTerminationTimeoutSeconds);
179 }
180 hostapd_process_.reset();
181}
182
Peter Qiubf8e36c2014-12-03 22:59:45 -0800183void Service::ReleaseResources() {
184 StopHostapdProcess();
185 dhcp_server_.reset();
186 config_->ReleaseDevice();
187}
188
Peter Qiufda548c2015-01-13 14:39:19 -0800189void Service::HostapdEventCallback(HostapdMonitor::Event event,
190 const std::string& data) {
191 switch (event) {
192 case HostapdMonitor::kStationConnected:
193 LOG(INFO) << "Station connected: " << data;
194 break;
195 case HostapdMonitor::kStationDisconnected:
196 LOG(INFO) << "Station disconnected: " << data;
197 break;
198 default:
199 LOG(ERROR) << "Unknown event: " << event;
200 break;
201 }
202}
203
Peter Qiu376e4042014-11-13 09:40:28 -0800204} // namespace apmanager