1 /*
2 * Copyright (C) 2018 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 "server_configurable_flags/disaster_recovery.h"
18 #include "server_configurable_flags/get_flags.h"
19
20 #if defined(__BIONIC__)
21 #include <cutils/properties.h>
22 #endif // __BIONIC__
23 #include <cctype>
24 #include <cstring>
25 #include <string>
26
27 #include "android-base/file.h"
28 #include "android-base/logging.h"
29 #include "android-base/properties.h"
30 #include "android-base/strings.h"
31 #include "android-base/unique_fd.h"
32
33 #define SYSTEM_PROPERTY_PREFIX "persist.device_config."
34
35 #define ATTEMPTED_BOOT_COUNT_PROPERTY "persist.device_config.attempted_boot_count"
36
37 #define RESET_PERFORMED_PROPERTY "device_config.reset_performed"
38
39 #define RESET_FLAGS_FILE_PATH "/data/server_configurable_flags/reset_flags"
40
41 #define ATTEMPTED_BOOT_COUNT_THRESHOLD 4
42
43 namespace server_configurable_flags {
44
MakeSystemPropertyName(const std::string & experiment_category_name,const std::string & experiment_flag_name)45 static std::string MakeSystemPropertyName(const std::string& experiment_category_name,
46 const std::string& experiment_flag_name) {
47 return SYSTEM_PROPERTY_PREFIX + experiment_category_name + "." + experiment_flag_name;
48 }
49
ValidateCharacters(const std::string & segment)50 static bool ValidateCharacters(const std::string& segment) {
51 for (char c : segment) {
52 if (!isalnum(c) && !strchr(":@_.-", c)) {
53 return false;
54 }
55 }
56 return true;
57 }
58
ValidateExperimentSegment(const std::string & segment)59 static bool ValidateExperimentSegment(const std::string& segment) {
60 return ValidateCharacters(segment) && !segment.empty() && segment[0] != '.' &&
61 *segment.rbegin() != '.';
62 }
63
64 #if defined(__BIONIC__)
ResetFlag(const char * key,const char * value,void * cookie)65 static void ResetFlag(const char* key, const char* value, void* cookie) {
66 if (strcmp(ATTEMPTED_BOOT_COUNT_PROPERTY, key) &&
67 android::base::StartsWith(key, SYSTEM_PROPERTY_PREFIX) && strlen(value) > 0 &&
68 android::base::SetProperty(key, "")) {
69 std::string* reset_flags = static_cast<std::string*>(cookie);
70 if (reset_flags->length() > 0) {
71 reset_flags->append(";");
72 }
73 reset_flags->append(key);
74 android::base::SetProperty(RESET_PERFORMED_PROPERTY, "true");
75 }
76 }
77
78 // Reset all system properties used as flags into empty value,
79 // and record reset flags' names in /data/server_configurable_flags/reset_flags
ResetAllFlags()80 static void ResetAllFlags() {
81 std::string reset_flags;
82 property_list(ResetFlag, &reset_flags);
83
84 if (reset_flags.length() > 0) {
85 android::base::unique_fd fd(
86 TEMP_FAILURE_RETRY(open(RESET_FLAGS_FILE_PATH, O_RDWR | O_CREAT | O_TRUNC, 0666)));
87 if (fd == -1) {
88 LOG(INFO) << __FUNCTION__ << " failed to open file " << RESET_FLAGS_FILE_PATH;
89 } else if (!WriteStringToFd(reset_flags, fd)) {
90 LOG(INFO) << __FUNCTION__ << " failed to write file " << RESET_FLAGS_FILE_PATH;
91 } else {
92 LOG(INFO) << __FUNCTION__ << " successfully write to file " << RESET_FLAGS_FILE_PATH;
93 }
94 }
95 }
96 #endif // __BIONIC__
97
ServerConfigurableFlagsReset(ResetMode reset_mode)98 void ServerConfigurableFlagsReset(ResetMode reset_mode) {
99 LOG(INFO) << __FUNCTION__ << " reset_mode value: " << reset_mode;
100 #if defined(__BIONIC__)
101 if (reset_mode == BOOT_FAILURE) {
102 int fail_count = android::base::GetIntProperty(ATTEMPTED_BOOT_COUNT_PROPERTY, 0);
103 if (fail_count < ATTEMPTED_BOOT_COUNT_THRESHOLD) {
104 LOG(INFO) << __FUNCTION__ << " attempted boot count is under threshold, skipping reset.";
105
106 // ATTEMPTED_BOOT_COUNT_PROPERTY will be reset to 0 when sys.boot_completed is set to 1.
107 // The code lives in flags_health_check.rc.
108 android::base::SetProperty(ATTEMPTED_BOOT_COUNT_PROPERTY, std::to_string(fail_count + 1));
109 } else {
110 LOG(INFO) << __FUNCTION__ << " attempted boot count reaches threshold, resetting flags.";
111 ResetAllFlags();
112 }
113 } else if (reset_mode == UPDATABLE_CRASHING) {
114 LOG(INFO) << __FUNCTION__ << " updatable crashing detected, resetting flags.";
115 ResetAllFlags();
116 } else {
117 LOG(ERROR) << __FUNCTION__ << " invalid reset_mode, skipping reset.";
118 }
119 #else
120 LOG(ERROR) << __FUNCTION__ << " ServerConfigurableFlagsReset is not available for this build.";
121 #endif // __BIONIC__
122 }
123
GetServerConfigurableFlag(const std::string & experiment_category_name,const std::string & experiment_flag_name,const std::string & default_value)124 std::string GetServerConfigurableFlag(const std::string& experiment_category_name,
125 const std::string& experiment_flag_name,
126 const std::string& default_value) {
127 if (!ValidateExperimentSegment(experiment_category_name)) {
128 LOG(ERROR) << __FUNCTION__ << " invalid category name " << experiment_category_name;
129 return default_value;
130 }
131 if (!ValidateExperimentSegment(experiment_flag_name)) {
132 LOG(ERROR) << __FUNCTION__ << " invalid flag name " << experiment_flag_name;
133 return default_value;
134 }
135 return android::base::GetProperty(
136 MakeSystemPropertyName(experiment_category_name, experiment_flag_name), default_value);
137 }
138
139 } // namespace server_configurable_flags
140