(九十五)Android O wpa_supplicant.conf配置檔案探究
1.wpa_supplicant.conf簡介
首先說下原始碼位置,與wpa_supplicant.conf相關的配置檔案、指令碼如下紅框標註處。
其中wpa_supplicant.conf 不是手機中的wpa_supplicant.conf,開啟看一下是wpa_supplicant.conf的配置說明。
##### Example wpa_supplicant configuration file ############################### # # This file describes configuration file format and lists all available option. # Please also take a look at simpler configuration examples in 'examples' # subdirectory. # # Empty lines and lines starting with # are ignored # NOTE! This file may contain password information and should probably be made # readable only by root user on multiuser systems. # Note: All file paths in this configuration file should use full (absolute, # not relative to working directory) path in order to allow working directory # to be changed. This can happen if wpa_supplicant is run in the background. # Whether to allow wpa_supplicant to update (overwrite) configuration # # This option can be used to allow wpa_supplicant to overwrite configuration # file whenever configuration is changed (e.g., new network block is added with # wpa_cli or wpa_gui, or a password is changed). This is required for # wpa_cli/wpa_gui to be able to store the configuration changes permanently. # Please note that overwriting configuration file will remove the comments from # it. #update_config=1
簡單翻譯一下,wpa_supplicant.conf是wpa_supplicant.conf配置檔案的一個例子,這個檔案描述了配置檔案格式以及列舉了所有的可選選項。請看一下在examples資料夾下的例子。
請注意檔案有可能包含密碼資訊,所以儘可能地在多使用者系統中將這個檔案設為root使用者只讀。
注意,所有這個檔案中的路徑請用絕對路徑,不用用相對路徑,只是為了允許工作目錄可以改變。在supplicant執行在後臺時有可能會發生這個事情。
那順勢看下wpa_supplicant.conf在example的例子吧:
是指這個麼
2. 探討wpa_supplicant.conf由來
2.1 wpa_supplicant_conf.mk
# Include this makefile to generate your hardware specific wpa_supplicant.conf
# Requires: WIFI_DRIVER_SOCKET_IFACE
LOCAL_PATH := $(call my-dir)
########################
include $(CLEAR_VARS)
LOCAL_MODULE := wpa_supplicant.conf
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/wifi
include $(BUILD_SYSTEM)/base_rules.mk
WPA_SUPPLICANT_CONF_TEMPLATE := $(LOCAL_PATH)/wpa_supplicant_template.conf
WPA_SUPPLICANT_CONF_SCRIPT := $(LOCAL_PATH)/wpa_supplicant_conf.sh
$(LOCAL_BUILT_MODULE): PRIVATE_WIFI_DRIVER_SOCKET_IFACE := $(WIFI_DRIVER_SOCKET_IFACE)
$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE := $(WPA_SUPPLICANT_CONF_TEMPLATE)
$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT := $(WPA_SUPPLICANT_CONF_SCRIPT)
$(LOCAL_BUILT_MODULE) : $(WPA_SUPPLICANT_CONF_TEMPLATE) $(WPA_SUPPLICANT_CONF_SCRIPT)
@echo Target wpa_supplicant.conf: [email protected]
@mkdir -p $(dir [email protected])
$(hide) WIFI_DRIVER_SOCKET_IFACE="$(PRIVATE_WIFI_DRIVER_SOCKET_IFACE)" \
bash $(PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT) $(PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE) > [email protected]
########################
先看一下mk檔案,模組名直接就是wpa_supplicant.conf,試著make一下
ninja: no work to do.
ninja: no work to do.
wildcard(out/target/product/generic_x86_64/clean_steps.mk) was changed, regenerating...
$(shell uname -rsm) was changed, regenerating...
[526/824] including system/sepolicy/Android.mk ...
system/sepolicy/Android.mk:79: warning: BOARD_SEPOLICY_VERS not specified, assuming current platform version
[824/824] including tools/tradefederation/core/Android.mk ...
[ 93% 14/15] glob tools/metalava/src/main/java/**/*.kt
ninja: error: unknown target 'wpa_supplicant.conf'
10:28:42 ninja failed with: exit status 1
#### failed to build some targets (02:08 (mm:ss)) ####
emmm,本地編譯有點問題,編譯其他的是可以的,待續
還是回頭來看下mk檔案,其中用到了兩個其他的檔案,分別是wpa_supplicant_template.conf 和 wpa_supplicant_conf.sh
指令碼+檔案最後生成在$(TARGET_OUT_VENDOR)/etc/wifi目錄下的wpa_supplicant.conf
LOCAL_MODULE := wpa_supplicant.conf
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/wifi
可以看手機的vendor/etc/wifi下看到該檔案
另外在/data/misc/wifi下也有該檔案
2.2 wpa_supplicant_template.conf
[email protected]:~/expand/aosp/aosp/external/wpa_supplicant_8/wpa_supplicant$ cat wpa_supplicant_template.conf
##### wpa_supplicant configuration file template #####
update_config=1
eapol_version=1
ap_scan=1
fast_reauth=1
pmf=1
p2p_add_cli_chan=1
2.3 wpa_supplicant_conf.sh
[email protected]:~/expand/aosp/aosp/external/wpa_supplicant_8/wpa_supplicant$ cat wpa_supplicant_conf.sh
#!/bin/bash
#
# Copyright (C) 2010 The Android Open Source Project
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
#
# Generate a wpa_supplicant.conf from the template.
# $1: the template file name
if [ -n "$WIFI_DRIVER_SOCKET_IFACE" ]
then
sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1 | sed -e "s/wlan0/$WIFI_DRIVER_SOCKET_IFACE/"
else
sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1
fi
學習一下sed命令的功能。
sed
作用:主要用於替換指定的字元;查詢或替換指定字串時,必須把字串用//來註釋下,比如root 必須是/root/;
sed 只要不適用-i引數,一般都是在輸出終端上顯示而已,無法更改原始檔;
引數-e: --expression,多重編輯;
引數-n:不帶-n則列出檔案所有內容,加上-n只列出結果sed特殊處理的那一行;
引數-i:直接修改讀取的內容檔案,而不是輸出到終端;
功能s:替換、取代;
功能d:刪除;
功能a:新增;
如上命令應該是去除註釋、去除空格、去除空行,替換wlan0為WIFI_DRIVER_SOCKET_IFACE
執行下看下效果
自己寫個例子試下
2.4 supplicant啟動流程
bool SupplicantManager::StartSupplicant() {
char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
int count = 200; /* wait at most 20 seconds for completion */
const prop_info* pi;
unsigned serial = 0;
/* Check whether already running */
if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
strcmp(supp_status, "running") == 0) {
return true;
}
/* Before starting the daemon, make sure its config file exists */
if (ensure_config_file_exists(kSupplicantConfigFile) < 0) {
LOG(ERROR) << "Wi-Fi will not be enabled";
return false;
}
/*
* Some devices have another configuration file for the p2p interface.
* However, not all devices have this, and we'll let it slide if it
* is missing. For devices that do expect this file to exist,
* supplicant will refuse to start and emit a good error message.
* No need to check for it here.
*/
(void)ensure_config_file_exists(kP2pConfigFile);
/*
* Get a reference to the status property, so we can distinguish
* the case where it goes stopped => running => stopped (i.e.,
* it start up, but fails right away) from the case in which
* it starts in the stopped state and never manages to start
* running at all.
*/
pi = __system_property_find(kSupplicantInitProperty);
if (pi != NULL) {
serial = __system_property_serial(pi);
}
property_set("ctl.start", kSupplicantServiceName);
sched_yield();
while (count-- > 0) {
if (pi == NULL) {
pi = __system_property_find(kSupplicantInitProperty);
}
if (pi != NULL) {
/*
* property serial updated means that init process is scheduled
* after we sched_yield, further property status checking is based on this
*/
if (__system_property_serial(pi) != serial) {
__system_property_read(pi, NULL, supp_status);
if (strcmp(supp_status, "running") == 0) {
return true;
} else if (strcmp(supp_status, "stopped") == 0) {
return false;
}
}
}
usleep(100000);
}
return false;
}
關注下如下程式碼
/* Before starting the daemon, make sure its config file exists */
if (ensure_config_file_exists(kSupplicantConfigFile) < 0) {
LOG(ERROR) << "Wi-Fi will not be enabled";
return false;
}
看下對應常量宣告
const char kSupplicantInitProperty[] = "init.svc.wpa_supplicant";
const char kSupplicantConfigTemplatePath[] =
"/etc/wifi/wpa_supplicant.conf";
const char kSupplicantConfigFile[] = "/data/misc/wifi/wpa_supplicant.conf";
const char kP2pConfigFile[] = "/data/misc/wifi/p2p_supplicant.conf";
const char kSupplicantServiceName[] = "wpa_supplicant";
constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
如下是確保配置檔案存在的邏輯程式碼
- 嘗試對/data/misc/wifi/wpa_supplicant.conf其進行讀寫,如果已經有該檔案則返回,不能讀寫的話嘗試改寫讀寫許可權。
- 先看下/system/etc/wifi有沒有配置檔案,沒有的話再看下vendor/etc/wifi
- 開啟/data/misc/wifi/wpa_supplicant.conf
- 將etc/wifi下的配置檔案拷貝到/data/misc/wifi/wpa_supplicant.conf
- 修改許可權使用者組
int ensure_config_file_exists(const char* config_file) {
char buf[2048];
int srcfd, destfd;
int nread;
int ret;
std::string templatePath;
ret = access(config_file, R_OK | W_OK);
if ((ret == 0) || (errno == EACCES)) {
if ((ret != 0) && (chmod(config_file, kConfigFileMode) != 0)) {
LOG(ERROR) << "Cannot set RW to \"" << config_file << "\": "
<< strerror(errno);
return false;
}
return true;
} else if (errno != ENOENT) {
LOG(ERROR) << "Cannot access \"" << config_file << "\": "
<< strerror(errno);
return false;
}
std::string configPathSystem =
std::string("/system") + std::string(kSupplicantConfigTemplatePath);
std::string configPathVendor =
std::string("/vendor") + std::string(kSupplicantConfigTemplatePath);
srcfd = TEMP_FAILURE_RETRY(open(configPathSystem.c_str(), O_RDONLY));
templatePath = configPathSystem;
if (srcfd < 0) {
int errnoSystem = errno;
srcfd = TEMP_FAILURE_RETRY(open(configPathVendor.c_str(), O_RDONLY));
templatePath = configPathVendor;
if (srcfd < 0) {
int errnoVendor = errno;
LOG(ERROR) << "Cannot open \"" << configPathSystem << "\": "
<< strerror(errnoSystem);
LOG(ERROR) << "Cannot open \"" << configPathVendor << "\": "
<< strerror(errnoVendor);
return false;
}
}
destfd = TEMP_FAILURE_RETRY(open(config_file,
O_CREAT | O_RDWR,
kConfigFileMode));
if (destfd < 0) {
close(srcfd);
LOG(ERROR) << "Cannot create \"" << config_file << "\": "
<< strerror(errno);
return false;
}
while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) {
if (nread < 0) {
LOG(ERROR) << "Error reading \"" << templatePath
<< "\": " << strerror(errno);
close(srcfd);
close(destfd);
unlink(config_file);
return false;
}
TEMP_FAILURE_RETRY(write(destfd, buf, nread));
}
close(destfd);
close(srcfd);
/* chmod is needed because open() didn't set permisions properly */
if (chmod(config_file, kConfigFileMode) < 0) {
LOG(ERROR) << "Error changing permissions of " << config_file
<< " to 0660: " << strerror(errno);
unlink(config_file);
return false;
}
return true;
}
到這裡也梳理完了如下兩個配置檔案的由來。
- /data/misc/wifi/wpa_supplicant.conf
- /vendor/etc/wifi/wpa_supplicant.conf
3. 總結
/vendor/etc/wifi/wpa_supplicant.conf 這個配置檔案是由wpa_supplicant下的wpa_supplicant_template.conf作簡單處理生成的。
/data/misc/wifi/wpa_supplicant.conf是由/vendor/etc/wifi/wpa_supplicant.conf 拷貝過來的。