gboolean ot_admin_builtin_cleanup (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeSysroot *sysroot = NULL; gboolean ret = FALSE; context = g_option_context_new ("Delete untagged deployments and repository objects"); if (!ostree_admin_option_context_parse (context, options, &argc, &argv, OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, &sysroot, cancellable, error)) goto out; if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) goto out; ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
/** * ostree_sysroot_simple_write_deployment: * @sysroot: Sysroot * @osname: (allow-none): OS name * @new_deployment: Prepend this deployment to the list * @merge_deployment: (allow-none): Use this deployment for configuration merge * @flags: Flags controlling behavior * @cancellable: Cancellable * @error: Error * * Prepend @new_deployment to the list of deployments, commit, and * cleanup. By default, all other deployments for the given @osname * except the merge deployment and the booted deployment will be * garbage collected. * * If %OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN is * specified, then all current deployments will be kept. */ gboolean ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, const char *osname, OstreeDeployment *new_deployment, OstreeDeployment *merge_deployment, OstreeSysrootSimpleWriteDeploymentFlags flags, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; guint i; OstreeDeployment *booted_deployment = NULL; g_autoptr(GPtrArray) deployments = NULL; g_autoptr(GPtrArray) new_deployments = g_ptr_array_new_with_free_func (g_object_unref); gboolean retain = (flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN) > 0; deployments = ostree_sysroot_get_deployments (sysroot); booted_deployment = ostree_sysroot_get_booted_deployment (sysroot); if (osname == NULL && booted_deployment) osname = ostree_deployment_get_osname (booted_deployment); g_ptr_array_add (new_deployments, g_object_ref (new_deployment)); for (i = 0; i < deployments->len; i++) { OstreeDeployment *deployment = deployments->pdata[i]; /* Keep deployments with different osnames, as well as the * booted and merge deployments */ if (retain || (osname != NULL && strcmp (ostree_deployment_get_osname (deployment), osname) != 0) || ostree_deployment_equal (deployment, booted_deployment) || ostree_deployment_equal (deployment, merge_deployment)) { g_ptr_array_add (new_deployments, g_object_ref (deployment)); } } if (!ostree_sysroot_write_deployments (sysroot, new_deployments, cancellable, error)) goto out; if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) goto out; ret = TRUE; out: return ret; }
gboolean ot_admin_builtin_upgrade (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = g_option_context_new (""); 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 (opt_pull_only && opt_deploy_only) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Cannot simultaneously specify --pull-only and --deploy-only"); return FALSE; } else if (opt_pull_only && opt_reboot) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Cannot simultaneously specify --pull-only and --reboot"); return FALSE; } g_autoptr(OstreeSysrootUpgrader) upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname, cancellable, error); if (!upgrader) return FALSE; g_autoptr(GKeyFile) origin = ostree_sysroot_upgrader_dup_origin (upgrader); if (origin != NULL) { gboolean origin_changed = FALSE; if (opt_override_commit != NULL) { /* Override the commit to pull and deploy. */ g_key_file_set_string (origin, "origin", "override-commit", opt_override_commit); origin_changed = TRUE; } else { /* Strip any override-commit from the origin file so * we always upgrade to the latest available commit. */ origin_changed = g_key_file_remove_key (origin, "origin", "override-commit", NULL); } /* Should we consider requiring --discard-hotfix here? */ origin_changed |= g_key_file_remove_key (origin, "origin", "unlocked", NULL); if (origin_changed) { /* XXX GCancellable parameter is not used. */ if (!ostree_sysroot_upgrader_set_origin (upgrader, origin, NULL, error)) return FALSE; } } gboolean changed; OstreeSysrootUpgraderPullFlags upgraderpullflags = 0; if (opt_deploy_only) upgraderpullflags |= OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_SYNTHETIC; { 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); if (opt_allow_downgrade) upgraderpullflags |= OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER; if (!ostree_sysroot_upgrader_pull (upgrader, 0, upgraderpullflags, progress, &changed, cancellable, error)) { /* In the pull-only case, we do a cleanup here to ensure that if * multiple commits were pulled, we garbage collect any old * partially-pulled intermediate commits before pulling more. This is * really a best practice in general, but for maximum compatiblity, we * only do cleanup if a user specifies the new --pull-only option. * Otherwise, we would break the case of trying to deploy a commit that * isn't directly referenced. */ if (opt_pull_only) (void) ostree_sysroot_cleanup (sysroot, NULL, NULL); return FALSE; } if (progress) ostree_async_progress_finish (progress); } if (!changed) { g_print ("No update available.\n"); } else { if (!opt_pull_only) { if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error)) return FALSE; } if (opt_reboot) { if (!ot_admin_execve_reboot (sysroot, error)) return FALSE; } } return TRUE; }
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, 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; }