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,