rpm-ostree/src/rpmostree-builtin-rollback.c

130 lines
4.2 KiB
C
Raw Normal View History

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2014 Colin Walters <walters@verbum.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <glib-unix.h>
#include "rpmostree-builtins.h"
#include "libgsystem.h"
static gboolean opt_reboot;
static GOptionEntry option_entries[] = {
{ "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Initiate a reboot after rollback is prepared", NULL },
{ NULL }
};
gboolean
rpmostree_builtin_rollback (int argc,
char **argv,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
GOptionContext *context = g_option_context_new ("- Revert to the previously booted tree");
gs_unref_object OstreeSysroot *sysroot = NULL;
gs_free char *origin_description = NULL;
gs_unref_ptrarray GPtrArray *deployments = NULL;
gs_unref_ptrarray GPtrArray *new_deployments =
g_ptr_array_new_with_free_func (g_object_unref);
OstreeDeployment *booted_deployment = NULL;
guint i;
guint booted_index;
guint index_to_prepend;
g_option_context_add_main_entries (context, option_entries, NULL);
if (!g_option_context_parse (context, &argc, &argv, error))
goto out;
sysroot = ostree_sysroot_new_default ();
if (!ostree_sysroot_load (sysroot, cancellable, error))
goto out;
booted_deployment = ostree_sysroot_get_booted_deployment (sysroot);
if (booted_deployment == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Not currently booted into an OSTree system");
goto out;
}
deployments = ostree_sysroot_get_deployments (sysroot);
if (deployments->len < 2)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Found %u deployments, at least 2 required for rollback",
deployments->len);
goto out;
}
g_assert (booted_deployment != NULL);
for (booted_index = 0; booted_index < deployments->len; booted_index++)
{
if (deployments->pdata[booted_index] == booted_deployment)
break;
}
g_assert (booted_index < deployments->len);
g_assert (deployments->pdata[booted_index] == booted_deployment);
if (booted_index != 0)
{
/* There is an earlier deployment, let's assume we want to just
* insert the current one in front.
*/
index_to_prepend = booted_index;
}
else
{
/* We're booted into the first, let's roll back to the previous */
index_to_prepend = 1;
}
g_ptr_array_add (new_deployments, g_object_ref (deployments->pdata[index_to_prepend]));
for (i = 0; i < deployments->len; i++)
{
if (i == index_to_prepend)
continue;
g_ptr_array_add (new_deployments, g_object_ref (deployments->pdata[i]));
}
g_print ("Moving '%s.%d' to be first deployment\n",
ostree_deployment_get_csum (deployments->pdata[index_to_prepend]),
ostree_deployment_get_deployserial (deployments->pdata[index_to_prepend]));
if (!ostree_sysroot_write_deployments (sysroot, new_deployments, cancellable,
error))
goto out;
if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
goto out;
if (opt_reboot)
gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
cancellable, error,
"systemctl", "reboot", NULL);
ret = TRUE;
out:
return ret;
}