Home | History | Annotate | Download | only in libwifi_system
      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 
     31 namespace android {
     32 namespace wifi_system {
     33 namespace {
     34 
     35 const char kSupplicantInitProperty[] = "init.svc.wpa_supplicant";
     36 const char kSupplicantConfigTemplatePath[] =
     37     "/etc/wifi/wpa_supplicant.conf";
     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 const char kWiFiEntropyFile[] = "/data/misc/wifi/entropy.bin";
     44 
     45 const unsigned char kDummyKey[21] = {0x02, 0x11, 0xbe, 0x33, 0x43, 0x35, 0x68,
     46                                      0x47, 0x84, 0x99, 0xa9, 0x2b, 0x1c, 0xd3,
     47                                      0xee, 0xff, 0xf1, 0xe2, 0xf3, 0xf4, 0xf5};
     48 
     49 int ensure_config_file_exists(const char* config_file) {
     50   char buf[2048];
     51   int srcfd, destfd;
     52   int nread;
     53   int ret;
     54   std::string templatePath;
     55 
     56   ret = access(config_file, R_OK | W_OK);
     57   if ((ret == 0) || (errno == EACCES)) {
     58     if ((ret != 0) && (chmod(config_file, kConfigFileMode) != 0)) {
     59       LOG(ERROR) << "Cannot set RW to \"" << config_file << "\": "
     60                  << strerror(errno);
     61       return false;
     62     }
     63     return true;
     64   } else if (errno != ENOENT) {
     65     LOG(ERROR) << "Cannot access \"" << config_file << "\": "
     66                << strerror(errno);
     67     return false;
     68   }
     69 
     70   std::string configPathSystem =
     71       std::string("/system") + std::string(kSupplicantConfigTemplatePath);
     72   std::string configPathVendor =
     73       std::string("/vendor") + std::string(kSupplicantConfigTemplatePath);
     74   srcfd = TEMP_FAILURE_RETRY(open(configPathSystem.c_str(), O_RDONLY));
     75   templatePath = configPathSystem;
     76   if (srcfd < 0) {
     77     int errnoSystem = errno;
     78     srcfd = TEMP_FAILURE_RETRY(open(configPathVendor.c_str(), O_RDONLY));
     79     templatePath = configPathVendor;
     80     if (srcfd < 0) {
     81       int errnoVendor = errno;
     82       LOG(ERROR) << "Cannot open \"" << configPathSystem << "\": "
     83                  << strerror(errnoSystem);
     84       LOG(ERROR) << "Cannot open \"" << configPathVendor << "\": "
     85                  << strerror(errnoVendor);
     86       return false;
     87     }
     88   }
     89 
     90   destfd = TEMP_FAILURE_RETRY(open(config_file,
     91                                    O_CREAT | O_RDWR,
     92                                    kConfigFileMode));
     93   if (destfd < 0) {
     94     close(srcfd);
     95     LOG(ERROR) << "Cannot create \"" << config_file << "\": "
     96                << strerror(errno);
     97     return false;
     98   }
     99 
    100   while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) {
    101     if (nread < 0) {
    102       LOG(ERROR) << "Error reading \"" << templatePath
    103                  << "\": " << strerror(errno);
    104       close(srcfd);
    105       close(destfd);
    106       unlink(config_file);
    107       return false;
    108     }
    109     TEMP_FAILURE_RETRY(write(destfd, buf, nread));
    110   }
    111 
    112   close(destfd);
    113   close(srcfd);
    114 
    115   /* chmod is needed because open() didn't set permisions properly */
    116   if (chmod(config_file, kConfigFileMode) < 0) {
    117     LOG(ERROR) << "Error changing permissions of " << config_file
    118                << " to 0660: " << strerror(errno);
    119     unlink(config_file);
    120     return false;
    121   }
    122 
    123   return true;
    124 }
    125 
    126 }  // namespace
    127 
    128 bool SupplicantManager::StartSupplicant() {
    129   char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
    130   int count = 200; /* wait at most 20 seconds for completion */
    131   const prop_info* pi;
    132   unsigned serial = 0;
    133 
    134   /* Check whether already running */
    135   if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
    136       strcmp(supp_status, "running") == 0) {
    137     return true;
    138   }
    139 
    140   /* Before starting the daemon, make sure its config file exists */
    141   if (ensure_config_file_exists(kSupplicantConfigFile) < 0) {
    142     LOG(ERROR) << "Wi-Fi will not be enabled";
    143     return false;
    144   }
    145 
    146   /*
    147    * Some devices have another configuration file for the p2p interface.
    148    * However, not all devices have this, and we'll let it slide if it
    149    * is missing.  For devices that do expect this file to exist,
    150    * supplicant will refuse to start and emit a good error message.
    151    * No need to check for it here.
    152    */
    153   (void)ensure_config_file_exists(kP2pConfigFile);
    154 
    155   if (!EnsureEntropyFileExists()) {
    156     LOG(ERROR) << "Wi-Fi entropy file was not created";
    157   }
    158 
    159   /*
    160    * Get a reference to the status property, so we can distinguish
    161    * the case where it goes stopped => running => stopped (i.e.,
    162    * it start up, but fails right away) from the case in which
    163    * it starts in the stopped state and never manages to start
    164    * running at all.
    165    */
    166   pi = __system_property_find(kSupplicantInitProperty);
    167   if (pi != NULL) {
    168     serial = __system_property_serial(pi);
    169   }
    170 
    171   property_set("ctl.start", kSupplicantServiceName);
    172   sched_yield();
    173 
    174   while (count-- > 0) {
    175     if (pi == NULL) {
    176       pi = __system_property_find(kSupplicantInitProperty);
    177     }
    178     if (pi != NULL) {
    179       /*
    180        * property serial updated means that init process is scheduled
    181        * after we sched_yield, further property status checking is based on this
    182        */
    183       if (__system_property_serial(pi) != serial) {
    184         __system_property_read(pi, NULL, supp_status);
    185         if (strcmp(supp_status, "running") == 0) {
    186           return true;
    187         } else if (strcmp(supp_status, "stopped") == 0) {
    188           return false;
    189         }
    190       }
    191     }
    192     usleep(100000);
    193   }
    194   return false;
    195 }
    196 
    197 bool SupplicantManager::StopSupplicant() {
    198   char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
    199   int count = 50; /* wait at most 5 seconds for completion */
    200 
    201   /* Check whether supplicant already stopped */
    202   if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
    203       strcmp(supp_status, "stopped") == 0) {
    204     return true;
    205   }
    206 
    207   property_set("ctl.stop", kSupplicantServiceName);
    208   sched_yield();
    209 
    210   while (count-- > 0) {
    211     if (property_get(kSupplicantInitProperty, supp_status, NULL)) {
    212       if (strcmp(supp_status, "stopped") == 0) return true;
    213     }
    214     usleep(100000);
    215   }
    216   LOG(ERROR) << "Failed to stop supplicant";
    217   return false;
    218 }
    219 
    220 bool SupplicantManager::IsSupplicantRunning() {
    221   char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
    222   if (property_get(kSupplicantInitProperty, supp_status, NULL)) {
    223     return strcmp(supp_status, "running") == 0;
    224   }
    225   return false;  // Failed to read service status from init.
    226 }
    227 
    228 bool SupplicantManager::EnsureEntropyFileExists() {
    229   int ret;
    230   int destfd;
    231 
    232   ret = access(kWiFiEntropyFile, R_OK | W_OK);
    233   if ((ret == 0) || (errno == EACCES)) {
    234     if ((ret != 0) &&
    235         (chmod(kWiFiEntropyFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0)) {
    236       PLOG(ERROR) << "Cannot set RW to " << kWiFiEntropyFile;
    237       return false;
    238     }
    239     return true;
    240   }
    241   destfd = TEMP_FAILURE_RETRY(open(kWiFiEntropyFile, O_CREAT | O_RDWR, 0660));
    242   if (destfd < 0) {
    243     PLOG(ERROR) << "Cannot create " << kWiFiEntropyFile;
    244     return false;
    245   }
    246 
    247   if (TEMP_FAILURE_RETRY(write(destfd, kDummyKey, sizeof(kDummyKey))) !=
    248       sizeof(kDummyKey)) {
    249     PLOG(ERROR) << "Error writing " << kWiFiEntropyFile;
    250     close(destfd);
    251     return false;
    252   }
    253   close(destfd);
    254 
    255   /* chmod is needed because open() didn't set permisions properly */
    256   if (chmod(kWiFiEntropyFile, 0660) < 0) {
    257     PLOG(ERROR) << "Error changing permissions of " << kWiFiEntropyFile
    258                 << " to 0600 ";
    259     unlink(kWiFiEntropyFile);
    260     return false;
    261   }
    262 
    263   return true;
    264 }
    265 
    266 }  // namespace wifi_system
    267 }  // namespace android
    268