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>
2017-12-15 20:01:46 +03:00
# include <libdnf/libdnf.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"
2018-01-16 19:13:44 +03:00
# include "rpmostree-core.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"
2017-12-15 20:01:46 +03:00
# include "libsd-time-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
2018-02-13 17:25:20 +03:00
# define RPMOSTREE_AUTOMATIC_TIMER_OBJPATH \
" /org/freedesktop/systemd1/unit/rpm_2dostreed_2dautomatic_2etimer "
2017-12-15 20:01:46 +03:00
# define RPMOSTREE_AUTOMATIC_SERVICE_OBJPATH \
" /org/freedesktop/systemd1/unit/rpm_2dostreed_2dautomatic_2eservice "
2014-06-13 21:16:43 +04:00
static gboolean opt_pretty ;
2017-04-21 04:51:35 +03:00
static gboolean opt_verbose ;
2018-05-04 17:20:20 +03:00
static gboolean opt_verbose_advisories ;
2016-06-09 23:00:24 +03:00
static gboolean opt_json ;
2018-02-17 01:27:42 +03:00
static gboolean opt_only_booted ;
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 } ,
2018-05-04 17:20:20 +03:00
{ " verbose " , ' v ' , 0 , G_OPTION_ARG_NONE , & opt_verbose , " Print additional fields (e.g. StateRoot); implies -a " , NULL } ,
{ " advisories " , ' a ' , 0 , G_OPTION_ARG_NONE , & opt_verbose_advisories , " Expand advisories listing " , 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 " } ,
2018-02-17 01:27:42 +03:00
{ " booted " , ' b ' , 0 , G_OPTION_ARG_NONE , & opt_only_booted , " Only print the booted deployment " , NULL } ,
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
}
2018-02-13 17:25:20 +03:00
typedef enum {
AUTO_UPDATE_SDSTATE_TIMER_UNKNOWN ,
AUTO_UPDATE_SDSTATE_TIMER_INACTIVE ,
AUTO_UPDATE_SDSTATE_SERVICE_FAILED ,
2018-03-07 23:26:04 +03:00
AUTO_UPDATE_SDSTATE_SERVICE_RUNNING ,
AUTO_UPDATE_SDSTATE_SERVICE_EXITED ,
2018-02-13 17:25:20 +03:00
} AutoUpdateSdState ;
2017-12-15 20:01:46 +03:00
static gboolean
2018-02-13 17:25:20 +03:00
get_last_auto_update_run ( GDBusConnection * connection ,
AutoUpdateSdState * out_state ,
char * * out_last_run ,
GCancellable * cancellable ,
GError * * error )
2017-12-15 20:01:46 +03:00
{
GLNX_AUTO_PREFIX_ERROR ( " Querying systemd for last auto-update run " , error ) ;
2018-02-13 17:25:20 +03:00
/* Check if the timer is running, otherwise systemd won't even keep timestamp info on dead
* services . Also good to tell users if the policy is not none , but timer is off ( though
* we don ' t print it as an error ; e . g . the timer might have been explicitly masked ) . */
g_autoptr ( GDBusProxy ) timer_unit_proxy =
2017-12-15 20:01:46 +03:00
g_dbus_proxy_new_sync ( connection , G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS , NULL ,
2018-02-13 17:25:20 +03:00
" org.freedesktop.systemd1 " , RPMOSTREE_AUTOMATIC_TIMER_OBJPATH ,
2017-12-15 20:01:46 +03:00
" org.freedesktop.systemd1.Unit " , cancellable , error ) ;
2018-02-13 17:25:20 +03:00
if ( ! timer_unit_proxy )
2017-12-15 20:01:46 +03:00
return FALSE ;
2018-02-13 17:25:20 +03:00
g_autoptr ( GVariant ) timer_state_val =
g_dbus_proxy_get_cached_property ( timer_unit_proxy , " ActiveState " ) ;
2017-12-15 20:01:46 +03:00
/* let's not error out if we can't msg systemd (e.g. bad sepol); just mark as unknown */
2018-02-13 17:25:20 +03:00
if ( timer_state_val = = NULL )
{
* out_state = AUTO_UPDATE_SDSTATE_TIMER_UNKNOWN ;
return TRUE ; /* NB early return */
}
const char * timer_state = g_variant_get_string ( timer_state_val , NULL ) ;
if ( g_str_equal ( timer_state , " inactive " ) )
2017-12-15 20:01:46 +03:00
{
2018-02-13 17:25:20 +03:00
* out_state = AUTO_UPDATE_SDSTATE_TIMER_INACTIVE ;
2017-12-15 20:01:46 +03:00
return TRUE ; /* NB early return */
}
2018-02-13 17:25:20 +03:00
g_autoptr ( GDBusProxy ) service_unit_proxy =
g_dbus_proxy_new_sync ( connection , G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS , NULL ,
" org.freedesktop.systemd1 " , RPMOSTREE_AUTOMATIC_SERVICE_OBJPATH ,
" org.freedesktop.systemd1.Unit " , cancellable , error ) ;
if ( ! service_unit_proxy )
return FALSE ;
g_autoptr ( GVariant ) service_state_val =
g_dbus_proxy_get_cached_property ( service_unit_proxy , " ActiveState " ) ;
const char * service_state = g_variant_get_string ( service_state_val , NULL ) ;
if ( g_str_equal ( service_state , " failed " ) )
2017-12-15 20:01:46 +03:00
{
2018-02-13 17:25:20 +03:00
* out_state = AUTO_UPDATE_SDSTATE_SERVICE_FAILED ;
2017-12-15 20:01:46 +03:00
return TRUE ; /* NB early return */
}
2018-03-07 23:26:04 +03:00
else if ( g_str_equal ( service_state , " active " ) )
{
* out_state = AUTO_UPDATE_SDSTATE_SERVICE_RUNNING ;
return TRUE ; /* NB early return */
}
2017-12-15 20:01:46 +03:00
g_autoptr ( GDBusProxy ) service_proxy =
g_dbus_proxy_new_sync ( connection , G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS , NULL ,
" org.freedesktop.systemd1 " , RPMOSTREE_AUTOMATIC_SERVICE_OBJPATH ,
" org.freedesktop.systemd1.Service " , cancellable , error ) ;
if ( ! service_proxy )
return FALSE ;
g_autoptr ( GVariant ) t_val =
g_dbus_proxy_get_cached_property ( service_proxy , " ExecMainExitTimestamp " ) ;
g_autofree char * last_run = NULL ;
if ( t_val )
{
guint64 t = g_variant_get_uint64 ( t_val ) ;
if ( t > 0 )
{
char time_rel [ FORMAT_TIMESTAMP_RELATIVE_MAX ] = " " ;
libsd_format_timestamp_relative ( time_rel , sizeof ( time_rel ) , t ) ;
last_run = g_strdup ( time_rel ) ;
}
}
2018-03-07 23:26:04 +03:00
* out_state = AUTO_UPDATE_SDSTATE_SERVICE_EXITED ;
2017-12-15 20:01:46 +03:00
* out_last_run = g_steal_pointer ( & last_run ) ;
return TRUE ;
}
2018-01-17 23:25:43 +03:00
static gboolean
print_daemon_state ( RPMOSTreeSysroot * sysroot_proxy ,
2017-12-15 20:01:46 +03:00
GBusType bus_type ,
2018-01-17 23:25:43 +03:00
GCancellable * cancellable ,
GError * * error )
{
glnx_unref_object RPMOSTreeTransaction * txn_proxy = NULL ;
if ( ! rpmostree_transaction_connect_active ( sysroot_proxy , NULL , & txn_proxy ,
cancellable , error ) )
return FALSE ;
2017-12-15 20:01:46 +03:00
const char * policy = rpmostree_sysroot_get_automatic_update_policy ( sysroot_proxy ) ;
2018-01-17 23:25:43 +03:00
g_print ( " State: %s " , txn_proxy ? " busy " : " idle " ) ;
2017-12-15 20:01:46 +03:00
if ( g_str_equal ( policy , " none " ) )
g_print ( " ; auto updates disabled \n " ) ;
else
{
g_print ( " ; auto updates enabled " ) ;
/* don't try to get info from systemd if we're not on the system bus */
if ( bus_type ! = G_BUS_TYPE_SYSTEM )
g_print ( " (%s) \n " , policy ) ;
else
{
2018-02-13 17:25:20 +03:00
AutoUpdateSdState state ;
2017-12-15 20:01:46 +03:00
g_autofree char * last_run = NULL ;
GDBusConnection * connection =
g_dbus_proxy_get_connection ( G_DBUS_PROXY ( sysroot_proxy ) ) ;
2018-02-13 17:25:20 +03:00
if ( ! get_last_auto_update_run ( connection , & state , & last_run , cancellable , error ) )
2017-12-15 20:01:46 +03:00
return FALSE ;
2018-02-13 17:25:20 +03:00
switch ( state )
{
case AUTO_UPDATE_SDSTATE_TIMER_UNKNOWN :
{
g_print ( " (%s; unknown timer state) \n " , policy ) ;
break ;
}
case AUTO_UPDATE_SDSTATE_TIMER_INACTIVE :
{
g_print ( " (%s; timer inactive) \n " , policy ) ;
break ;
}
case AUTO_UPDATE_SDSTATE_SERVICE_FAILED :
{
g_print ( " (%s; %s%slast run failed%s%s) \n " , policy ,
get_red_start ( ) , get_bold_start ( ) ,
get_bold_end ( ) , get_red_end ( ) ) ;
break ;
}
2018-03-07 23:26:04 +03:00
case AUTO_UPDATE_SDSTATE_SERVICE_RUNNING :
{
g_print ( " (%s; running) \n " , policy ) ;
break ;
}
case AUTO_UPDATE_SDSTATE_SERVICE_EXITED :
2018-02-13 17:25:20 +03:00
{
if ( last_run )
2018-02-14 17:27:06 +03:00
/* e.g. "last run 4h 32min ago" */
2018-02-13 17:25:20 +03:00
g_print ( " (%s; last run %s) \n " , policy , last_run ) ;
else
g_print ( " (%s; no runs since boot) \n " , policy ) ;
break ;
}
default :
{
g_assert_not_reached ( ) ;
}
}
2017-12-15 20:01:46 +03:00
}
}
2018-01-17 23:25:43 +03:00
if ( txn_proxy )
{
const char * title = rpmostree_transaction_get_title ( txn_proxy ) ;
g_print ( " Transaction: %s \n " , title ) ;
}
return TRUE ;
}
2018-04-25 01:10:10 +03:00
/* Print the result of rpmostree_context_get_rpmmd_repo_commit_metadata() */
static void
print_origin_repos ( gboolean host_endian ,
guint maxkeylen , GVariantDict * commit_meta )
{
g_autoptr ( GVariant ) reposdata =
g_variant_dict_lookup_value ( commit_meta , " rpmostree.rpmmd-repos " , G_VARIANT_TYPE ( " aa{sv} " ) ) ;
if ( ! reposdata )
return ;
const guint n = g_variant_n_children ( reposdata ) ;
for ( guint i = 0 ; i < n ; i + + )
{
g_autoptr ( GVariant ) child = g_variant_get_child_value ( reposdata , i ) ;
g_autoptr ( GVariantDict ) cdict = g_variant_dict_new ( child ) ;
const char * id = NULL ;
if ( ! g_variant_dict_lookup ( cdict , " id " , " &s " , & id ) )
continue ;
guint64 ts ;
if ( ! g_variant_dict_lookup ( cdict , " timestamp " , " t " , & ts ) )
continue ;
/* `compose tree` commits are canonicalized to BE, but client-side commits
* are not . Whee .
*/
if ( ! host_endian )
ts = GUINT64_FROM_BE ( ts ) ;
g_autofree char * timestamp_string = rpmostree_timestamp_str_from_unix_utc ( ts ) ;
g_print ( " %*s%s %s (%s) \n " , maxkeylen + 2 , " " ,
libsd_special_glyph ( TREE_RIGHT ) , id , timestamp_string ) ;
}
}
2016-05-31 20:12:36 +03:00
static gboolean
2018-04-07 19:09:40 +03:00
print_one_deployment ( RPMOSTreeSysroot * sysroot_proxy ,
GVariant * child ,
gboolean first ,
gboolean have_any_live_overlay ,
gboolean have_multiple_stateroots ,
2018-05-04 17:20:19 +03:00
const char * booted_osname ,
const char * cached_update_deployment_id ,
GVariant * cached_update ,
gboolean * out_printed_cached_update ,
2018-04-07 19:09:40 +03:00
GError * * error )
2014-06-11 21:47:10 +04:00
{
2018-04-07 19:09:40 +03:00
/* Add the long keys here */
const guint max_key_len = MAX ( strlen ( " InactiveBaseReplacements " ) ,
strlen ( " InterruptedLiveCommit " ) ) ;
g_autoptr ( GVariantDict ) dict = g_variant_dict_new ( child ) ;
/* osname should always be present. */
const gchar * os_name ;
const gchar * id ;
int serial ;
const gchar * checksum ;
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 ) ) ;
g_assert ( g_variant_dict_lookup ( dict , " checksum " , " &s " , & checksum ) ) ;
gboolean is_booted ;
if ( ! g_variant_dict_lookup ( dict , " booted " , " b " , & is_booted ) )
is_booted = FALSE ;
if ( ! is_booted & & opt_only_booted )
return TRUE ;
const gchar * origin_refspec ;
g_autofree const gchar * * origin_packages = NULL ;
g_autofree const gchar * * origin_requested_packages = NULL ;
g_autofree const gchar * * origin_requested_local_packages = NULL ;
g_autoptr ( GVariant ) origin_base_removals = NULL ;
g_autofree const gchar * * origin_requested_base_removals = NULL ;
g_autoptr ( GVariant ) origin_base_local_replacements = NULL ;
g_autofree const gchar * * origin_requested_base_local_replacements = NULL ;
if ( g_variant_dict_lookup ( dict , " origin " , " &s " , & origin_refspec ) )
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
{
2018-04-07 19:09:40 +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 " ) ;
origin_base_removals =
g_variant_dict_lookup_value ( dict , " base-removals " , G_VARIANT_TYPE ( " av " ) ) ;
origin_requested_base_removals =
lookup_array_and_canonicalize ( dict , " requested-base-removals " ) ;
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 " ) ;
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
}
2018-04-07 19:09:40 +03:00
else
origin_refspec = NULL ;
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
2018-04-07 19:09:40 +03:00
const gchar * version_string ;
if ( ! g_variant_dict_lookup ( dict , " version " , " &s " , & version_string ) )
version_string = NULL ;
const gchar * unlocked ;
if ( ! g_variant_dict_lookup ( dict , " unlocked " , " &s " , & unlocked ) )
unlocked = NULL ;
2015-09-08 17:31:22 +03:00
2018-04-07 19:09:40 +03:00
gboolean regenerate_initramfs ;
if ( ! g_variant_dict_lookup ( dict , " regenerate-initramfs " , " b " , & regenerate_initramfs ) )
regenerate_initramfs = FALSE ;
2017-01-04 20:29:01 +03:00
2018-04-07 19:09:40 +03:00
g_autoptr ( GVariant ) signatures =
g_variant_dict_lookup_value ( dict , " signatures " , G_VARIANT_TYPE ( " av " ) ) ;
2015-09-08 17:31:22 +03:00
2018-04-07 19:09:40 +03:00
if ( ! first )
g_print ( " \n " ) ;
2015-07-16 21:11:35 +03:00
2018-04-07 19:09:40 +03:00
g_print ( " %s " , is_booted ? libsd_special_glyph ( BLACK_CIRCLE ) : " " ) ;
2015-09-08 17:31:22 +03:00
2018-04-07 19:09:40 +03:00
RpmOstreeRefspecType refspectype = RPMOSTREE_REFSPEC_TYPE_OSTREE ;
if ( origin_refspec )
{
const char * refspec_data ;
if ( ! rpmostree_refspec_classify ( origin_refspec , & refspectype , & refspec_data , error ) )
return FALSE ;
g_autofree char * canonrefspec = rpmostree_refspec_to_string ( refspectype , refspec_data ) ;
switch ( refspectype )
2018-01-16 19:13:44 +03:00
{
2018-06-09 00:09:10 +03:00
case RPMOSTREE_REFSPEC_TYPE_CHECKSUM :
2018-04-07 19:09:40 +03:00
case RPMOSTREE_REFSPEC_TYPE_OSTREE :
{
g_print ( " %s " , canonrefspec ) ;
}
break ;
case RPMOSTREE_REFSPEC_TYPE_ROJIG :
{
g_autoptr ( GVariant ) rojig_description = NULL ;
g_variant_dict_lookup ( dict , " rojig-description " , " @a{sv} " , & rojig_description ) ;
if ( rojig_description )
2018-02-11 20:52:16 +03:00
{
2018-04-07 19:09:40 +03:00
g_autoptr ( GVariantDict ) dict = g_variant_dict_new ( rojig_description ) ;
const char * repo = NULL ;
g_variant_dict_lookup ( dict , " repo " , " &s " , & repo ) ;
const char * name = NULL ;
g_variant_dict_lookup ( dict , " name " , " &s " , & name ) ;
const char * evr = NULL ;
g_variant_dict_lookup ( dict , " evr " , " &s " , & evr ) ;
const char * arch = NULL ;
g_variant_dict_lookup ( dict , " arch " , " &s " , & arch ) ;
g_assert ( repo & & name ) ;
g_print ( " %s:%s " , repo , name ) ;
if ( evr & & arch )
g_print ( " -%s.%s " , evr , arch ) ;
2018-02-11 20:52:16 +03:00
}
2018-04-07 19:09:40 +03:00
else
2018-02-11 20:52:16 +03:00
{
2018-04-07 19:09:40 +03:00
g_print ( " %s " , canonrefspec ) ;
2018-02-11 20:52:16 +03:00
}
2018-04-07 19:09:40 +03:00
}
break ;
2018-01-16 19:13:44 +03:00
}
2018-04-07 19:09:40 +03:00
}
else
g_print ( " %s " , checksum ) ;
g_print ( " \n " ) ;
2017-02-24 17:44:40 +03:00
2018-04-07 19:09:40 +03:00
const char * remote_not_found = NULL ;
g_variant_dict_lookup ( dict , " remote-error " , " s " , & remote_not_found ) ;
if ( remote_not_found )
{
g_print ( " %s%s " , get_red_start ( ) , get_bold_start ( ) ) ;
rpmostree_print_kv ( " OstreeRemoteStatus " , max_key_len , remote_not_found ) ;
g_print ( " %s%s " , get_bold_end ( ) , get_red_end ( ) ) ;
}
2018-03-14 19:02:49 +03:00
2018-04-07 19:09:40 +03:00
const char * base_checksum = NULL ;
g_variant_dict_lookup ( dict , " base-checksum " , " &s " , & base_checksum ) ;
gboolean is_locally_assembled = FALSE ;
if ( base_checksum ! = NULL )
is_locally_assembled = TRUE ;
/* Load the commit metadata into a dict */
g_autoptr ( GVariantDict ) commit_meta_dict =
( { g_autoptr ( GVariant ) commit_meta_v = NULL ;
g_assert ( g_variant_dict_lookup ( dict , " base-commit-meta " , " @a{sv} " , & commit_meta_v ) ) ;
g_variant_dict_new ( commit_meta_v ) ; } ) ;
g_autoptr ( GVariantDict ) layered_commit_meta_dict = NULL ;
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 ) ;
}
2017-10-19 22:31:50 +03:00
2018-04-07 19:09:40 +03:00
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-10-19 22:31:50 +03:00
2018-04-07 19:09:40 +03:00
guint64 t = 0 ;
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 ) ) ;
g_autofree char * timestamp_string = rpmostree_timestamp_str_from_unix_utc ( t ) ;
2017-03-21 19:48:09 +03:00
2018-04-07 19:09:40 +03:00
rpmostree_print_timestamp_version ( version_string , timestamp_string , max_key_len ) ;
2017-02-24 17:44:40 +03:00
2018-04-07 19:09:40 +03:00
const gchar * live_inprogress ;
const gchar * live_replaced ;
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 ;
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
2018-04-07 19:09:40 +03:00
const gboolean is_ostree_or_verbose = opt_verbose | | refspectype = = RPMOSTREE_REFSPEC_TYPE_OSTREE ;
2018-02-11 20:52:16 +03:00
2018-04-07 19:09:40 +03:00
if ( is_locally_assembled & & is_ostree_or_verbose )
{
if ( have_live_changes )
rpmostree_print_kv ( " BootedBaseCommit " , max_key_len , base_checksum ) ;
else
rpmostree_print_kv ( " BaseCommit " , max_key_len , base_checksum ) ;
2018-04-25 01:10:10 +03:00
if ( opt_verbose )
print_origin_repos ( FALSE , max_key_len , commit_meta_dict ) ;
2018-04-07 19:09:40 +03:00
if ( opt_verbose | | have_any_live_overlay )
rpmostree_print_kv ( " Commit " , max_key_len , checksum ) ;
2018-04-25 01:10:10 +03:00
if ( opt_verbose )
print_origin_repos ( TRUE , max_key_len , layered_commit_meta_dict ) ;
2018-04-07 19:09:40 +03:00
}
else if ( is_ostree_or_verbose )
{
if ( have_live_changes )
rpmostree_print_kv ( " BootedCommit " , max_key_len , checksum ) ;
if ( ! have_live_changes | | opt_verbose )
rpmostree_print_kv ( " Commit " , max_key_len , checksum ) ;
2018-04-25 01:10:10 +03:00
if ( opt_verbose )
print_origin_repos ( FALSE , max_key_len , commit_meta_dict ) ;
2018-04-07 19:09: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
2018-04-07 19:09:40 +03:00
if ( live_inprogress )
{
if ( is_booted )
g_print ( " %s%s " , get_red_start ( ) , get_bold_start ( ) ) ;
rpmostree_print_kv ( " InterruptedLiveCommit " , max_key_len , live_inprogress ) ;
if ( is_booted )
g_print ( " %s%s " , get_bold_end ( ) , get_red_end ( ) ) ;
}
if ( live_replaced )
{
if ( is_booted )
g_print ( " %s%s " , get_red_start ( ) , get_bold_start ( ) ) ;
rpmostree_print_kv ( " LiveCommit " , max_key_len , live_replaced ) ;
if ( is_booted )
g_print ( " %s%s " , get_bold_end ( ) , get_red_end ( ) ) ;
}
2017-01-27 07:31:53 +03:00
2018-03-29 16:24:53 +03:00
gboolean is_staged = FALSE ;
g_variant_dict_lookup ( dict , " staged " , " b " , & is_staged ) ;
if ( opt_verbose & & ( is_staged | | first ) )
rpmostree_print_kv ( " Staged " , max_key_len , is_staged ? " yes " : " no " ) ;
2018-04-07 19:09:40 +03:00
/* This used to be OSName; see https://github.com/ostreedev/ostree/pull/794 */
if ( opt_verbose | | have_multiple_stateroots )
rpmostree_print_kv ( " StateRoot " , max_key_len , os_name ) ;
2014-06-12 18:32:21 +04:00
2018-04-07 19:09:40 +03:00
gboolean gpg_enabled ;
if ( ! g_variant_dict_lookup ( dict , " gpg-enabled " , " b " , & gpg_enabled ) )
gpg_enabled = FALSE ;
2016-07-21 23:28:20 +03:00
2018-04-07 19:09:40 +03:00
if ( gpg_enabled )
rpmostree_print_gpg_info ( signatures , opt_verbose , max_key_len ) ;
2014-06-11 21:47:10 +04:00
2018-05-04 17:20:19 +03:00
/* Print rpm diff and advisories summary if this is a pending deployment matching the
* deployment on which the cached update is based . */
if ( first & & ! is_booted & &
g_strcmp0 ( os_name , booted_osname ) = = 0 & &
g_strcmp0 ( id , cached_update_deployment_id ) = = 0 )
{
g_auto ( GVariantDict ) dict ;
g_variant_dict_init ( & dict , cached_update ) ;
g_autoptr ( GVariant ) rpm_diff =
g_variant_dict_lookup_value ( & dict , " rpm-diff " , G_VARIANT_TYPE ( " a{sv} " ) ) ;
g_autoptr ( GVariant ) advisories =
g_variant_dict_lookup_value ( & dict , " advisories " , G_VARIANT_TYPE ( " a(suuasa{sv}) " ) ) ;
2018-05-04 17:20:20 +03:00
if ( ! rpmostree_print_diff_advisories ( rpm_diff , advisories , opt_verbose ,
opt_verbose_advisories , max_key_len , error ) )
2018-05-04 17:20:19 +03:00
return FALSE ;
* out_printed_cached_update = TRUE ;
}
2018-04-07 19:09:40 +03:00
/* print base overrides before overlays */
g_autoptr ( GPtrArray ) active_removals = g_ptr_array_new_with_free_func ( g_free ) ;
if ( origin_base_removals )
{
g_autoptr ( GString ) str = g_string_new ( " " ) ;
const guint n = g_variant_n_children ( origin_base_removals ) ;
for ( guint i = 0 ; i < n ; i + + )
2017-07-04 20:50:52 +03:00
{
2018-04-07 19:09:40 +03:00
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 ) ;
2017-07-04 20:50:52 +03:00
if ( str - > len )
2018-04-07 19:09:40 +03:00
g_string_append ( str , " , " ) ;
g_string_append ( str , nevra ) ;
g_ptr_array_add ( active_removals , g_strdup ( name ) ) ;
2017-07-04 20:50:52 +03:00
}
2018-04-07 19:09:40 +03:00
g_ptr_array_add ( active_removals , NULL ) ;
if ( str - > len )
rpmostree_print_kv ( " RemovedBasePackages " , max_key_len , str - > str ) ;
}
2017-06-20 19:21:57 +03:00
2018-04-07 19:09:40 +03:00
/* only print inactive base removal requests in verbose mode */
if ( origin_requested_base_removals & & opt_verbose )
print_packages ( " InactiveBaseRemovals " , max_key_len ,
origin_requested_base_removals ,
( const char * const * ) active_removals - > pdata ) ;
2017-07-04 20:50:52 +03:00
2018-04-07 19:09:40 +03:00
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 ( " " ) ;
g_autoptr ( GHashTable ) grouped_diffs =
g_hash_table_new_full ( g_str_hash , g_str_equal , g_free ,
( GDestroyNotify ) g_ptr_array_unref ) ;
2017-09-21 05:11:37 +03:00
2018-04-07 19:09:40 +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-07-04 20:50:52 +03:00
{
2018-04-07 19:09:40 +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 )
2017-07-04 20:50:52 +03:00
{
2018-04-07 19:09:40 +03:00
pkgs = g_ptr_array_new_with_free_func ( g_free ) ;
g_hash_table_insert ( grouped_diffs , g_strdup ( diff ) , pkgs ) ;
2017-07-04 20:50:52 +03:00
}
2018-04-07 19:09:40 +03:00
g_ptr_array_add ( pkgs , g_strdup ( name_new ) ) ;
g_string_truncate ( str , original_size ) ;
2017-07-04 20:50:52 +03:00
}
2018-04-07 19:09:40 +03:00
else
2017-09-21 05:11:37 +03:00
{
if ( str - > len )
g_string_append ( str , " , " ) ;
2018-04-07 19:09:40 +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
2018-04-07 19:09:40 +03:00
GLNX_HASH_TABLE_FOREACH_KV ( grouped_diffs , const char * , diff , GPtrArray * , pkgs )
{
2017-07-04 20:50:52 +03:00
if ( str - > len )
2018-04-07 19:09:40 +03:00
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
}
2018-04-07 19:09:40 +03:00
g_ptr_array_add ( active_replacements , NULL ) ;
2017-06-05 19:37:56 +03:00
2018-04-07 19:09:40 +03:00
if ( str - > len )
rpmostree_print_kv ( " ReplacedBasePackages " , max_key_len , str - > str ) ;
}
2017-03-03 23:48:56 +03:00
2018-04-07 19:09:40 +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-01-04 20:29:01 +03:00
2018-04-07 19:09:40 +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 ,
origin_requested_packages , origin_packages ) ;
2017-03-03 23:48:56 +03:00
2018-04-07 19:09:40 +03:00
if ( origin_packages )
print_packages ( " LayeredPackages " , max_key_len ,
origin_packages , NULL ) ;
2017-01-04 20:29:01 +03:00
2018-04-07 19:09:40 +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
2018-04-07 19:09:40 +03:00
if ( regenerate_initramfs )
{
g_autoptr ( GString ) buf = g_string_new ( " " ) ;
g_autofree char * * initramfs_args = NULL ;
2017-01-04 20:29:01 +03:00
2018-04-07 19:09:40 +03:00
g_variant_dict_lookup ( dict , " initramfs-args " , " ^a&s " , & initramfs_args ) ;
2017-07-26 00:49:10 +03:00
2018-04-07 19:09:40 +03:00
for ( char * * iter = initramfs_args ; iter & & * iter ; iter + + )
2017-07-26 00:49:10 +03:00
{
2018-04-07 19:09:40 +03:00
g_string_append ( buf , * iter ) ;
g_string_append_c ( buf , ' ' ) ;
2017-07-26 00:49:10 +03:00
}
2018-04-07 19:09:40 +03:00
if ( buf - > len = = 0 )
g_string_append ( buf , " regenerate " ) ;
rpmostree_print_kv ( " Initramfs " , max_key_len , buf - > str ) ;
}
gboolean pinned = FALSE ;
g_variant_dict_lookup ( dict , " pinned " , " b " , & pinned ) ;
if ( pinned )
rpmostree_print_kv ( " Pinned " , max_key_len , " yes " ) ;
if ( unlocked & & g_strcmp0 ( unlocked , " none " ) ! = 0 )
{
g_print ( " %s%s " , get_red_start ( ) , get_bold_start ( ) ) ;
rpmostree_print_kv ( " Unlocked " , max_key_len , unlocked ) ;
g_print ( " %s%s " , get_bold_end ( ) , get_red_end ( ) ) ;
}
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 )
{
g_print ( " %s%s " , get_red_start ( ) , get_bold_start ( ) ) ;
rpmostree_print_kv ( " EndOfLife " , max_key_len , end_of_life_string ) ;
g_print ( " %s%s " , get_bold_end ( ) , get_red_end ( ) ) ;
}
return TRUE ;
}
/* We will have an optimized path for the case where there are just
* two deployments , this code will be the generic fallback .
*/
static gboolean
print_deployments ( RPMOSTreeSysroot * sysroot_proxy ,
GVariant * deployments ,
2018-05-04 17:20:19 +03:00
GVariant * cached_update ,
gboolean * out_printed_cached_update ,
2018-04-07 19:09:40 +03:00
GCancellable * cancellable ,
GError * * error )
{
GVariantIter iter ;
/* First, gather global state */
2018-05-04 17:20:19 +03:00
const char * booted_osname = NULL ;
2018-04-07 19:09:40 +03:00
gboolean have_any_live_overlay = FALSE ;
gboolean have_multiple_stateroots = FALSE ;
const char * last_osname = NULL ;
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 ;
const char * osname = NULL ;
g_assert ( g_variant_dict_lookup ( dict , " osname " , " &s " , & osname ) ) ;
if ( ! last_osname )
last_osname = osname ;
else if ( ! g_str_equal ( osname , last_osname ) )
have_multiple_stateroots = TRUE ;
2018-05-04 17:20:19 +03:00
gboolean is_booted ;
if ( ! g_variant_dict_lookup ( dict , " booted " , " b " , & is_booted ) )
is_booted = FALSE ;
if ( is_booted )
booted_osname = osname ;
2018-04-07 19:09:40 +03:00
}
g_print ( " Deployments: \n " ) ;
2018-05-04 17:20:19 +03:00
/* just unpack this so that each iteration doesn't have to dig for it */
const char * cached_update_deployment_id = NULL ;
if ( cached_update )
{
g_auto ( GVariantDict ) dict ;
g_variant_dict_init ( & dict , cached_update ) ;
g_variant_dict_lookup ( & dict , " deployment " , " &s " , & cached_update_deployment_id ) ;
}
2018-04-07 19:09:40 +03:00
g_variant_iter_init ( & iter , deployments ) ;
gboolean first = TRUE ;
while ( TRUE )
{
g_autoptr ( GVariant ) child = g_variant_iter_next_value ( & iter ) ;
if ( child = = NULL )
break ;
if ( ! print_one_deployment ( sysroot_proxy , child , first , have_any_live_overlay ,
2018-05-04 17:20:19 +03:00
have_multiple_stateroots , booted_osname ,
cached_update_deployment_id , cached_update ,
out_printed_cached_update , error ) )
2018-04-07 19:09:40 +03:00
return FALSE ;
if ( first )
first = FALSE ;
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 ;
2017-03-23 21:50:18 +03:00
_cleanup_peer_ GPid peer_pid = 0 ;
2015-06-13 03:19:16 +03:00
2017-12-15 20:01:46 +03:00
GBusType bus_type ;
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 ,
2017-12-15 20:01:46 +03:00
& peer_pid , & bus_type ,
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
}
2018-01-17 23:25:43 +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
2018-01-17 23:25:43 +03:00
g_autoptr ( GVariant ) deployments = rpmostree_sysroot_dup_deployments ( sysroot_proxy ) ;
2017-12-15 20:01:46 +03:00
g_autoptr ( GVariant ) cached_update = NULL ;
if ( rpmostree_os_get_has_cached_update_rpm_diff ( os_proxy ) )
cached_update = rpmostree_os_dup_cached_update ( os_proxy ) ;
2016-05-31 20:12:36 +03:00
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 ) ;
2017-12-15 20:01:46 +03:00
json_builder_set_member_name ( builder , " cached-update " ) ;
JsonNode * cached_update_node ;
if ( cached_update )
cached_update_node = json_gvariant_serialize ( cached_update ) ;
else
cached_update_node = json_node_new ( JSON_NODE_NULL ) ;
json_builder_add_value ( builder , cached_update_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
2017-12-20 23:50:46 +03:00
glnx_unref_object GOutputStream * stdout_gio = g_unix_output_stream_new ( 1 , FALSE ) ;
2018-05-04 17:20:19 +03:00
/* NB: watch out for the misleading API docs */
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
{
2017-12-15 20:01:46 +03:00
if ( ! print_daemon_state ( sysroot_proxy , bus_type , cancellable , error ) )
2018-01-17 23:25:43 +03:00
return FALSE ;
2018-05-04 17:20:19 +03:00
gboolean printed_cached_update = FALSE ;
if ( ! print_deployments ( sysroot_proxy , deployments , cached_update ,
& printed_cached_update , cancellable , error ) )
2017-12-28 23:11:32 +03:00
return FALSE ;
2017-12-15 20:01:46 +03:00
const char * policy = rpmostree_sysroot_get_automatic_update_policy ( sysroot_proxy ) ;
gboolean auto_updates_enabled = ( ! g_str_equal ( policy , " none " ) ) ;
2018-05-04 17:20:19 +03:00
if ( cached_update & & ! printed_cached_update & & auto_updates_enabled )
2017-12-15 20:01:46 +03:00
{
g_print ( " \n " ) ;
if ( ! rpmostree_print_cached_update ( cached_update , opt_verbose ,
2018-05-04 17:20:20 +03:00
opt_verbose_advisories , cancellable , error ) )
2017-12-15 20:01:46 +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
}