xref: /aosp_15_r20/system/apex/apexd/apexd_main.cpp (revision 33f3758387333dbd2962d7edbd98681940d895da)
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