2008-10-21 16:00:00 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2015-08-26 20:43:36 +02:00
|
|
|
#include "builtins.h"
|
|
|
|
|
2019-05-22 02:48:33 +02:00
|
|
|
#include <android/api-level.h>
|
2016-04-05 22:43:40 +02:00
|
|
|
#include <dirent.h>
|
2015-03-20 17:45:18 +01:00
|
|
|
#include <errno.h>
|
2008-10-21 16:00:00 +02:00
|
|
|
#include <fcntl.h>
|
2017-08-04 22:22:36 +02:00
|
|
|
#include <fts.h>
|
2018-11-08 09:14:35 +01:00
|
|
|
#include <glob.h>
|
2017-04-07 01:30:22 +02:00
|
|
|
#include <linux/loop.h>
|
|
|
|
#include <linux/module.h>
|
2015-07-08 23:57:07 +02:00
|
|
|
#include <mntent.h>
|
2015-03-20 17:45:18 +01:00
|
|
|
#include <net/if.h>
|
2016-04-05 17:10:25 +02:00
|
|
|
#include <sched.h>
|
2017-04-07 01:30:22 +02:00
|
|
|
#include <signal.h>
|
2008-10-21 16:00:00 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2015-03-20 17:45:18 +01:00
|
|
|
#include <string.h>
|
2008-10-21 16:00:00 +02:00
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/resource.h>
|
2017-04-07 01:30:22 +02:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/stat.h>
|
2016-03-28 01:55:59 +02:00
|
|
|
#include <sys/syscall.h>
|
2017-04-07 22:46:21 +02:00
|
|
|
#include <sys/system_properties.h>
|
2015-01-30 06:31:23 +01:00
|
|
|
#include <sys/time.h>
|
2015-03-20 17:45:18 +01:00
|
|
|
#include <sys/types.h>
|
2012-04-18 02:20:16 +02:00
|
|
|
#include <sys/wait.h>
|
2015-03-20 17:45:18 +01:00
|
|
|
#include <unistd.h>
|
2012-01-13 14:48:47 +01:00
|
|
|
|
2019-07-16 13:15:48 +02:00
|
|
|
#include <ApexProperties.sysprop.h>
|
2017-07-06 23:20:11 +02:00
|
|
|
#include <android-base/chrono_utils.h>
|
2016-04-05 22:43:40 +02:00
|
|
|
#include <android-base/file.h>
|
2017-04-07 01:30:22 +02:00
|
|
|
#include <android-base/logging.h>
|
2015-12-18 20:39:59 +01:00
|
|
|
#include <android-base/parseint.h>
|
2017-03-29 01:40:41 +02:00
|
|
|
#include <android-base/properties.h>
|
2017-08-01 23:10:11 +02:00
|
|
|
#include <android-base/stringprintf.h>
|
2017-03-29 01:40:41 +02:00
|
|
|
#include <android-base/strings.h>
|
2017-08-01 23:10:11 +02:00
|
|
|
#include <android-base/unique_fd.h>
|
2016-06-25 03:28:03 +02:00
|
|
|
#include <bootloader_message/bootloader_message.h>
|
2015-03-20 17:45:18 +01:00
|
|
|
#include <cutils/android_reboot.h>
|
2017-03-29 01:40:41 +02:00
|
|
|
#include <fs_mgr.h>
|
2018-10-23 22:10:33 +02:00
|
|
|
#include <fscrypt/fscrypt.h>
|
|
|
|
#include <fscrypt/fscrypt_init_extensions.h>
|
2019-01-04 03:16:56 +01:00
|
|
|
#include <libgsi/libgsi.h>
|
2017-04-07 01:30:22 +02:00
|
|
|
#include <selinux/android.h>
|
|
|
|
#include <selinux/label.h>
|
|
|
|
#include <selinux/selinux.h>
|
2017-08-16 20:34:50 +02:00
|
|
|
#include <system/thread_defs.h>
|
2015-03-20 17:45:18 +01:00
|
|
|
|
2018-02-14 00:36:14 +01:00
|
|
|
#include "action_manager.h"
|
2015-08-26 20:43:36 +02:00
|
|
|
#include "bootchart.h"
|
2008-10-21 16:00:00 +02:00
|
|
|
#include "init.h"
|
Proper mount namespace configuration for bionic
This CL fixes the design problem of the previous mechanism for providing
the bootstrap bionic and the runtime bionic to the same path.
Previously, bootstrap bionic was self-bind-mounted; i.e.
/system/bin/libc.so is bind-mounted to itself. And the runtime bionic
was bind-mounted on top of the bootstrap bionic. This has not only caused
problems like `adb sync` not working(b/122737045), but also is quite
difficult to understand due to the double-and-self mounting.
This is the new design:
Most importantly, these four are all distinct:
1) bootstrap bionic (/system/lib/bootstrap/libc.so)
2) runtime bionic (/apex/com.android.runtime/lib/bionic/libc.so)
3) mount point for 1) and 2) (/bionic/lib/libc.so)
4) symlink for 3) (/system/lib/libc.so -> /bionic/lib/libc.so)
Inside the mount namespace of the pre-apexd processes, 1) is
bind-mounted to 3). Likewise, inside the mount namespace of the
post-apexd processes, 2) is bind-mounted to 3). In other words, there is
no self-mount, and no double-mount.
Another change is that mount points are under /bionic and the legacy
paths become symlinks to the mount points. This is to make sure that
there is no bind mounts under /system, which is breaking some apps.
Finally, code for creating mount namespaces, mounting bionic, etc are
refactored to mount_namespace.cpp
Bug: 120266448
Bug: 123275379
Test: m, device boots, adb sync/push/pull works,
especially with following paths:
/bionic/lib64/libc.so
/bionic/bin/linker64
/system/lib64/bootstrap/libc.so
/system/bin/bootstrap/linker64
Change-Id: Icdfbdcc1efca540ac854d4df79e07ee61fca559f
2019-01-16 15:00:59 +01:00
|
|
|
#include "mount_namespace.h"
|
2017-07-27 21:54:48 +02:00
|
|
|
#include "parser.h"
|
2008-10-21 16:00:00 +02:00
|
|
|
#include "property_service.h"
|
2017-03-13 19:54:47 +01:00
|
|
|
#include "reboot.h"
|
2017-08-25 19:39:25 +02:00
|
|
|
#include "rlimit_parser.h"
|
2019-05-29 00:58:35 +02:00
|
|
|
#include "selabel.h"
|
2017-11-27 23:45:26 +01:00
|
|
|
#include "selinux.h"
|
2015-07-31 21:45:25 +02:00
|
|
|
#include "service.h"
|
2019-06-26 19:46:20 +02:00
|
|
|
#include "service_list.h"
|
2017-09-13 00:58:47 +02:00
|
|
|
#include "subcontext.h"
|
2010-04-14 05:35:46 +02:00
|
|
|
#include "util.h"
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2016-12-19 22:03:47 +01:00
|
|
|
using namespace std::literals::string_literals;
|
|
|
|
|
2019-01-07 23:25:31 +01:00
|
|
|
using android::base::Basename;
|
2019-04-23 02:46:37 +02:00
|
|
|
using android::base::StartsWith;
|
2017-08-01 23:10:11 +02:00
|
|
|
using android::base::unique_fd;
|
2019-01-30 22:25:35 +01:00
|
|
|
using android::fs_mgr::Fstab;
|
|
|
|
using android::fs_mgr::ReadFstabFromFile;
|
2017-08-01 23:10:11 +02:00
|
|
|
|
builtins.c: Don't require file open() for chmod/chown
42a9349dc4e98019d27d7f8d19bc6c431695d7e1 modified init's
builtin chmod, chown, and mkdir calls to avoid following
symlinks. This addressed a number of attacks we were seeing
at the time where poorly written init scripts were following
attacker supplied symlinks resulting in rooting vulnerabilities.
To avoid race conditions, the previous implementation only ran
fchown / fchmod on file descriptors opened with open(O_NOFOLLOW).
Unfortunately, unlike the normal "chown" or "chmod" calls, this
requires read or write access to the underlying file. This
isn't ideal, as opening some files may have side effects, or
init may not have permission to open certain files (such as when
SELinux is enabled).
Instead of using open(O_NOFOLLOW) + fchown(), use lchown() instead.
As before, the target of the symlink won't be modified by chown.
This also supports setting the ownership of symlinks.
Instead of using open(O_NOFOLLOW) + fchmod(), use
fchmodat(AT_SYMLINK_NOFOLLOW) instead. As before, the target of the
symlink won't be modified by chmod.
This change will continue to ensure that chown/chmod/mkdir doesn't
follow symlinks, without requiring init to open every file in
read-only or read-write mode.
This change depends on bionic commit I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
Addresses the following mako/occam SELinux denial:
audit(1422770408.951:6): avc: denied { write } for pid=1 comm="init" name="smd7" dev="tmpfs" ino=7207 scontext=u:r:init:s0 tcontext=u:object_r:radio_device:s0 tclass=chr_file
Change-Id: I14fde956784d65c44e7aa91dd7eea9a004df3081
2015-02-01 06:39:46 +01:00
|
|
|
#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
|
|
|
|
|
2017-06-22 21:53:17 +02:00
|
|
|
namespace android {
|
|
|
|
namespace init {
|
|
|
|
|
2019-07-30 19:51:59 +02:00
|
|
|
// There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
|
|
|
|
// device, such as '/sys/class/leds/jogball-backlight/brightness'. As of this writing, there
|
|
|
|
// are 81 such failures on cuttlefish. Instead of spamming the log reporting them, we do not
|
|
|
|
// report such failures unless we're running at the DEBUG log level.
|
|
|
|
class ErrorIgnoreEnoent {
|
|
|
|
public:
|
|
|
|
ErrorIgnoreEnoent()
|
|
|
|
: ignore_error_(errno == ENOENT &&
|
|
|
|
android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
|
|
|
|
explicit ErrorIgnoreEnoent(int errno_to_append)
|
|
|
|
: error_(errno_to_append),
|
|
|
|
ignore_error_(errno_to_append == ENOENT &&
|
|
|
|
android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
operator android::base::expected<T, ResultError>() {
|
|
|
|
if (ignore_error_) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return error_;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
ErrorIgnoreEnoent& operator<<(T&& t) {
|
|
|
|
error_ << t;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Error error_;
|
|
|
|
bool ignore_error_;
|
|
|
|
};
|
|
|
|
|
|
|
|
inline ErrorIgnoreEnoent ErrnoErrorIgnoreEnoent() {
|
|
|
|
return ErrorIgnoreEnoent(errno);
|
|
|
|
}
|
|
|
|
|
2019-06-26 23:44:37 +02:00
|
|
|
std::vector<std::string> late_import_paths;
|
|
|
|
|
2016-11-11 02:43:47 +01:00
|
|
|
static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
|
2015-12-18 20:39:59 +01:00
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> reboot_into_recovery(const std::vector<std::string>& options) {
|
2018-04-13 19:38:57 +02:00
|
|
|
LOG(ERROR) << "Rebooting into recovery";
|
2016-05-10 17:52:06 +02:00
|
|
|
std::string err;
|
|
|
|
if (!write_bootloader_message(options, &err)) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "Failed to set bootloader message: " << err;
|
2016-05-10 17:52:06 +02:00
|
|
|
}
|
2017-06-28 07:08:45 +02:00
|
|
|
property_set("sys.powerctl", "reboot,recovery");
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2015-07-08 23:57:07 +02:00
|
|
|
}
|
|
|
|
|
2017-07-28 01:20:58 +02:00
|
|
|
template <typename F>
|
|
|
|
static void ForEachServiceInClass(const std::string& classname, F function) {
|
|
|
|
for (const auto& service : ServiceList::GetInstance()) {
|
|
|
|
if (service->classnames().count(classname)) std::invoke(function, service);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_class_start(const BuiltinArguments& args) {
|
2018-11-28 07:08:02 +01:00
|
|
|
// Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
|
2019-05-15 22:04:13 +02:00
|
|
|
if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2017-07-28 01:20:58 +02:00
|
|
|
// Starting a class does not start services which are explicitly disabled.
|
|
|
|
// They must be started individually.
|
2017-10-03 22:15:03 +02:00
|
|
|
for (const auto& service : ServiceList::GetInstance()) {
|
2019-05-15 22:04:13 +02:00
|
|
|
if (service->classnames().count(args[1])) {
|
2017-10-03 22:15:03 +02:00
|
|
|
if (auto result = service->StartIfNotDisabled(); !result) {
|
|
|
|
LOG(ERROR) << "Could not start service '" << service->name()
|
2019-05-15 22:04:13 +02:00
|
|
|
<< "' as part of class '" << args[1] << "': " << result.error();
|
2017-10-03 22:15:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_class_start_post_data(const BuiltinArguments& args) {
|
Support for stopping/starting post-data-mount class subsets.
On devices that use FDE and APEX at the same time, we need to bring up a
minimal framework to be able to mount the /data partition. During this
period, a tmpfs /data filesystem is created, which doesn't contain any
of the updated APEXEs. As a consequence, all those processes will be
using the APEXes from the /system partition.
This is obviously not desired, as APEXes in /system may be old and/or
contain security issues. Additionally, it would create a difference
between FBE and FDE devices at runtime.
Ideally, we restart all processes that have started after we created the
tmpfs /data. We can't (re)start based on class names alone, because some
classes (eg 'hal') contain services that are required to start apexd
itself and that shouldn't be killed (eg the graphics HAL).
To address this, keep track of which processes are started after /data
is mounted, with a new 'mark_post_data' keyword. Additionally, create
'class_reset_post_data', which resets all services in the class that
were created after the initial /data mount, and 'class_start_post_data',
which starts all services in the class that were started after /data was
mounted.
On a device with FBE, these keywords wouldn't be used; on a device with
FDE, we'd use them to bring down the right processes after the user has
entered the correct secret, and restart them.
Bug: 118485723
Test: manually verified process list
Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb
2019-04-23 16:26:01 +02:00
|
|
|
if (args.context != kInitContext) {
|
|
|
|
return Error() << "command 'class_start_post_data' only available in init context";
|
|
|
|
}
|
2019-07-16 13:15:48 +02:00
|
|
|
static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
|
|
|
|
|
|
|
|
if (!is_apex_updatable) {
|
|
|
|
// No need to start these on devices that don't support APEX, since they're not
|
|
|
|
// stopped either.
|
|
|
|
return {};
|
|
|
|
}
|
2019-05-15 22:04:13 +02:00
|
|
|
for (const auto& service : ServiceList::GetInstance()) {
|
|
|
|
if (service->classnames().count(args[1])) {
|
|
|
|
if (auto result = service->StartIfPostData(); !result) {
|
|
|
|
LOG(ERROR) << "Could not start service '" << service->name()
|
|
|
|
<< "' as part of class '" << args[1] << "': " << result.error();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
Support for stopping/starting post-data-mount class subsets.
On devices that use FDE and APEX at the same time, we need to bring up a
minimal framework to be able to mount the /data partition. During this
period, a tmpfs /data filesystem is created, which doesn't contain any
of the updated APEXEs. As a consequence, all those processes will be
using the APEXes from the /system partition.
This is obviously not desired, as APEXes in /system may be old and/or
contain security issues. Additionally, it would create a difference
between FBE and FDE devices at runtime.
Ideally, we restart all processes that have started after we created the
tmpfs /data. We can't (re)start based on class names alone, because some
classes (eg 'hal') contain services that are required to start apexd
itself and that shouldn't be killed (eg the graphics HAL).
To address this, keep track of which processes are started after /data
is mounted, with a new 'mark_post_data' keyword. Additionally, create
'class_reset_post_data', which resets all services in the class that
were created after the initial /data mount, and 'class_start_post_data',
which starts all services in the class that were started after /data was
mounted.
On a device with FBE, these keywords wouldn't be used; on a device with
FDE, we'd use them to bring down the right processes after the user has
entered the correct secret, and restart them.
Bug: 118485723
Test: manually verified process list
Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb
2019-04-23 16:26:01 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_class_stop(const BuiltinArguments& args) {
|
2017-07-28 01:20:58 +02:00
|
|
|
ForEachServiceInClass(args[1], &Service::Stop);
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_class_reset(const BuiltinArguments& args) {
|
2017-07-28 01:20:58 +02:00
|
|
|
ForEachServiceInClass(args[1], &Service::Reset);
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2010-12-04 01:33:31 +01:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_class_reset_post_data(const BuiltinArguments& args) {
|
Support for stopping/starting post-data-mount class subsets.
On devices that use FDE and APEX at the same time, we need to bring up a
minimal framework to be able to mount the /data partition. During this
period, a tmpfs /data filesystem is created, which doesn't contain any
of the updated APEXEs. As a consequence, all those processes will be
using the APEXes from the /system partition.
This is obviously not desired, as APEXes in /system may be old and/or
contain security issues. Additionally, it would create a difference
between FBE and FDE devices at runtime.
Ideally, we restart all processes that have started after we created the
tmpfs /data. We can't (re)start based on class names alone, because some
classes (eg 'hal') contain services that are required to start apexd
itself and that shouldn't be killed (eg the graphics HAL).
To address this, keep track of which processes are started after /data
is mounted, with a new 'mark_post_data' keyword. Additionally, create
'class_reset_post_data', which resets all services in the class that
were created after the initial /data mount, and 'class_start_post_data',
which starts all services in the class that were started after /data was
mounted.
On a device with FBE, these keywords wouldn't be used; on a device with
FDE, we'd use them to bring down the right processes after the user has
entered the correct secret, and restart them.
Bug: 118485723
Test: manually verified process list
Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb
2019-04-23 16:26:01 +02:00
|
|
|
if (args.context != kInitContext) {
|
|
|
|
return Error() << "command 'class_reset_post_data' only available in init context";
|
|
|
|
}
|
2019-07-16 13:15:48 +02:00
|
|
|
static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
|
|
|
|
if (!is_apex_updatable) {
|
|
|
|
// No need to stop these on devices that don't support APEX.
|
|
|
|
return {};
|
|
|
|
}
|
Support for stopping/starting post-data-mount class subsets.
On devices that use FDE and APEX at the same time, we need to bring up a
minimal framework to be able to mount the /data partition. During this
period, a tmpfs /data filesystem is created, which doesn't contain any
of the updated APEXEs. As a consequence, all those processes will be
using the APEXes from the /system partition.
This is obviously not desired, as APEXes in /system may be old and/or
contain security issues. Additionally, it would create a difference
between FBE and FDE devices at runtime.
Ideally, we restart all processes that have started after we created the
tmpfs /data. We can't (re)start based on class names alone, because some
classes (eg 'hal') contain services that are required to start apexd
itself and that shouldn't be killed (eg the graphics HAL).
To address this, keep track of which processes are started after /data
is mounted, with a new 'mark_post_data' keyword. Additionally, create
'class_reset_post_data', which resets all services in the class that
were created after the initial /data mount, and 'class_start_post_data',
which starts all services in the class that were started after /data was
mounted.
On a device with FBE, these keywords wouldn't be used; on a device with
FDE, we'd use them to bring down the right processes after the user has
entered the correct secret, and restart them.
Bug: 118485723
Test: manually verified process list
Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb
2019-04-23 16:26:01 +02:00
|
|
|
ForEachServiceInClass(args[1], &Service::ResetIfPostData);
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
Support for stopping/starting post-data-mount class subsets.
On devices that use FDE and APEX at the same time, we need to bring up a
minimal framework to be able to mount the /data partition. During this
period, a tmpfs /data filesystem is created, which doesn't contain any
of the updated APEXEs. As a consequence, all those processes will be
using the APEXes from the /system partition.
This is obviously not desired, as APEXes in /system may be old and/or
contain security issues. Additionally, it would create a difference
between FBE and FDE devices at runtime.
Ideally, we restart all processes that have started after we created the
tmpfs /data. We can't (re)start based on class names alone, because some
classes (eg 'hal') contain services that are required to start apexd
itself and that shouldn't be killed (eg the graphics HAL).
To address this, keep track of which processes are started after /data
is mounted, with a new 'mark_post_data' keyword. Additionally, create
'class_reset_post_data', which resets all services in the class that
were created after the initial /data mount, and 'class_start_post_data',
which starts all services in the class that were started after /data was
mounted.
On a device with FBE, these keywords wouldn't be used; on a device with
FDE, we'd use them to bring down the right processes after the user has
entered the correct secret, and restart them.
Bug: 118485723
Test: manually verified process list
Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb
2019-04-23 16:26:01 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_class_restart(const BuiltinArguments& args) {
|
2018-11-28 07:08:02 +01:00
|
|
|
// Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1.
|
|
|
|
if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2017-07-28 01:20:58 +02:00
|
|
|
ForEachServiceInClass(args[1], &Service::Restart);
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2017-03-10 23:04:37 +01:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_domainname(const BuiltinArguments& args) {
|
2017-08-03 21:54:07 +02:00
|
|
|
if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
|
2017-05-05 03:17:33 +02:00
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_enable(const BuiltinArguments& args) {
|
2017-07-28 01:20:58 +02:00
|
|
|
Service* svc = ServiceList::GetInstance().FindService(args[1]);
|
2017-08-01 22:50:23 +02:00
|
|
|
if (!svc) return Error() << "Could not find service";
|
|
|
|
|
2017-08-23 01:13:59 +02:00
|
|
|
if (auto result = svc->Enable(); !result) {
|
|
|
|
return Error() << "Could not enable service: " << result.error();
|
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2014-05-03 06:14:29 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_exec(const BuiltinArguments& args) {
|
2017-09-13 00:58:47 +02:00
|
|
|
auto service = Service::MakeTemporaryOneshotService(args.args);
|
2017-07-28 23:48:41 +02:00
|
|
|
if (!service) {
|
2019-07-30 18:34:41 +02:00
|
|
|
return Error() << "Could not create exec service: " << service.error();
|
2017-07-28 23:48:41 +02:00
|
|
|
}
|
2019-07-30 18:34:41 +02:00
|
|
|
if (auto result = (*service)->ExecStart(); !result) {
|
2017-08-23 01:13:59 +02:00
|
|
|
return Error() << "Could not start exec service: " << result.error();
|
2017-07-28 23:48:41 +02:00
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
|
2019-07-30 18:34:41 +02:00
|
|
|
ServiceList::GetInstance().AddService(std::move(*service));
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2017-03-28 01:27:30 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_exec_background(const BuiltinArguments& args) {
|
2017-09-13 00:58:47 +02:00
|
|
|
auto service = Service::MakeTemporaryOneshotService(args.args);
|
2017-09-18 21:16:27 +02:00
|
|
|
if (!service) {
|
2019-07-30 18:34:41 +02:00
|
|
|
return Error() << "Could not create exec background service: " << service.error();
|
2017-09-18 21:16:27 +02:00
|
|
|
}
|
2019-07-30 18:34:41 +02:00
|
|
|
if (auto result = (*service)->Start(); !result) {
|
2017-09-18 21:16:27 +02:00
|
|
|
return Error() << "Could not start exec background service: " << result.error();
|
|
|
|
}
|
|
|
|
|
2019-07-30 18:34:41 +02:00
|
|
|
ServiceList::GetInstance().AddService(std::move(*service));
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2017-09-18 21:16:27 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_exec_start(const BuiltinArguments& args) {
|
2017-07-28 01:20:58 +02:00
|
|
|
Service* service = ServiceList::GetInstance().FindService(args[1]);
|
2017-07-28 23:48:41 +02:00
|
|
|
if (!service) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "Service not found";
|
2017-07-28 23:48:41 +02:00
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
|
2017-08-23 01:13:59 +02:00
|
|
|
if (auto result = service->ExecStart(); !result) {
|
|
|
|
return Error() << "Could not start exec service: " << result.error();
|
2017-07-28 23:48:41 +02:00
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_export(const BuiltinArguments& args) {
|
2017-08-23 00:41:03 +02:00
|
|
|
if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
|
|
|
|
return ErrnoError() << "setenv() failed";
|
2017-08-01 22:50:23 +02:00
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_hostname(const BuiltinArguments& args) {
|
2017-08-03 21:54:07 +02:00
|
|
|
if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
|
2017-05-05 03:17:33 +02:00
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_ifup(const BuiltinArguments& args) {
|
2017-08-01 22:50:23 +02:00
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
|
|
strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
|
|
|
|
|
2019-07-09 00:09:36 +02:00
|
|
|
unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
|
2017-08-01 22:50:23 +02:00
|
|
|
if (s < 0) return ErrnoError() << "opening socket failed";
|
|
|
|
|
|
|
|
if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
|
|
|
|
return ErrnoError() << "ioctl(..., SIOCGIFFLAGS, ...) failed";
|
|
|
|
}
|
|
|
|
|
|
|
|
ifr.ifr_flags |= IFF_UP;
|
|
|
|
|
|
|
|
if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
|
|
|
|
return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
|
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_insmod(const BuiltinArguments& args) {
|
2016-05-17 12:49:10 +02:00
|
|
|
int flags = 0;
|
|
|
|
auto it = args.begin() + 1;
|
2008-12-18 03:08:08 +01:00
|
|
|
|
2016-05-17 12:49:10 +02:00
|
|
|
if (!(*it).compare("-f")) {
|
|
|
|
flags = MODULE_INIT_IGNORE_VERMAGIC | MODULE_INIT_IGNORE_MODVERSIONS;
|
|
|
|
it++;
|
2008-12-18 03:08:08 +01:00
|
|
|
}
|
|
|
|
|
2016-05-17 12:49:10 +02:00
|
|
|
std::string filename = *it++;
|
|
|
|
std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' ');
|
2017-08-01 22:50:23 +02:00
|
|
|
|
|
|
|
unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
|
|
|
|
if (fd == -1) return ErrnoError() << "open(\"" << filename << "\") failed";
|
|
|
|
|
|
|
|
int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
|
|
|
|
if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_interface_restart(const BuiltinArguments& args) {
|
2018-05-08 20:21:37 +02:00
|
|
|
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
|
|
|
|
if (!svc) return Error() << "interface " << args[1] << " not found";
|
|
|
|
svc->Restart();
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2018-05-08 20:21:37 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_interface_start(const BuiltinArguments& args) {
|
2018-05-08 20:21:37 +02:00
|
|
|
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
|
|
|
|
if (!svc) return Error() << "interface " << args[1] << " not found";
|
|
|
|
if (auto result = svc->Start(); !result) {
|
|
|
|
return Error() << "Could not start interface: " << result.error();
|
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2018-05-08 20:21:37 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_interface_stop(const BuiltinArguments& args) {
|
2018-05-08 20:21:37 +02:00
|
|
|
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
|
|
|
|
if (!svc) return Error() << "interface " << args[1] << " not found";
|
|
|
|
svc->Stop();
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2018-05-08 20:21:37 +02:00
|
|
|
}
|
|
|
|
|
2017-08-10 21:22:44 +02:00
|
|
|
// mkdir <path> [mode] [owner] [group]
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_mkdir(const BuiltinArguments& args) {
|
2008-10-21 16:00:00 +02:00
|
|
|
mode_t mode = 0755;
|
2015-07-30 22:52:55 +02:00
|
|
|
if (args.size() >= 3) {
|
2016-10-12 02:09:00 +02:00
|
|
|
mode = std::strtoul(args[2].c_str(), 0, 8);
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2017-08-10 21:22:44 +02:00
|
|
|
if (!make_dir(args[1], mode)) {
|
|
|
|
/* chmod in case the directory already exists */
|
|
|
|
if (errno == EEXIST) {
|
|
|
|
if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return ErrnoError() << "fchmodat() failed";
|
2017-08-10 21:22:44 +02:00
|
|
|
}
|
|
|
|
} else {
|
2019-07-30 19:51:59 +02:00
|
|
|
return ErrnoErrorIgnoreEnoent() << "mkdir() failed";
|
2017-08-10 21:22:44 +02:00
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2015-07-30 22:52:55 +02:00
|
|
|
if (args.size() >= 4) {
|
2017-08-03 21:54:07 +02:00
|
|
|
auto uid = DecodeUid(args[3]);
|
|
|
|
if (!uid) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
|
2017-05-05 02:40:33 +02:00
|
|
|
}
|
2017-08-03 21:54:07 +02:00
|
|
|
Result<gid_t> gid = -1;
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2015-07-30 22:52:55 +02:00
|
|
|
if (args.size() == 5) {
|
2017-08-03 21:54:07 +02:00
|
|
|
gid = DecodeUid(args[4]);
|
|
|
|
if (!gid) {
|
2019-07-30 18:34:41 +02:00
|
|
|
return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
|
2017-05-05 02:40:33 +02:00
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2017-08-03 21:54:07 +02:00
|
|
|
if (lchown(args[1].c_str(), *uid, *gid) == -1) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return ErrnoError() << "lchown failed";
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
2012-08-15 00:43:46 +02:00
|
|
|
|
|
|
|
/* chown may have cleared S_ISUID and S_ISGID, chmod again */
|
|
|
|
if (mode & (S_ISUID | S_ISGID)) {
|
2017-08-10 21:22:44 +02:00
|
|
|
if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return ErrnoError() << "fchmodat failed";
|
2012-08-15 00:43:46 +02:00
|
|
|
}
|
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2018-10-23 22:10:33 +02:00
|
|
|
if (fscrypt_is_native()) {
|
|
|
|
if (fscrypt_set_directory_policy(args[1].c_str())) {
|
2018-04-13 19:38:57 +02:00
|
|
|
return reboot_into_recovery(
|
|
|
|
{"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
|
2016-05-10 17:52:06 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2016-06-23 20:11:39 +02:00
|
|
|
/* umount <path> */
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_umount(const BuiltinArguments& args) {
|
2017-08-01 22:50:23 +02:00
|
|
|
if (umount(args[1].c_str()) < 0) {
|
|
|
|
return ErrnoError() << "umount() failed";
|
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2016-06-23 20:11:39 +02:00
|
|
|
}
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
static struct {
|
|
|
|
const char *name;
|
|
|
|
unsigned flag;
|
|
|
|
} mount_flags[] = {
|
|
|
|
{ "noatime", MS_NOATIME },
|
2011-07-14 13:39:09 +02:00
|
|
|
{ "noexec", MS_NOEXEC },
|
2008-10-21 16:00:00 +02:00
|
|
|
{ "nosuid", MS_NOSUID },
|
|
|
|
{ "nodev", MS_NODEV },
|
|
|
|
{ "nodiratime", MS_NODIRATIME },
|
|
|
|
{ "ro", MS_RDONLY },
|
|
|
|
{ "rw", 0 },
|
|
|
|
{ "remount", MS_REMOUNT },
|
2012-08-14 20:34:34 +02:00
|
|
|
{ "bind", MS_BIND },
|
|
|
|
{ "rec", MS_REC },
|
|
|
|
{ "unbindable", MS_UNBINDABLE },
|
|
|
|
{ "private", MS_PRIVATE },
|
|
|
|
{ "slave", MS_SLAVE },
|
|
|
|
{ "shared", MS_SHARED },
|
2008-10-21 16:00:00 +02:00
|
|
|
{ "defaults", 0 },
|
|
|
|
{ 0, 0 },
|
|
|
|
};
|
|
|
|
|
2010-12-04 01:33:31 +01:00
|
|
|
#define DATA_MNT_POINT "/data"
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
/* mount <type> <device> <path> <flags ...> <options> */
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_mount(const BuiltinArguments& args) {
|
2017-08-01 23:10:11 +02:00
|
|
|
const char* options = nullptr;
|
2008-10-21 16:00:00 +02:00
|
|
|
unsigned flags = 0;
|
2017-08-01 23:10:11 +02:00
|
|
|
bool wait = false;
|
2008-10-21 16:00:00 +02:00
|
|
|
|
2017-08-01 23:10:11 +02:00
|
|
|
for (size_t na = 4; na < args.size(); na++) {
|
|
|
|
size_t i;
|
2008-10-21 16:00:00 +02:00
|
|
|
for (i = 0; mount_flags[i].name; i++) {
|
2015-07-30 22:52:55 +02:00
|
|
|
if (!args[na].compare(mount_flags[i].name)) {
|
2008-10-21 16:00:00 +02:00
|
|
|
flags |= mount_flags[i].flag;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-20 02:10:24 +02:00
|
|
|
if (!mount_flags[i].name) {
|
2017-08-01 23:10:11 +02:00
|
|
|
if (!args[na].compare("wait")) {
|
|
|
|
wait = true;
|
|
|
|
// If our last argument isn't a flag, wolf it up as an option string.
|
|
|
|
} else if (na + 1 == args.size()) {
|
2015-07-30 22:52:55 +02:00
|
|
|
options = args[na].c_str();
|
2017-08-01 23:10:11 +02:00
|
|
|
}
|
2010-04-20 02:10:24 +02:00
|
|
|
}
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2017-08-01 23:10:11 +02:00
|
|
|
const char* system = args[1].c_str();
|
|
|
|
const char* source = args[2].c_str();
|
|
|
|
const char* target = args[3].c_str();
|
2008-11-20 04:37:30 +01:00
|
|
|
|
2017-08-01 23:10:11 +02:00
|
|
|
if (android::base::StartsWith(source, "loop@")) {
|
|
|
|
int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
|
|
|
|
unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
|
2017-08-01 22:50:23 +02:00
|
|
|
if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
|
2008-11-20 04:37:30 +01:00
|
|
|
|
2017-08-01 23:10:11 +02:00
|
|
|
for (size_t n = 0;; n++) {
|
|
|
|
std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
|
|
|
|
unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
|
2017-08-01 22:50:23 +02:00
|
|
|
if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
|
2008-11-20 04:37:30 +01:00
|
|
|
|
2017-08-01 23:10:11 +02:00
|
|
|
loop_info info;
|
2008-11-20 04:37:30 +01:00
|
|
|
/* if it is a blank loop device */
|
|
|
|
if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
|
|
|
|
/* if it becomes our loop device */
|
2017-08-01 23:10:11 +02:00
|
|
|
if (ioctl(loop, LOOP_SET_FD, fd.get()) >= 0) {
|
|
|
|
if (mount(tmp.c_str(), target, system, flags, options) < 0) {
|
2008-11-20 04:37:30 +01:00
|
|
|
ioctl(loop, LOOP_CLR_FD, 0);
|
2017-08-01 22:50:23 +02:00
|
|
|
return ErrnoError() << "mount() failed";
|
2008-11-20 04:37:30 +01:00
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-11-20 04:37:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "out of loopback devices";
|
2008-11-20 04:37:30 +01:00
|
|
|
} else {
|
2010-04-20 02:10:24 +02:00
|
|
|
if (wait)
|
2016-11-11 02:43:47 +01:00
|
|
|
wait_for_file(source, kCommandRetryTimeout);
|
2008-11-20 04:37:30 +01:00
|
|
|
if (mount(source, target, system, flags, options) < 0) {
|
2019-07-30 19:51:59 +02:00
|
|
|
return ErrnoErrorIgnoreEnoent() << "mount() failed";
|
2008-11-20 04:37:30 +01:00
|
|
|
}
|
|
|
|
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
2011-02-18 03:09:47 +01:00
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2012-04-18 02:20:16 +02:00
|
|
|
}
|
2011-03-09 02:01:29 +01:00
|
|
|
|
2016-01-14 04:18:21 +01:00
|
|
|
/* Imports .rc files from the specified paths. Default ones are applied if none is given.
|
|
|
|
*
|
|
|
|
* start_index: index of the first path in the args list
|
|
|
|
*/
|
2016-08-23 20:58:09 +02:00
|
|
|
static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
|
2017-07-27 21:54:48 +02:00
|
|
|
auto& action_manager = ActionManager::GetInstance();
|
2017-07-28 01:20:58 +02:00
|
|
|
auto& service_list = ServiceList::GetInstance();
|
|
|
|
Parser parser = CreateParser(action_manager, service_list);
|
2016-08-23 20:58:09 +02:00
|
|
|
if (end_index <= start_index) {
|
2017-02-22 12:37:57 +01:00
|
|
|
// Fallbacks for partitions on which early mount isn't enabled.
|
2017-07-27 21:54:48 +02:00
|
|
|
for (const auto& path : late_import_paths) {
|
|
|
|
parser.ParseConfig(path);
|
2016-01-14 04:18:21 +01:00
|
|
|
}
|
2017-07-27 21:54:48 +02:00
|
|
|
late_import_paths.clear();
|
2016-01-14 04:18:21 +01:00
|
|
|
} else {
|
2016-08-23 20:58:09 +02:00
|
|
|
for (size_t i = start_index; i < end_index; ++i) {
|
2016-01-14 04:18:21 +01:00
|
|
|
parser.ParseConfig(args[i]);
|
|
|
|
}
|
2015-07-22 23:23:33 +02:00
|
|
|
}
|
2017-03-13 20:24:49 +01:00
|
|
|
|
|
|
|
// Turning this on and letting the INFO logging be discarded adds 0.2s to
|
|
|
|
// Nexus 9 boot time, so it's disabled by default.
|
2017-04-20 00:31:58 +02:00
|
|
|
if (false) DumpState();
|
2015-07-22 23:23:33 +02:00
|
|
|
}
|
|
|
|
|
2016-08-23 20:58:09 +02:00
|
|
|
/* Queue event based on fs_mgr return code.
|
|
|
|
*
|
|
|
|
* code: return code of fs_mgr_mount_all
|
|
|
|
*
|
|
|
|
* This function might request a reboot, in which case it will
|
|
|
|
* not return.
|
|
|
|
*
|
|
|
|
* return code is processed based on input code
|
|
|
|
*/
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> queue_fs_event(int code) {
|
2016-08-23 20:58:09 +02:00
|
|
|
if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
|
2016-04-19 00:37:31 +02:00
|
|
|
ActionManager::GetInstance().QueueEventTrigger("encrypt");
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2016-08-23 20:58:09 +02:00
|
|
|
} else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
|
2012-04-18 02:20:16 +02:00
|
|
|
property_set("ro.crypto.state", "encrypted");
|
2015-04-29 00:07:10 +02:00
|
|
|
property_set("ro.crypto.type", "block");
|
2016-04-19 00:37:31 +02:00
|
|
|
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2016-08-23 20:58:09 +02:00
|
|
|
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
|
2012-04-18 02:20:16 +02:00
|
|
|
property_set("ro.crypto.state", "unencrypted");
|
2016-03-05 00:52:33 +01:00
|
|
|
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2016-08-23 20:58:09 +02:00
|
|
|
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
|
2016-03-05 00:52:33 +01:00
|
|
|
property_set("ro.crypto.state", "unsupported");
|
2015-07-24 02:53:11 +02:00
|
|
|
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2016-08-23 20:58:09 +02:00
|
|
|
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
|
2014-07-02 23:26:54 +02:00
|
|
|
/* Setup a wipe via recovery, and reboot into recovery */
|
2019-01-04 03:16:56 +01:00
|
|
|
if (android::gsi::IsGsiRunning()) {
|
|
|
|
return Error() << "cannot wipe within GSI";
|
|
|
|
}
|
2016-06-25 00:12:21 +02:00
|
|
|
PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
|
2016-12-19 22:03:47 +01:00
|
|
|
const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
|
2018-04-13 19:38:57 +02:00
|
|
|
return reboot_into_recovery(options);
|
2014-07-02 23:26:54 +02:00
|
|
|
/* If reboot worked, there is no return. */
|
2016-08-23 20:58:09 +02:00
|
|
|
} else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
|
2018-10-23 22:10:33 +02:00
|
|
|
if (fscrypt_install_keyring()) {
|
|
|
|
return Error() << "fscrypt_install_keyring() failed";
|
2015-03-26 16:49:42 +01:00
|
|
|
}
|
|
|
|
property_set("ro.crypto.state", "encrypted");
|
2015-04-29 00:07:10 +02:00
|
|
|
property_set("ro.crypto.type", "file");
|
2015-03-26 16:49:42 +01:00
|
|
|
|
|
|
|
// Although encrypted, we have device key, so we do not need to
|
|
|
|
// do anything different from the nonencrypted case.
|
2015-07-24 02:53:11 +02:00
|
|
|
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2017-04-21 21:41:48 +02:00
|
|
|
} else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
|
2018-10-23 22:10:33 +02:00
|
|
|
if (fscrypt_install_keyring()) {
|
|
|
|
return Error() << "fscrypt_install_keyring() failed";
|
2017-04-21 21:41:48 +02:00
|
|
|
}
|
|
|
|
property_set("ro.crypto.state", "encrypted");
|
|
|
|
property_set("ro.crypto.type", "file");
|
|
|
|
|
2018-01-30 18:56:03 +01:00
|
|
|
// Although encrypted, vold has already set the device up, so we do not need to
|
|
|
|
// do anything different from the nonencrypted case.
|
|
|
|
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2017-04-21 21:41:48 +02:00
|
|
|
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
|
2018-10-23 22:10:33 +02:00
|
|
|
if (fscrypt_install_keyring()) {
|
|
|
|
return Error() << "fscrypt_install_keyring() failed";
|
2017-04-21 21:41:48 +02:00
|
|
|
}
|
2018-04-17 21:28:12 +02:00
|
|
|
property_set("ro.crypto.state", "encrypted");
|
2017-04-21 21:41:48 +02:00
|
|
|
property_set("ro.crypto.type", "file");
|
|
|
|
|
2018-01-30 18:56:03 +01:00
|
|
|
// Although encrypted, vold has already set the device up, so we do not need to
|
|
|
|
// do anything different from the nonencrypted case.
|
|
|
|
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2016-08-23 20:58:09 +02:00
|
|
|
} else if (code > 0) {
|
2017-08-01 22:50:23 +02:00
|
|
|
Error() << "fs_mgr_mount_all() returned unexpected error " << code;
|
2012-04-18 02:20:16 +02:00
|
|
|
}
|
2014-07-02 22:16:04 +02:00
|
|
|
/* else ... < 0: error */
|
2011-02-18 03:09:47 +01:00
|
|
|
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "Invalid code: " << code;
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2016-08-23 20:58:09 +02:00
|
|
|
/* mount_all <fstab> [ <path> ]* [--<options>]*
|
|
|
|
*
|
|
|
|
* This function might request a reboot, in which case it will
|
|
|
|
* not return.
|
|
|
|
*/
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_mount_all(const BuiltinArguments& args) {
|
2016-08-23 20:58:09 +02:00
|
|
|
std::size_t na = 0;
|
|
|
|
bool import_rc = true;
|
|
|
|
bool queue_event = true;
|
|
|
|
int mount_mode = MOUNT_MODE_DEFAULT;
|
2019-04-17 21:55:50 +02:00
|
|
|
const auto& fstab_file = args[1];
|
2016-08-23 20:58:09 +02:00
|
|
|
std::size_t path_arg_end = args.size();
|
2017-03-04 03:57:09 +01:00
|
|
|
const char* prop_post_fix = "default";
|
2016-08-23 20:58:09 +02:00
|
|
|
|
|
|
|
for (na = args.size() - 1; na > 1; --na) {
|
|
|
|
if (args[na] == "--early") {
|
2016-11-16 21:08:30 +01:00
|
|
|
path_arg_end = na;
|
|
|
|
queue_event = false;
|
|
|
|
mount_mode = MOUNT_MODE_EARLY;
|
2017-03-04 03:57:09 +01:00
|
|
|
prop_post_fix = "early";
|
2016-08-23 20:58:09 +02:00
|
|
|
} else if (args[na] == "--late") {
|
|
|
|
path_arg_end = na;
|
|
|
|
import_rc = false;
|
|
|
|
mount_mode = MOUNT_MODE_LATE;
|
2017-03-04 03:57:09 +01:00
|
|
|
prop_post_fix = "late";
|
2016-08-23 20:58:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-23 01:50:31 +02:00
|
|
|
std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
|
2017-07-06 23:20:11 +02:00
|
|
|
android::base::Timer t;
|
2019-04-17 21:55:50 +02:00
|
|
|
|
|
|
|
Fstab fstab;
|
|
|
|
if (!ReadFstabFromFile(fstab_file, &fstab)) {
|
|
|
|
return Error() << "Could not read fstab";
|
2017-08-01 22:50:23 +02:00
|
|
|
}
|
2019-08-15 22:07:24 +02:00
|
|
|
auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
|
2017-07-06 23:20:11 +02:00
|
|
|
property_set(prop_name, std::to_string(t.duration().count()));
|
2016-08-23 20:58:09 +02:00
|
|
|
|
2019-05-22 02:48:33 +02:00
|
|
|
if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
|
2016-08-23 20:58:09 +02:00
|
|
|
/* Paths of .rc files are specified at the 2nd argument and beyond */
|
2017-09-13 00:58:47 +02:00
|
|
|
import_late(args.args, 2, path_arg_end);
|
2016-08-23 20:58:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (queue_event) {
|
|
|
|
/* queue_fs_event will queue event based on mount_fstab return code
|
|
|
|
* and return processed return code*/
|
2019-08-15 22:07:24 +02:00
|
|
|
auto queue_fs_result = queue_fs_event(mount_fstab_return_code);
|
2017-08-01 22:50:23 +02:00
|
|
|
if (!queue_fs_result) {
|
|
|
|
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
|
|
|
|
}
|
2016-08-23 20:58:09 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2016-08-23 20:58:09 +02:00
|
|
|
}
|
|
|
|
|
init: add umount_all builtin.
umount_all is the cleanup step for mount_all.
In particular, the mount_all builtin creates a verity device,
'postinstall-verity', for the following line:
system /postinstall ... ... slotselect_other,logical,avb_keys=...
cppreopt umounts /postinstall but doesn't destroy the postinstall-verity
device, causing OTA to fail (because it cannot destroy the
system_[other] device). umount_all also destroy the verity device.
Note that mount_all does not map system_[other]; it is mapped by
first stage init. Hence, umount_all doesn't destroy it either. The OTA
client is reponsible for unmapping the device itself.
Bug: 129988285
Test: flash, boot, then check `dmctl list devices`, then OTA
Change-Id: Id3ab65b3860b6ea6cfec310ab13652009c81f415
Merged-In: Id3ab65b3860b6ea6cfec310ab13652009c81f415
2019-04-09 19:11:34 +02:00
|
|
|
/* umount_all <fstab> */
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_umount_all(const BuiltinArguments& args) {
|
2019-04-17 21:55:50 +02:00
|
|
|
Fstab fstab;
|
|
|
|
if (!ReadFstabFromFile(args[1], &fstab)) {
|
|
|
|
return Error() << "Could not read fstab";
|
|
|
|
}
|
|
|
|
|
2019-08-15 22:07:24 +02:00
|
|
|
if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
|
|
|
|
return Error() << "umount_fstab() failed " << result;
|
init: add umount_all builtin.
umount_all is the cleanup step for mount_all.
In particular, the mount_all builtin creates a verity device,
'postinstall-verity', for the following line:
system /postinstall ... ... slotselect_other,logical,avb_keys=...
cppreopt umounts /postinstall but doesn't destroy the postinstall-verity
device, causing OTA to fail (because it cannot destroy the
system_[other] device). umount_all also destroy the verity device.
Note that mount_all does not map system_[other]; it is mapped by
first stage init. Hence, umount_all doesn't destroy it either. The OTA
client is reponsible for unmapping the device itself.
Bug: 129988285
Test: flash, boot, then check `dmctl list devices`, then OTA
Change-Id: Id3ab65b3860b6ea6cfec310ab13652009c81f415
Merged-In: Id3ab65b3860b6ea6cfec310ab13652009c81f415
2019-04-09 19:11:34 +02:00
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
init: add umount_all builtin.
umount_all is the cleanup step for mount_all.
In particular, the mount_all builtin creates a verity device,
'postinstall-verity', for the following line:
system /postinstall ... ... slotselect_other,logical,avb_keys=...
cppreopt umounts /postinstall but doesn't destroy the postinstall-verity
device, causing OTA to fail (because it cannot destroy the
system_[other] device). umount_all also destroy the verity device.
Note that mount_all does not map system_[other]; it is mapped by
first stage init. Hence, umount_all doesn't destroy it either. The OTA
client is reponsible for unmapping the device itself.
Bug: 129988285
Test: flash, boot, then check `dmctl list devices`, then OTA
Change-Id: Id3ab65b3860b6ea6cfec310ab13652009c81f415
Merged-In: Id3ab65b3860b6ea6cfec310ab13652009c81f415
2019-04-09 19:11:34 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_swapon_all(const BuiltinArguments& args) {
|
2018-12-01 00:21:04 +01:00
|
|
|
Fstab fstab;
|
|
|
|
if (!ReadFstabFromFile(args[1], &fstab)) {
|
|
|
|
return Error() << "Could not read fstab '" << args[1] << "'";
|
|
|
|
}
|
2013-07-10 03:42:09 +02:00
|
|
|
|
2019-08-15 22:07:24 +02:00
|
|
|
if (!fs_mgr_swapon_all(fstab)) {
|
|
|
|
return Error() << "fs_mgr_swapon_all() failed";
|
2018-12-01 00:21:04 +01:00
|
|
|
}
|
2013-07-10 03:42:09 +02:00
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2013-07-10 03:42:09 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_setprop(const BuiltinArguments& args) {
|
2019-04-23 02:46:37 +02:00
|
|
|
if (StartsWith(args[1], "ctl.")) {
|
|
|
|
return Error()
|
|
|
|
<< "Cannot set ctl. properties from init; call the Service functions directly";
|
|
|
|
}
|
|
|
|
if (args[1] == kRestoreconProperty) {
|
|
|
|
return Error() << "Cannot set '" << kRestoreconProperty
|
|
|
|
<< "' from init; use the restorecon builtin directly";
|
|
|
|
}
|
|
|
|
|
2017-06-23 01:50:31 +02:00
|
|
|
property_set(args[1], args[2]);
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_setrlimit(const BuiltinArguments& args) {
|
2017-09-13 00:58:47 +02:00
|
|
|
auto rlimit = ParseRlimit(args.args);
|
2017-08-25 19:39:25 +02:00
|
|
|
if (!rlimit) return rlimit.error();
|
2017-08-01 22:50:23 +02:00
|
|
|
|
2017-08-25 19:39:25 +02:00
|
|
|
if (setrlimit(rlimit->first, &rlimit->second) == -1) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return ErrnoError() << "setrlimit failed";
|
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_start(const BuiltinArguments& args) {
|
2017-07-28 01:20:58 +02:00
|
|
|
Service* svc = ServiceList::GetInstance().FindService(args[1]);
|
2017-08-01 22:50:23 +02:00
|
|
|
if (!svc) return Error() << "service " << args[1] << " not found";
|
2017-08-23 01:13:59 +02:00
|
|
|
if (auto result = svc->Start(); !result) {
|
2019-07-30 19:51:59 +02:00
|
|
|
return ErrorIgnoreEnoent() << "Could not start service: " << result.error();
|
2017-08-23 01:13:59 +02:00
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_stop(const BuiltinArguments& args) {
|
2017-07-28 01:20:58 +02:00
|
|
|
Service* svc = ServiceList::GetInstance().FindService(args[1]);
|
2017-08-01 22:50:23 +02:00
|
|
|
if (!svc) return Error() << "service " << args[1] << " not found";
|
2015-07-31 21:45:25 +02:00
|
|
|
svc->Stop();
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_restart(const BuiltinArguments& args) {
|
2017-07-28 01:20:58 +02:00
|
|
|
Service* svc = ServiceList::GetInstance().FindService(args[1]);
|
2017-08-01 22:50:23 +02:00
|
|
|
if (!svc) return Error() << "service " << args[1] << " not found";
|
2015-07-31 21:45:25 +02:00
|
|
|
svc->Restart();
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_trigger(const BuiltinArguments& args) {
|
2015-07-24 02:53:11 +02:00
|
|
|
ActionManager::GetInstance().QueueEventTrigger(args[1]);
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2017-11-27 23:45:26 +01:00
|
|
|
static int MakeSymlink(const std::string& target, const std::string& linkpath) {
|
|
|
|
std::string secontext;
|
|
|
|
// Passing 0 for mode should work.
|
|
|
|
if (SelabelLookupFileContext(linkpath, 0, &secontext) && !secontext.empty()) {
|
|
|
|
setfscreatecon(secontext.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
int rc = symlink(target.c_str(), linkpath.c_str());
|
|
|
|
|
|
|
|
if (!secontext.empty()) {
|
|
|
|
int save_errno = errno;
|
|
|
|
setfscreatecon(nullptr);
|
|
|
|
errno = save_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_symlink(const BuiltinArguments& args) {
|
2017-11-27 23:45:26 +01:00
|
|
|
if (MakeSymlink(args[1], args[2]) < 0) {
|
2017-08-23 01:15:26 +02:00
|
|
|
// The symlink builtin is often used to create symlinks for older devices to be backwards
|
|
|
|
// compatible with new paths, therefore we skip reporting this error.
|
2019-07-30 19:51:59 +02:00
|
|
|
return ErrnoErrorIgnoreEnoent() << "symlink() failed";
|
2017-08-01 22:50:23 +02:00
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_rm(const BuiltinArguments& args) {
|
2017-08-01 22:50:23 +02:00
|
|
|
if (unlink(args[1].c_str()) < 0) {
|
|
|
|
return ErrnoError() << "unlink() failed";
|
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2011-01-19 02:37:41 +01:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_rmdir(const BuiltinArguments& args) {
|
2017-08-01 22:50:23 +02:00
|
|
|
if (rmdir(args[1].c_str()) < 0) {
|
|
|
|
return ErrnoError() << "rmdir() failed";
|
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2011-01-19 02:37:41 +01:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_sysclktz(const BuiltinArguments& args) {
|
2016-10-12 02:09:00 +02:00
|
|
|
struct timezone tz = {};
|
2017-08-01 22:50:23 +02:00
|
|
|
if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
|
|
|
|
return Error() << "Unable to parse mins_west_of_gmt";
|
2016-10-12 02:09:00 +02:00
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
|
|
|
|
if (settimeofday(nullptr, &tz) == -1) {
|
|
|
|
return ErrnoError() << "settimeofday() failed";
|
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-12-18 03:08:08 +01:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_verity_update_state(const BuiltinArguments& args) {
|
2019-01-07 23:25:31 +01:00
|
|
|
int mode;
|
|
|
|
if (!fs_mgr_load_verity_state(&mode)) {
|
|
|
|
return Error() << "fs_mgr_load_verity_state() failed";
|
2017-08-01 22:50:23 +02:00
|
|
|
}
|
2019-01-07 23:25:31 +01:00
|
|
|
|
|
|
|
Fstab fstab;
|
|
|
|
if (!ReadDefaultFstab(&fstab)) {
|
|
|
|
return Error() << "Failed to read default fstab";
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& entry : fstab) {
|
|
|
|
if (!fs_mgr_is_verity_enabled(entry)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// To be consistent in vboot 1.0 and vboot 2.0 (AVB), use "system" for the partition even
|
|
|
|
// for system as root, so it has property [partition.system.verified].
|
|
|
|
std::string partition = entry.mount_point == "/" ? "system" : Basename(entry.mount_point);
|
|
|
|
property_set("partition." + partition + ".verified", std::to_string(mode));
|
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2015-03-19 11:00:34 +01:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_write(const BuiltinArguments& args) {
|
2017-08-03 21:54:07 +02:00
|
|
|
if (auto result = WriteFile(args[1], args[2]); !result) {
|
2019-07-30 19:51:59 +02:00
|
|
|
return ErrorIgnoreEnoent()
|
|
|
|
<< "Unable to write to file '" << args[1] << "': " << result.error();
|
2017-05-05 03:17:33 +02:00
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> readahead_file(const std::string& filename, bool fully) {
|
2019-07-09 00:09:36 +02:00
|
|
|
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_CLOEXEC)));
|
2017-08-16 20:34:50 +02:00
|
|
|
if (fd == -1) {
|
|
|
|
return ErrnoError() << "Error opening file";
|
|
|
|
}
|
|
|
|
if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED)) {
|
|
|
|
return ErrnoError() << "Error posix_fadvise file";
|
|
|
|
}
|
|
|
|
if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
|
|
|
|
return ErrnoError() << "Error readahead file";
|
|
|
|
}
|
|
|
|
if (fully) {
|
|
|
|
char buf[BUFSIZ];
|
|
|
|
ssize_t n;
|
|
|
|
while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
|
|
|
|
}
|
|
|
|
if (n != 0) {
|
|
|
|
return ErrnoError() << "Error reading file";
|
|
|
|
}
|
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2017-08-16 20:34:50 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_readahead(const BuiltinArguments& args) {
|
2017-08-04 22:22:36 +02:00
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
if (stat(args[1].c_str(), &sb)) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return ErrnoError() << "Error opening " << args[1];
|
2017-08-04 22:22:36 +02:00
|
|
|
}
|
|
|
|
|
2017-08-16 20:34:50 +02:00
|
|
|
bool readfully = false;
|
|
|
|
if (args.size() == 3 && args[2] == "--fully") {
|
|
|
|
readfully = true;
|
|
|
|
}
|
2017-08-04 22:22:36 +02:00
|
|
|
// We will do readahead in a forked process in order not to block init
|
|
|
|
// since it may block while it reads the
|
|
|
|
// filesystem metadata needed to locate the requested blocks. This
|
|
|
|
// occurs frequently with ext[234] on large files using indirect blocks
|
|
|
|
// instead of extents, giving the appearance that the call blocks until
|
|
|
|
// the requested data has been read.
|
|
|
|
pid_t pid = fork();
|
|
|
|
if (pid == 0) {
|
2017-08-16 20:34:50 +02:00
|
|
|
if (setpriority(PRIO_PROCESS, 0, static_cast<int>(ANDROID_PRIORITY_LOWEST)) != 0) {
|
|
|
|
PLOG(WARNING) << "setpriority failed";
|
|
|
|
}
|
|
|
|
if (android_set_ioprio(0, IoSchedClass_IDLE, 7)) {
|
|
|
|
PLOG(WARNING) << "ioprio_get failed";
|
|
|
|
}
|
2017-08-04 22:22:36 +02:00
|
|
|
android::base::Timer t;
|
|
|
|
if (S_ISREG(sb.st_mode)) {
|
2017-08-16 20:34:50 +02:00
|
|
|
if (auto result = readahead_file(args[1], readfully); !result) {
|
|
|
|
LOG(WARNING) << "Unable to readahead '" << args[1] << "': " << result.error();
|
2017-08-04 22:22:36 +02:00
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
} else if (S_ISDIR(sb.st_mode)) {
|
|
|
|
char* paths[] = {const_cast<char*>(args[1].data()), nullptr};
|
|
|
|
std::unique_ptr<FTS, decltype(&fts_close)> fts(
|
|
|
|
fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr), fts_close);
|
|
|
|
if (!fts) {
|
|
|
|
PLOG(ERROR) << "Error opening directory: " << args[1];
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
// Traverse the entire hierarchy and do readahead
|
|
|
|
for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;
|
|
|
|
ftsent = fts_read(fts.get())) {
|
|
|
|
if (ftsent->fts_info & FTS_F) {
|
2017-08-16 20:34:50 +02:00
|
|
|
const std::string filename = ftsent->fts_accpath;
|
|
|
|
if (auto result = readahead_file(filename, readfully); !result) {
|
|
|
|
LOG(WARNING)
|
|
|
|
<< "Unable to readahead '" << filename << "': " << result.error();
|
2017-08-04 22:22:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-16 20:34:50 +02:00
|
|
|
LOG(INFO) << "Readahead " << args[1] << " took " << t << " asynchronously";
|
2017-08-04 22:22:36 +02:00
|
|
|
_exit(0);
|
|
|
|
} else if (pid < 0) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return ErrnoError() << "Fork failed";
|
2017-08-04 22:22:36 +02:00
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2017-08-04 22:22:36 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_copy(const BuiltinArguments& args) {
|
2017-08-03 21:54:07 +02:00
|
|
|
auto file_contents = ReadFile(args[1]);
|
|
|
|
if (!file_contents) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
|
2017-05-05 03:17:33 +02:00
|
|
|
}
|
2017-08-03 21:54:07 +02:00
|
|
|
if (auto result = WriteFile(args[2], *file_contents); !result) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
|
2009-08-27 01:39:25 +02:00
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2009-08-27 01:39:25 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_chown(const BuiltinArguments& args) {
|
2017-08-03 21:54:07 +02:00
|
|
|
auto uid = DecodeUid(args[1]);
|
|
|
|
if (!uid) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
2017-05-05 02:40:33 +02:00
|
|
|
|
|
|
|
// GID is optional and pushes the index of path out by one if specified.
|
|
|
|
const std::string& path = (args.size() == 4) ? args[3] : args[2];
|
2017-08-03 21:54:07 +02:00
|
|
|
Result<gid_t> gid = -1;
|
2017-05-05 02:40:33 +02:00
|
|
|
|
|
|
|
if (args.size() == 4) {
|
2017-08-03 21:54:07 +02:00
|
|
|
gid = DecodeUid(args[2]);
|
|
|
|
if (!gid) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
|
2017-05-05 02:40:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 22:50:23 +02:00
|
|
|
if (lchown(path.c_str(), *uid, *gid) == -1) {
|
2019-07-30 19:51:59 +02:00
|
|
|
return ErrnoErrorIgnoreEnoent() << "lchown() failed";
|
2017-08-01 22:50:23 +02:00
|
|
|
}
|
2017-05-05 02:40:33 +02:00
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static mode_t get_mode(const char *s) {
|
|
|
|
mode_t mode = 0;
|
|
|
|
while (*s) {
|
|
|
|
if (*s >= '0' && *s <= '7') {
|
|
|
|
mode = (mode<<3) | (*s-'0');
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_chmod(const BuiltinArguments& args) {
|
2015-07-30 22:52:55 +02:00
|
|
|
mode_t mode = get_mode(args[1].c_str());
|
|
|
|
if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
|
2019-07-30 19:51:59 +02:00
|
|
|
return ErrnoErrorIgnoreEnoent() << "fchmodat() failed";
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_restorecon(const BuiltinArguments& args) {
|
2019-07-30 18:34:41 +02:00
|
|
|
auto restorecon_info = ParseRestorecon(args.args);
|
|
|
|
if (!restorecon_info) {
|
|
|
|
return restorecon_info.error();
|
|
|
|
}
|
2012-01-13 14:48:47 +01:00
|
|
|
|
2019-07-30 18:34:41 +02:00
|
|
|
const auto& [flag, paths] = *restorecon_info;
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
for (const auto& path : paths) {
|
|
|
|
if (selinux_android_restorecon(path.c_str(), flag) < 0) {
|
|
|
|
ret = errno;
|
2016-11-15 00:40:18 +01:00
|
|
|
}
|
2012-01-13 14:48:47 +01:00
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
|
2019-07-30 19:51:59 +02:00
|
|
|
if (ret) return ErrnoErrorIgnoreEnoent() << "selinux_android_restorecon() failed";
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2013-10-09 22:02:09 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_restorecon_recursive(const BuiltinArguments& args) {
|
2017-09-13 00:58:47 +02:00
|
|
|
std::vector<std::string> non_const_args(args.args);
|
2016-11-15 00:40:18 +01:00
|
|
|
non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
|
2017-09-13 00:58:47 +02:00
|
|
|
return do_restorecon({std::move(non_const_args), args.context});
|
2012-01-13 14:48:47 +01:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_loglevel(const BuiltinArguments& args) {
|
2016-08-05 01:09:39 +02:00
|
|
|
// TODO: support names instead/as well?
|
2016-10-12 02:09:00 +02:00
|
|
|
int log_level = -1;
|
|
|
|
android::base::ParseInt(args[1], &log_level);
|
2016-08-05 01:09:39 +02:00
|
|
|
android::base::LogSeverity severity;
|
|
|
|
switch (log_level) {
|
|
|
|
case 7: severity = android::base::DEBUG; break;
|
|
|
|
case 6: severity = android::base::INFO; break;
|
|
|
|
case 5:
|
|
|
|
case 4: severity = android::base::WARNING; break;
|
|
|
|
case 3: severity = android::base::ERROR; break;
|
|
|
|
case 2:
|
|
|
|
case 1:
|
|
|
|
case 0: severity = android::base::FATAL; break;
|
|
|
|
default:
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "invalid log level " << log_level;
|
2016-08-05 01:09:39 +02:00
|
|
|
}
|
|
|
|
android::base::SetMinimumLogSeverity(severity);
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2008-10-21 16:00:00 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_load_persist_props(const BuiltinArguments& args) {
|
2019-04-23 02:46:37 +02:00
|
|
|
// Devices with FDE have load_persist_props called twice; the first time when the temporary
|
|
|
|
// /data partition is mounted and then again once /data is truly mounted. We do not want to
|
|
|
|
// read persistent properties from the temporary /data partition or mark persistent properties
|
|
|
|
// as having been loaded during the first call, so we return in that case.
|
|
|
|
std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
|
|
|
|
std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
|
|
|
|
if (crypto_state == "encrypted" && crypto_type == "block") {
|
|
|
|
static size_t num_calls = 0;
|
|
|
|
if (++num_calls == 1) return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
SendLoadPersistentPropertiesMessage();
|
|
|
|
|
|
|
|
start_waiting_for_property("ro.persistent_properties.ready", "true");
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2011-03-09 02:01:29 +01:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_load_system_props(const BuiltinArguments& args) {
|
2019-01-12 18:42:31 +01:00
|
|
|
LOG(INFO) << "deprecated action `load_system_props` called.";
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2014-06-17 00:06:21 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_wait(const BuiltinArguments& args) {
|
2017-08-01 22:50:23 +02:00
|
|
|
auto timeout = kCommandRetryTimeout;
|
|
|
|
if (args.size() == 3) {
|
|
|
|
int timeout_int;
|
|
|
|
if (!android::base::ParseInt(args[2], &timeout_int)) {
|
|
|
|
return Error() << "failed to parse timeout";
|
2016-10-12 02:09:00 +02:00
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
timeout = std::chrono::seconds(timeout_int);
|
2016-10-12 02:09:00 +02:00
|
|
|
}
|
2017-08-01 22:50:23 +02:00
|
|
|
|
|
|
|
if (wait_for_file(args[1].c_str(), timeout) != 0) {
|
|
|
|
return Error() << "wait_for_file() failed";
|
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2010-04-20 02:10:24 +02:00
|
|
|
}
|
2015-03-26 16:49:42 +01:00
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_wait_for_prop(const BuiltinArguments& args) {
|
2017-01-26 01:27:03 +01:00
|
|
|
const char* name = args[1].c_str();
|
|
|
|
const char* value = args[2].c_str();
|
|
|
|
size_t value_len = strlen(value);
|
|
|
|
|
2018-02-14 01:50:08 +01:00
|
|
|
if (!IsLegalPropertyName(name)) {
|
|
|
|
return Error() << "IsLegalPropertyName(" << name << ") failed";
|
2017-01-26 01:27:03 +01:00
|
|
|
}
|
|
|
|
if (value_len >= PROP_VALUE_MAX) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "value too long";
|
2017-01-26 01:27:03 +01:00
|
|
|
}
|
2017-02-02 19:52:39 +01:00
|
|
|
if (!start_waiting_for_property(name, value)) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return Error() << "already waiting for a property";
|
2017-01-26 01:27:03 +01:00
|
|
|
}
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2017-01-26 01:27:03 +01:00
|
|
|
}
|
|
|
|
|
2015-05-28 18:35:06 +02:00
|
|
|
static bool is_file_crypto() {
|
2017-03-29 01:40:41 +02:00
|
|
|
return android::base::GetProperty("ro.crypto.type", "") == "file";
|
2015-05-28 18:35:06 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> ExecWithRebootOnFailure(const std::string& reboot_reason,
|
|
|
|
const BuiltinArguments& args) {
|
2018-04-13 19:38:57 +02:00
|
|
|
auto service = Service::MakeTemporaryOneshotService(args.args);
|
|
|
|
if (!service) {
|
2019-07-30 18:34:41 +02:00
|
|
|
return Error() << "Could not create exec service: " << service.error();
|
2018-04-13 19:38:57 +02:00
|
|
|
}
|
2019-07-30 18:34:41 +02:00
|
|
|
(*service)->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
|
2018-04-13 19:38:57 +02:00
|
|
|
if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
|
2019-01-04 03:16:56 +01:00
|
|
|
// TODO (b/122850122): support this in gsi
|
|
|
|
if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
|
2018-04-13 19:38:57 +02:00
|
|
|
LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
|
2018-10-11 19:38:05 +02:00
|
|
|
if (auto result = reboot_into_recovery(
|
|
|
|
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
|
|
|
|
!result) {
|
|
|
|
LOG(FATAL) << "Could not reboot into recovery: " << result.error();
|
|
|
|
}
|
2018-04-13 19:38:57 +02:00
|
|
|
} else {
|
|
|
|
LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2019-07-30 18:34:41 +02:00
|
|
|
if (auto result = (*service)->ExecStart(); !result) {
|
2018-04-13 19:38:57 +02:00
|
|
|
return Error() << "Could not start exec service: " << result.error();
|
|
|
|
}
|
2019-07-30 18:34:41 +02:00
|
|
|
ServiceList::GetInstance().AddService(std::move(*service));
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2018-04-13 19:38:57 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_installkey(const BuiltinArguments& args) {
|
|
|
|
if (!is_file_crypto()) return {};
|
2017-08-01 22:50:23 +02:00
|
|
|
|
2018-10-23 22:10:33 +02:00
|
|
|
auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;
|
2017-08-10 21:22:44 +02:00
|
|
|
if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
|
2017-08-01 22:50:23 +02:00
|
|
|
return ErrnoError() << "Failed to create " << unencrypted_dir;
|
2017-03-29 23:50:01 +02:00
|
|
|
}
|
2018-04-13 19:38:57 +02:00
|
|
|
return ExecWithRebootOnFailure(
|
|
|
|
"enablefilecrypto_failed",
|
|
|
|
{{"exec", "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto"}, args.context});
|
2015-03-26 16:49:42 +01:00
|
|
|
}
|
2015-05-28 18:35:06 +02:00
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_init_user0(const BuiltinArguments& args) {
|
2018-04-13 19:38:57 +02:00
|
|
|
return ExecWithRebootOnFailure(
|
|
|
|
"init_user0_failed",
|
|
|
|
{{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context});
|
2016-02-01 17:37:13 +01:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_mark_post_data(const BuiltinArguments& args) {
|
Support for stopping/starting post-data-mount class subsets.
On devices that use FDE and APEX at the same time, we need to bring up a
minimal framework to be able to mount the /data partition. During this
period, a tmpfs /data filesystem is created, which doesn't contain any
of the updated APEXEs. As a consequence, all those processes will be
using the APEXes from the /system partition.
This is obviously not desired, as APEXes in /system may be old and/or
contain security issues. Additionally, it would create a difference
between FBE and FDE devices at runtime.
Ideally, we restart all processes that have started after we created the
tmpfs /data. We can't (re)start based on class names alone, because some
classes (eg 'hal') contain services that are required to start apexd
itself and that shouldn't be killed (eg the graphics HAL).
To address this, keep track of which processes are started after /data
is mounted, with a new 'mark_post_data' keyword. Additionally, create
'class_reset_post_data', which resets all services in the class that
were created after the initial /data mount, and 'class_start_post_data',
which starts all services in the class that were started after /data was
mounted.
On a device with FBE, these keywords wouldn't be used; on a device with
FDE, we'd use them to bring down the right processes after the user has
entered the correct secret, and restart them.
Bug: 118485723
Test: manually verified process list
Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb
2019-04-23 16:26:01 +02:00
|
|
|
ServiceList::GetInstance().MarkPostData();
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
Support for stopping/starting post-data-mount class subsets.
On devices that use FDE and APEX at the same time, we need to bring up a
minimal framework to be able to mount the /data partition. During this
period, a tmpfs /data filesystem is created, which doesn't contain any
of the updated APEXEs. As a consequence, all those processes will be
using the APEXes from the /system partition.
This is obviously not desired, as APEXes in /system may be old and/or
contain security issues. Additionally, it would create a difference
between FBE and FDE devices at runtime.
Ideally, we restart all processes that have started after we created the
tmpfs /data. We can't (re)start based on class names alone, because some
classes (eg 'hal') contain services that are required to start apexd
itself and that shouldn't be killed (eg the graphics HAL).
To address this, keep track of which processes are started after /data
is mounted, with a new 'mark_post_data' keyword. Additionally, create
'class_reset_post_data', which resets all services in the class that
were created after the initial /data mount, and 'class_start_post_data',
which starts all services in the class that were started after /data was
mounted.
On a device with FBE, these keywords wouldn't be used; on a device with
FDE, we'd use them to bring down the right processes after the user has
entered the correct secret, and restart them.
Bug: 118485723
Test: manually verified process list
Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb
2019-04-23 16:26:01 +02:00
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_parse_apex_configs(const BuiltinArguments& args) {
|
2018-11-08 09:14:35 +01:00
|
|
|
glob_t glob_result;
|
2019-06-07 12:18:46 +02:00
|
|
|
static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
|
2018-11-16 08:23:20 +01:00
|
|
|
const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
|
|
|
|
if (ret != 0 && ret != GLOB_NOMATCH) {
|
2018-11-08 09:14:35 +01:00
|
|
|
globfree(&glob_result);
|
|
|
|
return Error() << "glob pattern '" << glob_pattern << "' failed";
|
|
|
|
}
|
|
|
|
std::vector<std::string> configs;
|
|
|
|
Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance());
|
|
|
|
for (size_t i = 0; i < glob_result.gl_pathc; i++) {
|
2019-06-07 12:18:46 +02:00
|
|
|
std::string path = glob_result.gl_pathv[i];
|
|
|
|
// Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
|
|
|
|
// /apex/<name> paths, so unless we filter them out, we will parse the
|
|
|
|
// same file twice.
|
|
|
|
std::vector<std::string> paths = android::base::Split(path, "/");
|
2019-07-05 04:22:18 +02:00
|
|
|
if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
|
2019-06-07 12:18:46 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
configs.push_back(path);
|
2018-11-08 09:14:35 +01:00
|
|
|
}
|
|
|
|
globfree(&glob_result);
|
|
|
|
|
|
|
|
bool success = true;
|
|
|
|
for (const auto& c : configs) {
|
|
|
|
if (c.back() == '/') {
|
|
|
|
// skip if directory
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
success &= parser.ParseConfigFile(c);
|
|
|
|
}
|
2018-11-12 04:08:41 +01:00
|
|
|
ServiceList::GetInstance().MarkServicesUpdate();
|
2018-11-08 09:14:35 +01:00
|
|
|
if (success) {
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2018-11-08 09:14:35 +01:00
|
|
|
} else {
|
|
|
|
return Error() << "Could not parse apex configs";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-10 20:08:01 +02:00
|
|
|
static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
|
2019-02-22 14:15:25 +01:00
|
|
|
if (SwitchToDefaultMountNamespace()) {
|
2019-06-10 20:08:01 +02:00
|
|
|
return {};
|
2019-02-22 14:15:25 +01:00
|
|
|
} else {
|
|
|
|
return Error() << "Failed to enter into default mount namespace";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-14 01:50:08 +01:00
|
|
|
// Builtin-function-map start
|
2019-07-23 01:05:36 +02:00
|
|
|
const BuiltinFunctionMap& GetBuiltinFunctionMap() {
|
2015-08-26 20:43:36 +02:00
|
|
|
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
|
2017-03-28 01:27:30 +02:00
|
|
|
// clang-format off
|
2019-07-23 01:05:36 +02:00
|
|
|
static const BuiltinFunctionMap builtin_functions = {
|
2017-09-13 00:58:47 +02:00
|
|
|
{"bootchart", {1, 1, {false, do_bootchart}}},
|
|
|
|
{"chmod", {2, 2, {true, do_chmod}}},
|
|
|
|
{"chown", {2, 3, {true, do_chown}}},
|
|
|
|
{"class_reset", {1, 1, {false, do_class_reset}}},
|
Support for stopping/starting post-data-mount class subsets.
On devices that use FDE and APEX at the same time, we need to bring up a
minimal framework to be able to mount the /data partition. During this
period, a tmpfs /data filesystem is created, which doesn't contain any
of the updated APEXEs. As a consequence, all those processes will be
using the APEXes from the /system partition.
This is obviously not desired, as APEXes in /system may be old and/or
contain security issues. Additionally, it would create a difference
between FBE and FDE devices at runtime.
Ideally, we restart all processes that have started after we created the
tmpfs /data. We can't (re)start based on class names alone, because some
classes (eg 'hal') contain services that are required to start apexd
itself and that shouldn't be killed (eg the graphics HAL).
To address this, keep track of which processes are started after /data
is mounted, with a new 'mark_post_data' keyword. Additionally, create
'class_reset_post_data', which resets all services in the class that
were created after the initial /data mount, and 'class_start_post_data',
which starts all services in the class that were started after /data was
mounted.
On a device with FBE, these keywords wouldn't be used; on a device with
FDE, we'd use them to bring down the right processes after the user has
entered the correct secret, and restart them.
Bug: 118485723
Test: manually verified process list
Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb
2019-04-23 16:26:01 +02:00
|
|
|
{"class_reset_post_data", {1, 1, {false, do_class_reset_post_data}}},
|
2017-09-13 00:58:47 +02:00
|
|
|
{"class_restart", {1, 1, {false, do_class_restart}}},
|
|
|
|
{"class_start", {1, 1, {false, do_class_start}}},
|
Support for stopping/starting post-data-mount class subsets.
On devices that use FDE and APEX at the same time, we need to bring up a
minimal framework to be able to mount the /data partition. During this
period, a tmpfs /data filesystem is created, which doesn't contain any
of the updated APEXEs. As a consequence, all those processes will be
using the APEXes from the /system partition.
This is obviously not desired, as APEXes in /system may be old and/or
contain security issues. Additionally, it would create a difference
between FBE and FDE devices at runtime.
Ideally, we restart all processes that have started after we created the
tmpfs /data. We can't (re)start based on class names alone, because some
classes (eg 'hal') contain services that are required to start apexd
itself and that shouldn't be killed (eg the graphics HAL).
To address this, keep track of which processes are started after /data
is mounted, with a new 'mark_post_data' keyword. Additionally, create
'class_reset_post_data', which resets all services in the class that
were created after the initial /data mount, and 'class_start_post_data',
which starts all services in the class that were started after /data was
mounted.
On a device with FBE, these keywords wouldn't be used; on a device with
FDE, we'd use them to bring down the right processes after the user has
entered the correct secret, and restart them.
Bug: 118485723
Test: manually verified process list
Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb
2019-04-23 16:26:01 +02:00
|
|
|
{"class_start_post_data", {1, 1, {false, do_class_start_post_data}}},
|
2017-09-13 00:58:47 +02:00
|
|
|
{"class_stop", {1, 1, {false, do_class_stop}}},
|
|
|
|
{"copy", {2, 2, {true, do_copy}}},
|
|
|
|
{"domainname", {1, 1, {true, do_domainname}}},
|
|
|
|
{"enable", {1, 1, {false, do_enable}}},
|
|
|
|
{"exec", {1, kMax, {false, do_exec}}},
|
|
|
|
{"exec_background", {1, kMax, {false, do_exec_background}}},
|
|
|
|
{"exec_start", {1, 1, {false, do_exec_start}}},
|
|
|
|
{"export", {2, 2, {false, do_export}}},
|
|
|
|
{"hostname", {1, 1, {true, do_hostname}}},
|
|
|
|
{"ifup", {1, 1, {true, do_ifup}}},
|
|
|
|
{"init_user0", {0, 0, {false, do_init_user0}}},
|
|
|
|
{"insmod", {1, kMax, {true, do_insmod}}},
|
|
|
|
{"installkey", {1, 1, {false, do_installkey}}},
|
2018-05-08 20:21:37 +02:00
|
|
|
{"interface_restart", {1, 1, {false, do_interface_restart}}},
|
|
|
|
{"interface_start", {1, 1, {false, do_interface_start}}},
|
|
|
|
{"interface_stop", {1, 1, {false, do_interface_stop}}},
|
2017-09-13 00:58:47 +02:00
|
|
|
{"load_persist_props", {0, 0, {false, do_load_persist_props}}},
|
|
|
|
{"load_system_props", {0, 0, {false, do_load_system_props}}},
|
|
|
|
{"loglevel", {1, 1, {false, do_loglevel}}},
|
Support for stopping/starting post-data-mount class subsets.
On devices that use FDE and APEX at the same time, we need to bring up a
minimal framework to be able to mount the /data partition. During this
period, a tmpfs /data filesystem is created, which doesn't contain any
of the updated APEXEs. As a consequence, all those processes will be
using the APEXes from the /system partition.
This is obviously not desired, as APEXes in /system may be old and/or
contain security issues. Additionally, it would create a difference
between FBE and FDE devices at runtime.
Ideally, we restart all processes that have started after we created the
tmpfs /data. We can't (re)start based on class names alone, because some
classes (eg 'hal') contain services that are required to start apexd
itself and that shouldn't be killed (eg the graphics HAL).
To address this, keep track of which processes are started after /data
is mounted, with a new 'mark_post_data' keyword. Additionally, create
'class_reset_post_data', which resets all services in the class that
were created after the initial /data mount, and 'class_start_post_data',
which starts all services in the class that were started after /data was
mounted.
On a device with FBE, these keywords wouldn't be used; on a device with
FDE, we'd use them to bring down the right processes after the user has
entered the correct secret, and restart them.
Bug: 118485723
Test: manually verified process list
Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb
2019-04-23 16:26:01 +02:00
|
|
|
{"mark_post_data", {0, 0, {false, do_mark_post_data}}},
|
2017-09-13 00:58:47 +02:00
|
|
|
{"mkdir", {1, 4, {true, do_mkdir}}},
|
2018-02-05 17:01:54 +01:00
|
|
|
// TODO: Do mount operations in vendor_init.
|
|
|
|
// mount_all is currently too complex to run in vendor_init as it queues action triggers,
|
|
|
|
// imports rc scripts, etc. It should be simplified and run in vendor_init context.
|
|
|
|
// mount and umount are run in the same context as mount_all for symmetry.
|
2017-09-13 00:58:47 +02:00
|
|
|
{"mount_all", {1, kMax, {false, do_mount_all}}},
|
|
|
|
{"mount", {3, kMax, {false, do_mount}}},
|
2018-11-08 09:14:35 +01:00
|
|
|
{"parse_apex_configs", {0, 0, {false, do_parse_apex_configs}}},
|
2017-09-13 00:58:47 +02:00
|
|
|
{"umount", {1, 1, {false, do_umount}}},
|
init: add umount_all builtin.
umount_all is the cleanup step for mount_all.
In particular, the mount_all builtin creates a verity device,
'postinstall-verity', for the following line:
system /postinstall ... ... slotselect_other,logical,avb_keys=...
cppreopt umounts /postinstall but doesn't destroy the postinstall-verity
device, causing OTA to fail (because it cannot destroy the
system_[other] device). umount_all also destroy the verity device.
Note that mount_all does not map system_[other]; it is mapped by
first stage init. Hence, umount_all doesn't destroy it either. The OTA
client is reponsible for unmapping the device itself.
Bug: 129988285
Test: flash, boot, then check `dmctl list devices`, then OTA
Change-Id: Id3ab65b3860b6ea6cfec310ab13652009c81f415
Merged-In: Id3ab65b3860b6ea6cfec310ab13652009c81f415
2019-04-09 19:11:34 +02:00
|
|
|
{"umount_all", {1, 1, {false, do_umount_all}}},
|
2017-09-13 00:58:47 +02:00
|
|
|
{"readahead", {1, 2, {true, do_readahead}}},
|
|
|
|
{"restart", {1, 1, {false, do_restart}}},
|
|
|
|
{"restorecon", {1, kMax, {true, do_restorecon}}},
|
|
|
|
{"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}},
|
|
|
|
{"rm", {1, 1, {true, do_rm}}},
|
|
|
|
{"rmdir", {1, 1, {true, do_rmdir}}},
|
2018-01-19 01:14:25 +01:00
|
|
|
{"setprop", {2, 2, {true, do_setprop}}},
|
2017-09-13 00:58:47 +02:00
|
|
|
{"setrlimit", {3, 3, {false, do_setrlimit}}},
|
|
|
|
{"start", {1, 1, {false, do_start}}},
|
|
|
|
{"stop", {1, 1, {false, do_stop}}},
|
|
|
|
{"swapon_all", {1, 1, {false, do_swapon_all}}},
|
2019-02-22 14:15:25 +01:00
|
|
|
{"enter_default_mount_ns", {0, 0, {false, do_enter_default_mount_ns}}},
|
2017-09-13 00:58:47 +02:00
|
|
|
{"symlink", {2, 2, {true, do_symlink}}},
|
|
|
|
{"sysclktz", {1, 1, {false, do_sysclktz}}},
|
|
|
|
{"trigger", {1, 1, {false, do_trigger}}},
|
|
|
|
{"verity_update_state", {0, 0, {false, do_verity_update_state}}},
|
|
|
|
{"wait", {1, 2, {true, do_wait}}},
|
2017-10-17 19:43:52 +02:00
|
|
|
{"wait_for_prop", {2, 2, {false, do_wait_for_prop}}},
|
2017-09-13 00:58:47 +02:00
|
|
|
{"write", {2, 2, {true, do_write}}},
|
2015-08-26 20:43:36 +02:00
|
|
|
};
|
2017-03-28 01:27:30 +02:00
|
|
|
// clang-format on
|
2015-08-26 20:43:36 +02:00
|
|
|
return builtin_functions;
|
|
|
|
}
|
2018-02-14 01:50:08 +01:00
|
|
|
// Builtin-function-map end
|
2017-06-22 21:53:17 +02:00
|
|
|
|
|
|
|
} // namespace init
|
|
|
|
} // namespace android
|