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 #define LOG_TAG "apexd"
18
19 #include <android-base/logging.h>
20 #include <android-base/properties.h>
21 #include <selinux/android.h>
22 #include <strings.h>
23 #include <sys/stat.h>
24
25 #include <memory>
26
27 #include "apex_file_repository.h"
28 #include "apexd.h"
29 #include "apexd_checkpoint_vold.h"
30 #include "apexd_lifecycle.h"
31 #include "apexd_metrics_stats.h"
32 #include "apexservice.h"
33 #include "com_android_apex_flags.h"
34
35 namespace flags = com::android::apex::flags;
36
37 namespace {
38
39 using android::base::SetDefaultTag;
40
HandleSubcommand(int argc,char ** argv)41 int HandleSubcommand(int argc, char** argv) {
42 if (strcmp("--bootstrap", argv[1]) == 0) {
43 SetDefaultTag("apexd-bootstrap");
44 return android::apex::OnBootstrap();
45 }
46
47 if (strcmp("--unmount-all", argv[1]) == 0) {
48 SetDefaultTag("apexd-unmount-all");
49 bool also_include_staged_apexes =
50 argc >= 3 && strcmp("--also-include-staged-apexes", argv[2]) == 0;
51 std::unique_ptr<android::apex::ApexSessionManager> session_manager;
52 if (also_include_staged_apexes) {
53 session_manager = android::apex::ApexSessionManager::Create(
54 android::apex::GetSessionsDir());
55 android::apex::InitializeSessionManager(session_manager.get());
56 }
57 return android::apex::UnmountAll(also_include_staged_apexes);
58 }
59
60 if (strcmp("--otachroot-bootstrap", argv[1]) == 0) {
61 SetDefaultTag("apexd-otachroot");
62 bool also_include_staged_apexes =
63 argc >= 3 && strcmp("--also-include-staged-apexes", argv[2]) == 0;
64 std::unique_ptr<android::apex::ApexSessionManager> session_manager;
65 if (also_include_staged_apexes) {
66 session_manager = android::apex::ApexSessionManager::Create(
67 android::apex::GetSessionsDir());
68 android::apex::InitializeSessionManager(session_manager.get());
69 }
70 return android::apex::OnOtaChrootBootstrap(also_include_staged_apexes);
71 }
72
73 if (strcmp("--snapshotde", argv[1]) == 0) {
74 SetDefaultTag("apexd-snapshotde");
75 // Need to know if checkpointing is enabled so that a prerestore snapshot
76 // can be taken if it's not.
77 android::base::Result<android::apex::VoldCheckpointInterface>
78 vold_service_st = android::apex::VoldCheckpointInterface::Create();
79 if (!vold_service_st.ok()) {
80 LOG(ERROR) << "Could not retrieve vold service: "
81 << vold_service_st.error();
82 } else {
83 android::apex::InitializeVold(&*vold_service_st);
84 }
85
86 // We are running regular apexd, which starts after /metadata/apex/sessions
87 // and /data/apex/sessions have been created by init. It is safe to create
88 // ApexSessionManager.
89 auto session_manager = android::apex::ApexSessionManager::Create(
90 android::apex::GetSessionsDir());
91 android::apex::InitializeSessionManager(session_manager.get());
92
93 int result = android::apex::SnapshotOrRestoreDeUserData();
94
95 if (result == 0) {
96 // Notify other components (e.g. init) that all APEXs are ready to be used
97 // Note that it's important that the binder service is registered at this
98 // point, since other system services might depend on it.
99 android::apex::OnAllPackagesReady();
100 }
101 return result;
102 }
103
104 if (strcmp("--vm", argv[1]) == 0) {
105 SetDefaultTag("apexd-vm");
106 return android::apex::OnStartInVmMode();
107 }
108
109 LOG(ERROR) << "Unknown subcommand: " << argv[1];
110 return 1;
111 }
112
InstallSigtermSignalHandler()113 void InstallSigtermSignalHandler() {
114 struct sigaction action = {};
115 action.sa_handler = [](int /*signal*/) {
116 // Handle SIGTERM gracefully.
117 // By default, when SIGTERM is received a process will exit with non-zero
118 // exit code, which will trigger reboot_on_failure handler if one is
119 // defined. This doesn't play well with userspace reboot which might
120 // terminate apexd with SIGTERM if apexd was running at the moment of
121 // userspace reboot, hence this custom handler to exit gracefully.
122 _exit(0);
123 };
124 sigaction(SIGTERM, &action, nullptr);
125 }
126
InstallSelinuxLogging()127 void InstallSelinuxLogging() {
128 union selinux_callback cb;
129 cb.func_log = selinux_log_callback;
130 selinux_set_callback(SELINUX_CB_LOG, cb);
131 }
132
133 } // namespace
134
main(int argc,char ** argv)135 int main(int argc, char** argv) {
136 android::base::InitLogging(argv, &android::base::KernelLogger);
137 // TODO(b/158468454): add a -v flag or an external setting to change severity.
138 android::base::SetMinimumLogSeverity(android::base::INFO);
139
140 // Two flags are used here:
141 // CLI flag `--enable-brand-new-apex`: used to control the feature usage in
142 // individual targets
143 // AConfig flag `enable_brand_new_apex`: used to advance
144 // the feature to different release stages, and applies to all targets
145 if (flags::enable_brand_new_apex()) {
146 if (argv[1] != nullptr && strcmp("--enable-brand-new-apex", argv[1]) == 0) {
147 android::apex::ApexFileRepository::EnableBrandNewApex();
148 argc--;
149 argv++;
150 }
151 }
152
153 const bool has_subcommand = argv[1] != nullptr;
154 LOG(INFO) << "Started. subcommand = "
155 << (has_subcommand ? argv[1] : "(null)");
156
157 // set umask to 022 so that files/dirs created are accessible to other
158 // processes e.g.) /apex/apex-info-list.xml is supposed to be read by other
159 // processes
160 umask(022);
161
162 // In some scenarios apexd needs to adjust the selinux label of the files.
163 // Install the selinux logging callback so that we can catch potential errors.
164 InstallSelinuxLogging();
165
166 InstallSigtermSignalHandler();
167
168 android::apex::SetConfig(android::apex::kDefaultConfig);
169
170 android::apex::ApexdLifecycle& lifecycle =
171 android::apex::ApexdLifecycle::GetInstance();
172 bool booting = lifecycle.IsBooting();
173
174 if (has_subcommand) {
175 return HandleSubcommand(argc, argv);
176 }
177
178 // We are running regular apexd, which starts after /metadata/apex/sessions
179 // and /data/apex/sessions have been created by init. It is safe to create
180 // ApexSessionManager.
181 auto session_manager = android::apex::ApexSessionManager::Create(
182 android::apex::GetSessionsDir());
183 android::apex::InitializeSessionManager(session_manager.get());
184
185 android::base::Result<android::apex::VoldCheckpointInterface>
186 vold_service_st = android::apex::VoldCheckpointInterface::Create();
187 android::apex::VoldCheckpointInterface* vold_service = nullptr;
188 if (!vold_service_st.ok()) {
189 LOG(ERROR) << "Could not retrieve vold service: "
190 << vold_service_st.error();
191 } else {
192 vold_service = &*vold_service_st;
193 }
194 android::apex::Initialize(vold_service);
195 android::apex::InitMetrics(std::make_unique<android::apex::StatsLog>());
196
197 if (booting) {
198 auto res = session_manager->MigrateFromOldSessionsDir(
199 android::apex::kOldApexSessionsDir);
200 if (!res.ok()) {
201 LOG(ERROR) << "Failed to migrate sessions to /metadata partition : "
202 << res.error();
203 }
204 android::apex::OnStart();
205 } else {
206 // TODO(b/172911822): Trying to use data apex related ApexFileRepository
207 // apis without initializing it should throw error. Also, unit tests should
208 // not pass without initialization.
209 // TODO(b/172911822): Consolidate this with Initialize() when
210 // ApexFileRepository can act as cache and re-scanning is not expensive
211 android::apex::InitializeDataApex();
212 }
213 // start apexservice before ApexdLifecycle::WaitForBootStatus which waits for
214 // IApexService::markBootComplete().
215 android::apex::binder::CreateAndRegisterService();
216 android::apex::binder::StartThreadPool();
217
218 if (booting) {
219 // Notify other components (e.g. init) that all APEXs are correctly mounted
220 // and activated (but are not yet ready to be used). Configuration based on
221 // activated APEXs may be performed at this point, but use of APEXs
222 // themselves should wait for the ready status instead, which is set when
223 // the "--snapshotde" subcommand is received and snapshot/restore is
224 // complete.
225 android::apex::OnAllPackagesActivated(/*is_bootstrap=*/false);
226 lifecycle.WaitForBootStatus(session_manager->HasActiveSession());
227 // Run cleanup routine on boot complete.
228 // This should run before AllowServiceShutdown() to prevent
229 // service_manager killing apexd in the middle of the cleanup.
230 android::apex::BootCompletedCleanup();
231 }
232
233 android::apex::binder::AllowServiceShutdown();
234
235 android::apex::binder::JoinThreadPool();
236 return 1;
237 }
238