blob: be0c33a8a03dd852599a068bcbd2e6406010e8db [file] [log] [blame]
Christopher Wiley8c784cd2016-08-23 09:30:20 -07001/*
2 * Copyright (C) 2016 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 */
16
17#include "wifi_system/supplicant_manager.h"
18
19#include <android-base/logging.h>
20#include <cutils/properties.h>
21#include <fcntl.h>
22#include <string.h>
23#include <sys/stat.h>
24#include <unistd.h>
25
26// This ugliness is necessary to access internal implementation details
27// of the property subsystem.
28#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
29#include <sys/_system_properties.h>
30
Christopher Wiley8c784cd2016-08-23 09:30:20 -070031namespace android {
32namespace wifi_system {
33namespace {
34
35const char kSupplicantInitProperty[] = "init.svc.wpa_supplicant";
36const char kSupplicantConfigTemplatePath[] =
Jiyong Parkcdeb0572017-03-08 09:40:22 +090037 "/etc/wifi/wpa_supplicant.conf";
Christopher Wiley8c784cd2016-08-23 09:30:20 -070038const char kSupplicantConfigFile[] = "/data/misc/wifi/wpa_supplicant.conf";
39const char kP2pConfigFile[] = "/data/misc/wifi/p2p_supplicant.conf";
40const char kSupplicantServiceName[] = "wpa_supplicant";
41constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
42
43int ensure_config_file_exists(const char* config_file) {
44 char buf[2048];
45 int srcfd, destfd;
46 int nread;
47 int ret;
Jiyong Parkcdeb0572017-03-08 09:40:22 +090048 std::string templatePath;
Christopher Wiley8c784cd2016-08-23 09:30:20 -070049
50 ret = access(config_file, R_OK | W_OK);
51 if ((ret == 0) || (errno == EACCES)) {
52 if ((ret != 0) && (chmod(config_file, kConfigFileMode) != 0)) {
53 LOG(ERROR) << "Cannot set RW to \"" << config_file << "\": "
54 << strerror(errno);
55 return false;
56 }
57 return true;
58 } else if (errno != ENOENT) {
59 LOG(ERROR) << "Cannot access \"" << config_file << "\": "
60 << strerror(errno);
61 return false;
62 }
63
Ningyuan Wangb0b13ea2017-06-01 15:43:09 -070064 std::string configPathSystem =
65 std::string("/system") + std::string(kSupplicantConfigTemplatePath);
66 std::string configPathVendor =
67 std::string("/vendor") + std::string(kSupplicantConfigTemplatePath);
68 srcfd = TEMP_FAILURE_RETRY(open(configPathSystem.c_str(), O_RDONLY));
69 templatePath = configPathSystem;
Christopher Wiley8c784cd2016-08-23 09:30:20 -070070 if (srcfd < 0) {
Ningyuan Wangb0b13ea2017-06-01 15:43:09 -070071 int errnoSystem = errno;
72 srcfd = TEMP_FAILURE_RETRY(open(configPathVendor.c_str(), O_RDONLY));
73 templatePath = configPathVendor;
Jiyong Parkcdeb0572017-03-08 09:40:22 +090074 if (srcfd < 0) {
Ningyuan Wangb0b13ea2017-06-01 15:43:09 -070075 int errnoVendor = errno;
76 LOG(ERROR) << "Cannot open \"" << configPathSystem << "\": "
77 << strerror(errnoSystem);
78 LOG(ERROR) << "Cannot open \"" << configPathVendor << "\": "
79 << strerror(errnoVendor);
Jiyong Parkcdeb0572017-03-08 09:40:22 +090080 return false;
81 }
Christopher Wiley8c784cd2016-08-23 09:30:20 -070082 }
83
84 destfd = TEMP_FAILURE_RETRY(open(config_file,
85 O_CREAT | O_RDWR,
86 kConfigFileMode));
87 if (destfd < 0) {
88 close(srcfd);
89 LOG(ERROR) << "Cannot create \"" << config_file << "\": "
90 << strerror(errno);
91 return false;
92 }
93
94 while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) {
95 if (nread < 0) {
Jiyong Parkcdeb0572017-03-08 09:40:22 +090096 LOG(ERROR) << "Error reading \"" << templatePath
Christopher Wiley8c784cd2016-08-23 09:30:20 -070097 << "\": " << strerror(errno);
98 close(srcfd);
99 close(destfd);
100 unlink(config_file);
101 return false;
102 }
103 TEMP_FAILURE_RETRY(write(destfd, buf, nread));
104 }
105
106 close(destfd);
107 close(srcfd);
108
109 /* chmod is needed because open() didn't set permisions properly */
110 if (chmod(config_file, kConfigFileMode) < 0) {
111 LOG(ERROR) << "Error changing permissions of " << config_file
112 << " to 0660: " << strerror(errno);
113 unlink(config_file);
114 return false;
115 }
116
117 return true;
118}
119
120} // namespace
121
122bool SupplicantManager::StartSupplicant() {
123 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
124 int count = 200; /* wait at most 20 seconds for completion */
125 const prop_info* pi;
126 unsigned serial = 0;
127
128 /* Check whether already running */
129 if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
130 strcmp(supp_status, "running") == 0) {
131 return true;
132 }
133
134 /* Before starting the daemon, make sure its config file exists */
135 if (ensure_config_file_exists(kSupplicantConfigFile) < 0) {
136 LOG(ERROR) << "Wi-Fi will not be enabled";
137 return false;
138 }
139
140 /*
141 * Some devices have another configuration file for the p2p interface.
142 * However, not all devices have this, and we'll let it slide if it
143 * is missing. For devices that do expect this file to exist,
144 * supplicant will refuse to start and emit a good error message.
145 * No need to check for it here.
146 */
147 (void)ensure_config_file_exists(kP2pConfigFile);
148
Christopher Wiley8c784cd2016-08-23 09:30:20 -0700149 /*
150 * Get a reference to the status property, so we can distinguish
151 * the case where it goes stopped => running => stopped (i.e.,
152 * it start up, but fails right away) from the case in which
153 * it starts in the stopped state and never manages to start
154 * running at all.
155 */
156 pi = __system_property_find(kSupplicantInitProperty);
157 if (pi != NULL) {
158 serial = __system_property_serial(pi);
159 }
160
161 property_set("ctl.start", kSupplicantServiceName);
162 sched_yield();
163
164 while (count-- > 0) {
165 if (pi == NULL) {
166 pi = __system_property_find(kSupplicantInitProperty);
167 }
168 if (pi != NULL) {
169 /*
170 * property serial updated means that init process is scheduled
171 * after we sched_yield, further property status checking is based on this
172 */
173 if (__system_property_serial(pi) != serial) {
174 __system_property_read(pi, NULL, supp_status);
175 if (strcmp(supp_status, "running") == 0) {
176 return true;
177 } else if (strcmp(supp_status, "stopped") == 0) {
178 return false;
179 }
180 }
181 }
182 usleep(100000);
183 }
184 return false;
185}
186
187bool SupplicantManager::StopSupplicant() {
188 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
189 int count = 50; /* wait at most 5 seconds for completion */
190
191 /* Check whether supplicant already stopped */
192 if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
193 strcmp(supp_status, "stopped") == 0) {
194 return true;
195 }
196
197 property_set("ctl.stop", kSupplicantServiceName);
198 sched_yield();
199
200 while (count-- > 0) {
201 if (property_get(kSupplicantInitProperty, supp_status, NULL)) {
202 if (strcmp(supp_status, "stopped") == 0) return true;
203 }
204 usleep(100000);
205 }
206 LOG(ERROR) << "Failed to stop supplicant";
207 return false;
208}
209
210bool SupplicantManager::IsSupplicantRunning() {
211 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
212 if (property_get(kSupplicantInitProperty, supp_status, NULL)) {
213 return strcmp(supp_status, "running") == 0;
214 }
215 return false; // Failed to read service status from init.
216}
217
Christopher Wiley8c784cd2016-08-23 09:30:20 -0700218} // namespace wifi_system
219} // namespace android