OSDN Git Service

dd1d9af88de2f3c433fcecaa5d1a96685557c479
[android-x86/frameworks-native.git] / cmds / installd / otapreopt.cpp
1 /*
2  ** Copyright 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 <algorithm>
18 #include <inttypes.h>
19 #include <random>
20 #include <selinux/android.h>
21 #include <selinux/avc.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/capability.h>
25 #include <sys/prctl.h>
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28
29 #include <android-base/logging.h>
30 #include <android-base/macros.h>
31 #include <android-base/stringprintf.h>
32 #include <cutils/fs.h>
33 #include <cutils/log.h>
34 #include <cutils/properties.h>
35 #include <private/android_filesystem_config.h>
36
37 #include <commands.h>
38 #include <globals.h>
39 #include <installd_deps.h>  // Need to fill in requirements of commands.
40 #include <string_helpers.h>
41 #include <system_properties.h>
42 #include <utils.h>
43
44 #ifndef LOG_TAG
45 #define LOG_TAG "otapreopt"
46 #endif
47
48 #define BUFFER_MAX    1024  /* input buffer for commands */
49 #define TOKEN_MAX     16    /* max number of arguments in buffer */
50 #define REPLY_MAX     256   /* largest reply allowed */
51
52 using android::base::StringPrintf;
53
54 namespace android {
55 namespace installd {
56
57 static constexpr const char* kBootClassPathPropertyName = "env.BOOTCLASSPATH";
58 static constexpr const char* kAndroidRootPathPropertyName = "env.ANDROID_ROOT";
59 static constexpr const char* kOTARootDirectory = "/system-b";
60 static constexpr size_t kISAIndex = 3;
61
62 template<typename T>
63 static constexpr T RoundDown(T x, typename std::decay<T>::type n) {
64     return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n);
65 }
66
67 template<typename T>
68 static constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) {
69     return RoundDown(x + n - 1, n);
70 }
71
72 class OTAPreoptService {
73  public:
74     static constexpr const char* kOTADataDirectory = "/data/ota";
75
76     // Main driver. Performs the following steps.
77     //
78     // 1) Parse options (read system properties etc from B partition).
79     //
80     // 2) Read in package data.
81     //
82     // 3) Prepare environment variables.
83     //
84     // 4) Prepare(compile) boot image, if necessary.
85     //
86     // 5) Run update.
87     int Main(int argc, char** argv) {
88         if (!ReadSystemProperties()) {
89             LOG(ERROR)<< "Failed reading system properties.";
90             return 1;
91         }
92
93         if (!ReadEnvironment()) {
94             LOG(ERROR) << "Failed reading environment properties.";
95             return 2;
96         }
97
98         if (!ReadPackage(argc, argv)) {
99             LOG(ERROR) << "Failed reading command line file.";
100             return 3;
101         }
102
103         PrepareEnvironment();
104
105         if (!PrepareBootImage()) {
106             LOG(ERROR) << "Failed preparing boot image.";
107             return 4;
108         }
109
110         int dexopt_retcode = RunPreopt();
111
112         return dexopt_retcode;
113     }
114
115     int GetProperty(const char* key, char* value, const char* default_value) {
116         const std::string* prop_value = system_properties_.GetProperty(key);
117         if (prop_value == nullptr) {
118             if (default_value == nullptr) {
119                 return 0;
120             }
121             // Copy in the default value.
122             strncpy(value, default_value, kPropertyValueMax - 1);
123             value[kPropertyValueMax - 1] = 0;
124             return strlen(default_value);// TODO: Need to truncate?
125         }
126         size_t size = std::min(kPropertyValueMax - 1, prop_value->length());
127         strncpy(value, prop_value->data(), size);
128         value[size] = 0;
129         return static_cast<int>(size);
130     }
131
132 private:
133     bool ReadSystemProperties() {
134         // TODO(agampe): What to do about the things in default.prop? It's only heap sizes, so it's easy
135         //               to emulate for now, but has issues (e.g., vendors modifying the boot classpath
136         //               may require larger values here - revisit). That's why this goes first, so that
137         //               if those dummy values are overridden in build.prop, that's what we'll get.
138         //
139         // Note: It seems we'll get access to the B root partition, so we should read the default.prop
140         //       file.
141         // if (!system_properties_.Load(b_mount_path_ + "/default.prop") {
142         //   return false;
143         // }
144         system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xms", "64m");
145         system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xmx", "64m");
146         system_properties_.SetProperty("dalvik.vm.dex2oat-Xms", "64m");
147         system_properties_.SetProperty("dalvik.vm.dex2oat-Xmx", "512m");
148
149         // TODO(agampe): Do this properly/test.
150         return system_properties_.Load(b_mount_path_ + "/system/build.prop");
151     }
152
153     bool ReadEnvironment() {
154         // Read important environment variables. For simplicity, store them as
155         // system properties.
156         // TODO(agampe): We'll have to parse init.environ.rc for BOOTCLASSPATH.
157         //               For now, just the A version.
158         const char* boot_classpath = getenv("BOOTCLASSPATH");
159         if (boot_classpath == nullptr) {
160             return false;
161         }
162         system_properties_.SetProperty(kBootClassPathPropertyName, boot_classpath);
163
164         const char* root_path = getenv("ANDROID_ROOT");
165         if (root_path == nullptr) {
166             return false;
167         }
168         system_properties_.SetProperty(kAndroidRootPathPropertyName, b_mount_path_ + root_path);
169
170         return true;
171     }
172
173     bool ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) {
174         size_t index = 0;
175         while (index < ARRAY_SIZE(package_parameters_) &&
176                 argv[index + 1] != nullptr) {
177             package_parameters_[index] = argv[index + 1];
178             index++;
179         }
180         if (index != ARRAY_SIZE(package_parameters_)) {
181             LOG(ERROR) << "Wrong number of parameters";
182             return false;
183         }
184
185         return true;
186     }
187
188     void PrepareEnvironment() {
189         CHECK(system_properties_.GetProperty(kBootClassPathPropertyName) != nullptr);
190         const std::string& boot_cp =
191                 *system_properties_.GetProperty(kBootClassPathPropertyName);
192         environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_cp.c_str()));
193         environ_.push_back(StringPrintf("ANDROID_DATA=%s", kOTADataDirectory));
194         CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr);
195         const std::string& android_root =
196                 *system_properties_.GetProperty(kAndroidRootPathPropertyName);
197         environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root.c_str()));
198
199         for (const std::string& e : environ_) {
200             putenv(const_cast<char*>(e.c_str()));
201         }
202     }
203
204     // Ensure that we have the right boot image. The first time any app is
205     // compiled, we'll try to generate it.
206     bool PrepareBootImage() {
207         if (package_parameters_[kISAIndex] == nullptr) {
208             LOG(ERROR) << "Instruction set missing.";
209             return false;
210         }
211         const char* isa = package_parameters_[kISAIndex];
212
213         // Check whether the file exists where expected.
214         std::string dalvik_cache = std::string(kOTADataDirectory) + "/" + DALVIK_CACHE;
215         std::string isa_path = dalvik_cache + "/" + isa;
216         std::string art_path = isa_path + "/system@framework@boot.art";
217         std::string oat_path = isa_path + "/system@framework@boot.oat";
218         if (access(art_path.c_str(), F_OK) == 0 &&
219                 access(oat_path.c_str(), F_OK) == 0) {
220             // Files exist, assume everything is alright.
221             return true;
222         }
223
224         // Create the directories, if necessary.
225         if (access(dalvik_cache.c_str(), F_OK) != 0) {
226             if (mkdir(dalvik_cache.c_str(), 0711) != 0) {
227                 PLOG(ERROR) << "Could not create dalvik-cache dir";
228                 return false;
229             }
230         }
231         if (access(isa_path.c_str(), F_OK) != 0) {
232             if (mkdir(isa_path.c_str(), 0711) != 0) {
233                 PLOG(ERROR) << "Could not create dalvik-cache isa dir";
234                 return false;
235             }
236         }
237
238         // Prepare to create.
239         // TODO: Delete files, just for a blank slate.
240         const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName);
241
242         std::string preopted_boot_art_path = StringPrintf("%s/system/framework/%s/boot.art",
243                                                           b_mount_path_.c_str(),
244                                                           isa);
245         if (access(preopted_boot_art_path.c_str(), F_OK) == 0) {
246           return PatchoatBootImage(art_path, isa);
247         } else {
248           // No preopted boot image. Try to compile.
249           return Dex2oatBootImage(boot_cp, art_path, oat_path, isa);
250         }
251     }
252
253     bool PatchoatBootImage(const std::string& art_path, const char* isa) {
254         // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
255
256         std::vector<std::string> cmd;
257         cmd.push_back(b_mount_path_ + "/system/bin/patchoat");
258
259         cmd.push_back("--input-image-location=/system/framework/boot.art");
260         cmd.push_back(StringPrintf("--output-image-file=%s", art_path.c_str()));
261
262         cmd.push_back(StringPrintf("--instruction-set=%s", isa));
263
264         int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
265                                                           ART_BASE_ADDRESS_MAX_DELTA);
266         cmd.push_back(StringPrintf("--base-offset-delta=0x%x", ART_BASE_ADDRESS + base_offset));
267
268         std::string error_msg;
269         bool result = Exec(cmd, &error_msg);
270         if (!result) {
271             LOG(ERROR) << "Could not generate boot image: " << error_msg;
272         }
273         return result;
274     }
275
276     bool Dex2oatBootImage(const std::string& boot_cp,
277                           const std::string& art_path,
278                           const std::string& oat_path,
279                           const char* isa) {
280         // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
281         std::vector<std::string> cmd;
282         cmd.push_back(b_mount_path_ + "/system/bin/dex2oat");
283         cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
284         for (const std::string& boot_part : Split(boot_cp, ':')) {
285             cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
286         }
287         cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str()));
288
289         int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
290                 ART_BASE_ADDRESS_MAX_DELTA);
291         cmd.push_back(StringPrintf("--base=0x%x", ART_BASE_ADDRESS + base_offset));
292
293         cmd.push_back(StringPrintf("--instruction-set=%s", isa));
294
295         // These things are pushed by AndroidRuntime, see frameworks/base/core/jni/AndroidRuntime.cpp.
296         AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xms",
297                 "-Xms",
298                 true,
299                 cmd);
300         AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xmx",
301                 "-Xmx",
302                 true,
303                 cmd);
304         AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-filter",
305                 "--compiler-filter=",
306                 false,
307                 cmd);
308         cmd.push_back(StringPrintf("--image-classes=%s/system/etc/preloaded-classes",
309                                    b_mount_path_.c_str()));
310         // TODO: Compiled-classes.
311         const std::string* extra_opts =
312                 system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags");
313         if (extra_opts != nullptr) {
314             std::vector<std::string> extra_vals = Split(*extra_opts, ' ');
315             cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end());
316         }
317         // TODO: Should we lower this? It's usually set close to max, because
318         //       normally there's not much else going on at boot.
319         AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-threads",
320                 "-j",
321                 false,
322                 cmd);
323         AddCompilerOptionFromSystemProperty(
324                 StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(),
325                 "--instruction-set-variant=",
326                 false,
327                 cmd);
328         AddCompilerOptionFromSystemProperty(
329                 StringPrintf("dalvik.vm.isa.%s.features", isa).c_str(),
330                 "--instruction-set-features=",
331                 false,
332                 cmd);
333
334         std::string error_msg;
335         bool result = Exec(cmd, &error_msg);
336         if (!result) {
337             LOG(ERROR) << "Could not generate boot image: " << error_msg;
338         }
339         return result;
340     }
341
342     static const char* ParseNull(const char* arg) {
343         return (strcmp(arg, "!") == 0) ? nullptr : arg;
344     }
345
346     int RunPreopt() {
347         /* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags,
348            volume_uuid, use_profiles */
349         int ret = dexopt(package_parameters_[0],
350                 atoi(package_parameters_[1]),
351                 package_parameters_[2],
352                 package_parameters_[3],
353                 atoi(package_parameters_[4]),
354                 package_parameters_[5],
355                 atoi(package_parameters_[6]),
356                 ParseNull(package_parameters_[7]),
357                 (atoi(package_parameters_[8]) == 0 ? false : true));
358         return ret;
359     }
360
361     ////////////////////////////////////
362     // Helpers, mostly taken from ART //
363     ////////////////////////////////////
364
365     // Wrapper on fork/execv to run a command in a subprocess.
366     bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
367         const std::string command_line(Join(arg_vector, ' '));
368
369         CHECK_GE(arg_vector.size(), 1U) << command_line;
370
371         // Convert the args to char pointers.
372         const char* program = arg_vector[0].c_str();
373         std::vector<char*> args;
374         for (size_t i = 0; i < arg_vector.size(); ++i) {
375             const std::string& arg = arg_vector[i];
376             char* arg_str = const_cast<char*>(arg.c_str());
377             CHECK(arg_str != nullptr) << i;
378             args.push_back(arg_str);
379         }
380         args.push_back(nullptr);
381
382         // Fork and exec.
383         pid_t pid = fork();
384         if (pid == 0) {
385             // No allocation allowed between fork and exec.
386
387             // Change process groups, so we don't get reaped by ProcessManager.
388             setpgid(0, 0);
389
390             execv(program, &args[0]);
391
392             PLOG(ERROR) << "Failed to execv(" << command_line << ")";
393             // _exit to avoid atexit handlers in child.
394             _exit(1);
395         } else {
396             if (pid == -1) {
397                 *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
398                         command_line.c_str(), strerror(errno));
399                 return false;
400             }
401
402             // wait for subprocess to finish
403             int status;
404             pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
405             if (got_pid != pid) {
406                 *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
407                         "wanted %d, got %d: %s",
408                         command_line.c_str(), pid, got_pid, strerror(errno));
409                 return false;
410             }
411             if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
412                 *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
413                         command_line.c_str());
414                 return false;
415             }
416         }
417         return true;
418     }
419
420     // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc.
421     static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
422         constexpr size_t kPageSize = PAGE_SIZE;
423         CHECK_EQ(min_delta % kPageSize, 0u);
424         CHECK_EQ(max_delta % kPageSize, 0u);
425         CHECK_LT(min_delta, max_delta);
426
427         std::default_random_engine generator;
428         generator.seed(GetSeed());
429         std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta);
430         int32_t r = distribution(generator);
431         if (r % 2 == 0) {
432             r = RoundUp(r, kPageSize);
433         } else {
434             r = RoundDown(r, kPageSize);
435         }
436         CHECK_LE(min_delta, r);
437         CHECK_GE(max_delta, r);
438         CHECK_EQ(r % kPageSize, 0u);
439         return r;
440     }
441
442     static uint64_t GetSeed() {
443 #ifdef __BIONIC__
444         // Bionic exposes arc4random, use it.
445         uint64_t random_data;
446         arc4random_buf(&random_data, sizeof(random_data));
447         return random_data;
448 #else
449 #error "This is only supposed to run with bionic. Otherwise, implement..."
450 #endif
451     }
452
453     void AddCompilerOptionFromSystemProperty(const char* system_property,
454             const char* prefix,
455             bool runtime,
456             std::vector<std::string>& out) {
457         const std::string* value =
458         system_properties_.GetProperty(system_property);
459         if (value != nullptr) {
460             if (runtime) {
461                 out.push_back("--runtime-arg");
462             }
463             if (prefix != nullptr) {
464                 out.push_back(StringPrintf("%s%s", prefix, value->c_str()));
465             } else {
466                 out.push_back(*value);
467             }
468         }
469     }
470
471     // The path where the B partitions are mounted.
472     // TODO(agampe): If we're running this *inside* the change-root, we wouldn't need this.
473     std::string b_mount_path_;
474
475     // Stores the system properties read out of the B partition. We need to use these properties
476     // to compile, instead of the A properties we could get from init/get_property.
477     SystemProperties system_properties_;
478
479     const char* package_parameters_[9];
480
481     // Store environment values we need to set.
482     std::vector<std::string> environ_;
483 };
484
485 OTAPreoptService gOps;
486
487 ////////////////////////
488 // Plug-in functions. //
489 ////////////////////////
490
491 int get_property(const char *key, char *value, const char *default_value) {
492     // TODO: Replace with system-properties map.
493     return gOps.GetProperty(key, value, default_value);
494 }
495
496 // Compute the output path of
497 bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir,
498                              const char *apk_path,
499                              const char *instruction_set) {
500     // TODO: Insert B directory.
501     char *file_name_start;
502     char *file_name_end;
503
504     file_name_start = strrchr(apk_path, '/');
505     if (file_name_start == nullptr) {
506         ALOGE("apk_path '%s' has no '/'s in it\n", apk_path);
507         return false;
508     }
509     file_name_end = strrchr(file_name_start, '.');
510     if (file_name_end == nullptr) {
511         ALOGE("apk_path '%s' has no extension\n", apk_path);
512         return false;
513     }
514
515     // Calculate file_name
516     file_name_start++;  // Move past '/', is valid as file_name_end is valid.
517     size_t file_name_len = file_name_end - file_name_start;
518     std::string file_name(file_name_start, file_name_len);
519
520     // <apk_parent_dir>/oat/<isa>/<file_name>.odex.b
521     snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex.b", oat_dir, instruction_set,
522              file_name.c_str());
523     return true;
524 }
525
526 /*
527  * Computes the odex file for the given apk_path and instruction_set.
528  * /system/framework/whatever.jar -> /system/framework/oat/<isa>/whatever.odex
529  *
530  * Returns false if it failed to determine the odex file path.
531  */
532 bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path,
533                               const char *instruction_set) {
534     if (StringPrintf("%soat/%s/odex.b", apk_path, instruction_set).length() + 1 > PKG_PATH_MAX) {
535         ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path);
536         return false;
537     }
538
539     const char *path_end = strrchr(apk_path, '/');
540     if (path_end == nullptr) {
541         ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
542         return false;
543     }
544     std::string path_component(apk_path, path_end - apk_path);
545
546     const char *name_begin = path_end + 1;
547     const char *extension_start = strrchr(name_begin, '.');
548     if (extension_start == nullptr) {
549         ALOGE("apk_path '%s' has no extension.\n", apk_path);
550         return false;
551     }
552     std::string name_component(name_begin, extension_start - name_begin);
553
554     std::string new_path = StringPrintf("%s/oat/%s/%s.odex.b",
555                                         path_component.c_str(),
556                                         instruction_set,
557                                         name_component.c_str());
558     CHECK_LT(new_path.length(), PKG_PATH_MAX);
559     strcpy(path, new_path.c_str());
560     return true;
561 }
562
563 bool create_cache_path(char path[PKG_PATH_MAX],
564                        const char *src,
565                        const char *instruction_set) {
566     size_t srclen = strlen(src);
567
568         /* demand that we are an absolute path */
569     if ((src == 0) || (src[0] != '/') || strstr(src,"..")) {
570         return false;
571     }
572
573     if (srclen > PKG_PATH_MAX) {        // XXX: PKG_NAME_MAX?
574         return false;
575     }
576
577     std::string from_src = std::string(src + 1);
578     std::replace(from_src.begin(), from_src.end(), '/', '@');
579
580     std::string assembled_path = StringPrintf("%s/%s/%s/%s%s",
581                                               OTAPreoptService::kOTADataDirectory,
582                                               DALVIK_CACHE,
583                                               instruction_set,
584                                               from_src.c_str(),
585                                               DALVIK_CACHE_POSTFIX2);
586
587     if (assembled_path.length() + 1 > PKG_PATH_MAX) {
588         return false;
589     }
590     strcpy(path, assembled_path.c_str());
591
592     return true;
593 }
594
595 bool initialize_globals() {
596     const char* data_path = getenv("ANDROID_DATA");
597     if (data_path == nullptr) {
598         ALOGE("Could not find ANDROID_DATA");
599         return false;
600     }
601     return init_globals_from_data_and_root(data_path, kOTARootDirectory);
602 }
603
604 static bool initialize_directories() {
605     // This is different from the normal installd. We only do the base
606     // directory, the rest will be created on demand when each app is compiled.
607     mode_t old_umask = umask(0);
608     LOG(INFO) << "Old umask: " << old_umask;
609     if (access(OTAPreoptService::kOTADataDirectory, R_OK) < 0) {
610         ALOGE("Could not access %s\n", OTAPreoptService::kOTADataDirectory);
611         return false;
612     }
613     return true;
614 }
615
616 static int log_callback(int type, const char *fmt, ...) {
617     va_list ap;
618     int priority;
619
620     switch (type) {
621         case SELINUX_WARNING:
622             priority = ANDROID_LOG_WARN;
623             break;
624         case SELINUX_INFO:
625             priority = ANDROID_LOG_INFO;
626             break;
627         default:
628             priority = ANDROID_LOG_ERROR;
629             break;
630     }
631     va_start(ap, fmt);
632     LOG_PRI_VA(priority, "SELinux", fmt, ap);
633     va_end(ap);
634     return 0;
635 }
636
637 static int otapreopt_main(const int argc, char *argv[]) {
638     int selinux_enabled = (is_selinux_enabled() > 0);
639
640     setenv("ANDROID_LOG_TAGS", "*:v", 1);
641     android::base::InitLogging(argv);
642
643     ALOGI("otapreopt firing up\n");
644
645     if (argc < 2) {
646         ALOGE("Expecting parameters");
647         exit(1);
648     }
649
650     union selinux_callback cb;
651     cb.func_log = log_callback;
652     selinux_set_callback(SELINUX_CB_LOG, cb);
653
654     if (!initialize_globals()) {
655         ALOGE("Could not initialize globals; exiting.\n");
656         exit(1);
657     }
658
659     if (!initialize_directories()) {
660         ALOGE("Could not create directories; exiting.\n");
661         exit(1);
662     }
663
664     if (selinux_enabled && selinux_status_open(true) < 0) {
665         ALOGE("Could not open selinux status; exiting.\n");
666         exit(1);
667     }
668
669     int ret = android::installd::gOps.Main(argc, argv);
670
671     return ret;
672 }
673
674 }  // namespace installd
675 }  // namespace android
676
677 int main(const int argc, char *argv[]) {
678     return android::installd::otapreopt_main(argc, argv);
679 }