2015-05-25 20:19:21 +03:00
/*
* Copyright ( C ) 2015 Red Hat , Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program 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 General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
2015-06-10 05:27:39 +03:00
# include "config.h"
2015-08-24 22:44:40 +03:00
# include "rpmostreed-deployment-utils.h"
2017-01-11 22:05:18 +03:00
# include "rpmostree-origin.h"
# include "rpmostree-util.h"
Introduce `ex livefs`
There are a few different use cases here. First, for layering new packages,
there's no good reason for us to force a reboot. Second, we want some support
for cherry-picking security updates and allowing admins to restart services. Finally,
at some point we should offer support for entirely replacing the running tree
if that's what the user wants.
Until now we've been very conservative, but there's a spectrum here. In
particular, this patch changes things so we push a rollback before we start
doing anything live. I think in practice, many use cases would be totally fine
with doing most changes live, and falling back to the rollback if something went
wrong.
This initial code drop *only* supports live layering of new packages. However,
a lot of the base infrastructure is laid for future work.
For now, this will be classified as an experimental feature, hence `ex livefs`.
Part of: https://github.com/projectatomic/rpm-ostree/issues/639
Closes: #652
Approved by: jlebon
2017-03-01 01:16:48 +03:00
# include "rpmostree-sysroot-core.h"
# include "rpmostreed-utils.h"
2015-05-25 20:19:21 +03:00
2015-06-10 05:27:39 +03:00
# include <libglnx.h>
2015-05-25 20:19:21 +03:00
2016-03-02 00:51:16 +03:00
/* Get a currently unique (for this host) identifier for the
* deployment ; TODO - adding the deployment timestamp would make it
* persistently unique , needs API in libostree .
*/
2015-05-25 20:19:21 +03:00
char *
2015-08-24 22:44:40 +03:00
rpmostreed_deployment_generate_id ( OstreeDeployment * deployment )
2015-05-25 20:19:21 +03:00
{
g_return_val_if_fail ( OSTREE_IS_DEPLOYMENT ( deployment ) , NULL ) ;
2016-03-02 00:51:16 +03:00
return g_strdup_printf ( " %s-%s.%u " ,
2017-06-22 00:09:03 +03:00
ostree_deployment_get_osname ( deployment ) ,
ostree_deployment_get_csum ( deployment ) ,
ostree_deployment_get_deployserial ( deployment ) ) ;
2015-05-25 20:19:21 +03:00
}
2015-06-11 05:17:49 +03:00
OstreeDeployment *
2015-08-24 22:44:40 +03:00
rpmostreed_deployment_get_for_id ( OstreeSysroot * sysroot ,
const gchar * deploy_id )
2015-06-11 05:17:49 +03:00
{
2017-07-07 20:45:57 +03:00
g_autoptr ( GPtrArray ) deployments = ostree_sysroot_get_deployments ( sysroot ) ;
for ( guint i = 0 ; i < deployments - > len ; i + + )
2015-06-11 05:17:49 +03:00
{
2015-08-24 22:44:40 +03:00
g_autofree gchar * id = rpmostreed_deployment_generate_id ( deployments - > pdata [ i ] ) ;
2017-07-07 20:45:57 +03:00
if ( g_strcmp0 ( deploy_id , id ) = = 0 )
return g_object_ref ( deployments - > pdata [ i ] ) ;
2015-06-11 05:17:49 +03:00
}
2017-07-07 20:45:57 +03:00
return NULL ;
2015-06-11 05:17:49 +03:00
}
2015-05-25 20:19:21 +03:00
static GVariant *
2015-08-24 22:44:40 +03:00
rpmostreed_deployment_gpg_results ( OstreeRepo * repo ,
const gchar * origin_refspec ,
2016-07-21 23:28:20 +03:00
const gchar * csum ,
2017-06-22 00:09:03 +03:00
gboolean * out_enabled )
2015-05-25 20:19:21 +03:00
{
GError * error = NULL ;
GVariant * ret = NULL ;
2015-06-10 05:27:39 +03:00
g_autofree gchar * remote = NULL ;
glnx_unref_object OstreeGpgVerifyResult * result = NULL ;
2015-05-25 20:19:21 +03:00
guint n_sigs , i ;
gboolean gpg_verify ;
2016-12-05 23:21:32 +03:00
g_auto ( GVariantBuilder ) builder ;
2015-05-25 20:19:21 +03:00
g_variant_builder_init ( & builder , G_VARIANT_TYPE ( " av " ) ) ;
if ( ! ostree_parse_refspec ( origin_refspec , & remote , NULL , & error ) )
goto out ;
2015-12-29 16:14:04 +03:00
if ( remote )
{
if ( ! ostree_repo_remote_get_gpg_verify ( repo , remote , & gpg_verify , & error ) )
2017-06-22 00:09:03 +03:00
goto out ;
2015-12-29 16:14:04 +03:00
}
else
{
gpg_verify = FALSE ;
}
2015-05-25 20:19:21 +03:00
2016-07-21 23:28:20 +03:00
* out_enabled = gpg_verify ;
2015-05-25 20:19:21 +03:00
if ( ! gpg_verify )
goto out ;
2016-11-17 00:32:51 +03:00
result = ostree_repo_verify_commit_for_remote ( repo , csum , remote , NULL , & error ) ;
2015-05-25 20:19:21 +03:00
if ( ! result )
2017-06-22 00:09:03 +03:00
goto out ;
2015-05-25 20:19:21 +03:00
n_sigs = ostree_gpg_verify_result_count_all ( result ) ;
if ( n_sigs < 1 )
goto out ;
for ( i = 0 ; i < n_sigs ; i + + )
{
g_variant_builder_add ( & builder , " v " ,
ostree_gpg_verify_result_get_all ( result , i ) ) ;
}
ret = g_variant_builder_end ( & builder ) ;
out :
/* NOT_FOUND just means the commit is not signed. */
if ( error & & ! g_error_matches ( error , G_IO_ERROR , G_IO_ERROR_NOT_FOUND ) )
g_warning ( " error loading gpg verify result %s " , error - > message ) ;
g_clear_error ( & error ) ;
return ret ;
}
2015-06-10 05:27:39 +03:00
GVariant *
2015-08-24 22:44:40 +03:00
rpmostreed_deployment_generate_blank_variant ( void )
2015-06-10 05:27:39 +03:00
{
2015-09-08 17:31:22 +03:00
GVariantDict dict ;
g_variant_dict_init ( & dict , NULL ) ;
return g_variant_dict_end ( & dict ) ;
2015-06-10 05:27:39 +03:00
}
2015-05-25 20:19:21 +03:00
2015-09-27 19:44:57 +03:00
static void
variant_add_commit_details ( GVariantDict * dict ,
2017-01-27 07:31:53 +03:00
const char * prefix ,
GVariant * commit )
2015-05-25 20:19:21 +03:00
{
2015-09-27 19:44:57 +03:00
g_autoptr ( GVariant ) metadata = NULL ;
g_autofree gchar * version_commit = NULL ;
2015-05-25 20:19:21 +03:00
guint64 timestamp = 0 ;
2016-06-09 04:37:40 +03:00
timestamp = ostree_commit_get_timestamp ( commit ) ;
metadata = g_variant_get_child_value ( commit , 0 ) ;
if ( metadata ! = NULL )
g_variant_lookup ( metadata , " version " , " s " , & version_commit ) ;
2015-09-27 19:44:57 +03:00
if ( version_commit ! = NULL )
2017-01-27 07:31:53 +03:00
g_variant_dict_insert ( dict , glnx_strjoina ( prefix ? : " " , " version " ) ,
" s " , version_commit ) ;
2015-09-27 19:44:57 +03:00
if ( timestamp > 0 )
2017-01-27 07:31:53 +03:00
g_variant_dict_insert ( dict , glnx_strjoina ( prefix ? : " " , " timestamp " ) ,
" t " , timestamp ) ;
2015-09-27 19:44:57 +03:00
}
2017-06-05 19:37:56 +03:00
static void
variant_add_from_hash_table ( GVariantDict * dict ,
const char * key ,
GHashTable * table )
{
g_autofree char * * values = ( char * * ) g_hash_table_get_keys_as_array ( table , NULL ) ;
g_variant_dict_insert ( dict , key , " ^as " , values ) ;
}
2015-09-27 19:44:57 +03:00
GVariant *
2017-03-25 17:34:24 +03:00
rpmostreed_deployment_generate_variant ( OstreeSysroot * sysroot ,
OstreeDeployment * deployment ,
2016-06-25 00:40:46 +03:00
const char * booted_id ,
2016-06-09 04:37:40 +03:00
OstreeRepo * repo ,
2017-02-24 17:44:40 +03:00
GError * * error )
2015-09-27 19:44:57 +03:00
{
g_autoptr ( GVariant ) commit = NULL ;
2017-01-11 22:05:18 +03:00
g_autoptr ( RpmOstreeOrigin ) origin = NULL ;
2015-09-27 19:44:57 +03:00
g_autofree gchar * id = NULL ;
2017-02-24 17:14:24 +03:00
g_autofree char * base_checksum = NULL ;
2015-09-27 19:44:57 +03:00
GVariant * sigs = NULL ; /* floating variant */
GVariantDict dict ;
2017-01-27 07:31:53 +03:00
const char * refspec ;
g_autofree char * pending_base_commitrev = NULL ;
2015-09-27 19:44:57 +03:00
const gchar * osname = ostree_deployment_get_osname ( deployment ) ;
const gchar * csum = ostree_deployment_get_csum ( deployment ) ;
gint serial = ostree_deployment_get_deployserial ( deployment ) ;
2016-10-25 20:43:50 +03:00
gboolean gpg_enabled = FALSE ;
2017-02-24 17:44:40 +03:00
gboolean is_layered = FALSE ;
Introduce `ex livefs`
There are a few different use cases here. First, for layering new packages,
there's no good reason for us to force a reboot. Second, we want some support
for cherry-picking security updates and allowing admins to restart services. Finally,
at some point we should offer support for entirely replacing the running tree
if that's what the user wants.
Until now we've been very conservative, but there's a spectrum here. In
particular, this patch changes things so we push a rollback before we start
doing anything live. I think in practice, many use cases would be totally fine
with doing most changes live, and falling back to the rollback if something went
wrong.
This initial code drop *only* supports live layering of new packages. However,
a lot of the base infrastructure is laid for future work.
For now, this will be classified as an experimental feature, hence `ex livefs`.
Part of: https://github.com/projectatomic/rpm-ostree/issues/639
Closes: #652
Approved by: jlebon
2017-03-01 01:16:48 +03:00
g_autofree char * live_inprogress = NULL ;
g_autofree char * live_replaced = NULL ;
2017-02-24 17:44:40 +03:00
g_auto ( GStrv ) layered_pkgs = NULL ;
2017-07-04 20:50:52 +03:00
g_autoptr ( GVariant ) removed_base_pkgs = NULL ;
g_autoptr ( GVariant ) replaced_base_pkgs = NULL ;
2016-06-09 04:37:40 +03:00
if ( ! ostree_repo_load_variant ( repo ,
2017-06-22 00:09:03 +03:00
OSTREE_OBJECT_TYPE_COMMIT ,
csum ,
& commit ,
error ) )
2016-06-09 04:37:40 +03:00
return NULL ;
2017-06-22 00:09:03 +03:00
2015-09-27 19:44:57 +03:00
id = rpmostreed_deployment_generate_id ( deployment ) ;
2015-05-25 20:19:21 +03:00
2017-02-16 20:46:13 +03:00
origin = rpmostree_origin_parse_deployment ( deployment , error ) ;
2017-01-11 22:05:18 +03:00
if ( ! origin )
return NULL ;
2015-05-25 20:19:21 +03:00
2017-01-27 07:31:53 +03:00
refspec = rpmostree_origin_get_refspec ( origin ) ;
2015-09-08 17:31:22 +03:00
g_variant_dict_init ( & dict , NULL ) ;
g_variant_dict_insert ( & dict , " id " , " s " , id ) ;
if ( osname ! = NULL )
g_variant_dict_insert ( & dict , " osname " , " s " , osname ) ;
g_variant_dict_insert ( & dict , " serial " , " i " , serial ) ;
g_variant_dict_insert ( & dict , " checksum " , " s " , csum ) ;
2017-02-24 17:44:40 +03:00
2017-06-05 19:37:56 +03:00
if ( ! rpmostree_deployment_get_layered_info ( repo , deployment , & is_layered , & base_checksum ,
2017-07-04 20:50:52 +03:00
& layered_pkgs , & removed_base_pkgs ,
& replaced_base_pkgs , error ) )
2017-02-24 17:44:40 +03:00
return NULL ;
if ( is_layered )
2017-03-21 19:48:09 +03:00
{
g_autoptr ( GVariant ) base_commit = NULL ;
if ( ! ostree_repo_load_variant ( repo ,
OSTREE_OBJECT_TYPE_COMMIT ,
base_checksum ,
& base_commit ,
error ) )
return NULL ;
g_variant_dict_insert ( & dict , " base-checksum " , " s " , base_checksum ) ;
variant_add_commit_details ( & dict , " base- " , base_commit ) ;
}
2017-01-11 22:05:18 +03:00
else
2017-02-24 17:44:40 +03:00
base_checksum = g_strdup ( csum ) ;
2017-01-27 07:31:53 +03:00
sigs = rpmostreed_deployment_gpg_results ( repo , refspec , base_checksum , & gpg_enabled ) ;
variant_add_commit_details ( & dict , NULL , commit ) ;
if ( ! ostree_repo_resolve_rev ( repo , refspec , TRUE ,
& pending_base_commitrev , error ) )
return NULL ;
if ( pending_base_commitrev & & strcmp ( pending_base_commitrev , base_checksum ) ! = 0 )
{
g_autoptr ( GVariant ) pending_base_commit = NULL ;
if ( ! ostree_repo_load_variant ( repo ,
OSTREE_OBJECT_TYPE_COMMIT ,
pending_base_commitrev ,
& pending_base_commit ,
error ) )
return NULL ;
2017-02-24 17:44:40 +03:00
2017-01-27 07:31:53 +03:00
g_variant_dict_insert ( & dict , " pending-base-checksum " , " s " , pending_base_commitrev ) ;
variant_add_commit_details ( & dict , " pending-base- " , pending_base_commit ) ;
}
2015-09-27 19:44:57 +03:00
Introduce `ex livefs`
There are a few different use cases here. First, for layering new packages,
there's no good reason for us to force a reboot. Second, we want some support
for cherry-picking security updates and allowing admins to restart services. Finally,
at some point we should offer support for entirely replacing the running tree
if that's what the user wants.
Until now we've been very conservative, but there's a spectrum here. In
particular, this patch changes things so we push a rollback before we start
doing anything live. I think in practice, many use cases would be totally fine
with doing most changes live, and falling back to the rollback if something went
wrong.
This initial code drop *only* supports live layering of new packages. However,
a lot of the base infrastructure is laid for future work.
For now, this will be classified as an experimental feature, hence `ex livefs`.
Part of: https://github.com/projectatomic/rpm-ostree/issues/639
Closes: #652
Approved by: jlebon
2017-03-01 01:16:48 +03:00
if ( ! rpmostree_syscore_deployment_get_live ( sysroot , deployment , - 1 ,
& live_inprogress , & live_replaced ,
error ) )
return NULL ;
if ( live_inprogress )
g_variant_dict_insert ( & dict , " live-inprogress " , " s " , live_inprogress ) ;
if ( live_replaced )
g_variant_dict_insert ( & dict , " live-replaced " , " s " , live_replaced ) ;
2017-01-27 07:31:53 +03:00
g_variant_dict_insert ( & dict , " origin " , " s " , refspec ) ;
2017-03-10 00:39:03 +03:00
2017-06-05 19:37:56 +03:00
variant_add_from_hash_table ( & dict , " requested-packages " ,
rpmostree_origin_get_packages ( origin ) ) ;
variant_add_from_hash_table ( & dict , " requested-local-packages " ,
rpmostree_origin_get_local_packages ( origin ) ) ;
2017-06-20 19:21:57 +03:00
variant_add_from_hash_table ( & dict , " requested-base-removals " ,
rpmostree_origin_get_overrides_remove ( origin ) ) ;
2017-07-04 20:50:52 +03:00
variant_add_from_hash_table ( & dict , " requested-base-local-replacements " ,
rpmostree_origin_get_overrides_local_replace ( origin ) ) ;
2017-02-24 17:44:40 +03:00
2017-07-04 20:50:52 +03:00
g_variant_dict_insert ( & dict , " packages " , " ^as " , layered_pkgs ) ;
g_variant_dict_insert_value ( & dict , " base-removals " , removed_base_pkgs ) ;
g_variant_dict_insert_value ( & dict , " base-local-replacements " , replaced_base_pkgs ) ;
2017-02-24 17:44:40 +03:00
2015-09-08 17:31:22 +03:00
if ( sigs ! = NULL )
g_variant_dict_insert_value ( & dict , " signatures " , sigs ) ;
2016-07-21 23:28:20 +03:00
g_variant_dict_insert ( & dict , " gpg-enabled " , " b " , gpg_enabled ) ;
2015-09-08 17:31:22 +03:00
2016-06-01 20:53:13 +03:00
g_variant_dict_insert ( & dict , " unlocked " , " s " ,
2017-06-05 19:37:56 +03:00
ostree_deployment_unlocked_state_to_string ( ostree_deployment_get_unlocked ( deployment ) ) ) ;
2016-06-01 20:53:13 +03:00
2017-01-04 20:29:01 +03:00
g_variant_dict_insert ( & dict , " regenerate-initramfs " , " b " ,
rpmostree_origin_get_regenerate_initramfs ( origin ) ) ;
2017-02-24 17:44:40 +03:00
{ const char * const * args = rpmostree_origin_get_initramfs_args ( origin ) ;
2017-01-04 20:29:01 +03:00
if ( args & & * args )
g_variant_dict_insert ( & dict , " initramfs-args " , " ^as " , args ) ;
}
2016-06-25 00:40:46 +03:00
if ( booted_id ! = NULL )
g_variant_dict_insert ( & dict , " booted " , " b " , g_strcmp0 ( booted_id , id ) = = 0 ) ;
2015-09-08 17:31:22 +03:00
return g_variant_dict_end ( & dict ) ;
2015-05-25 20:19:21 +03:00
}
2015-06-11 05:17:49 +03:00
2015-09-27 19:44:57 +03:00
GVariant *
rpmostreed_commit_generate_cached_details_variant ( OstreeDeployment * deployment ,
OstreeRepo * repo ,
2016-06-09 04:37:40 +03:00
const gchar * refspec ,
2017-06-22 00:09:03 +03:00
GError * * error )
2015-09-27 19:44:57 +03:00
{
2016-06-09 04:37:40 +03:00
g_autoptr ( GVariant ) commit = NULL ;
2015-09-27 19:44:57 +03:00
g_autofree gchar * origin_refspec = NULL ;
2016-07-18 19:54:28 +03:00
g_autofree gchar * head = NULL ;
2016-07-21 23:28:20 +03:00
gboolean gpg_enabled ;
2015-09-27 19:44:57 +03:00
const gchar * osname ;
GVariant * sigs = NULL ; /* floating variant */
GVariantDict dict ;
osname = ostree_deployment_get_osname ( deployment ) ;
if ( refspec )
origin_refspec = g_strdup ( refspec ) ;
else
2017-01-11 22:05:18 +03:00
{
g_autoptr ( RpmOstreeOrigin ) origin = NULL ;
2017-02-24 17:44:40 +03:00
2017-02-16 20:46:13 +03:00
origin = rpmostree_origin_parse_deployment ( deployment , error ) ;
2017-01-11 22:05:18 +03:00
if ( ! origin )
return NULL ;
origin_refspec = g_strdup ( rpmostree_origin_get_refspec ( origin ) ) ;
}
2015-09-27 19:44:57 +03:00
2016-07-18 18:36:50 +03:00
g_assert ( origin_refspec ) ;
2015-09-27 19:44:57 +03:00
2016-07-18 19:54:28 +03:00
/* allow_noent=TRUE since the ref may have been deleted for a
* rebase .
*/
if ( ! ostree_repo_resolve_rev ( repo , origin_refspec ,
TRUE , & head , error ) )
return NULL ;
if ( head = = NULL )
head = g_strdup ( ostree_deployment_get_csum ( deployment ) ) ;
2016-06-27 19:25:57 +03:00
2016-06-09 04:37:40 +03:00
if ( ! ostree_repo_load_variant ( repo ,
2017-06-22 00:09:03 +03:00
OSTREE_OBJECT_TYPE_COMMIT ,
head ,
& commit ,
error ) )
2016-06-09 04:37:40 +03:00
return NULL ;
2015-09-27 19:44:57 +03:00
2016-07-21 23:28:20 +03:00
sigs = rpmostreed_deployment_gpg_results ( repo , origin_refspec , head , & gpg_enabled ) ;
2015-09-27 19:44:57 +03:00
g_variant_dict_init ( & dict , NULL ) ;
if ( osname ! = NULL )
g_variant_dict_insert ( & dict , " osname " , " s " , osname ) ;
g_variant_dict_insert ( & dict , " checksum " , " s " , head ) ;
2017-01-27 07:31:53 +03:00
variant_add_commit_details ( & dict , NULL , commit ) ;
2015-09-27 19:44:57 +03:00
g_variant_dict_insert ( & dict , " origin " , " s " , origin_refspec ) ;
if ( sigs ! = NULL )
g_variant_dict_insert_value ( & dict , " signatures " , sigs ) ;
2016-07-21 23:28:20 +03:00
g_variant_dict_insert ( & dict , " gpg-enabled " , " b " , gpg_enabled ) ;
2016-07-18 18:36:50 +03:00
return g_variant_dict_end ( & dict ) ;
2015-09-27 19:44:57 +03:00
}