diff options
author | Colin Walters <walters@verbum.org> | 2014-05-08 09:16:36 -0400 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2014-05-08 18:59:24 -0400 |
commit | f47a20fb816a16532d6f9885b046d6a4aed278c1 (patch) | |
tree | 4eb23638c9abfc3e908e9d19e309ed967452d846 | |
parent | 466671407de87cc366b4cb5c590d108a5516e30d (diff) | |
download | ostree-f47a20fb816a16532d6f9885b046d6a4aed278c1.tar.gz |
Support /etc/ostree/remotes.d
For many OS install scenarios, one runs through an installer which may
come with embedded data, and then the OS is configured post-install to
receive updates.
In this model, it'd be nice to avoid the post-install having to rewrite
the /ostree/repo/config file.
Additionally, it feels weird for admins to interact with "/ostree" -
let's make the system feel more like Unix and have our important
configuration in /etc.
https://bugzilla.gnome.org/show_bug.cgi?id=729343
-rw-r--r-- | Makefile-libostree.am | 7 | ||||
-rw-r--r-- | Makefile-ostree.am | 4 | ||||
-rw-r--r-- | doc/ostree.repo-config.xml | 13 | ||||
-rw-r--r-- | src/libostree/ostree-repo.c | 155 | ||||
-rw-r--r-- | src/libostree/ostree-repo.h | 2 | ||||
-rw-r--r-- | src/ostree/ot-builtin-remote.c | 121 |
6 files changed, 274 insertions, 28 deletions
diff --git a/Makefile-libostree.am b/Makefile-libostree.am index d2c039cf..509378a5 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -95,7 +95,9 @@ libostree_1_la_SOURCES += \ $(NULL) endif -libostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -DLOCALEDIR=\"$(datadir)/locale\" -DGPGVPATH=\"$(GPGVPATH)\" $(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_DEP_LZMA_CFLAGS) +libostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree \ + -DLOCALEDIR=\"$(datadir)/locale\" -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DGPGVPATH=\"$(GPGVPATH)\" $(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_DEP_LZMA_CFLAGS) libostree_1_la_LDFLAGS = -version-number 1:0:0 -Bsymbolic-functions -export-symbols-regex '^ostree_' libostree_1_la_LIBADD = libotutil.la libostree-kernel-args.la $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_DEP_LZMA_LIBS) @@ -139,3 +141,6 @@ gpgreadmedir = $(pkgdatadir)/trusted.gpg.d EXTRA_DIST += src/libostree/README-gpg endif +install-mkdir-remotes-d-hook: + mkdir -p $(sysconfdir)/ostree/remotes.d +INSTALL_DATA_HOOKS += install-mkdir-remotes-d-hook diff --git a/Makefile-ostree.am b/Makefile-ostree.am index 219276f6..f1381acd 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -70,7 +70,9 @@ ostree_SOURCES += \ src/ostree/ot-admin-functions.c \ $(NULL) -ostree_bin_shared_cflags = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -I$(srcdir)/src/ostree -DLOCALEDIR=\"$(datadir)/locale\" +ostree_bin_shared_cflags = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -I$(srcdir)/src/ostree \ + -DLOCALEDIR=\"$(datadir)/locale\" -DSYSCONFDIR=\"$(sysconfdir)\" \ + $(NULL) ostree_bin_shared_ldadd = libotutil.la libostree-kernel-args.la libostree-1.la ostree_CFLAGS = $(ostree_bin_shared_cflags) $(OT_INTERNAL_GIO_UNIX_CFLAGS) diff --git a/doc/ostree.repo-config.xml b/doc/ostree.repo-config.xml index 42408143..d2f65421 100644 --- a/doc/ostree.repo-config.xml +++ b/doc/ostree.repo-config.xml @@ -131,6 +131,19 @@ Boston, MA 02111-1307, USA. </variablelist> </refsect1> + + <refsect1> + <title>/etc/ostree/remotes.d</title> + + <para> + In addition to the <filename>/ostree/repo/config</filename> + file, remotes may also be specified in + <filename>/etc/ostree/remotes.d</filename>. The remote + configuration file must end in <literal>.conf</literal>; files + whose name does not end in <literal>.conf</literal> will be + ignored. + </para> + </refsect1> <refsect1> <title>See Also</title> diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index f8595409..41ba5f87 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -224,6 +224,12 @@ ostree_repo_new (GFile *path) return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL); } +static GFile * +get_default_repo_path (void) +{ + return g_file_new_for_path ("/ostree/repo"); +} + /** * ostree_repo_new_default: * @@ -245,12 +251,25 @@ ostree_repo_new_default (void) } else { - gs_unref_object GFile *default_repo_path = g_file_new_for_path ("/ostree/repo"); + gs_unref_object GFile *default_repo_path = get_default_repo_path (); return ostree_repo_new (default_repo_path); } } /** + * ostree_repo_is_system: + * @repo: Repository + * + * Returns: %TRUE if this repository is the root-owned system global repository + */ +gboolean +ostree_repo_is_system (OstreeRepo *repo) +{ + gs_unref_object GFile *default_repo_path = get_default_repo_path (); + return g_file_equal (repo->repodir, default_repo_path); +} + +/** * ostree_repo_get_config: * @self: * @@ -452,6 +471,137 @@ ostree_repo_create (OstreeRepo *self, return ret; } +static gboolean +enumerate_directory_allow_noent (GFile *dirpath, + const char *queryargs, + GFileQueryInfoFlags queryflags, + GFileEnumerator **out_direnum, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GError *temp_error = NULL; + gs_unref_object GFileEnumerator *ret_direnum = NULL; + + ret_direnum = g_file_enumerate_children (dirpath, queryargs, queryflags, + cancellable, &temp_error); + if (!ret_direnum) + { + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&temp_error); + ret = TRUE; + } + else + g_propagate_error (error, temp_error); + + goto out; + } + + ret = TRUE; + gs_transfer_out_value (out_direnum, &ret_direnum); + out: + return ret; +} + +GS_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, local_keyfile_unref, g_key_file_unref) +#define local_cleanup_keyfile __attribute__ ((cleanup(local_keyfile_unref))) + +static gboolean +append_one_remote_config (OstreeRepo *self, + GFile *path, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + local_cleanup_keyfile GKeyFile *remotedata = g_key_file_new (); + gs_strfreev char **groups = NULL; + char **iter; + + if (!g_key_file_load_from_file (remotedata, gs_file_get_path_cached (path), + 0, error)) + goto out; + + groups = g_key_file_get_groups (remotedata, NULL); + for (iter = groups; iter && *iter; iter++) + { + const char *group = *iter; + char **subiter; + gs_strfreev char **keys = NULL; + + /* Whitelist of allowed groups for now */ + if (!g_str_has_prefix (group, "remote \"")) + continue; + + if (g_key_file_has_group (self->config, group)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Multiple specifications found for %s", group); + goto out; + } + + keys = g_key_file_get_keys (remotedata, group, NULL, NULL); + g_assert (keys); + for (subiter = keys; subiter && *subiter; subiter++) + { + const char *key = *subiter; + gs_free char *value = g_key_file_get_value (remotedata, group, key, NULL); + g_assert (value); + g_key_file_set_value (self->config, group, key, value); + } + } + + ret = TRUE; + out: + return ret; +} + +static gboolean +append_remotes_d (OstreeRepo *self, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_unref_object GFile *etc_ostree_remotes_d = NULL; + gs_unref_object GFileEnumerator *direnum = NULL; + + etc_ostree_remotes_d = g_file_new_for_path (SYSCONFDIR "/ostree/remotes.d"); + if (!enumerate_directory_allow_noent (etc_ostree_remotes_d, OSTREE_GIO_FAST_QUERYINFO, 0, + &direnum, + cancellable, error)) + goto out; + if (direnum) + { + while (TRUE) + { + GFileInfo *file_info; + GFile *path; + const char *name; + guint32 type; + + if (!gs_file_enumerator_iterate (direnum, &file_info, &path, + NULL, error)) + goto out; + if (file_info == NULL) + break; + + name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); + type = g_file_info_get_attribute_uint32 (file_info, "standard::type"); + + if (type == G_FILE_TYPE_REGULAR && + g_str_has_suffix (name, ".conf")) + { + if (!append_one_remote_config (self, path, cancellable, error)) + goto out; + } + } + } + + ret = TRUE; + out: + return ret; +} + gboolean ostree_repo_open (OstreeRepo *self, GCancellable *cancellable, @@ -532,6 +682,9 @@ ostree_repo_open (OstreeRepo *self, TRUE, &self->enable_uncompressed_cache, error)) goto out; + if (!append_remotes_d (self, cancellable, error)) + goto out; + if (!gs_file_open_dir_fd (self->objects_dir, &self->objects_dir_fd, cancellable, error)) goto out; diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index f522fce0..0ff114b3 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -52,6 +52,8 @@ gboolean ostree_repo_open (OstreeRepo *self, void ostree_repo_set_disable_fsync (OstreeRepo *self, gboolean disable_fsync); +gboolean ostree_repo_is_system (OstreeRepo *repo); + gboolean ostree_repo_create (OstreeRepo *self, OstreeRepoMode mode, GCancellable *cancellable, diff --git a/src/ostree/ot-builtin-remote.c b/src/ostree/ot-builtin-remote.c index de6dcba8..442e118c 100644 --- a/src/ostree/ot-builtin-remote.c +++ b/src/ostree/ot-builtin-remote.c @@ -63,6 +63,47 @@ parse_keyvalue (const char *keyvalue, return TRUE; } +GS_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, local_keyfile_unref, g_key_file_unref) +#define local_cleanup_keyfile __attribute__ ((cleanup(local_keyfile_unref))) + +static gboolean +add_remote_to_keyfile (GKeyFile *new_keyfile, + const char *key, + const char *url, + GPtrArray *branches, + GError **error) +{ + gboolean ret = FALSE; + char **iter; + + g_key_file_set_string (new_keyfile, key, "url", url); + + for (iter = opt_set; iter && *iter; iter++) + { + const char *keyvalue = *iter; + gs_free char *subkey = NULL; + gs_free char *subvalue = NULL; + + if (!parse_keyvalue (keyvalue, &subkey, &subvalue, error)) + goto out; + + g_key_file_set_string (new_keyfile, key, subkey, subvalue); + } + + if (branches->len > 0) + g_key_file_set_string_list (new_keyfile, key, "branches", + (const char *const *)branches->pdata, + branches->len); + + if (opt_no_gpg_verify) + g_key_file_set_boolean (new_keyfile, key, "gpg-verify", FALSE); + + ret = TRUE; + out: + return ret; +} + + gboolean ostree_builtin_remote (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error) { @@ -71,8 +112,8 @@ ostree_builtin_remote (int argc, char **argv, OstreeRepo *repo, GCancellable *ca const char *op; gs_free char *key = NULL; guint i; - gs_unref_ptrarray GPtrArray *branches = NULL; GKeyFile *config = NULL; + const char *remote_name; context = g_option_context_new ("OPERATION NAME [args] - Control remote repository configuration"); g_option_context_add_main_entries (context, options, NULL); @@ -91,13 +132,29 @@ ostree_builtin_remote (int argc, char **argv, OstreeRepo *repo, GCancellable *ca } op = argv[1]; - key = g_strdup_printf ("remote \"%s\"", argv[2]); + remote_name = argv[2]; + key = g_strdup_printf ("remote \"%s\"", remote_name); config = ostree_repo_copy_config (repo); if (!strcmp (op, "add")) { - char **iter; + const char *url = argv[3]; + gs_unref_object GFile *etc_ostree_remotes_d = g_file_new_for_path (SYSCONFDIR "/ostree/remotes.d"); + gs_free char *target_name = NULL; + gs_unref_object GFile *target_conf = NULL; + local_cleanup_keyfile GKeyFile *new_keyfile = NULL; + GKeyFile *target_keyfile = NULL; + gs_unref_ptrarray GPtrArray *branches = NULL; + + if (strchr (remote_name, '/') != NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid character '/' in remote name: %s", + remote_name); + goto out; + } + if (argc < 4) { usage_error (context, "URL must be specified", error); @@ -108,29 +165,46 @@ ostree_builtin_remote (int argc, char **argv, OstreeRepo *repo, GCancellable *ca for (i = 4; i < argc; i++) g_ptr_array_add (branches, argv[i]); - g_key_file_set_string (config, key, "url", argv[3]); - - for (iter = opt_set; iter && *iter; iter++) + if (ostree_repo_is_system (repo)) { - const char *keyvalue = *iter; - gs_free char *subkey = NULL; - gs_free char *subvalue = NULL; - - if (!parse_keyvalue (keyvalue, &subkey, &subvalue, error)) - goto out; - - g_key_file_set_string (config, key, subkey, subvalue); + new_keyfile = g_key_file_new (); + + target_keyfile = new_keyfile; + + target_name = g_strconcat (remote_name, ".conf", NULL); + target_conf = g_file_get_child (etc_ostree_remotes_d, target_name); + + if (g_file_query_exists (target_conf, NULL)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Remote configuration already exists: %s", + gs_file_get_path_cached (target_conf)); + goto out; + } + } + else + { + target_keyfile = config; } - if (branches->len > 0) - g_key_file_set_string_list (config, key, "branches", - (const char *const *)branches->pdata, - branches->len); - - if (opt_no_gpg_verify) - g_key_file_set_boolean (config, key, "gpg-verify", FALSE); + if (!add_remote_to_keyfile (target_keyfile, key, url, branches, error)) + goto out; - g_free (key); + /* For the system repository, write to /etc/ostree/remotes.d */ + if (ostree_repo_is_system (repo)) + { + gsize len; + gs_free char *data = g_key_file_to_data (target_keyfile, &len, error); + if (!g_file_replace_contents (target_conf, data, len, + NULL, FALSE, 0, NULL, + cancellable, error)) + goto out; + } + else + { + if (!ostree_repo_write_config (repo, config, error)) + goto out; + } } else if (!strcmp (op, "show-url")) { @@ -147,9 +221,6 @@ ostree_builtin_remote (int argc, char **argv, OstreeRepo *repo, GCancellable *ca usage_error (context, "Unknown operation", error); goto out; } - - if (!ostree_repo_write_config (repo, config, error)) - goto out; ret = TRUE; out: |