max / alloy
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
1 file changed,
+41 insertions,
-5 deletions
| @@ -1545,11 +1545,20 @@ | |||
| 1545 | 1545 | verdict: Box::new(update_reference_parses), | |
| 1546 | 1546 | }, | |
| 1547 | 1547 | // A way in on first boot. | |
| 1548 | + | // | |
| 1549 | + | // The whole listing rather than `list-unit-files greetd.service`, and | |
| 1550 | + | // the difference is the server profile being installable at all. | |
| 1551 | + | // Measured 2026-08-25 by installing a server-profile ISO in | |
| 1552 | + | // build/vmtest: a pattern that matches nothing exits 1, systemd prints | |
| 1553 | + | // `0 unit files listed`, and a check that cannot run its own verdict | |
| 1554 | + | // fails the install. That profile prunes greetd on purpose, so the | |
| 1555 | + | // pattern form failed every server install and passed every client one. | |
| 1556 | + | // Unfiltered, the command exits 0 either way and absence reaches | |
| 1557 | + | // [`greeter_ready`], which is where the two are told apart. | |
| 1548 | 1558 | Check { | |
| 1549 | 1559 | invocation: Invocation::new("systemctl") | |
| 1550 | 1560 | .arg(format!("--root={root}")) | |
| 1551 | - | .arg("list-unit-files") | |
| 1552 | - | .arg(GREETER_UNIT), | |
| 1561 | + | .arg("list-unit-files"), | |
| 1553 | 1562 | verdict: Box::new(|listing| greeter_ready(listing, GREETER_UNIT)), | |
| 1554 | 1563 | }, | |
| 1555 | 1564 | ]; | |
| @@ -1696,9 +1705,11 @@ | |||
| 1696 | 1705 | ||
| 1697 | 1706 | /// Whether the target will present a way to log in. | |
| 1698 | 1707 | /// | |
| 1699 | - | /// Reads a `systemctl list-unit-files` listing, which exits 0 whether the unit | |
| 1700 | - | /// exists or not, so absence and disabled-ness are told apart here rather than | |
| 1701 | - | /// by an exit status. | |
| 1708 | + | /// Reads a whole `systemctl list-unit-files` listing, which exits 0 whether the | |
| 1709 | + | /// unit is there or not, so absence and disabled-ness are told apart here rather | |
| 1710 | + | /// than by an exit status. Asking for the unit by name does not have that | |
| 1711 | + | /// property: a pattern matching nothing exits 1, which is why the caller does | |
| 1712 | + | /// not narrow the listing. | |
| 1702 | 1713 | /// | |
| 1703 | 1714 | /// Absent is fine and not an oversight: the `server` profile prunes greetd | |
| 1704 | 1715 | /// deliberately and logs in on a getty. Present but disabled is the failure, | |
| @@ -8429,6 +8440,31 @@ | |||
| 8429 | 8440 | ); | |
| 8430 | 8441 | } | |
| 8431 | 8442 | ||
| 8443 | + | // The listing the check actually reads is the whole one, and on a server | |
| 8444 | + | // machine greetd is simply not in it. The shape that failed every server | |
| 8445 | + | // install was asking systemd for the unit by name instead. | |
| 8446 | + | #[test] | |
| 8447 | + | fn a_server_listing_with_no_greeter_line_passes() { | |
| 8448 | + | let listing = "UNIT FILE STATE PRESET\n\ | |
| 8449 | + | getty@.service enabled enabled\n\ | |
| 8450 | + | sshd.service enabled enabled\n\ | |
| 8451 | + | firewalld.service enabled enabled\n\n\ | |
| 8452 | + | 3 unit files listed.\n"; | |
| 8453 | + | assert!(greeter_ready(listing, GREETER_UNIT).is_ok()); | |
| 8454 | + | } | |
| 8455 | + | ||
| 8456 | + | // And it still finds a disabled greeter in a listing full of other units, | |
| 8457 | + | // which is the case the unfiltered read must not lose. | |
| 8458 | + | #[test] | |
| 8459 | + | fn a_disabled_greeter_is_still_found_in_a_full_listing() { | |
| 8460 | + | let listing = "UNIT FILE STATE PRESET\n\ | |
| 8461 | + | getty@.service enabled enabled\n\ | |
| 8462 | + | greetd.service disabled enabled\n\ | |
| 8463 | + | sshd.service enabled enabled\n\n\ | |
| 8464 | + | 3 unit files listed.\n"; | |
| 8465 | + | assert!(greeter_ready(listing, GREETER_UNIT).is_err()); | |
| 8466 | + | } | |
| 8467 | + | ||
| 8432 | 8468 | fn choices(encryption: Option<&EncryptionChoice>) -> Choices<'_> { | |
| 8433 | 8469 | Choices { | |
| 8434 | 8470 | hostname: "alloy", |