1
0
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:
Adrian Vovk 2025-03-13 18:21:24 +01:00 committed by GitHub
commit cdc8a99bc6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 131 additions and 113 deletions

View File

@ -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.

View File

@ -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`.

View File

@ -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");

View File

@ -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);

View File

@ -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

View File

@ -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