diff --git a/man/ostree-admin-pin.xml b/man/ostree-admin-pin.xml index ba3aa4a0c9..78edde4705 100644 --- a/man/ostree-admin-pin.xml +++ b/man/ostree-admin-pin.xml @@ -58,8 +58,10 @@ License along with this library. If not, see . Ensures the deployment at INDEX, will not be garbage - collected by default. This is termed "pinning". If the + collected by default. This is termed "pinning". If the -u option is provided, undoes a pinning operation. + INDEX can be >= 0 or one of booted, pending or + rollback strings. diff --git a/src/ostree/ot-admin-builtin-pin.c b/src/ostree/ot-admin-builtin-pin.c index de219d7e43..9cad5c878f 100644 --- a/src/ostree/ot-admin-builtin-pin.c +++ b/src/ostree/ot-admin-builtin-pin.c @@ -28,9 +28,74 @@ static gboolean opt_unpin; +typedef enum +{ + OSTREE_DEPLOYMENT_INDEX = 0, + OSTREE_DEPLOYMENT_BOOTED = 1, + OSTREE_DEPLOYMENT_PENDING = 2, + OSTREE_DEPLOYMENT_ROLLBACK = 3 +} OstreeDeploymentType; + static GOptionEntry options[] = { { "unpin", 'u', 0, G_OPTION_ARG_NONE, &opt_unpin, "Unset pin", NULL }, { NULL } }; +static gint64 +get_deployment_index (const OstreeDeployment *target_deployment, const GPtrArray *deployments) +{ + for (gint i = 0; i < deployments->len; ++i) + if (deployments->pdata[i] == target_deployment) + return i; + + return -1; +} + +static gint64 +get_deployment_index_for_type (OstreeSysroot *sysroot, OstreeDeploymentType type) +{ + g_autoptr (GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot); + const OstreeDeployment *booted_deployment = ostree_sysroot_get_booted_deployment (sysroot); + if (type == OSTREE_DEPLOYMENT_BOOTED) + return get_deployment_index (booted_deployment, deployments); + + g_autoptr (OstreeDeployment) pending_deployment = NULL; + g_autoptr (OstreeDeployment) rollback_deployment = NULL; + if (booted_deployment) + ostree_sysroot_query_deployments_for (sysroot, NULL, &pending_deployment, &rollback_deployment); + + if (type == OSTREE_DEPLOYMENT_PENDING) + return get_deployment_index (pending_deployment, deployments); + else if (type == OSTREE_DEPLOYMENT_ROLLBACK) + return get_deployment_index (rollback_deployment, deployments); + + return -1; +} + +static gboolean +do_pinning (OstreeSysroot *sysroot, const gint64 deploy_index, GError **error) +{ + g_autoptr (OstreeDeployment) target_deployment + = ot_admin_get_indexed_deployment (sysroot, deploy_index, error); + if (!target_deployment) + return FALSE; + + const gboolean current_pin = ostree_deployment_is_pinned (target_deployment); + const gboolean desired_pin = !opt_unpin; + if (current_pin == desired_pin) + { + g_print ("Deployment %lld is already %s\n", (long long int)deploy_index, + current_pin ? "pinned" : "unpinned"); + return TRUE; + } + else if (ostree_sysroot_deployment_set_pinned (sysroot, target_deployment, desired_pin, error)) + { + g_print ("Deployment %lld is now %s\n", (long long int)deploy_index, + desired_pin ? "pinned" : "unpinned"); + return TRUE; + } + + return FALSE; +} + gboolean ot_admin_builtin_pin (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) @@ -54,32 +119,23 @@ ot_admin_builtin_pin (int argc, char **argv, OstreeCommandInvocation *invocation char *endptr = NULL; errno = 0; - const guint64 deploy_index = g_ascii_strtoull (deploy_index_str, &endptr, 10); + gint64 deploy_index = g_ascii_strtoull (deploy_index_str, &endptr, 10); if (*endptr != '\0') - return glnx_throw (error, "Invalid index: %s", deploy_index_str); - if (errno == ERANGE) + { + if (!g_strcmp0 (deploy_index_str, "booted")) + deploy_index = get_deployment_index_for_type (sysroot, OSTREE_DEPLOYMENT_BOOTED); + else if (!g_strcmp0 (deploy_index_str, "pending")) + deploy_index = get_deployment_index_for_type (sysroot, OSTREE_DEPLOYMENT_PENDING); + else if (!g_strcmp0 (deploy_index_str, "rollback")) + deploy_index = get_deployment_index_for_type (sysroot, OSTREE_DEPLOYMENT_ROLLBACK); + + return glnx_throw (error, "Invalid index: %s", deploy_index_str); + } + else if (errno == ERANGE) return glnx_throw (error, "Index too large: %s", deploy_index_str); - g_autoptr (OstreeDeployment) target_deployment - = ot_admin_get_indexed_deployment (sysroot, deploy_index, error); - if (!target_deployment) + if (!do_pinning (sysroot, deploy_index, error)) return FALSE; - - gboolean current_pin = ostree_deployment_is_pinned (target_deployment); - const gboolean desired_pin = !opt_unpin; - if (current_pin == desired_pin) - { - g_print ("Deployment %s is already %s\n", deploy_index_str, - current_pin ? "pinned" : "unpinned"); - } - else - { - if (!ostree_sysroot_deployment_set_pinned (sysroot, target_deployment, desired_pin, - error)) - return FALSE; - g_print ("Deployment %s is now %s\n", deploy_index_str, - desired_pin ? "pinned" : "unpinned"); - } } return TRUE; diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index 68a54751f0..53face6a2d 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -53,7 +53,8 @@ static OstreeCommand admin_subcommands[] = { { "stateroot-init", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_os_init, "Initialize empty state for given operating system" }, { "pin", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_pin, - "Change the \"pinning\" state of a deployment" }, + "Change the \"pinning\" state of a deployment, INDEX can be >= 0 or one of booted, pending or " + "rollback strings" }, { "post-copy", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_post_copy, "Update the repo and deployments as needed after a copy" }, { "set-origin", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_set_origin,