mirror of
https://github.com/systemd/systemd.git
synced 2025-03-28 02:50:16 +03:00
factory-reset: Redo logic for checking for support
Previously, the factory reset Varlink service provided a method for checking whether factory reset is supported on a system. However, this method actually just checked if the system is using EFI as its firmware. Really, there's two things to check here: can we communicate the factory reset request to the next boot, and does the operating system actually know how to factory reset itself. The old EFI check failed on both fronts. It was clearly intended to address only the "can we communicate" case. However, the factory reset hooks were designed to support non-EFI systems too, and it's entirely possible that a non-EFI system can persist the request for the next boot. So: an EFI check is insufficient. The second question is the more useful one anyway. Being able to request a factory reset on the next boot isn't particularly useful if there's nothing actually _performing_ the factory request. So, I redid the way this works. It's actually quite hard to answer the first question: on non-EFI systems, something would have to somehow tell the Varlink service that requests _can_ be persisted. Instead, I decided to focus on the second question. If there is something configured to destroy data when a factory reset is requested, then we can pretty safely assume that the OS supports factory resetting. So, I have the initrd communicate to userspace that "something is ready to respond to a factory reset request here". I don't think it's very likely that there will be an OS that supports factory resetting in the initrd, but can't manage to communicate the request across a reboot. So this is an acceptable compromise to address both questions. Why drop the Varlink API and just have apps read the stamp file directly? I figured that the extra layer of indirection is useless. The Varlink API would just be a very ineffeicient (and harder to implement!) way to check for the presence of the stamp file...
This commit is contained in:
parent
cdc6710cf3
commit
4ba31a115a
@ -43,16 +43,17 @@ The mechanism works as follows:
|
||||
[`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.
|
||||
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.
|
||||
|
||||
* 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.
|
||||
* 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)
|
||||
@ -91,7 +92,9 @@ The UEFI support works as follows:
|
||||
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 resume its usual setup operation, i.e. reformatting the
|
||||
empty partition with a file system.
|
||||
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
|
||||
|
||||
@ -104,9 +107,10 @@ boot, the request should then be fed back into the booted kernel via the
|
||||
## 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 the end-user triggers a factory reset, the UI can start
|
||||
the process by asking systemd to activate the `factory-reset.target` unit.
|
||||
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
|
||||
@ -119,8 +123,13 @@ 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` or the
|
||||
Varlink service. Ensure that your service is ordered before the target.
|
||||
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
|
||||
|
||||
|
@ -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