summaryrefslogtreecommitdiff
path: root/src/libostree/ostree-impl-system-generator.c
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2017-05-11 14:54:12 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2017-05-16 16:13:05 +0000
commit30705889cb867c18cdb7fed8e55dc46477c069fa (patch)
treeb6247fa4e4d51ad7a44bfef90381f3aad47c9d52 /src/libostree/ostree-impl-system-generator.c
parentd815ba2a81ad14d9d4edc31dcd282dcc2a3a8fb9 (diff)
downloadostree-30705889cb867c18cdb7fed8e55dc46477c069fa.tar.gz
Switch to using a systemd generator for /var
If one wants to set up a mount for `/var` in `/etc/fstab`, it won't be mounted since `ostree-prepare-root` set up a bind mount for `/var` to `/sysroot/ostree/$stateroot/var`, and systemd will take the already extant mount over what's in `/etc/fstab`. There are a few options to fix this, but what I settled on is parsing `/etc/fstab` in a generator (exactly like `systemd-fstab-generator` does), except here we look for an explicit mount for `/var`, and if one *isn't* found, synthesize the default ostree mount to the stateroot. Another nice property is that if an admin creates a `var.mount` unit in `/etc` for example, that will also override our mount. Note that today ostree doesn't hard depend on systemd, so this behavior only kicks in if we're built with systemd *and* libmount support (for parsing `/etc/fstab`). I didn't really test that case though. Initially I started writing this as a "pure libc" program, but at one point decided to use `libostree.so` to find the booted deployment. That didn't work out because `/boot` wasn't necessarily mounted and hence we couldn't find the bootloader config. A leftover artifact from this is that the generator code calls into libostree via the "cmd private" infrastructure. But it's an easy way to share code, and doesn't hurt. Closes: #859 Approved by: jlebon
Diffstat (limited to 'src/libostree/ostree-impl-system-generator.c')
-rw-r--r--src/libostree/ostree-impl-system-generator.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/src/libostree/ostree-impl-system-generator.c b/src/libostree/ostree-impl-system-generator.c
new file mode 100644
index 00000000..7c4d49df
--- /dev/null
+++ b/src/libostree/ostree-impl-system-generator.c
@@ -0,0 +1,219 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2017 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <glib-unix.h>
+#include <gio/gunixoutputstream.h>
+#include <errno.h>
+#include <stdio.h>
+#ifdef HAVE_LIBMOUNT
+#include <libmount.h>
+#endif
+#include <stdbool.h>
+#include "otutil.h"
+
+#include "ostree.h"
+#include "ostree-core-private.h"
+#include "ostree-cmdprivate.h"
+
+#ifdef HAVE_LIBMOUNT
+typedef FILE OtLibMountFile;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(OtLibMountFile, endmntent);
+
+/* Taken from systemd path-util.c */
+static bool
+is_path (const char *p)
+{
+ return !!strchr (p, '/');
+}
+
+/* Taken from systemd path-util.c */
+static char*
+path_kill_slashes (char *path)
+{
+ char *f, *t;
+ bool slash = false;
+
+ /* Removes redundant inner and trailing slashes. Modifies the
+ * passed string in-place.
+ *
+ * For example: ///foo///bar/ becomes /foo/bar
+ */
+
+ for (f = path, t = path; *f; f++)
+ {
+ if (*f == '/')
+ {
+ slash = true;
+ continue;
+ }
+
+ if (slash)
+ {
+ slash = false;
+ *(t++) = '/';
+ }
+
+ *(t++) = *f;
+ }
+
+ /* Special rule, if we are talking of the root directory, a
+ trailing slash is good */
+
+ if (t == path && slash)
+ *(t++) = '/';
+
+ *t = 0;
+ return path;
+}
+
+/* Written by ostree-sysroot-deploy.c. We parse out the stateroot here since we
+ * need to know it to mount /var. Unfortunately we can't easily use the
+ * libostree API to find the booted deployment since /boot might not have been
+ * mounted yet.
+ */
+static char *
+stateroot_from_ostree_cmdline (const char *ostree_cmdline,
+ GError **error)
+{
+ static GRegex *regex;
+ static gsize regex_initialized;
+ if (g_once_init_enter (&regex_initialized))
+ {
+ regex = g_regex_new ("^/ostree/boot.[01]/([^/]+)/", 0, 0, NULL);
+ g_assert (regex);
+ g_once_init_leave (&regex_initialized, 1);
+ }
+
+ g_autoptr(GMatchInfo) match = NULL;
+ if (!g_regex_match (regex, ostree_cmdline, 0, &match))
+ return glnx_null_throw (error, "Failed to parse %s", ostree_cmdline);
+
+ return g_match_info_fetch (match, 1);
+}
+#endif
+
+/* Implementation of ostree-system-generator */
+gboolean
+_ostree_impl_system_generator (const char *ostree_cmdline,
+ const char *normal_dir,
+ const char *early_dir,
+ const char *late_dir,
+ GError **error)
+{
+#ifdef HAVE_LIBMOUNT
+ /* Not currently cancellable, but define a var in case we care later */
+ GCancellable *cancellable = NULL;
+ /* Some path constants to avoid typos */
+ static const char fstab_path[] = "/etc/fstab";
+ static const char var_path[] = "/var";
+
+ /* ostree-prepare-root was patched to write the stateroot to this file */
+ g_autofree char *stateroot = stateroot_from_ostree_cmdline (ostree_cmdline, error);
+ if (!stateroot)
+ return FALSE;
+
+ /* Load /etc/fstab if it exists, and look for a /var mount */
+ g_autoptr(OtLibMountFile) fstab = setmntent (fstab_path, "re");
+ gboolean found_var_mnt = FALSE;
+ if (!fstab)
+ {
+ if (errno != ENOENT)
+ return glnx_throw_errno_prefix (error, "Reading %s", fstab_path);
+ }
+ else
+ {
+ /* Parse it */
+ struct mntent *me;
+ while ((me = getmntent (fstab)))
+ {
+ g_autofree char *where = g_strdup (me->mnt_dir);
+ if (is_path (where))
+ path_kill_slashes (where);
+
+ /* We're only looking for /var here */
+ if (strcmp (where, var_path) != 0)
+ continue;
+
+ found_var_mnt = TRUE;
+ break;
+ }
+ }
+
+ /* If we found /var, we're done */
+ if (found_var_mnt)
+ return TRUE;
+
+ /* Prepare to write to the output unit dir; we use the "normal" dir
+ * that overrides /usr, but not /etc.
+ */
+ glnx_fd_close int normal_dir_dfd = -1;
+ if (!glnx_opendirat (AT_FDCWD, normal_dir, TRUE, &normal_dir_dfd, error))
+ return FALSE;
+
+ /* Generate our bind mount unit */
+ const char *stateroot_var_path = glnx_strjoina ("/sysroot/ostree/deploy/", stateroot, "/var");
+
+ glnx_fd_close int tmpfd = -1;
+ g_autofree char *tmppath = NULL;
+ if (!glnx_open_tmpfile_linkable_at (normal_dir_dfd, ".", O_WRONLY,
+ &tmpfd, &tmppath, error))
+ return FALSE;
+ g_autoptr(GOutputStream) outstream = g_unix_output_stream_new (tmpfd, FALSE);
+ gsize bytes_written;
+ /* This code is inspired by systemd's fstab-generator.c.
+ *
+ * Note that our unit doesn't run if systemd.volatile is enabled;
+ * see https://github.com/ostreedev/ostree/pull/856
+ */
+ if (!g_output_stream_printf (outstream, &bytes_written, cancellable, error,
+ "##\n# Automatically generated by ostree-system-generator\n##\n\n"
+ "[Unit]\n"
+ "Documentation=man:ostree(1)\n"
+ "ConditionKernelCommandLine=!systemd.volatile\n"
+ /* We need /sysroot mounted writable first */
+ "After=ostree-remount.service\n"
+ "Before=local-fs.target\n"
+ "\n"
+ "[Mount]\n"
+ "Where=%s\n"
+ "What=%s\n"
+ "Options=bind\n",
+ var_path,
+ stateroot_var_path))
+ return FALSE;
+ if (!g_output_stream_flush (outstream, cancellable, error))
+ return FALSE;
+ g_clear_object (&outstream);
+ /* It should be readable */
+ if (fchmod (tmpfd, 0644) < 0)
+ return glnx_throw_errno_prefix (error, "fchmod");
+ /* Error out if somehow it already exists, that'll help us debug conflicts */
+ if (!glnx_link_tmpfile_at (normal_dir_dfd, GLNX_LINK_TMPFILE_NOREPLACE,
+ tmpfd, tmppath, normal_dir_dfd,
+ "var.mount", error))
+ return FALSE;
+
+ return TRUE;
+#else
+ return glnx_throw (error, "Not implemented");
+#endif
+}