2014-06-11 21:47:10 +04:00
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
2014-06-12 18:32:21 +04:00
* Copyright ( C ) 2014 Anne LoVerso < anne . loverso @ students . olin . edu >
2016-05-31 20:12:36 +03:00
* Copyright ( C ) 2016 Red Hat , Inc .
2014-06-11 21:47:10 +04:00
*
* 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>
2016-06-01 20:17:20 +03:00
# include <stdio.h>
2014-06-11 21:47:10 +04:00
# include <glib-unix.h>
2016-06-14 15:34:07 +03:00
# include <gio/gunixoutputstream.h>
2016-06-09 23:00:24 +03:00
# include <json-glib/json-glib.h>
2014-06-11 21:47:10 +04:00
# include "rpmostree-builtins.h"
2018-01-10 23:58:47 +03:00
# include "rpmostree-libbuiltin.h"
2015-06-13 03:19:16 +03:00
# include "rpmostree-dbus-helpers.h"
2016-08-30 22:57:40 +03:00
# include "rpmostree-util.h"
2017-07-04 20:50:52 +03:00
# include "rpmostree-rpm-util.h"
2016-05-29 21:15:29 +03:00
# include "libsd-locale-util.h"
2014-06-11 21:47:10 +04:00
2015-06-13 03:19:16 +03:00
# include <libglnx.h>
2014-06-11 21:47:10 +04:00
2014-06-13 21:16:43 +04:00
static gboolean opt_pretty ;
2017-04-21 04:51:35 +03:00
static gboolean opt_verbose ;
2016-06-09 23:00:24 +03:00
static gboolean opt_json ;
2017-12-20 23:50:46 +03:00
static const char * opt_jsonpath ;
2014-06-13 21:16:43 +04:00
static GOptionEntry option_entries [ ] = {
2017-10-16 06:11:05 +03:00
{ " pretty " , ' p ' , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_NONE , & opt_pretty , " This option is deprecated and no longer has any effect " , NULL } ,
2017-04-21 04:51:35 +03:00
{ " verbose " , ' v ' , 0 , G_OPTION_ARG_NONE , & opt_verbose , " Print additional fields (e.g. StateRoot) " , NULL } ,
2016-06-09 23:00:24 +03:00
{ " json " , 0 , 0 , G_OPTION_ARG_NONE , & opt_json , " Output JSON " , NULL } ,
2017-12-20 23:50:46 +03:00
{ " jsonpath " , ' J ' , 0 , G_OPTION_ARG_STRING , & opt_jsonpath , " Filter JSONPath expression " , " EXPRESSION " } ,
2014-06-13 21:16:43 +04:00
{ NULL }
} ;
2017-12-15 23:00:41 +03:00
/* return space available for printing value side of kv */
static guint
get_textarea_width ( guint maxkeylen )
{
const guint columns = glnx_console_columns ( ) ;
/* +2 for initial leading spaces */
const guint right_side_width = maxkeylen + 2 + strlen ( " : " ) ;
if ( right_side_width > = columns )
return G_MAXUINT ; /* can't even print keys without wrapping, nothing pretty to do here */
/* the sha is already 64 chars, so no point in trying to use less */
return MAX ( OSTREE_SHA256_STRING_LEN , columns - right_side_width ) ;
2017-06-23 20:56:39 +03:00
}
2016-06-14 16:41:36 +03:00
static GVariant *
get_active_txn ( RPMOSTreeSysroot * sysroot_proxy )
{
GVariant * txn = rpmostree_sysroot_get_active_transaction ( sysroot_proxy ) ;
const char * a , * b , * c ;
if ( txn )
2016-10-28 22:54:12 +03:00
{
g_variant_get ( txn , " (&s&s&s) " , & a , & b , & c ) ;
if ( * a )
return txn ;
}
2016-06-14 16:41:36 +03:00
return NULL ;
}
2017-02-24 17:44:40 +03:00
static void
print_packages ( const char * k , guint max_key_len ,
const char * const * pkgs ,
const char * const * omit_pkgs )
{
2017-12-15 23:00:41 +03:00
g_autoptr ( GPtrArray ) packages_sorted = g_ptr_array_new_with_free_func ( g_free ) ;
2017-02-24 17:44:40 +03:00
static gsize regex_initialized ;
static GRegex * safe_chars_regex ;
if ( g_once_init_enter ( & regex_initialized ) )
{
safe_chars_regex = g_regex_new ( " ^[[:alnum:]-._]+$ " , 0 , 0 , NULL ) ;
g_assert ( safe_chars_regex ) ;
g_once_init_leave ( & regex_initialized , 1 ) ;
}
for ( char * * iter = ( char * * ) pkgs ; iter & & * iter ; iter + + )
{
if ( omit_pkgs ! = NULL & & g_strv_contains ( omit_pkgs , * iter ) )
continue ;
/* don't quote if it just has common pkgname/shell-safe chars */
if ( g_regex_match ( safe_chars_regex , * iter , 0 , 0 ) )
g_ptr_array_add ( packages_sorted , g_strdup ( * iter ) ) ;
else
g_ptr_array_add ( packages_sorted , g_shell_quote ( * iter ) ) ;
}
2017-12-15 23:00:41 +03:00
const guint n_packages = packages_sorted - > len ;
if ( n_packages = = 0 )
return ;
2018-01-11 00:03:10 +03:00
rpmostree_print_kv_no_newline ( k , max_key_len , " " ) ;
2017-12-15 23:00:41 +03:00
/* wrap pkglist output ourselves rather than letting the terminal cut us up */
const guint area_width = get_textarea_width ( max_key_len ) ;
guint current_width = 0 ;
for ( guint i = 0 ; i < n_packages ; i + + )
2017-02-24 17:44:40 +03:00
{
2017-12-15 23:00:41 +03:00
const char * pkg = packages_sorted - > pdata [ i ] ;
const guint pkg_width = strlen ( pkg ) ;
/* first print */
if ( current_width = = 0 )
{
g_print ( " %s " , pkg ) ;
current_width + = pkg_width ;
}
else if ( ( current_width + pkg_width + 1 ) < = area_width ) /* +1 for space separator */
{
g_print ( " %s " , pkg ) ;
current_width + = ( pkg_width + 1 ) ;
}
else
{
/* always print at least one per line, even if we overflow */
putc ( ' \n ' , stdout ) ;
2018-01-11 00:03:10 +03:00
rpmostree_print_kv_no_newline ( " " , max_key_len , pkg ) ;
2017-12-15 23:00:41 +03:00
current_width = pkg_width ;
}
2017-02-24 17:44:40 +03:00
}
2017-12-15 23:00:41 +03:00
putc ( ' \n ' , stdout ) ;
2017-02-24 17:44:40 +03:00
}
2017-03-03 23:48:56 +03:00
static const gchar * *
lookup_array_and_canonicalize ( GVariantDict * dict ,
const char * key )
{
g_autofree const gchar * * ret = NULL ;
if ( g_variant_dict_lookup ( dict , key , " ^a&s " , & ret ) )
{
/* Canonicalize length 0 strv to NULL */
if ( ! * ret )
g_clear_pointer ( & ret , g_free ) ;
}
return g_steal_pointer ( & ret ) ;
}
2017-09-21 05:11:37 +03:00
static void
gv_nevra_to_evr ( GString * buffer ,
GVariant * gv_nevra )
2017-07-04 20:50:52 +03:00
{
guint64 epoch ;
const char * version , * release ;
g_variant_get ( gv_nevra , " (sst&s&ss) " , NULL , NULL , & epoch , & version , & release , NULL ) ;
2017-09-21 05:11:37 +03:00
rpmostree_custom_nevra ( buffer , NULL , epoch , version , release , NULL ,
PKG_NEVRA_FLAGS_EPOCH_VERSION_RELEASE ) ;
2017-07-04 20:50:52 +03:00
}
2016-05-31 20:12:36 +03:00
/* We will have an optimized path for the case where there are just
* two deployments , this code will be the generic fallback .
*/
static gboolean
2016-06-01 20:17:20 +03:00
status_generic ( RPMOSTreeSysroot * sysroot_proxy ,
RPMOSTreeOS * os_proxy ,
GVariant * deployments ,
GCancellable * cancellable ,
GError * * error )
2014-06-11 21:47:10 +04:00
{
2015-09-08 17:31:22 +03:00
GVariantIter iter ;
2016-06-01 20:17:20 +03:00
gboolean first = TRUE ;
2016-06-14 16:41:36 +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
/* First, gather global state */
gboolean have_any_live_overlay = FALSE ;
g_variant_iter_init ( & iter , deployments ) ;
while ( TRUE )
{
g_autoptr ( GVariant ) child = g_variant_iter_next_value ( & iter ) ;
if ( ! child )
break ;
g_autoptr ( GVariantDict ) dict = g_variant_dict_new ( child ) ;
const gchar * live_inprogress ;
if ( ! g_variant_dict_lookup ( dict , " live-inprogress " , " &s " , & live_inprogress ) )
live_inprogress = NULL ;
const gchar * live_replaced ;
if ( ! g_variant_dict_lookup ( dict , " live-replaced " , " &s " , & live_replaced ) )
live_replaced = NULL ;
const gboolean have_live_changes = live_inprogress | | live_replaced ;
have_any_live_overlay = have_any_live_overlay | | have_live_changes ;
}
2017-10-03 19:24:22 +03:00
glnx_unref_object RPMOSTreeTransaction * txn_proxy = NULL ;
if ( ! rpmostree_transaction_connect_active ( sysroot_proxy , NULL , & txn_proxy ,
cancellable , error ) )
return FALSE ;
if ( txn_proxy )
2016-06-14 16:41:36 +03:00
{
2017-10-03 19:24:22 +03:00
const char * title = rpmostree_transaction_get_title ( txn_proxy ) ;
g_print ( " State: transaction: %s \n " , title ) ;
2016-06-14 16:41:36 +03:00
}
else
g_print ( " State: idle \n " ) ;
g_print ( " Deployments: \n " ) ;
2014-06-13 21:16:43 +04:00
2015-09-08 17:31:22 +03:00
g_variant_iter_init ( & iter , deployments ) ;
2016-06-01 20:17:20 +03:00
while ( TRUE )
2014-06-11 21:47:10 +04:00
{
2016-06-01 20:17:20 +03:00
g_autoptr ( GVariant ) child = g_variant_iter_next_value ( & iter ) ;
g_autoptr ( GVariantDict ) dict = NULL ;
2017-10-19 22:31:50 +03:00
g_autoptr ( GVariantDict ) commit_meta_dict = NULL ;
g_autoptr ( GVariantDict ) layered_commit_meta_dict = NULL ;
2017-02-24 17:44:40 +03:00
gboolean is_locally_assembled = FALSE ;
g_autofree const gchar * * origin_packages = NULL ;
g_autofree const gchar * * origin_requested_packages = NULL ;
2017-03-03 23:48:56 +03:00
g_autofree const gchar * * origin_requested_local_packages = NULL ;
2017-07-04 20:50:52 +03:00
g_autoptr ( GVariant ) origin_base_removals = NULL ;
2017-06-20 19:21:57 +03:00
g_autofree const gchar * * origin_requested_base_removals = NULL ;
2017-07-04 20:50:52 +03:00
g_autoptr ( GVariant ) origin_base_local_replacements = NULL ;
g_autofree const gchar * * origin_requested_base_local_replacements = NULL ;
2016-06-01 20:17:20 +03:00
const gchar * origin_refspec ;
const gchar * id ;
const gchar * os_name ;
const gchar * checksum ;
const gchar * version_string ;
2016-06-01 20:53:49 +03:00
const gchar * unlocked ;
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
const gchar * live_inprogress ;
const gchar * live_replaced ;
2016-07-21 23:28:20 +03:00
gboolean gpg_enabled ;
2017-01-04 20:29:01 +03:00
gboolean regenerate_initramfs ;
2016-06-01 20:17:20 +03:00
guint64 t = 0 ;
int serial ;
gboolean is_booted ;
2017-01-27 07:31:53 +03:00
const gboolean was_first = first ;
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
/* Add the long keys here */
2017-07-04 20:50:52 +03:00
const guint max_key_len = MAX ( strlen ( " InactiveBaseReplacements " ) ,
2017-06-05 19:37:56 +03:00
strlen ( " InterruptedLiveCommit " ) ) ;
2016-06-01 20:17:20 +03:00
g_autoptr ( GVariant ) signatures = NULL ;
g_autofree char * timestamp_string = NULL ;
2015-09-08 17:31:22 +03:00
2016-06-01 20:17:20 +03:00
if ( child = = NULL )
break ;
2017-03-03 23:48:56 +03:00
dict = g_variant_dict_new ( child ) ;
2016-06-01 20:17:20 +03:00
/* osname should always be present. */
g_assert ( g_variant_dict_lookup ( dict , " osname " , " &s " , & os_name ) ) ;
g_assert ( g_variant_dict_lookup ( dict , " id " , " &s " , & id ) ) ;
g_assert ( g_variant_dict_lookup ( dict , " serial " , " i " , & serial ) ) ;
2016-12-05 23:21:32 +03:00
g_assert ( g_variant_dict_lookup ( dict , " checksum " , " &s " , & checksum ) ) ;
2017-02-24 17:44:40 +03:00
2016-12-05 23:21:32 +03:00
if ( g_variant_dict_lookup ( dict , " origin " , " &s " , & origin_refspec ) )
2014-06-12 18:32:21 +04:00
{
2017-03-03 23:48:56 +03:00
origin_packages =
lookup_array_and_canonicalize ( dict , " packages " ) ;
origin_requested_packages =
lookup_array_and_canonicalize ( dict , " requested-packages " ) ;
origin_requested_local_packages =
lookup_array_and_canonicalize ( dict , " requested-local-packages " ) ;
2017-06-20 19:21:57 +03:00
origin_base_removals =
2017-07-04 20:50:52 +03:00
g_variant_dict_lookup_value ( dict , " base-removals " , G_VARIANT_TYPE ( " av " ) ) ;
2017-06-20 19:21:57 +03:00
origin_requested_base_removals =
lookup_array_and_canonicalize ( dict , " requested-base-removals " ) ;
2017-07-04 20:50:52 +03:00
origin_base_local_replacements =
g_variant_dict_lookup_value ( dict , " base-local-replacements " ,
G_VARIANT_TYPE ( " a(vv) " ) ) ;
origin_requested_base_local_replacements =
lookup_array_and_canonicalize ( dict , " requested-base-local-replacements " ) ;
2014-06-12 18:32:21 +04:00
}
2016-06-01 20:17:20 +03:00
else
origin_refspec = NULL ;
if ( ! g_variant_dict_lookup ( dict , " version " , " &s " , & version_string ) )
version_string = NULL ;
2016-06-01 20:53:49 +03:00
if ( ! g_variant_dict_lookup ( dict , " unlocked " , " &s " , & unlocked ) )
unlocked = NULL ;
2015-09-08 17:31:22 +03:00
2017-01-04 20:29:01 +03:00
if ( ! g_variant_dict_lookup ( dict , " regenerate-initramfs " , " b " , & regenerate_initramfs ) )
regenerate_initramfs = FALSE ;
2016-06-01 20:17:20 +03:00
signatures = g_variant_dict_lookup_value ( dict , " signatures " ,
G_VARIANT_TYPE ( " av " ) ) ;
2015-09-08 17:31:22 +03:00
2016-06-01 20:17:20 +03:00
if ( first )
first = FALSE ;
else
g_print ( " \n " ) ;
2015-07-16 21:11:35 +03:00
2016-06-25 00:40:46 +03:00
if ( ! g_variant_dict_lookup ( dict , " booted " , " b " , & is_booted ) )
is_booted = FALSE ;
2015-06-13 03:19:16 +03:00
2016-06-01 20:17:20 +03:00
g_print ( " %s " , is_booted ? libsd_special_glyph ( BLACK_CIRCLE ) : " " ) ;
2015-09-08 17:31:22 +03:00
2016-06-01 20:17:20 +03:00
if ( origin_refspec )
2017-12-07 02:06:26 +03:00
g_print ( " ostree://%s " , origin_refspec ) ;
2016-06-01 20:17:20 +03:00
else
g_print ( " %s " , checksum ) ;
g_print ( " \n " ) ;
2017-02-24 17:44:40 +03:00
2017-03-21 19:48:09 +03:00
const char * base_checksum = NULL ;
g_variant_dict_lookup ( dict , " base-checksum " , " &s " , & base_checksum ) ;
if ( base_checksum ! = NULL )
is_locally_assembled = TRUE ;
2017-10-19 22:31:50 +03:00
/* Load the commit metadata into a dict */
{ g_autoptr ( GVariant ) commit_meta_v = NULL ;
g_assert ( g_variant_dict_lookup ( dict , " base-commit-meta " , " @a{sv} " , & commit_meta_v ) ) ;
commit_meta_dict = g_variant_dict_new ( commit_meta_v ) ;
}
if ( is_locally_assembled )
{
g_autoptr ( GVariant ) layered_commit_meta_v = NULL ;
g_assert ( g_variant_dict_lookup ( dict , " layered-commit-meta " , " @a{sv} " , & layered_commit_meta_v ) ) ;
layered_commit_meta_dict = g_variant_dict_new ( layered_commit_meta_v ) ;
}
const gchar * source_title = NULL ;
g_variant_dict_lookup ( commit_meta_dict , OSTREE_COMMIT_META_KEY_SOURCE_TITLE , " &s " , & source_title ) ;
if ( source_title )
g_print ( " %s %s \n " , libsd_special_glyph ( TREE_RIGHT ) , source_title ) ;
2017-03-21 19:48:09 +03:00
if ( is_locally_assembled )
g_assert ( g_variant_dict_lookup ( dict , " base-timestamp " , " t " , & t ) ) ;
else
g_assert ( g_variant_dict_lookup ( dict , " timestamp " , " t " , & t ) ) ;
2018-01-11 00:03:10 +03:00
timestamp_string = rpmostree_timestamp_str_from_unix_utc ( t ) ;
2017-03-21 19:48:09 +03:00
2018-01-11 00:03:10 +03:00
rpmostree_print_timestamp_version ( version_string , timestamp_string , max_key_len ) ;
2017-02-24 17:44:40 +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 ( ! g_variant_dict_lookup ( dict , " live-inprogress " , " &s " , & live_inprogress ) )
live_inprogress = NULL ;
if ( ! g_variant_dict_lookup ( dict , " live-replaced " , " &s " , & live_replaced ) )
live_replaced = NULL ;
const gboolean have_live_changes = live_inprogress | | live_replaced ;
2017-03-21 19:48:09 +03:00
if ( is_locally_assembled )
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 ( have_live_changes )
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " BootedBaseCommit " , max_key_len , base_checksum ) ;
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
else
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " BaseCommit " , max_key_len , base_checksum ) ;
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 ( opt_verbose | | have_any_live_overlay )
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " Commit " , max_key_len , checksum ) ;
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
}
else
{
if ( have_live_changes )
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " BootedCommit " , max_key_len , checksum ) ;
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 ( ! have_live_changes | | opt_verbose )
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " Commit " , max_key_len , checksum ) ;
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 ( live_inprogress )
{
if ( is_booted )
2018-01-10 23:58:47 +03:00
g_print ( " %s%s " , get_red_start ( ) , get_bold_start ( ) ) ;
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " InterruptedLiveCommit " , max_key_len , live_inprogress ) ;
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 ( is_booted )
2018-01-10 23:58:47 +03:00
g_print ( " %s%s " , get_bold_end ( ) , get_red_end ( ) ) ;
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 ( live_replaced )
{
if ( is_booted )
2018-01-10 23:58:47 +03:00
g_print ( " %s%s " , get_red_start ( ) , get_bold_start ( ) ) ;
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " LiveCommit " , max_key_len , live_replaced ) ;
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 ( is_booted )
2018-01-10 23:58:47 +03:00
g_print ( " %s%s " , get_bold_end ( ) , get_red_end ( ) ) ;
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
}
2017-01-27 07:31:53 +03:00
/* Show any difference between the baseref vs head, but only for the
booted commit , and only if there isn ' t a pending deployment . Otherwise
it ' s either unnecessary or too noisy .
*/
if ( is_booted & & was_first )
{
const gchar * pending_checksum = NULL ;
const gchar * pending_version = NULL ;
if ( g_variant_dict_lookup ( dict , " pending-base-checksum " , " &s " , & pending_checksum ) )
{
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( is_locally_assembled ? " PendingBaseCommit " : " PendingCommit " ,
2017-01-27 07:31:53 +03:00
max_key_len , pending_checksum ) ;
g_assert ( g_variant_dict_lookup ( dict , " pending-base-timestamp " , " t " , & t ) ) ;
g_variant_dict_lookup ( dict , " pending-base-version " , " &s " , & pending_version ) ;
if ( pending_version )
{
g_autoptr ( GDateTime ) timestamp = g_date_time_new_from_unix_utc ( t ) ;
g_autofree char * version_time = NULL ;
if ( timestamp ! = NULL )
timestamp_string = g_date_time_format ( timestamp , " %Y-%m-%d %T " ) ;
else
timestamp_string = g_strdup_printf ( " (invalid timestamp) " ) ;
version_time = g_strdup_printf ( " %s (%s) " , pending_version , timestamp_string ) ;
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( is_locally_assembled ? " PendingBaseVersion " : " PendingVersion " ,
2017-01-27 07:31:53 +03:00
max_key_len , version_time ) ;
}
}
}
2017-04-14 17:44:58 +03:00
/* This used to be OSName; see https://github.com/ostreedev/ostree/pull/794 */
2017-04-21 04:51:35 +03:00
if ( opt_verbose )
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " StateRoot " , max_key_len , os_name ) ;
2014-06-12 18:32:21 +04:00
2016-07-21 23:28:20 +03:00
if ( ! g_variant_dict_lookup ( dict , " gpg-enabled " , " b " , & gpg_enabled ) )
gpg_enabled = FALSE ;
if ( gpg_enabled )
2018-01-11 00:03:10 +03:00
rpmostree_print_gpg_info ( signatures , opt_verbose , max_key_len ) ;
2014-06-11 21:47:10 +04:00
2017-06-05 19:37:56 +03:00
/* print base overrides before overlays */
2017-07-04 20:50:52 +03:00
g_autoptr ( GPtrArray ) active_removals = g_ptr_array_new_with_free_func ( g_free ) ;
2017-06-20 19:21:57 +03:00
if ( origin_base_removals )
2017-07-04 20:50:52 +03:00
{
g_autoptr ( GString ) str = g_string_new ( " " ) ;
const guint n = g_variant_n_children ( origin_base_removals ) ;
for ( guint i = 0 ; i < n ; i + + )
{
g_autoptr ( GVariant ) gv_nevra ;
g_variant_get_child ( origin_base_removals , i , " v " , & gv_nevra ) ;
const char * name , * nevra ;
g_variant_get_child ( gv_nevra , 0 , " &s " , & nevra ) ;
g_variant_get_child ( gv_nevra , 1 , " &s " , & name ) ;
if ( str - > len )
g_string_append ( str , " , " ) ;
g_string_append ( str , nevra ) ;
g_ptr_array_add ( active_removals , g_strdup ( name ) ) ;
}
g_ptr_array_add ( active_removals , NULL ) ;
if ( str - > len )
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " RemovedBasePackages " , max_key_len , str - > str ) ;
2017-07-04 20:50:52 +03:00
}
2017-06-20 19:21:57 +03:00
/* only print inactive base removal requests in verbose mode */
if ( origin_requested_base_removals & & opt_verbose )
print_packages ( " InactiveBaseRemovals " , max_key_len ,
2017-07-04 20:50:52 +03:00
origin_requested_base_removals ,
( const char * const * ) active_removals - > pdata ) ;
g_autoptr ( GPtrArray ) active_replacements = g_ptr_array_new_with_free_func ( g_free ) ;
if ( origin_base_local_replacements )
{
g_autoptr ( GString ) str = g_string_new ( " " ) ;
2017-09-21 05:11:37 +03:00
g_autoptr ( GHashTable ) grouped_diffs =
g_hash_table_new_full ( g_str_hash , g_str_equal , g_free ,
( GDestroyNotify ) g_ptr_array_unref ) ;
2017-07-04 20:50:52 +03:00
const guint n = g_variant_n_children ( origin_base_local_replacements ) ;
for ( guint i = 0 ; i < n ; i + + )
{
g_autoptr ( GVariant ) gv_nevra_new ;
g_autoptr ( GVariant ) gv_nevra_old ;
g_variant_get_child ( origin_base_local_replacements , i , " (vv) " ,
& gv_nevra_new , & gv_nevra_old ) ;
const char * nevra_new , * name_new , * name_old ;
g_variant_get_child ( gv_nevra_new , 0 , " &s " , & nevra_new ) ;
g_variant_get_child ( gv_nevra_new , 1 , " &s " , & name_new ) ;
g_variant_get_child ( gv_nevra_old , 1 , " &s " , & name_old ) ;
/* if pkgnames match, print a nicer version like treediff */
if ( g_str_equal ( name_new , name_old ) )
{
2017-09-21 05:11:37 +03:00
/* let's just use str as a scratchpad to avoid excessive mallocs; the str
* needs to be stretched anyway for the final output */
gsize original_size = str - > len ;
gv_nevra_to_evr ( str , gv_nevra_old ) ;
g_string_append ( str , " -> " ) ;
gv_nevra_to_evr ( str , gv_nevra_new ) ;
const char * diff = str - > str + original_size ;
GPtrArray * pkgs = g_hash_table_lookup ( grouped_diffs , diff ) ;
if ( ! pkgs )
{
pkgs = g_ptr_array_new_with_free_func ( g_free ) ;
g_hash_table_insert ( grouped_diffs , g_strdup ( diff ) , pkgs ) ;
}
g_ptr_array_add ( pkgs , g_strdup ( name_new ) ) ;
g_string_truncate ( str , original_size ) ;
2017-07-04 20:50:52 +03:00
}
else
{
2017-09-21 05:11:37 +03:00
if ( str - > len )
g_string_append ( str , " , " ) ;
2017-07-04 20:50:52 +03:00
const char * nevra_old ;
g_variant_get_child ( gv_nevra_old , 0 , " &s " , & nevra_old ) ;
g_string_append_printf ( str , " %s -> %s " , nevra_old , nevra_new ) ;
}
g_ptr_array_add ( active_replacements , g_strdup ( nevra_new ) ) ;
}
2017-09-21 05:11:37 +03:00
GLNX_HASH_TABLE_FOREACH_KV ( grouped_diffs , const char * , diff , GPtrArray * , pkgs )
{
if ( str - > len )
g_string_append ( str , " , " ) ;
for ( guint i = 0 , n = pkgs - > len ; i < n ; i + + )
{
const char * pkgname = g_ptr_array_index ( pkgs , i ) ;
if ( i > 0 )
g_string_append_c ( str , ' ' ) ;
g_string_append ( str , pkgname ) ;
}
g_string_append_c ( str , ' ' ) ;
g_string_append ( str , diff ) ;
}
2017-07-04 20:50:52 +03:00
g_ptr_array_add ( active_replacements , NULL ) ;
2017-09-21 05:11:37 +03:00
2017-07-04 20:50:52 +03:00
if ( str - > len )
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " ReplacedBasePackages " , max_key_len , str - > str ) ;
2017-07-04 20:50:52 +03:00
}
if ( origin_requested_base_local_replacements & & opt_verbose )
print_packages ( " InactiveBaseReplacements " , max_key_len ,
origin_requested_base_local_replacements ,
( const char * const * ) active_replacements - > pdata ) ;
2017-06-05 19:37:56 +03:00
2017-06-20 19:12:36 +03:00
/* only print inactive layering requests in verbose mode */
if ( origin_requested_packages & & opt_verbose )
/* requested-packages - packages = inactive (i.e. dormant requests) */
print_packages ( " InactiveRequests " , max_key_len ,
2017-02-24 17:44:40 +03:00
origin_requested_packages , origin_packages ) ;
2017-03-03 23:48:56 +03:00
2016-06-01 20:17:20 +03:00
if ( origin_packages )
2017-02-24 17:44:40 +03:00
print_packages ( " LayeredPackages " , max_key_len ,
origin_packages , NULL ) ;
2017-01-04 20:29:01 +03:00
2017-03-03 23:48:56 +03:00
if ( origin_requested_local_packages )
print_packages ( " LocalPackages " , max_key_len ,
origin_requested_local_packages , NULL ) ;
2017-01-04 20:29:01 +03:00
if ( regenerate_initramfs )
{
g_autoptr ( GString ) buf = g_string_new ( " " ) ;
g_autofree char * * initramfs_args = NULL ;
g_variant_dict_lookup ( dict , " initramfs-args " , " ^a&s " , & initramfs_args ) ;
for ( char * * iter = initramfs_args ; iter & & * iter ; iter + + )
{
g_string_append ( buf , * iter ) ;
g_string_append_c ( buf , ' ' ) ;
}
if ( buf - > len = = 0 )
g_string_append ( buf , " regenerate " ) ;
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " Initramfs " , max_key_len , buf - > str ) ;
2017-01-04 20:29:01 +03:00
}
2016-06-01 20:53:49 +03:00
if ( unlocked & & g_strcmp0 ( unlocked , " none " ) ! = 0 )
{
2018-01-10 23:58:47 +03:00
g_print ( " %s%s " , get_red_start ( ) , get_bold_start ( ) ) ;
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " Unlocked " , max_key_len , unlocked ) ;
2018-01-10 23:58:47 +03:00
g_print ( " %s%s " , get_bold_end ( ) , get_red_end ( ) ) ;
2016-06-01 20:53:49 +03:00
}
2017-07-26 00:49:10 +03:00
const char * end_of_life_string = NULL ;
/* look for endoflife attribute in the deployment */
g_variant_dict_lookup ( dict , " endoflife " , " &s " , & end_of_life_string ) ;
if ( end_of_life_string )
{
2018-01-10 23:58:47 +03:00
g_print ( " %s%s " , get_red_start ( ) , get_bold_start ( ) ) ;
2018-01-11 00:03:10 +03:00
rpmostree_print_kv ( " EndOfLife " , max_key_len , end_of_life_string ) ;
2018-01-10 23:58:47 +03:00
g_print ( " %s%s " , get_bold_end ( ) , get_red_end ( ) ) ;
2017-07-26 00:49:10 +03:00
}
2015-04-07 20:49:21 +03:00
}
2016-05-31 20:12:36 +03:00
return TRUE ;
}
2017-12-28 23:11:32 +03:00
gboolean
2016-05-31 20:12:36 +03:00
rpmostree_builtin_status ( int argc ,
char * * argv ,
2017-03-15 21:52:43 +03:00
RpmOstreeCommandInvocation * invocation ,
2016-05-31 20:12:36 +03:00
GCancellable * cancellable ,
GError * * error )
{
2017-08-12 01:59:47 +03:00
g_autoptr ( GOptionContext ) context = g_option_context_new ( " " ) ;
2016-05-31 20:12:36 +03:00
glnx_unref_object RPMOSTreeOS * os_proxy = NULL ;
glnx_unref_object RPMOSTreeSysroot * sysroot_proxy = NULL ;
g_autoptr ( GVariant ) deployments = NULL ;
2017-03-23 21:50:18 +03:00
_cleanup_peer_ GPid peer_pid = 0 ;
2015-06-13 03:19:16 +03:00
2016-05-31 20:12:36 +03:00
if ( ! rpmostree_option_context_parse ( context ,
option_entries ,
& argc , & argv ,
2017-03-15 21:52:43 +03:00
invocation ,
2016-05-31 20:12:36 +03:00
cancellable ,
2017-03-31 16:07:29 +03:00
NULL , NULL ,
2016-05-31 20:12:36 +03:00
& sysroot_proxy ,
2018-01-10 20:03:35 +03:00
& peer_pid , NULL ,
2016-05-31 20:12:36 +03:00
error ) )
2017-12-28 23:11:32 +03:00
return FALSE ;
2016-05-31 20:12:36 +03:00
2017-12-20 23:50:46 +03:00
if ( opt_json & & opt_jsonpath )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_INVALID_ARGUMENT ,
" Cannot specify both --json and --jsonpath " ) ;
2017-12-28 23:11:32 +03:00
return FALSE ;
2017-12-20 23:50:46 +03:00
}
2016-05-31 20:12:36 +03:00
if ( ! rpmostree_load_os_proxy ( sysroot_proxy , NULL ,
cancellable , & os_proxy , error ) )
2017-12-28 23:11:32 +03:00
return FALSE ;
2016-05-31 20:12:36 +03:00
deployments = rpmostree_sysroot_dup_deployments ( sysroot_proxy ) ;
2017-12-20 23:50:46 +03:00
if ( opt_json | | opt_jsonpath )
2016-06-09 23:00:24 +03:00
{
2016-06-14 15:34:07 +03:00
glnx_unref_object JsonBuilder * builder = json_builder_new ( ) ;
json_builder_begin_object ( builder ) ;
2017-12-20 23:50:46 +03:00
2016-06-14 15:34:07 +03:00
json_builder_set_member_name ( builder , " deployments " ) ;
2017-12-20 23:50:46 +03:00
json_builder_add_value ( builder , json_gvariant_serialize ( deployments ) ) ;
2016-06-14 16:41:36 +03:00
json_builder_set_member_name ( builder , " transaction " ) ;
2017-12-20 23:50:46 +03:00
GVariant * txn = get_active_txn ( sysroot_proxy ) ;
JsonNode * txn_node =
txn ? json_gvariant_serialize ( txn ) : json_node_new ( JSON_NODE_NULL ) ;
2016-06-14 16:41:36 +03:00
json_builder_add_value ( builder , txn_node ) ;
2016-06-14 15:34:07 +03:00
json_builder_end_object ( builder ) ;
2017-12-20 23:50:46 +03:00
JsonNode * json_root = json_builder_get_root ( builder ) ;
glnx_unref_object JsonGenerator * generator = json_generator_new ( ) ;
if ( opt_json )
json_generator_set_root ( generator , json_root ) ;
else
{
JsonNode * result = json_path_query ( opt_jsonpath , json_root , error ) ;
if ( ! result )
{
g_prefix_error ( error , " While compiling jsonpath: " ) ;
2017-12-28 23:11:32 +03:00
return FALSE ;
2017-12-20 23:50:46 +03:00
}
json_generator_set_root ( generator , result ) ;
json_node_free ( result ) ;
}
2016-06-14 15:34:07 +03:00
json_node_free ( json_root ) ;
2016-09-23 21:31:09 +03:00
/* NB: watch out for the misleading API docs */
2017-12-20 23:50:46 +03:00
glnx_unref_object GOutputStream * stdout_gio = g_unix_output_stream_new ( 1 , FALSE ) ;
2016-09-23 21:31:09 +03:00
if ( json_generator_to_stream ( generator , stdout_gio , NULL , error ) < = 0
| | ( error ! = NULL & & * error ! = NULL ) )
2017-12-28 23:11:32 +03:00
return FALSE ;
2016-06-09 23:00:24 +03:00
}
else
{
if ( ! status_generic ( sysroot_proxy , os_proxy , deployments ,
cancellable , error ) )
2017-12-28 23:11:32 +03:00
return FALSE ;
2016-06-09 23:00:24 +03:00
}
2016-05-31 20:12:36 +03:00
2017-12-28 23:11:32 +03:00
return TRUE ;
2014-06-11 21:47:10 +04:00
}