mirror of
https://github.com/systemd/systemd.git
synced 2025-03-19 22:50:17 +03:00
Merge 5ad1b0dcd46d894ee7c98f784f2819578415733d into e707d0459cb0143cdd449362ae372173143b970f
This commit is contained in:
commit
cdc8a99bc6
@ -776,6 +776,6 @@ Tools using the Varlink protocol (such as `varlinkctl`) or sd-bus (such as
|
||||
`systemd-tpm2-clear`:
|
||||
|
||||
* `SYSTEMD_TPM2_ALLOW_CLEAR` – takes a boolean. Overrides the effect of the
|
||||
`systemd.factory_reset=` kernel command line option: if set to false,
|
||||
`systemd.tpm2_allow_clear=` kernel command line option: if set to false,
|
||||
requesting a TPM clearing is skipped, and the command immediately exits
|
||||
successfully.
|
||||
|
@ -11,25 +11,69 @@ In various scenarios it is important to be able to reset operating systems back
|
||||
into a "factory state", i.e. where all state, user data and configuration is
|
||||
reset so that it resembles the system state when it was originally shipped.
|
||||
|
||||
systemd natively supports a concept of factory reset, that can both act as a
|
||||
specific implementation for UEFI based systems, as well as a series of hook
|
||||
points and a template for implementations on other systems.
|
||||
|
||||
systemd natively supports a concept of factory reset, through a series of
|
||||
generic hook points that can be integrated with a factory reset mechanism.
|
||||
Factory reset always takes place during early boot, i.e. from a well-defined
|
||||
"clean" state. Factory reset operations may be requested from one boot to be
|
||||
"clean" state. Factory reset operations are requested from one boot to be
|
||||
executed on the next.
|
||||
|
||||
Specifically, the following concepts are available:
|
||||
The mechanism works as follows:
|
||||
|
||||
* The `factory-reset.target` unit may be used to request a factory reset
|
||||
operation and trigger a reboot in order to execute it. It by default executes
|
||||
three services: `systemd-factory-reset-request.service`,
|
||||
`systemd-tpm2-clear.service` and `systemd-factory-reset-reboot.service`.
|
||||
* The `factory-reset.target` unit is used to request a factory reset operation
|
||||
and trigger a reboot in order to execute it. Services invoked via this target
|
||||
prepare the system such that a factory reset will be requested on the next
|
||||
boot. Once these services are done,
|
||||
[`systemd-factory-reset-reboot.service`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset-reboot.service.html)
|
||||
is started, which triggers the reboot.
|
||||
|
||||
* On the next boot, `systemd-factory-reset-generator` checks whether or not a
|
||||
factory reset was requested. A factory reset may be requested via a kernel
|
||||
command line option (`systemd.factory_reset=1`) or via the UEFI variable
|
||||
`FactoryResetRequest` (see below). If either condition is met,
|
||||
`factory-reset-now.target` is added to the boot transaction.
|
||||
|
||||
* `factory-reset-now.target` will be started at boot whenever a factory reset is
|
||||
requested. Services ordered before this target do the work of factory resetting
|
||||
the system. Once these services are done,
|
||||
[`systemd-factory-reset-complete.service`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset-complete.service.html)
|
||||
marks the factory reset operation as completed. The boot process may then
|
||||
continue.
|
||||
|
||||
* The
|
||||
[`systemd-factory-reset`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset.html)
|
||||
tool can be used to query the current state of the factory request mechanism,
|
||||
i.e. whether a factory reset is currently being executed, or if one has been
|
||||
requested for the next boot. It also provides the
|
||||
`/run/systemd/io.systemd.FactoryReset` Varlink service for the same purpose.
|
||||
Early boot services that wish to participate in factory reset should use this
|
||||
service to determine whether the system is currently being reset.
|
||||
|
||||
* Not all systems will support factory reset. It's possible that there's nothing
|
||||
listening for the factory reset request, and nothing happens before
|
||||
`factory-reset-now.target` is reached. To avoid this situation, factory reset
|
||||
should only be requested if the `/run/systemd/factory-reset-supported` stamp
|
||||
file exists. Early boot services that participate in factory reset can create
|
||||
this file if they determine that they have a meaningful amount of work to do.
|
||||
|
||||
* The
|
||||
[`systemd-logind.service(8)`](https://www.freedesktop.org/software/systemd/man/latest/systemd-logind.service.html)
|
||||
unit supports automatically binding factory reset to special keypresses
|
||||
(typically long presses). See the
|
||||
[`logind.conf(5)`](https://www.freedesktop.org/software/systemd/man/latest/logind.conf.html)
|
||||
man page.
|
||||
|
||||
## Implementation for UEFI systems
|
||||
|
||||
systemd also provides an implementation of this mechanism for UEFI-based systems.
|
||||
This implementation can act completely standalone for distributions that rely on
|
||||
tools like `systemd-repart`, but it can also be extended to meet other needs.
|
||||
|
||||
The UEFI support works as follows:
|
||||
|
||||
* The
|
||||
[`systemd-factory-reset-request.service`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset-request.service.html)
|
||||
unit is typically invoked via `factory-reset.target`. It requests a factory
|
||||
reset operation for the next boot by setting the `FactoryResetRequest` EFI
|
||||
unit is invoked via `factory-reset.target`. It requests a factory reset
|
||||
operation for the next boot by setting the `FactoryResetRequest` EFI
|
||||
variable. The EFI variable contains information about the requesting OS, so
|
||||
that multi-boot scenarios are somewhat covered.
|
||||
|
||||
@ -41,79 +85,36 @@ Specifically, the following concepts are available:
|
||||
invalidating all prior keys associated with the security chip and generating
|
||||
a new seed key.
|
||||
|
||||
* The
|
||||
[`systemd-factory-reset-reboot.service`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset-reboot.service.html)
|
||||
unit automatically reboots the system as part of `factory-reset.target`. It
|
||||
is ordered after `systemd-tpm2-clear.service` and
|
||||
`systemd-factory-reset-request.service` in order to initiate the reboot that
|
||||
is supposed to execute the factory reset operations.
|
||||
|
||||
* The `factory-reset-now.target` unit is started at boot whenever a factory
|
||||
reset is requested for the boot. A factory reset may be requested via a
|
||||
kernel command line option (`systemd.factory_reset=1`) or via the UEFI
|
||||
variable `FactoryResetRequest` (see above). The
|
||||
`systemd-factory-reset-generator` unit generator checks both these conditions
|
||||
and adds `factory-reset-now.target` to the boot transaction, already in the
|
||||
initial RAM disk (initrd).
|
||||
|
||||
* The
|
||||
[`systemd-factory-reset-complete.service`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset-complete.service.html)
|
||||
unit is invoked after `factory-reset-now.target` and marks the factory reset
|
||||
operation as complete. The boot process then may continue.
|
||||
|
||||
* The
|
||||
[`systemd-repart`](https://www.freedesktop.org/software/systemd/man/latest/systemd-repart.html)
|
||||
tool can take the factory reset logic into account. Either on explicit
|
||||
request via the `--factory-reset=` logic, or automatically derived from the
|
||||
aforementioned kernel command line switch and EFI variable. When invoked for
|
||||
factory reset it will securely erase all partitions marked for that via the
|
||||
tool is one of the early-boot services that do the work of factory resetting
|
||||
the system. In normal operation, it starts on every boot. When invoked during
|
||||
a factory reset, it will erase all partitions marked for that via the
|
||||
`FactoryReset=` setting in its partition definition files. Once that is
|
||||
complete it will execute the usual setup operation, i.e. format new
|
||||
partitions again.
|
||||
|
||||
* The
|
||||
[`systemd-logind.service(8)`](https://www.freedesktop.org/software/systemd/man/latest/systemd-logind.service.html)
|
||||
unit supports automatically binding factory reset to special keypresses
|
||||
(typically long presses), see the
|
||||
[`logind.conf(5)`](https://www.freedesktop.org/software/systemd/man/latest/logind.conf.html)
|
||||
man page.
|
||||
|
||||
* The
|
||||
[`systemd-factory-reset`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset.html)
|
||||
tool can be used to query the current state of the factory request mechanism,
|
||||
i.e. whether a factory reset is currently being executed, or if one has been
|
||||
requested for the next boot.
|
||||
|
||||
* The `/run/systemd/io.systemd.FactoryReset` Varlink service provides two IPC
|
||||
APIs for working with factory reset: it permits querying whether the local
|
||||
system supports requesting a factory reset by starting
|
||||
`factory-reset.target`. This may be used by UIs to hide or show in the UI an
|
||||
interface to request a factory reset. The Varlink IPC service also reports
|
||||
the current factory reset state, much like the `systemd-factory-reset` tool
|
||||
mentioned above. This may be used by various early boot services that
|
||||
potentially intent to reset system state during a factory reset operation.
|
||||
|
||||
## Exposure in the UI
|
||||
|
||||
If a graphical UI shall expose a factory reset operation it should first check
|
||||
if requesting a factory reset is supported at all via the Varlink service
|
||||
mentioned above. Once a factory reset shall be executed it shall ask for
|
||||
activation of the `factory-reset.target` unit.
|
||||
|
||||
Alternatively, `systemd-logind.service`'s hotkey support may be used, for
|
||||
example to request factory reset if the reboot button is pressed for a long
|
||||
time.
|
||||
complete, it will resume its usual setup operation, i.e. reformatting the
|
||||
empty partition with a file system. If any partition definitions have
|
||||
`FactoryReset=` enabled, `systemd-repart` will create the
|
||||
`/run/systemd/factory-reset-supported` stamp file.
|
||||
|
||||
## Support for non-UEFI Systems
|
||||
|
||||
The above is a relatively bespoke solution for EFI systems. It uses EFI
|
||||
variables as stateful memory to request the factory reset on the next boot.
|
||||
On non-EFI systems, the `FactoryResetRequest` EFI variable cannot be used to
|
||||
communicate the factory reset request to the next boot. Instead, a service that
|
||||
somehow stores the request should be plugged into `factory-reset.target`. At
|
||||
boot, the request should then be fed back into the booted kernel via the
|
||||
`systemd.factory_reset=1` kernel command line option.
|
||||
|
||||
On non-EFI systems, a different mechanism should be devised. A service
|
||||
requesting the factory request can then be plugged into
|
||||
`factory-reset.target`. At boot the request should then be fed back to the
|
||||
booted kernel via the `systemd.factory_reset=1` kernel command line option, in
|
||||
order to execute the reset operation.
|
||||
## Exposure in the UI
|
||||
|
||||
If a graphical UI shall expose a factory reset operation, it should first check
|
||||
if requesting a factory reset is supported at all. This can be achieved by
|
||||
checking whether `/run/systemd/factory-reset-supported` exists. Once the end-user
|
||||
triggers a factory reset, the UI can start the process by asking systemd to
|
||||
activate the `factory-reset.target` unit.
|
||||
|
||||
Alternatively, `systemd-logind.service`'s hotkey support may be used. For
|
||||
example, it can be configured to request factory reset if the reboot button is
|
||||
pressed for a long time.
|
||||
|
||||
## Support for Resetting other Resources than Partitions + TPM
|
||||
|
||||
@ -122,16 +123,34 @@ partitions (via `systemd-repart`, see above) and reset the TPM (via
|
||||
`systemd-tpm2-clear.service`, see above).
|
||||
|
||||
In some cases other resources shall be reset/erased too. To support that,
|
||||
define your own service and plug it into `factory-reset-now.target`, ensuring
|
||||
it is ordered before that.
|
||||
define your own service and plug it into `factory-reset-now.target`. Ensure that
|
||||
your service is ordered before the target!
|
||||
|
||||
If your service should be enough to enable factory reset support, it should be
|
||||
create `/run/systemd/factory-reset-supported` on every boot. Order the service
|
||||
before `factory-reset.target`. You can use the Varlink API to determine at
|
||||
runtime whether or not your service needs to perform the factory reset.
|
||||
|
||||
## Factory Reset via Boot Menu
|
||||
|
||||
Factory reset can also be requested via the boot menu. A simple factory reset
|
||||
(that does not touch the TPM) at boot can be requested via a boot menu item
|
||||
containing the `systemd.factory_reset=1` kernel command line option. A more
|
||||
comprehensive factory reset operation (that also erases the TPM) can be
|
||||
requested by booting with `rd.systemd.unit=factory-reset.target`. Note that the
|
||||
latter will require one reboot (required since that's how TPM resets work),
|
||||
while the former will reset state and continue running without an additional
|
||||
reboot.
|
||||
Factory reset can also be requested via the boot menu, by booting with certain
|
||||
kernel command line arguments. The specifics vary by distribution, but here
|
||||
are some pointers:
|
||||
|
||||
The most potable solution would be to boot with
|
||||
`rd.systemd.unit=factory-reset.target` set. This will execute the entire factory
|
||||
reset process from within the initrd, including a reboot. To preserve the state
|
||||
of the TPM, you can pass `systemd.tpm2_allow_clear=false`.
|
||||
|
||||
Depending on the way your distribution uses the factory reset integration points,
|
||||
a simpler case may be possible. Booting with `systemd.factory_reset=1` will
|
||||
bypass `factory-reset.target` entirely, and skip a reboot. However, bear in mind
|
||||
that this may have unintended consequences: some firmware or hardware may not be
|
||||
completely reset this way, including the TPM.
|
||||
|
||||
Note that the portable solution requires that distributions include their entire
|
||||
factory reset integration in the initrd. If that is undesirable, alternatives
|
||||
do exist. For instance, image-based distributions that separate `/usr` from the
|
||||
rootfs can use something like `root=tmpfs systemd.unit=factory-reset.target` to
|
||||
trigger the factory reset from the real `/usr`.
|
||||
|
||||
|
@ -142,6 +142,9 @@ static int verb_request(int argc, char *argv[], void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (f == FACTORY_RESET_UNSUPPORTED || access("/run/systemd/factory-reset-supported", F_OK) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Factory reset is unsupported, refusing to request it.");
|
||||
if (!is_efi_boot())
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Not an EFI boot, requesting factory reset via EFI variable not supported.");
|
||||
@ -316,18 +319,6 @@ static int vl_method_get_factory_reset_mode(sd_varlink *link, sd_json_variant *p
|
||||
return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_STRING("mode", factory_reset_mode_to_string(f)));
|
||||
}
|
||||
|
||||
static int vl_method_can_request_factory_reset(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
int r;
|
||||
|
||||
assert(parameters);
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, /* table= */ NULL, /* userdata= */ NULL);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_BOOLEAN("supported", is_efi_boot()));
|
||||
}
|
||||
|
||||
static int varlink_service(void) {
|
||||
int r;
|
||||
|
||||
@ -344,8 +335,7 @@ static int varlink_service(void) {
|
||||
|
||||
r = sd_varlink_server_bind_method_many(
|
||||
varlink_server,
|
||||
"io.systemd.FactoryReset.GetFactoryResetMode", vl_method_get_factory_reset_mode,
|
||||
"io.systemd.FactoryReset.CanRequestFactoryReset", vl_method_can_request_factory_reset);
|
||||
"io.systemd.FactoryReset.GetFactoryResetMode", vl_method_get_factory_reset_mode);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind Varlink methods: %m");
|
||||
|
||||
|
@ -140,6 +140,7 @@ static char *arg_image = NULL;
|
||||
static char **arg_definitions = NULL;
|
||||
static bool arg_discard = true;
|
||||
static bool arg_can_factory_reset = false;
|
||||
static bool arg_stamp_factory_reset = false;
|
||||
static int arg_factory_reset = -1;
|
||||
static sd_id128_t arg_seed = SD_ID128_NULL;
|
||||
static bool arg_randomize = false;
|
||||
@ -8054,6 +8055,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
|
||||
ARG_DISCARD,
|
||||
ARG_FACTORY_RESET,
|
||||
ARG_CAN_FACTORY_RESET,
|
||||
ARG_STAMP_FACTORY_RESET,
|
||||
ARG_ROOT,
|
||||
ARG_IMAGE,
|
||||
ARG_IMAGE_POLICY,
|
||||
@ -8100,6 +8102,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
|
||||
{ "discard", required_argument, NULL, ARG_DISCARD },
|
||||
{ "factory-reset", required_argument, NULL, ARG_FACTORY_RESET },
|
||||
{ "can-factory-reset", no_argument, NULL, ARG_CAN_FACTORY_RESET },
|
||||
{ "stamp-factory-reset", no_argument, NULL, ARG_STAMP_FACTORY_RESET },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "image", required_argument, NULL, ARG_IMAGE },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
@ -8202,6 +8205,10 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
|
||||
arg_can_factory_reset = true;
|
||||
break;
|
||||
|
||||
case ARG_STAMP_FACTORY_RESET:
|
||||
arg_stamp_factory_reset = true;
|
||||
break;
|
||||
|
||||
case ARG_ROOT:
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root);
|
||||
if (r < 0)
|
||||
@ -9262,14 +9269,15 @@ static int run(int argc, char *argv[]) {
|
||||
return r;
|
||||
context->from_scratch = r > 0; /* Starting from scratch */
|
||||
|
||||
if (arg_can_factory_reset) {
|
||||
r = context_can_factory_reset(context);
|
||||
r = context_can_factory_reset(context);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (arg_can_factory_reset)
|
||||
return r == 0 ? EXIT_FAILURE : 0;
|
||||
if (arg_stamp_factory_reset && r > 0) {
|
||||
r = touch("/run/systemd/factory-reset-supported");
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
return 0;
|
||||
return log_error_errno(r, "Failed to create /run/systemd/factory-reset-supported file: %m");
|
||||
}
|
||||
|
||||
r = context_factory_reset(context);
|
||||
|
@ -8,7 +8,8 @@
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Factory Reset Initiation
|
||||
Description=Initiate Factory Reset
|
||||
Documentation=man:systemd.special(7)
|
||||
Wants=systemd-factory-reset-reboot.service
|
||||
Before=systemd-factory-reset-reboot.service
|
||||
AssertPathExists=/run/systemd/factory-reset-supported
|
||||
|
@ -22,14 +22,14 @@ ConditionDirectoryNotEmpty=|/sysusr/usr/local/lib/repart.d
|
||||
DefaultDependencies=no
|
||||
Wants=modprobe@loop.service modprobe@dm_mod.service
|
||||
After=initrd-usr-fs.target modprobe@loop.service modprobe@dm_mod.service systemd-tpm2-setup-early.service
|
||||
Before=initrd-root-fs.target factory-reset-now.target
|
||||
Before=initrd-root-fs.target factory-reset-now.target factory-reset.target
|
||||
Conflicts=shutdown.target initrd-switch-root.target
|
||||
Before=shutdown.target initrd-switch-root.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=systemd-repart --dry-run=no
|
||||
ExecStart=systemd-repart --dry-run=no --stamp-factory-reset
|
||||
|
||||
# The tool returns 76 if it can't find the root block device
|
||||
SuccessExitStatus=76
|
||||
|
Loading…
x
Reference in New Issue
Block a user