|
42 | 42 | #include "ostree-mount-util.h" |
43 | 43 | #include "otcore.h" |
44 | 44 |
|
| 45 | +static gboolean opt_shutdown; |
| 46 | + |
| 47 | +static GOptionEntry options[] = { { "shutdown", 'S', 0, G_OPTION_ARG_NONE, &opt_shutdown, |
| 48 | + "Perform shutdown unmounting", NULL }, |
| 49 | + { NULL } }; |
| 50 | + |
45 | 51 | static void |
46 | 52 | do_remount (const char *target, bool writable) |
47 | 53 | { |
@@ -133,10 +139,61 @@ relabel_dir_for_upper (const char *upper_path, const char *real_path, gboolean i |
133 | 139 | #endif |
134 | 140 | } |
135 | 141 |
|
| 142 | +// ostree-prepare-root sets things up so that /sysroot points to the "physical" (real) root in the |
| 143 | +// initramfs, and then with composefs `/` is an overlay+EROFS that holds references to content in |
| 144 | +// that physical filesystem. |
| 145 | +// |
| 146 | +// In a typical mutable system where the OS is in a mutable `/` (or `/usr), systemd explicitly |
| 147 | +// skips unmounting both `/` and `/usr`. It will remount them read-only though - and that's |
| 148 | +// the semantic we want to match here. |
| 149 | +static void |
| 150 | +do_shutdown (void) |
| 151 | +{ |
| 152 | + const char *sysroot = "/sysroot"; |
| 153 | + if (mount (sysroot, sysroot, NULL, MS_REMOUNT | MS_SILENT | MS_RDONLY, NULL) < 0) |
| 154 | + { |
| 155 | + // Hopefully at this point nothing has any write references, but if they |
| 156 | + // do we still want to continue. |
| 157 | + perror ("Remounting /sysroot read-only"); |
| 158 | + } |
| 159 | + // And fully detach it from the mountns because otherwise systemd thinks |
| 160 | + // it can be unmounted, but it can't - it's required by `/` (and in a |
| 161 | + // composefs setup `/etc`) and possibly `/var`. Again, we only really |
| 162 | + // care that it got mounted read-only and hence outstanding data flushed. |
| 163 | + // A better fix in the future would be to teach systemd to honor `-.mount` |
| 164 | + // having a `Requires=sysroot.mount` meaning we can't unmount the latter. |
| 165 | + if (umount2 (sysroot, MNT_DETACH) < 0) |
| 166 | + err (EXIT_FAILURE, "umount(/sysroot)"); |
| 167 | + |
| 168 | + // And finally: /etc |
| 169 | + // NOTE! This one is intentionally last in that we want to try to make |
| 170 | + // this read-only, but if it fails, systemd-shutdown will have another |
| 171 | + // attempt after a process killing spree. If anything happens to be |
| 172 | + // holding a writable fd at this point, conceptually it would have |
| 173 | + // created race conditions vs ostree-finalize-staged.service, and so |
| 174 | + // having this service fail will be a signal that those things need |
| 175 | + // to be fixed. |
| 176 | + do_remount ("/etc", false); |
| 177 | + // Don't add anything else after this. |
| 178 | +} |
| 179 | + |
136 | 180 | int |
137 | 181 | main (int argc, char *argv[]) |
138 | 182 | { |
139 | 183 | g_autoptr (GError) error = NULL; |
| 184 | + g_autoptr (GOptionContext) context = g_option_context_new (""); |
| 185 | + g_option_context_add_main_entries (context, options, NULL); |
| 186 | + if (!g_option_context_parse (context, &argc, &argv, &error)) |
| 187 | + errx (EXIT_FAILURE, "Error parsing options: %s", error->message); |
| 188 | + |
| 189 | + // Handle the shutdown option |
| 190 | + if (opt_shutdown) |
| 191 | + { |
| 192 | + do_shutdown (); |
| 193 | + return 0; |
| 194 | + } |
| 195 | + // Otherwise fall through to the default startup |
| 196 | + |
140 | 197 | g_autoptr (GVariant) ostree_run_metadata_v = NULL; |
141 | 198 | { |
142 | 199 | glnx_autofd int fd = open (OTCORE_RUN_BOOTED, O_RDONLY | O_CLOEXEC); |
|
0 commit comments