gboolean ot_remote_builtin_gpg_import (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; g_autoptr(GInputStream) source_stream = NULL; const char *remote_name; const char * const *key_ids; guint imported = 0; gboolean ret = FALSE; context = g_option_context_new ("NAME [KEY-ID...] - Import GPG keys"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); goto out; } if (opt_stdin && opt_keyrings != NULL) { ot_util_usage_error (context, "--keyring and --stdin are mutually exclusive", error); goto out; } remote_name = argv[1]; key_ids = (argc > 2) ? (const char * const *) argv + 2 : NULL; if (!open_source_stream (&source_stream, cancellable, error)) goto out; if (!ostree_repo_remote_gpg_import (repo, remote_name, source_stream, key_ids, &imported, cancellable, error)) goto out; /* XXX If we ever add internationalization, use ngettext() here. */ g_print ("Imported %u GPG key%s to remote \"%s\"\n", imported, (imported == 1) ? "" : "s", remote_name); ret = TRUE; out: g_option_context_free (context); return ret; }
gboolean ot_admin_builtin_os_init (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; glnx_unref_object OstreeSysroot *sysroot = NULL; gboolean ret = FALSE; const char *osname = NULL; context = g_option_context_new ("OSNAME - Initialize empty state for given operating system"); if (!ostree_admin_option_context_parse (context, options, &argc, &argv, OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER | OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED, &sysroot, cancellable, error)) goto out; if (!ostree_sysroot_ensure_initialized (sysroot, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "OSNAME must be specified", error); goto out; } osname = argv[1]; if (!ostree_sysroot_init_osname (sysroot, osname, cancellable, error)) goto out; g_print ("ostree/deploy/%s initialized as OSTree root\n", osname); ret = TRUE; out: return ret; }
gboolean ot_remote_builtin_delete_cookie (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(OstreeRepo) repo = NULL; g_autoptr(GOptionContext) context = g_option_context_new ("NAME DOMAIN PATH COOKIE_NAME- Remote one cookie from remote"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) return FALSE; if (argc < 5) { ot_util_usage_error (context, "NAME, DOMAIN, PATH and COOKIE_NAME must be specified", error); return FALSE; } const char *remote_name = argv[1]; const char *domain = argv[2]; const char *path = argv[3]; const char *cookie_name = argv[4]; g_autofree char *cookie_file = g_strdup_printf ("%s.cookies.txt", remote_name); if (!ot_delete_cookie_at (ostree_repo_get_dfd (repo), cookie_file, domain, path, cookie_name, error)) return FALSE; return TRUE; }
gboolean ot_remote_builtin_show_url (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; const char *remote_name; g_autofree char *remote_url = NULL; gboolean ret = FALSE; context = g_option_context_new ("NAME - Show remote repository URL"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); goto out; } remote_name = argv[1]; if (ostree_repo_remote_get_url (repo, remote_name, &remote_url, error)) { g_print ("%s\n", remote_url); ret = TRUE; } out: return ret; }
gboolean ot_remote_builtin_delete (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; const char *remote_name; gboolean ret = FALSE; context = g_option_context_new ("NAME - Delete a remote repository"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); goto out; } remote_name = argv[1]; if (!ostree_repo_remote_change (repo, NULL, opt_if_exists ? OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS : OSTREE_REPO_REMOTE_CHANGE_DELETE, remote_name, NULL, NULL, cancellable, error)) goto out; ret = TRUE; out: g_option_context_free (context); return ret; }
gboolean ostree_builtin_reset (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; g_autoptr(GHashTable) known_refs = NULL; gboolean ret = FALSE; const char *ref; const char *target = NULL; g_autofree char *checksum = NULL; /* FIXME: Add support for collection–refs. */ context = g_option_context_new ("REF COMMIT"); if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) goto out; if (!ostree_ensure_repo_writable (repo, error)) goto out; if (argc <= 2) { ot_util_usage_error (context, "A REF and COMMIT argument is required", error); goto out; } ref = argv[1]; target = argv[2]; if (!ostree_repo_list_refs (repo, NULL, &known_refs, cancellable, error)) goto out; if (!g_hash_table_contains (known_refs, ref)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR, "Invalid ref '%s'", ref); goto out; } if (!ostree_repo_resolve_rev (repo, target, FALSE, &checksum, error)) goto out; if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) goto out; ostree_repo_transaction_set_ref (repo, NULL, ref, checksum); if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) goto out; ret = TRUE; out: if (repo) ostree_repo_abort_transaction (repo, cancellable, NULL); return ret; }
gboolean ot_admin_builtin_unlock (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeSysroot) sysroot = NULL; OstreeDeployment *booted_deployment = NULL; OstreeDeploymentUnlockedState target_state; context = g_option_context_new (""); if (!ostree_admin_option_context_parse (context, options, &argc, &argv, OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, invocation, &sysroot, cancellable, error)) goto out; if (argc > 1) { ot_util_usage_error (context, "This command takes no extra arguments", error); goto out; } booted_deployment = ostree_sysroot_get_booted_deployment (sysroot); if (!booted_deployment) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not currently booted into an OSTree system"); goto out; } target_state = opt_hotfix ? OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX : OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT; if (!ostree_sysroot_deployment_unlock (sysroot, booted_deployment, target_state, cancellable, error)) goto out; switch (target_state) { case OSTREE_DEPLOYMENT_UNLOCKED_NONE: g_assert_not_reached (); break; case OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX: g_print ("Hotfix mode enabled. A writable overlayfs is now mounted on /usr\n" "for this booted deployment. A non-hotfixed clone has been created\n" "as the non-default rollback target.\n"); break; case OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT: g_print ("Development mode enabled. A writable overlayfs is now mounted on /usr.\n" "All changes there will be discarded on reboot.\n"); break; } ret = TRUE; out: return ret; }
gboolean ot_admin_builtin_init_fs (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error) { GOptionContext *context; gboolean ret = FALSE; gs_unref_object GFile *dir = NULL; gs_unref_object GFile *child = NULL; gs_unref_object OstreeSysroot *target_sysroot = NULL; guint i; const char *normal_toplevels[] = {"boot", "dev", "home", "proc", "run", "sys"}; context = g_option_context_new ("PATH - Initialize a root filesystem"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "PATH must be specified", error); goto out; } dir = g_file_new_for_path (argv[1]); target_sysroot = ostree_sysroot_new (dir); for (i = 0; i < G_N_ELEMENTS(normal_toplevels); i++) { child = g_file_get_child (dir, normal_toplevels[i]); if (!gs_file_ensure_directory_mode (child, 0755, cancellable, error)) goto out; g_clear_object (&child); } child = g_file_get_child (dir, "root"); if (!gs_file_ensure_directory_mode (child, 0700, cancellable, error)) goto out; g_clear_object (&child); child = g_file_get_child (dir, "tmp"); if (!gs_file_ensure_directory_mode (child, 01777, cancellable, error)) goto out; g_clear_object (&child); if (!ostree_sysroot_ensure_initialized (target_sysroot, cancellable, error)) goto out; ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ot_admin_builtin_deploy (int argc, char **argv, GError **error) { GOptionContext *context; OtAdminDeploy self_data; OtAdminDeploy *self = &self_data; gboolean ret = FALSE; const char *deploy_target = NULL; const char *revision = NULL; __attribute__((unused)) GCancellable *cancellable = NULL; if (!opt_ostree_dir) opt_ostree_dir = "/ostree"; memset (self, 0, sizeof (*self)); context = g_option_context_new ("NAME [REVISION] - Check out revision NAME (or REVISION as NAME)"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); goto out; } deploy_target = argv[1]; if (argc > 2) revision = argv[2]; if (!do_checkout (self, deploy_target, revision, cancellable, error)) goto out; if (!opt_no_kernel) { if (!do_update_kernel (self, deploy_target, cancellable, error)) goto out; } if (!update_current (deploy_target, cancellable, error)) goto out; ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
static void test_ot_util_usage_error (void) { g_autoptr(GError) error = NULL; g_autoptr(GOptionContext) context = g_option_context_new ("[TEST]"); GPrintFunc old_printerr = g_set_printerr_handler (util_usage_error_printerr); ot_util_usage_error (context, "find_me", &error); g_assert_nonnull (strstr (printerr_str->str, "[TEST]")); g_assert_nonnull (strstr (error->message, "find_me")); g_clear_error (&error); g_set_printerr_handler (old_printerr); g_string_free (printerr_str, TRUE); printerr_str = NULL; }
gboolean ostree_builtin_cat (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; gs_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; int i; const char *rev; gs_unref_object GOutputStream *stdout_stream = NULL; gs_unref_object GFile *root = NULL; gs_unref_object GFile *f = NULL; context = g_option_context_new ("COMMIT PATH... - Concatenate contents of files"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc <= 2) { ot_util_usage_error (context, "A COMMIT and at least one PATH argument are required", error); goto out; } rev = argv[1]; if (!ostree_repo_read_commit (repo, rev, &root, NULL, NULL, error)) goto out; stdout_stream = g_unix_output_stream_new (1, FALSE); for (i = 2; i < argc; i++) { g_clear_object (&f); f = g_file_resolve_relative_path (root, argv[i]); if (!cat_one_file (f, stdout_stream, cancellable, error)) goto out; } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ot_remote_builtin_list_cookies (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(OstreeRepo) repo = NULL; g_autoptr(GOptionContext) context = g_option_context_new ("NAME"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, invocation, &repo, cancellable, error)) return FALSE; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); return FALSE; } const char *remote_name = argv[1]; g_autofree char *cookie_file = g_strdup_printf ("%s.cookies.txt", remote_name); if (!ot_list_cookies_at (ostree_repo_get_dfd (repo), cookie_file, error)) return FALSE; return TRUE; }
gboolean ot_admin_builtin_switch (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = g_option_context_new ("REF"); g_autoptr(OstreeSysroot) sysroot = NULL; if (!ostree_admin_option_context_parse (context, options, &argc, &argv, OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, invocation, &sysroot, cancellable, error)) return FALSE; if (argc < 2) { ot_util_usage_error (context, "REF must be specified", error); return FALSE; } const char *new_provided_refspec = argv[1]; g_autoptr(OstreeSysrootUpgrader) upgrader = ostree_sysroot_upgrader_new_for_os_with_flags (sysroot, opt_osname, OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED, cancellable, error); if (!upgrader) return FALSE; GKeyFile *old_origin = ostree_sysroot_upgrader_get_origin (upgrader); g_autofree char *origin_refspec = g_key_file_get_string (old_origin, "origin", "refspec", NULL); g_autofree char *origin_remote = NULL; g_autofree char *origin_ref = NULL; if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error)) return FALSE; g_autofree char *new_remote = NULL; g_autofree char *new_ref = NULL; /* Allow just switching remotes */ if (g_str_has_suffix (new_provided_refspec, ":")) { new_remote = g_strdup (new_provided_refspec); new_remote[strlen(new_remote)-1] = '\0'; new_ref = g_strdup (origin_ref); } else { if (!ostree_parse_refspec (new_provided_refspec, &new_remote, &new_ref, error)) return FALSE; } const char* remote = new_remote ?: origin_remote; g_autofree char *new_refspec = NULL; if (remote) new_refspec = g_strconcat (remote, ":", new_ref, NULL); else new_refspec = g_strdup (new_ref); if (strcmp (origin_refspec, new_refspec) == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Old and new refs are equal: %s", new_refspec); return FALSE; } g_autoptr(GKeyFile) new_origin = ostree_sysroot_origin_new_from_refspec (sysroot, new_refspec); if (!ostree_sysroot_upgrader_set_origin (upgrader, new_origin, cancellable, error)) return FALSE; { g_auto(GLnxConsoleRef) console = { 0, }; glnx_console_lock (&console); g_autoptr(OstreeAsyncProgress) progress = NULL; if (console.is_tty) progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console); /* Always allow older...there's not going to be a chronological * relationship necessarily. */ gboolean changed; if (!ostree_sysroot_upgrader_pull (upgrader, 0, OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER, progress, &changed, cancellable, error)) return FALSE; if (progress) ostree_async_progress_finish (progress); } if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error)) return FALSE; OstreeRepo *repo = ostree_sysroot_repo (sysroot); if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) return FALSE; g_print ("Deleting ref '%s:%s'\n", origin_remote, origin_ref); ostree_repo_transaction_set_ref (repo, origin_remote, origin_ref, NULL); if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) return FALSE; if (opt_reboot) { if (!ot_admin_execve_reboot (sysroot, error)) return FALSE; } return TRUE; }
gboolean ot_remote_builtin_add (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; const char *remote_name; const char *remote_url; char **iter; g_autofree char *target_name = NULL; g_autoptr(GFile) target_conf = NULL; g_autoptr(GVariantBuilder) optbuilder = NULL; gboolean ret = FALSE; context = g_option_context_new ("NAME [metalink=|mirrorlist=]URL [BRANCH...] - Add a remote repository"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 3) { ot_util_usage_error (context, "NAME and URL must be specified", error); goto out; } remote_name = argv[1]; remote_url = argv[2]; optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); if (argc > 3) { g_autoptr(GPtrArray) branchesp = g_ptr_array_new (); int i; for (i = 3; i < argc; i++) g_ptr_array_add (branchesp, argv[i]); g_ptr_array_add (branchesp, NULL); g_variant_builder_add (optbuilder, "{s@v}", "branches", g_variant_new_variant (g_variant_new_strv ((const char*const*)branchesp->pdata, -1))); } /* We could just make users use --set instead for this since it's a string, * but e.g. when mirrorlist support is added, it'll be kinda awkward to type: * --set=contenturl=mirrorlist=... */ if (opt_contenturl != NULL) g_variant_builder_add (optbuilder, "{s@v}", "contenturl", g_variant_new_variant (g_variant_new_string (opt_contenturl))); for (iter = opt_set; iter && *iter; iter++) { const char *keyvalue = *iter; g_autofree char *subkey = NULL; g_autofree char *subvalue = NULL; if (!ot_parse_keyvalue (keyvalue, &subkey, &subvalue, error)) goto out; g_variant_builder_add (optbuilder, "{s@v}", subkey, g_variant_new_variant (g_variant_new_string (subvalue))); } if (opt_no_gpg_verify) g_variant_builder_add (optbuilder, "{s@v}", "gpg-verify", g_variant_new_variant (g_variant_new_boolean (FALSE))); if (!ostree_repo_remote_change (repo, NULL, opt_if_not_exists ? OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS : OSTREE_REPO_REMOTE_CHANGE_ADD, remote_name, remote_url, g_variant_builder_end (optbuilder), cancellable, error)) goto out; /* This is just a convenience option and is not as flexible as the full * "ostree remote gpg-import" command. It imports all keys from a file, * which is likely the most common case. * * XXX Not sure this interacts well with if-not-exists since we don't * know whether the remote already existed. We import regardless. */ if (opt_gpg_import != NULL) { g_autoptr(GFile) file = NULL; g_autoptr(GInputStream) input_stream = NULL; guint imported = 0; file = g_file_new_for_path (opt_gpg_import); input_stream = (GInputStream *) g_file_read (file, cancellable, error); if (input_stream == NULL) goto out; if (!ostree_repo_remote_gpg_import (repo, remote_name, input_stream, NULL, &imported, cancellable, error)) goto out; /* XXX If we ever add internationalization, use ngettext() here. */ g_print ("Imported %u GPG key%s to remote \"%s\"\n", imported, (imported == 1) ? "" : "s", remote_name); } ret = TRUE; out: g_option_context_free (context); return ret; }
gboolean ot_admin_builtin_switch (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; const char *new_provided_refspec = NULL; gs_unref_object OstreeRepo *repo = NULL; gs_free char *origin_refspec = NULL; gs_free char *origin_remote = NULL; gs_free char *origin_ref = NULL; gs_free char *new_remote = NULL; gs_free char *new_ref = NULL; gs_free char *new_refspec = NULL; gs_free char *new_revision = NULL; gs_unref_object GFile *deployment_path = NULL; gs_unref_object GFile *deployment_origin_path = NULL; gs_unref_object OstreeDeployment *merge_deployment = NULL; gs_unref_object OstreeDeployment *new_deployment = NULL; gs_unref_object OstreeSysrootUpgrader *upgrader = NULL; gs_unref_object OstreeAsyncProgress *progress = NULL; gboolean changed; GSConsole *console = NULL; gboolean in_status_line = FALSE; GKeyFile *old_origin; GKeyFile *new_origin = NULL; context = g_option_context_new ("REF - Construct new tree from current origin and deploy it, if it changed"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "REF must be specified", error); goto out; } new_provided_refspec = argv[1]; if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname, cancellable, error); if (!upgrader) goto out; old_origin = ostree_sysroot_upgrader_get_origin (upgrader); origin_refspec = g_key_file_get_string (old_origin, "origin", "refspec", NULL); if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error)) goto out; /* Allow just switching remotes */ if (g_str_has_suffix (new_provided_refspec, ":")) { new_remote = g_strdup (new_provided_refspec); new_remote[strlen(new_remote)-1] = '\0'; new_ref = g_strdup (origin_ref); } else { if (!ostree_parse_refspec (new_provided_refspec, &new_remote, &new_ref, error)) goto out; } if (!new_remote) new_refspec = g_strconcat (origin_remote, ":", new_ref, NULL); else new_refspec = g_strconcat (new_remote, ":", new_ref, NULL); if (strcmp (origin_refspec, new_refspec) == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Old and new refs are equal: %s", new_refspec); goto out; } new_origin = ostree_sysroot_origin_new_from_refspec (sysroot, new_refspec); if (!ostree_sysroot_upgrader_set_origin (upgrader, new_origin, cancellable, error)) goto out; console = gs_console_get (); if (console) { gs_console_begin_status_line (console, "", NULL, NULL); in_status_line = TRUE; progress = ostree_async_progress_new_and_connect (ot_common_pull_progress, console); } /* Always allow older...there's not going to be a chronological * relationship necessarily. */ if (!ostree_sysroot_upgrader_pull (upgrader, 0, OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER, progress, &changed, cancellable, error)) goto out; if (in_status_line) { gs_console_end_status_line (console, NULL, NULL); in_status_line = FALSE; } if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error)) goto out; if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) goto out; if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) goto out; g_print ("Deleting ref '%s:%s'\n", origin_remote, origin_ref); ostree_repo_transaction_set_ref (repo, origin_remote, origin_ref, NULL); if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) goto out; { gs_unref_object GFile *real_sysroot = g_file_new_for_path ("/"); if (opt_reboot && g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot)) { gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT, cancellable, error, "systemctl", "reboot", NULL); } } ret = TRUE; out: if (in_status_line) gs_console_end_status_line (console, NULL, NULL); if (new_origin) g_key_file_unref (new_origin); if (context) g_option_context_free (context); return ret; }
gboolean ot_admin_builtin_os_init (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeSysroot *sysroot = NULL; gboolean ret = FALSE; const char *osname = NULL; g_autoptr(GFile) deploy_dir = NULL; g_autoptr(GFile) dir = NULL; context = g_option_context_new ("OSNAME - Initialize empty state for given operating system"); if (!ostree_admin_option_context_parse (context, options, &argc, &argv, OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER | OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED, &sysroot, cancellable, error)) goto out; if (!ostree_sysroot_ensure_initialized (sysroot, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "OSNAME must be specified", error); goto out; } osname = argv[1]; deploy_dir = ot_gfile_get_child_build_path (ostree_sysroot_get_path (sysroot), "ostree", "deploy", osname, NULL); /* Ensure core subdirectories of /var exist, since we need them for * dracut generation, and the host will want them too. */ g_clear_object (&dir); dir = ot_gfile_get_child_build_path (deploy_dir, "var", "tmp", NULL); if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) goto out; if (chmod (gs_file_get_path_cached (dir), 01777) < 0) { gs_set_error_from_errno (error, errno); goto out; } g_clear_object (&dir); dir = ot_gfile_get_child_build_path (deploy_dir, "var", "lib", NULL); if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) goto out; g_clear_object (&dir); dir = ot_gfile_get_child_build_path (deploy_dir, "var", "run", NULL); if (!g_file_test (gs_file_get_path_cached (dir), G_FILE_TEST_IS_SYMLINK)) { if (symlink ("../run", gs_file_get_path_cached (dir)) < 0) { gs_set_error_from_errno (error, errno); goto out; } } dir = ot_gfile_get_child_build_path (deploy_dir, "var", "lock", NULL); if (!g_file_test (gs_file_get_path_cached (dir), G_FILE_TEST_IS_SYMLINK)) { if (symlink ("../run/lock", gs_file_get_path_cached (dir)) < 0) { gs_set_error_from_errno (error, errno); goto out; } } g_print ("%s initialized as OSTree root\n", gs_file_get_path_cached (deploy_dir)); ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ot_admin_builtin_set_origin (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; const char *remotename = NULL; const char *url = NULL; const char *branch = NULL; gs_unref_object OstreeRepo *repo = NULL; gs_unref_object OstreeSysroot *sysroot = NULL; OstreeDeployment *target_deployment = NULL; context = g_option_context_new ("REMOTENAME URL [BRANCH]"); if (!ostree_admin_option_context_parse (context, options, &argc, &argv, OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, &sysroot, cancellable, error)) goto out; if (argc < 3) { ot_util_usage_error (context, "REMOTENAME and URL must be specified", error); goto out; } remotename = argv[1]; url = argv[2]; if (argc > 3) branch = argv[3]; if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) goto out; if (opt_index == -1) { target_deployment = ostree_sysroot_get_booted_deployment (sysroot); if (target_deployment == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not currently booted into an OSTree system"); goto out; } } else { target_deployment = ot_admin_get_indexed_deployment (sysroot, opt_index, error); if (!target_deployment) goto out; } { char **iter; gs_unref_variant_builder GVariantBuilder *optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); for (iter = opt_set; iter && *iter; iter++) { const char *keyvalue = *iter; gs_free char *subkey = NULL; gs_free char *subvalue = NULL; if (!ot_parse_keyvalue (keyvalue, &subkey, &subvalue, error)) goto out; g_variant_builder_add (optbuilder, "{s@v}", subkey, g_variant_new_variant (g_variant_new_string (subvalue))); } if (!ostree_repo_remote_change (repo, NULL, OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS, remotename, url, g_variant_builder_end (optbuilder), cancellable, error)) goto out; } { GKeyFile *old_origin = ostree_deployment_get_origin (target_deployment); gs_free char *origin_refspec = g_key_file_get_string (old_origin, "origin", "refspec", NULL); gs_free char *new_refspec = NULL; gs_free char *origin_remote = NULL; gs_free char *origin_ref = NULL; if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error)) goto out; { gs_free char *new_refspec = g_strconcat (remotename, ":", branch ? branch : origin_ref, NULL); gs_unref_keyfile GKeyFile *new_origin = NULL; gs_unref_object GFile *origin_path = NULL; new_origin = ostree_sysroot_origin_new_from_refspec (sysroot, new_refspec); if (!ostree_sysroot_write_origin_file (sysroot, target_deployment, new_origin, cancellable, error)) goto out; } } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *refspec; GOptionContext *context; GKeyFile *origin = NULL; gs_unref_object OstreeRepo *repo = NULL; gs_unref_ptrarray GPtrArray *new_deployments = NULL; gs_unref_object OstreeDeployment *new_deployment = NULL; gs_unref_object OstreeDeployment *merge_deployment = NULL; gs_free char *revision = NULL; __attribute__((cleanup(_ostree_kernel_args_cleanup))) OstreeKernelArgs *kargs = NULL; context = g_option_context_new ("REFSPEC - Checkout revision REFSPEC as the new default deployment"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "REF/REV must be specified", error); goto out; } refspec = argv[1]; if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) goto out; /* Find the currently booted deployment, if any; we will ensure it * is present in the new deployment list. */ if (!ot_admin_require_booted_deployment_or_osname (sysroot, opt_osname, cancellable, error)) { g_prefix_error (error, "Looking for booted deployment: "); goto out; } if (opt_origin_path) { origin = g_key_file_new (); if (!g_key_file_load_from_file (origin, opt_origin_path, 0, error)) goto out; } else { origin = ostree_sysroot_origin_new_from_refspec (sysroot, refspec); } if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &revision, error)) goto out; merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, opt_osname); /* Here we perform cleanup of any leftover data from previous * partial failures. This avoids having to call gs_shutil_rm_rf() * at random points throughout the process. * * TODO: Add /ostree/transaction file, and only do this cleanup if * we find it. */ if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) { g_prefix_error (error, "Performing initial cleanup: "); goto out; } kargs = _ostree_kernel_args_new (); /* If they want the current kernel's args, they very likely don't * want the ones from the merge. */ if (opt_kernel_proc_cmdline) { gs_unref_object GFile *proc_cmdline_path = g_file_new_for_path ("/proc/cmdline"); gs_free char *proc_cmdline = NULL; gsize proc_cmdline_len = 0; gs_strfreev char **proc_cmdline_args = NULL; if (!g_file_load_contents (proc_cmdline_path, cancellable, &proc_cmdline, &proc_cmdline_len, NULL, error)) goto out; g_strchomp (proc_cmdline); proc_cmdline_args = g_strsplit (proc_cmdline, " ", -1); _ostree_kernel_args_replace_argv (kargs, proc_cmdline_args); } else if (merge_deployment) { OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (merge_deployment); gs_strfreev char **previous_args = g_strsplit (ostree_bootconfig_parser_get (bootconfig, "options"), " ", -1); _ostree_kernel_args_replace_argv (kargs, previous_args); } if (opt_kernel_argv) { _ostree_kernel_args_replace_argv (kargs, opt_kernel_argv); } if (opt_kernel_argv_append) { _ostree_kernel_args_append_argv (kargs, opt_kernel_argv_append); } { gs_strfreev char **kargs_strv = _ostree_kernel_args_to_strv (kargs); if (!ostree_sysroot_deploy_tree (sysroot, opt_osname, revision, origin, merge_deployment, kargs_strv, &new_deployment, cancellable, error)) goto out; } if (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname, new_deployment, merge_deployment, opt_retain ? OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN : 0, cancellable, error)) goto out; ret = TRUE; out: if (origin) g_key_file_unref (origin); if (context) g_option_context_free (context); return ret; }
gboolean ostree_builtin_show (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error) { GOptionContext *context; gboolean ret = FALSE; const char *rev; gs_free char *resolved_rev = NULL; context = g_option_context_new ("OBJECT - Output a metadata object"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc <= 1) { ot_util_usage_error (context, "An object argument is required", error); goto out; } rev = argv[1]; if (opt_print_metadata_key || opt_print_detached_metadata_key) { gboolean detached = opt_print_detached_metadata_key != NULL; const char *key = detached ? opt_print_detached_metadata_key : opt_print_metadata_key; if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error)) goto out; if (!do_print_metadata_key (repo, resolved_rev, detached, key, error)) goto out; } else if (opt_print_related) { if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error)) goto out; if (!do_print_related (repo, rev, resolved_rev, error)) goto out; } else if (opt_print_variant_type) { if (!do_print_variant_generic (G_VARIANT_TYPE (opt_print_variant_type), rev, error)) goto out; } else { gboolean found = FALSE; if (!ostree_validate_checksum_string (rev, NULL)) { if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error)) goto out; if (!print_object (repo, OSTREE_OBJECT_TYPE_COMMIT, resolved_rev, error)) goto out; } else { if (!print_if_found (repo, OSTREE_OBJECT_TYPE_COMMIT, rev, &found, cancellable, error)) goto out; if (!print_if_found (repo, OSTREE_OBJECT_TYPE_DIR_META, rev, &found, cancellable, error)) goto out; if (!print_if_found (repo, OSTREE_OBJECT_TYPE_DIR_TREE, rev, &found, cancellable, error)) goto out; if (!found) { gs_unref_object GFileInfo *finfo = NULL; gs_unref_variant GVariant *xattrs = NULL; GFileType filetype; if (!ostree_repo_load_file (repo, resolved_rev, NULL, &finfo, &xattrs, cancellable, error)) goto out; g_print ("Object: %s\nType: %s\n", rev, ostree_object_type_to_string (OSTREE_OBJECT_TYPE_FILE)); filetype = g_file_info_get_file_type (finfo); g_print ("File Type: "); switch (filetype) { case G_FILE_TYPE_REGULAR: g_print ("regular\n"); g_print ("Size: %" G_GUINT64_FORMAT "\n", g_file_info_get_size (finfo)); break; case G_FILE_TYPE_SYMBOLIC_LINK: g_print ("symlink\n"); g_print ("Target: %s\n", g_file_info_get_symlink_target (finfo)); break; default: g_printerr ("(unknown type %u)\n", (guint)filetype); } g_print ("Mode: 0%04o\n", g_file_info_get_attribute_uint32 (finfo, "unix::mode")); g_print ("Uid: %u\n", g_file_info_get_attribute_uint32 (finfo, "unix::uid")); g_print ("Gid: %u\n", g_file_info_get_attribute_uint32 (finfo, "unix::gid")); g_print ("Extended Attributes: "); if (xattrs) { gs_free char *xattr_string = g_variant_print (xattrs, TRUE); g_print ("{ %s }\n", xattr_string); } else { g_print ("(none)\n"); } } } } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
/* TODO: Add a man page. */ gboolean ostree_builtin_create_usb (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeAsyncProgress) progress = NULL; g_auto(GLnxConsoleRef) console = { 0, }; context = g_option_context_new ("MOUNT-PATH COLLECTION-ID REF [COLLECTION-ID REF...]"); /* Parse options. */ g_autoptr(OstreeRepo) src_repo = NULL; if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &src_repo, cancellable, error)) return FALSE; if (argc < 2) { ot_util_usage_error (context, "A MOUNT-PATH must be specified", error); return FALSE; } if (argc < 4) { ot_util_usage_error (context, "At least one COLLECTION-ID REF pair must be specified", error); return FALSE; } if (argc % 2 == 1) { ot_util_usage_error (context, "Only complete COLLECTION-ID REF pairs may be specified", error); return FALSE; } /* Open the USB stick, which must exist. Allow automounting and following symlinks. */ const char *mount_root_path = argv[1]; struct stat mount_root_stbuf; glnx_autofd int mount_root_dfd = -1; if (!glnx_opendirat (AT_FDCWD, mount_root_path, TRUE, &mount_root_dfd, error)) return FALSE; if (!glnx_fstat (mount_root_dfd, &mount_root_stbuf, error)) return FALSE; /* Read in the refs to add to the USB stick. */ g_autoptr(GPtrArray) refs = g_ptr_array_new_full (argc, (GDestroyNotify) ostree_collection_ref_free); for (gsize i = 2; i < argc; i += 2) { if (!ostree_validate_collection_id (argv[i], error) || !ostree_validate_rev (argv[i + 1], error)) return FALSE; g_ptr_array_add (refs, ostree_collection_ref_new (argv[i], argv[i + 1])); } /* Open the destination repository on the USB stick or create it if it doesn’t exist. * Check it’s below @mount_root_path, and that it’s not the same as the source * repository. * * If the destination file system supports xattrs (for example, ext4), we use * a BARE_USER repository; if it doesn’t (for example, FAT), we use ARCHIVE. * In either case, we want a lossless repository. */ const char *dest_repo_path = (opt_destination_repo != NULL) ? opt_destination_repo : ".ostree/repo"; if (!glnx_shutil_mkdir_p_at (mount_root_dfd, dest_repo_path, 0755, cancellable, error)) return FALSE; OstreeRepoMode mode = OSTREE_REPO_MODE_BARE_USER; if (TEMP_FAILURE_RETRY (fgetxattr (mount_root_dfd, "user.test", NULL, 0)) < 0 && (errno == ENOTSUP || errno == EOPNOTSUPP)) mode = OSTREE_REPO_MODE_ARCHIVE; g_debug ("%s: Creating repository in mode %u", G_STRFUNC, mode); g_autoptr(OstreeRepo) dest_repo = ostree_repo_create_at (mount_root_dfd, dest_repo_path, mode, NULL, cancellable, error); if (dest_repo == NULL) return FALSE; struct stat dest_repo_stbuf; if (!glnx_fstat (ostree_repo_get_dfd (dest_repo), &dest_repo_stbuf, error)) return FALSE; if (dest_repo_stbuf.st_dev != mount_root_stbuf.st_dev) { ot_util_usage_error (context, "--destination-repo must be a descendent of MOUNT-PATH", error); return FALSE; } if (ostree_repo_equal (src_repo, dest_repo)) { ot_util_usage_error (context, "--destination-repo must not be the source repository", error); return FALSE; } if (!ostree_ensure_repo_writable (dest_repo, error)) return FALSE; if (opt_disable_fsync) ostree_repo_set_disable_fsync (dest_repo, TRUE); /* Copy across all of the collection–refs to the destination repo. */ GVariantBuilder refs_builder; g_variant_builder_init (&refs_builder, G_VARIANT_TYPE ("a(sss)")); for (gsize i = 0; i < refs->len; i++) { const OstreeCollectionRef *ref = g_ptr_array_index (refs, i); g_variant_builder_add (&refs_builder, "(sss)", ref->collection_id, ref->ref_name, ""); } { GVariantBuilder builder; g_autoptr(GVariant) opts = NULL; OstreeRepoPullFlags flags = OSTREE_REPO_PULL_FLAGS_MIRROR; glnx_console_lock (&console); if (console.is_tty) progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{s@v}", "collection-refs", g_variant_new_variant (g_variant_builder_end (&refs_builder))); g_variant_builder_add (&builder, "{s@v}", "flags", g_variant_new_variant (g_variant_new_int32 (flags))); g_variant_builder_add (&builder, "{s@v}", "depth", g_variant_new_variant (g_variant_new_int32 (0))); opts = g_variant_ref_sink (g_variant_builder_end (&builder)); g_autofree char *src_repo_uri = g_file_get_uri (ostree_repo_get_path (src_repo)); if (!ostree_repo_pull_with_options (dest_repo, src_repo_uri, opts, progress, cancellable, error)) { ostree_repo_abort_transaction (dest_repo, cancellable, NULL); return FALSE; } if (progress != NULL) ostree_async_progress_finish (progress); } /* Ensure a summary file is present to make it easier to look up commit checksums. */ /* FIXME: It should be possible to work without this, but find_remotes_cb() in * ostree-repo-pull.c currently assumes a summary file (signed or unsigned) is * present. */ struct stat stbuf; if (!glnx_fstatat_allow_noent (ostree_repo_get_dfd (dest_repo), "summary", &stbuf, 0, error)) return FALSE; if (errno == ENOENT && !ostree_repo_regenerate_summary (dest_repo, NULL, cancellable, error)) return FALSE; /* Add the symlinks .ostree/repos.d/@symlink_name → @dest_repo_path, unless * the @dest_repo_path is a well-known one like ostree/repo, in which case no * symlink is necessary; #OstreeRepoFinderMount always looks there. */ if (!g_str_equal (dest_repo_path, "ostree/repo") && !g_str_equal (dest_repo_path, ".ostree/repo")) { if (!glnx_shutil_mkdir_p_at (mount_root_dfd, ".ostree/repos.d", 0755, cancellable, error)) return FALSE; /* Find a unique name for the symlink. If a symlink already targets * @dest_repo_path, use that and don’t create a new one. */ GLnxDirFdIterator repos_iter; gboolean need_symlink = TRUE; if (!glnx_dirfd_iterator_init_at (mount_root_dfd, ".ostree/repos.d", TRUE, &repos_iter, error)) return FALSE; while (TRUE) { struct dirent *repo_dent; if (!glnx_dirfd_iterator_next_dent (&repos_iter, &repo_dent, cancellable, error)) return FALSE; if (repo_dent == NULL) break; /* Does the symlink already point to this repository? (Or is the * repository itself present in repos.d?) We already guarantee that * they’re on the same device. */ if (repo_dent->d_ino == dest_repo_stbuf.st_ino) { need_symlink = FALSE; break; } } /* If we need a symlink, find a unique name for it and create it. */ if (need_symlink) { /* Relative to .ostree/repos.d. */ g_autofree char *relative_dest_repo_path = g_build_filename ("..", "..", dest_repo_path, NULL); guint i; const guint max_attempts = 100; for (i = 0; i < max_attempts; i++) { g_autofree char *symlink_path = g_strdup_printf (".ostree/repos.d/%02u-generated", i); int ret = TEMP_FAILURE_RETRY (symlinkat (relative_dest_repo_path, mount_root_dfd, symlink_path)); if (ret < 0 && errno != EEXIST) return glnx_throw_errno_prefix (error, "symlinkat(%s → %s)", symlink_path, relative_dest_repo_path); else if (ret >= 0) break; } if (i == max_attempts) return glnx_throw (error, "Could not find an unused symlink name for the repository"); } } /* Report success to the user. */ g_autofree char *src_repo_path = g_file_get_path (ostree_repo_get_path (src_repo)); g_print ("Copied %u/%u refs successfully from ‘%s’ to ‘%s’ repository in ‘%s’.\n", refs->len, refs->len, src_repo_path, dest_repo_path, mount_root_path); return TRUE; }
gboolean ostree_builtin_export (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; const char *rev; g_autoptr(GFile) root = NULL; g_autoptr(GFile) subtree = NULL; g_autofree char *commit = NULL; g_autoptr(GVariant) commit_data = NULL; struct archive *a; OstreeRepoExportArchiveOptions opts = { 0, }; context = g_option_context_new ("COMMIT - Stream COMMIT to stdout in tar format"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; #ifdef HAVE_LIBARCHIVE if (argc <= 1) { ot_util_usage_error (context, "A COMMIT argument is required", error); goto out; } rev = argv[1]; a = archive_write_new (); /* Yes, this is hardcoded for now. There is * archive_write_set_format_filter_by_ext() but it's fairly magic. * Many programs have support now for GNU tar, so should be a good * default. I also don't want to lock us into everything libarchive * supports. */ if (archive_write_set_format_gnutar (a) != ARCHIVE_OK) { propagate_libarchive_error (error, a); goto out; } if (archive_write_add_filter_none (a) != ARCHIVE_OK) { propagate_libarchive_error (error, a); goto out; } if (opt_output_path) { if (archive_write_open_filename (a, opt_output_path) != ARCHIVE_OK) { propagate_libarchive_error (error, a); goto out; } } else { if (archive_write_open_FILE (a, stdout) != ARCHIVE_OK) { propagate_libarchive_error (error, a); goto out; } } if (opt_no_xattrs) opts.disable_xattrs = TRUE; if (!ostree_repo_read_commit (repo, rev, &root, &commit, cancellable, error)) goto out; if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, commit, &commit_data, error)) goto out; opts.timestamp_secs = ostree_commit_get_timestamp (commit_data); if (opt_subpath) subtree = g_file_resolve_relative_path (root, opt_subpath); else subtree = g_object_ref (root); opts.path_prefix = opt_prefix; if (!ostree_repo_export_tree_to_archive (repo, &opts, (OstreeRepoFile*)subtree, a, cancellable, error)) goto out; if (archive_write_close (a) != ARCHIVE_OK) { propagate_libarchive_error (error, a); goto out; } #else g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "This version of ostree is not compiled with libarchive support"); goto out; #endif ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ostree_builtin_config (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; glnx_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; const char *op; const char *section_key; const char *value; g_autofree char *section = NULL; g_autofree char *key = NULL; GKeyFile *config = NULL; context = g_option_context_new ("- Change configuration settings"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "OPERATION must be specified", error); goto out; } op = argv[1]; if (!strcmp (op, "set")) { if (argc < 4) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "KEY and VALUE must be specified"); goto out; } section_key = argv[2]; value = argv[3]; if (!split_key_string (section_key, §ion, &key, error)) goto out; config = ostree_repo_copy_config (repo); g_key_file_set_string (config, section, key, value); if (!ostree_repo_write_config (repo, config, error)) goto out; } else if (!strcmp (op, "get")) { GKeyFile *readonly_config = NULL; g_autofree char *value = NULL; if (argc < 3) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "KEY must be specified"); goto out; } section_key = argv[2]; if (!split_key_string (section_key, §ion, &key, error)) goto out; readonly_config = ostree_repo_get_config (repo); value = g_key_file_get_string (readonly_config, section, key, error); if (value == NULL) goto out; g_print ("%s\n", value); } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown operation %s", op); goto out; } ret = TRUE; out: if (config) g_key_file_free (config); return ret; }
gboolean ostree_builtin_admin (int argc, char **argv, GFile *repo_path, GError **error) { GOptionContext *context; gboolean ret = FALSE; __attribute__((unused)) GCancellable *cancellable = NULL; const char *subcommand_name; OstreeAdminCommand *subcommand; int subcmd_argc; char **subcmd_argv = NULL; ot_lobj GFile *ostree_dir = NULL; context = g_option_context_new ("[OPTIONS] SUBCOMMAND - Run an administrative subcommand"); { GString *s = g_string_new ("Subcommands:\n"); subcommand = admin_subcommands; while (subcommand->name) { g_string_append_printf (s, " %s\n", subcommand->name); subcommand++; } g_option_context_set_description (context, s->str); g_string_free (s, TRUE); } g_option_context_add_main_entries (context, options, NULL); /* Skip subcommand options */ g_option_context_set_ignore_unknown_options (context, TRUE); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc <= 1) { ot_util_usage_error (context, "A valid SUBCOMMAND is required", error); goto out; } subcommand_name = argv[1]; subcommand = admin_subcommands; while (subcommand->name) { if (g_strcmp0 (subcommand_name, subcommand->name) == 0) break; subcommand++; } if (!subcommand->name) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Unknown command '%s'", subcommand_name); goto out; } ostree_dir = g_file_new_for_path (opt_ostree_dir); ostree_prep_builtin_argv (subcommand_name, argc-2, argv+2, &subcmd_argc, &subcmd_argv); if (!subcommand->fn (subcmd_argc, subcmd_argv, ostree_dir, error)) goto out; ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; gs_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; gs_free char *remote = NULL; OstreeRepoPullFlags pullflags = 0; GSConsole *console = NULL; gs_unref_ptrarray GPtrArray *refs_to_fetch = NULL; gs_unref_object OstreeAsyncProgress *progress = NULL; context = g_option_context_new ("REMOTE [BRANCH...] - Download data from remote repository"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "REMOTE must be specified", error); goto out; } if (opt_disable_fsync) ostree_repo_set_disable_fsync (repo, TRUE); if (opt_mirror) pullflags |= OSTREE_REPO_PULL_FLAGS_MIRROR; if (strchr (argv[1], ':') == NULL) { remote = g_strdup (argv[1]); if (argc > 2) { int i; refs_to_fetch = g_ptr_array_new (); for (i = 2; i < argc; i++) g_ptr_array_add (refs_to_fetch, argv[i]); g_ptr_array_add (refs_to_fetch, NULL); } } else { char *ref_to_fetch; refs_to_fetch = g_ptr_array_new (); if (!ostree_parse_refspec (argv[1], &remote, &ref_to_fetch, error)) goto out; /* Transfer ownership */ g_ptr_array_add (refs_to_fetch, ref_to_fetch); g_ptr_array_add (refs_to_fetch, NULL); } console = gs_console_get (); if (console) { gs_console_begin_status_line (console, "", NULL, NULL); progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console); } { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (opt_subpath) g_variant_builder_add (&builder, "{s@v}", "subdir", g_variant_new_variant (g_variant_new_string (opt_subpath))); g_variant_builder_add (&builder, "{s@v}", "flags", g_variant_new_variant (g_variant_new_int32 (pullflags))); if (refs_to_fetch) g_variant_builder_add (&builder, "{s@v}", "refs", g_variant_new_variant (g_variant_new_strv ((const char *const*) refs_to_fetch->pdata, -1))); g_variant_builder_add (&builder, "{s@v}", "depth", g_variant_new_variant (g_variant_new_int32 (opt_depth))); if (!ostree_repo_pull_with_options (repo, remote, g_variant_builder_end (&builder), progress, cancellable, error)) goto out; } if (progress) ostree_async_progress_finish (progress); ret = TRUE; out: if (console) gs_console_end_status_line (console, NULL, NULL); if (context) g_option_context_free (context); return ret; }
gboolean ostree_builtin_config (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; gboolean ret = FALSE; const char *op; const char *section_key; const char *value; g_autofree char *section = NULL; g_autofree char *key = NULL; GKeyFile *config = NULL; context = g_option_context_new ("(get KEY|set KEY VALUE)"); if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "OPERATION must be specified", error); goto out; } op = argv[1]; if (!strcmp (op, "set")) { if (opt_group) { if (argc < 4) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "GROUP name, KEY and VALUE must be specified"); goto out; } section = g_strdup(opt_group); key = g_strdup(argv[2]); value = argv[3]; } else { if (argc < 4) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "KEY and VALUE must be specified"); goto out; } section_key = argv[2]; value = argv[3]; if(!split_key_string (section_key, §ion, &key, error)) goto out; } config = ostree_repo_copy_config (repo); g_key_file_set_string (config, section, key, value); if (!ostree_repo_write_config (repo, config, error)) goto out; } else if (!strcmp (op, "get")) { GKeyFile *readonly_config = NULL; g_autofree char *value = NULL; if (opt_group) { if (argc < 3) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Group name and key must be specified"); goto out; } section = g_strdup(opt_group); key = g_strdup(argv[2]); } else { if(argc < 3) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "KEY must be specified"); goto out; } section_key = argv[2]; if (!split_key_string (section_key, §ion, &key, error)) goto out; } readonly_config = ostree_repo_get_config (repo); value = g_key_file_get_string (readonly_config, section, key, error); if (value == NULL) goto out; g_print ("%s\n", value); } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown operation %s", op); goto out; } ret = TRUE; out: if (config) g_key_file_free (config); return ret; }
gboolean ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; g_autofree char *formatted_freed_size = NULL; OstreeRepoPruneFlags pruneflags = 0; gint n_objects_total; gint n_objects_pruned; guint64 objsize_total; context = g_option_context_new ("- Search for unreachable objects"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (!opt_no_prune && !ostree_ensure_repo_writable (repo, error)) goto out; if (opt_delete_commit) { if (opt_no_prune) { ot_util_usage_error (context, "Cannot specify both --delete-commit and --no-prune", error); goto out; } if (opt_static_deltas_only) { if(!ostree_repo_prune_static_deltas (repo, opt_delete_commit, cancellable, error)) goto out; } else if (!delete_commit (repo, opt_delete_commit, cancellable, error)) goto out; } if (opt_keep_younger_than) { if (opt_no_prune) { ot_util_usage_error (context, "Cannot specify both --keep-younger-than and --no-prune", error); goto out; } if (!prune_commits_keep_younger_than_date (repo, opt_keep_younger_than, cancellable, error)) goto out; } if (opt_refs_only) pruneflags |= OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY; if (opt_no_prune) pruneflags |= OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE; if (!ostree_repo_prune (repo, pruneflags, opt_depth, &n_objects_total, &n_objects_pruned, &objsize_total, cancellable, error)) goto out; formatted_freed_size = g_format_size_full (objsize_total, 0); g_print ("Total objects: %u\n", n_objects_total); if (n_objects_pruned == 0) g_print ("No unreachable objects\n"); else if (pruneflags & OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE) g_print ("Would delete: %u objects, freeing %s\n", n_objects_pruned, formatted_freed_size); else g_print ("Deleted %u objects, %s freed\n", n_objects_pruned, formatted_freed_size); ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
static gboolean run (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GOptionContext) context = NULL; const char *dirpath; OtTrivialHttpd appstruct = { 0, }; OtTrivialHttpd *app = &appstruct; glnx_unref_object SoupServer *server = NULL; g_autoptr(GFileMonitor) dirmon = NULL; context = g_option_context_new ("[DIR] - Simple webserver"); g_option_context_add_main_entries (context, options, NULL); app->root_dfd = -1; if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc > 1) dirpath = argv[1]; else dirpath = "."; if (!glnx_opendirat (AT_FDCWD, dirpath, TRUE, &app->root_dfd, error)) goto out; if (!(opt_random_500s_percentage >= 0 && opt_random_500s_percentage <= 99)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid --random-500s=%u", opt_random_500s_percentage); goto out; } if (opt_log) { GOutputStream *stream = NULL; if (g_strcmp0 (opt_log, "-") == 0) { if (opt_daemonize) { ot_util_usage_error (context, "Cannot use --log-file=- and --daemonize at the same time", error); goto out; } stream = G_OUTPUT_STREAM (g_unix_output_stream_new (STDOUT_FILENO, FALSE)); } else { g_autoptr(GFile) log_file = NULL; GFileOutputStream* log_stream; log_file = g_file_new_for_path (opt_log); log_stream = g_file_create (log_file, G_FILE_CREATE_PRIVATE, cancellable, error); if (!log_stream) goto out; stream = G_OUTPUT_STREAM (log_stream); } app->log = stream; } #if SOUP_CHECK_VERSION(2, 48, 0) server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "ostree-httpd ", NULL); if (!soup_server_listen_all (server, opt_port, 0, error)) goto out; #else server = soup_server_new (SOUP_SERVER_PORT, opt_port, SOUP_SERVER_SERVER_HEADER, "ostree-httpd ", NULL); #endif soup_server_add_handler (server, NULL, httpd_callback, app, NULL); if (opt_port_file) { g_autofree char *portstr = NULL; #if SOUP_CHECK_VERSION(2, 48, 0) GSList *listeners = soup_server_get_listeners (server); g_autoptr(GSocket) listener = NULL; g_autoptr(GSocketAddress) addr = NULL; g_assert (listeners); listener = g_object_ref (listeners->data); g_slist_free (listeners); listeners = NULL; addr = g_socket_get_local_address (listener, error); if (!addr) goto out; g_assert (G_IS_INET_SOCKET_ADDRESS (addr)); portstr = g_strdup_printf ("%u\n", g_inet_socket_address_get_port ((GInetSocketAddress*)addr)); #else portstr = g_strdup_printf ("%u\n", soup_server_get_port (server)); #endif if (g_strcmp0 ("-", opt_port_file) == 0) { fputs (portstr, stdout); // not g_print - this must go to stdout, not a handler fflush (stdout); } else if (!g_file_set_contents (opt_port_file, portstr, strlen (portstr), error)) goto out; } #if !SOUP_CHECK_VERSION(2, 48, 0) soup_server_run_async (server); #endif if (opt_daemonize) { pid_t pid = fork(); if (pid == -1) { int errsv = errno; g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv), g_strerror (errsv)); goto out; } else if (pid > 0) { ret = TRUE; goto out; } /* Child, continue */ if (setsid () < 0) err (1, "setsid"); /* Daemonising: close stdout/stderr so $() et al work on us */ if (freopen("/dev/null", "r", stdin) == NULL) err (1, "freopen"); if (freopen("/dev/null", "w", stdout) == NULL) err (1, "freopen"); if (freopen("/dev/null", "w", stderr) == NULL) err (1, "freopen"); } else { /* Since we're used for testing purposes, let's just do this by * default. This ensures we exit when our parent does. */ if (prctl (PR_SET_PDEATHSIG, SIGTERM) != 0) { if (errno != ENOSYS) { glnx_set_error_from_errno (error); goto out; } } } app->running = TRUE; if (opt_autoexit) { gboolean is_symlink = FALSE; g_autoptr(GFile) root = NULL; g_autoptr(GFileInfo) info = NULL; root = g_file_new_for_path (dirpath); info = g_file_query_info (root, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); if (!info) goto out; is_symlink = g_file_info_get_is_symlink (info); if (is_symlink) dirmon = g_file_monitor_file (root, 0, cancellable, error); else dirmon = g_file_monitor_directory (root, 0, cancellable, error); if (!dirmon) goto out; g_signal_connect (dirmon, "changed", G_CALLBACK (on_dir_changed), app); } httpd_log (app, "serving at root %s\n", dirpath); while (app->running) g_main_context_iteration (NULL, TRUE); ret = TRUE; out: if (app->root_dfd != -1) (void) close (app->root_dfd); g_clear_object (&app->log); return ret; }
gboolean ot_admin_builtin_undeploy (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; gs_unref_object OstreeSysroot *sysroot = NULL; const char *deploy_index_str; int deploy_index; gs_unref_ptrarray GPtrArray *current_deployments = NULL; gs_unref_object OstreeDeployment *booted_deployment = NULL; gs_unref_object OstreeDeployment *target_deployment = NULL; context = g_option_context_new ("INDEX - Delete deployment INDEX"); if (!ostree_admin_option_context_parse (context, options, &argc, &argv, OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, &sysroot, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "INDEX must be specified", error); goto out; } if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; current_deployments = ostree_sysroot_get_deployments (sysroot); deploy_index_str = argv[1]; deploy_index = atoi (deploy_index_str); target_deployment = ot_admin_get_indexed_deployment (sysroot, deploy_index, error); if (!target_deployment) goto out; if (target_deployment == ostree_sysroot_get_booted_deployment (sysroot)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot undeploy currently booted deployment %i", deploy_index); goto out; } g_ptr_array_remove_index (current_deployments, deploy_index); if (!ostree_sysroot_write_deployments (sysroot, current_deployments, cancellable, error)) goto out; g_print ("Deleted deployment %s.%d\n", ostree_deployment_get_csum (target_deployment), ostree_deployment_get_deployserial (target_deployment)); if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) { g_prefix_error (error, "Performing final cleanup: "); goto out; } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ot_admin_builtin_deploy (int argc, char **argv, GError **error) { GOptionContext *context; OtAdminDeploy self_data; OtAdminDeploy *self = &self_data; gboolean ret = FALSE; const char *deploy_target = NULL; const char *revision = NULL; __attribute__((unused)) GCancellable *cancellable = NULL; memset (self, 0, sizeof (*self)); context = g_option_context_new ("NAME [REVISION] - Check out revision NAME (or REVISION as NAME)"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc < 3) { ot_util_usage_error (context, "NAME must be specified", error); goto out; } deploy_target = argv[2]; if (argc > 3) revision = argv[3]; if (!do_checkout (self, deploy_target, revision, cancellable, error)) goto out; if (!opt_checkout_only) { struct utsname utsname; const char *release; (void) uname (&utsname); if (strcmp (utsname.sysname, "Linux") != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unsupported machine %s", utsname.sysname); goto out; } release = utsname.release; if (!update_initramfs (release, deploy_target, cancellable, error)) goto out; if (!update_grub (release, cancellable, error)) goto out; } if (!update_current (deploy_target, cancellable, error)) goto out; ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ot_remote_builtin_summary (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; const char *remote_name; g_autoptr(GBytes) summary_bytes = NULL; g_autoptr(GBytes) signature_bytes = NULL; OstreeDumpFlags flags = OSTREE_DUMP_NONE; gboolean gpg_verify_summary; gboolean ret = FALSE; context = g_option_context_new ("NAME - Show remote summary"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); goto out; } remote_name = argv[1]; if (opt_cache_dir) { if (!ostree_repo_set_cache_dir (repo, AT_FDCWD, opt_cache_dir, cancellable, error)) goto out; } if (opt_raw) flags |= OSTREE_DUMP_RAW; if (!ostree_repo_remote_fetch_summary (repo, remote_name, &summary_bytes, &signature_bytes, cancellable, error)) goto out; if (summary_bytes == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Remote server has no summary file"); goto out; } ot_dump_summary_bytes (summary_bytes, flags); if (!ostree_repo_remote_get_gpg_verify_summary (repo, remote_name, &gpg_verify_summary, error)) goto out; if (!gpg_verify_summary) g_clear_pointer (&signature_bytes, g_bytes_unref); /* XXX Note we don't show signatures for "--raw". My intuition is * if someone needs to see or parse raw summary data, including * signatures in the output would probably just interfere. * If there's demand for it I suppose we could introduce a new * option for raw signature data like "--raw-signatures". */ if (signature_bytes != NULL && !opt_raw) { glnx_unref_object OstreeGpgVerifyResult *result = NULL; /* The actual signed summary verification happens above in * ostree_repo_remote_fetch_summary(). Here we just parse * the signatures again for the purpose of printing. */ result = ostree_repo_verify_summary (repo, remote_name, summary_bytes, signature_bytes, cancellable, error); if (result == NULL) goto out; g_print ("\n"); ostree_print_gpg_verify_result (result); } ret = TRUE; out: return ret; }