Christopher Wiley | 8c784cd | 2016-08-23 09:30:20 -0700 | [diff] [blame] | 1 | /* |
| 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 Wiley | 8c784cd | 2016-08-23 09:30:20 -0700 | [diff] [blame] | 31 | namespace android { |
| 32 | namespace wifi_system { |
| 33 | namespace { |
| 34 | |
| 35 | const char kSupplicantInitProperty[] = "init.svc.wpa_supplicant"; |
| 36 | const char kSupplicantConfigTemplatePath[] = |
Jiyong Park | cdeb057 | 2017-03-08 09:40:22 +0900 | [diff] [blame] | 37 | "/etc/wifi/wpa_supplicant.conf"; |
Christopher Wiley | 8c784cd | 2016-08-23 09:30:20 -0700 | [diff] [blame] | 38 | const char kSupplicantConfigFile[] = "/data/misc/wifi/wpa_supplicant.conf"; |
| 39 | const char kP2pConfigFile[] = "/data/misc/wifi/p2p_supplicant.conf"; |
| 40 | const char kSupplicantServiceName[] = "wpa_supplicant"; |
| 41 | constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; |
| 42 | |
| 43 | int ensure_config_file_exists(const char* config_file) { |
| 44 | char buf[2048]; |
| 45 | int srcfd, destfd; |
| 46 | int nread; |
| 47 | int ret; |
Jiyong Park | cdeb057 | 2017-03-08 09:40:22 +0900 | [diff] [blame] | 48 | std::string templatePath; |
Christopher Wiley | 8c784cd | 2016-08-23 09:30:20 -0700 | [diff] [blame] | 49 | |
| 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 Wang | b0b13ea | 2017-06-01 15:43:09 -0700 | [diff] [blame] | 64 | 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 Wiley | 8c784cd | 2016-08-23 09:30:20 -0700 | [diff] [blame] | 70 | if (srcfd < 0) { |
Ningyuan Wang | b0b13ea | 2017-06-01 15:43:09 -0700 | [diff] [blame] | 71 | int errnoSystem = errno; |
| 72 | srcfd = TEMP_FAILURE_RETRY(open(configPathVendor.c_str(), O_RDONLY)); |
| 73 | templatePath = configPathVendor; |
Jiyong Park | cdeb057 | 2017-03-08 09:40:22 +0900 | [diff] [blame] | 74 | if (srcfd < 0) { |
Ningyuan Wang | b0b13ea | 2017-06-01 15:43:09 -0700 | [diff] [blame] | 75 | int errnoVendor = errno; |
| 76 | LOG(ERROR) << "Cannot open \"" << configPathSystem << "\": " |
| 77 | << strerror(errnoSystem); |
| 78 | LOG(ERROR) << "Cannot open \"" << configPathVendor << "\": " |
| 79 | << strerror(errnoVendor); |
Jiyong Park | cdeb057 | 2017-03-08 09:40:22 +0900 | [diff] [blame] | 80 | return false; |
| 81 | } |
Christopher Wiley | 8c784cd | 2016-08-23 09:30:20 -0700 | [diff] [blame] | 82 | } |
| 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 Park | cdeb057 | 2017-03-08 09:40:22 +0900 | [diff] [blame] | 96 | LOG(ERROR) << "Error reading \"" << templatePath |
Christopher Wiley | 8c784cd | 2016-08-23 09:30:20 -0700 | [diff] [blame] | 97 | << "\": " << 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 | |
| 122 | bool 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 Wiley | 8c784cd | 2016-08-23 09:30:20 -0700 | [diff] [blame] | 149 | /* |
| 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 | |
| 187 | bool 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 | |
| 210 | bool 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 Wiley | 8c784cd | 2016-08-23 09:30:20 -0700 | [diff] [blame] | 218 | } // namespace wifi_system |
| 219 | } // namespace android |