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>
# include <glib-unix.h>
# include "rpmostree-builtins.h"
2015-06-13 03:19:16 +03:00
# include "rpmostree-dbus-helpers.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
2016-03-02 20:23:37 +03:00
# define HEADER_TIMESTAMP "TIMESTAMP (UTC)"
# define HEADER_VERSION "VERSION"
# define HEADER_ID "ID"
# define HEADER_OSNAME "OSNAME"
# define HEADER_REFSPEC "REFSPEC"
2014-06-13 21:16:43 +04:00
static gboolean opt_pretty ;
static GOptionEntry option_entries [ ] = {
{ " pretty " , ' p ' , 0 , G_OPTION_ARG_NONE , & opt_pretty , " Display status in formatted rows " , NULL } ,
{ NULL }
} ;
2015-06-13 03:19:16 +03:00
static void
2014-06-13 21:16:43 +04:00
printchar ( char * s , int n )
{
int i ;
for ( i = 0 ; i < n ; i + + )
g_print ( " %s " , s ) ;
g_print ( " \n " ) ;
}
2015-02-11 21:06:43 +03:00
static char *
format_layered_packages_plus ( char * * packages )
{
guint len = g_strv_length ( packages ) ;
if ( len > 0 )
return g_strdup_printf ( " (+%u) " , len) ;
else
return g_strdup ( " " ) ;
}
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
status_fallback ( RPMOSTreeSysroot * sysroot_proxy ,
RPMOSTreeOS * os_proxy ,
GVariant * deployments ,
GVariant * booted_deployment ,
GCancellable * cancellable ,
GError * * error )
2014-06-11 21:47:10 +04:00
{
2015-07-16 21:11:35 +03:00
g_autoptr ( GVariant ) booted_signatures = NULL ;
2015-09-08 17:31:22 +03:00
g_autoptr ( GPtrArray ) deployment_dicts = NULL ;
GVariantIter iter ;
GVariant * child ;
2015-10-06 23:53:21 +03:00
g_autofree gchar * booted_id = NULL ;
2015-06-13 03:19:16 +03:00
2015-07-16 21:11:35 +03:00
const guint CSUM_DISP_LEN = 10 ; /* number of checksum characters to display */
2015-06-13 03:19:16 +03:00
guint i , n ;
2016-03-02 20:23:37 +03:00
/* these column maximums are known thus constant */
2015-07-16 21:11:35 +03:00
guint max_timestamp_len = 19 ; /* length of timestamp "YYYY-MM-DD HH:MM:SS" */
guint max_id_len = CSUM_DISP_LEN ; /* length of checksum ID */
2016-03-02 20:23:37 +03:00
/* these column maximums can vary and are thus determined in code */
/* seed them with the header name length */
guint max_osname_len = strlen ( HEADER_OSNAME ) ;
guint max_refspec_len = strlen ( HEADER_REFSPEC ) ;
guint max_version_len = strlen ( HEADER_VERSION ) ;
/* minimum space between end of one entry and new column */
guint buffer = 5 ;
2015-06-13 03:19:16 +03:00
2014-06-13 21:16:43 +04:00
2015-06-13 03:19:16 +03:00
if ( booted_deployment )
{
2015-09-08 17:31:22 +03:00
GVariantDict dict ;
g_variant_dict_init ( & dict , booted_deployment ) ;
2015-10-06 23:53:21 +03:00
g_variant_dict_lookup ( & dict , " id " , " s " , & booted_id ) ;
2015-09-08 17:31:22 +03:00
booted_signatures = g_variant_dict_lookup_value ( & dict , " signatures " ,
G_VARIANT_TYPE ( " av " ) ) ;
g_variant_dict_clear ( & dict ) ;
2015-06-13 03:19:16 +03:00
}
2014-06-11 21:47:10 +04:00
2015-09-08 17:31:22 +03:00
deployment_dicts = g_ptr_array_new_with_free_func ( ( GDestroyNotify ) g_variant_dict_unref ) ;
g_variant_iter_init ( & iter , deployments ) ;
while ( ( child = g_variant_iter_next_value ( & iter ) ) ! = NULL )
2014-06-11 21:47:10 +04:00
{
2015-09-08 17:31:22 +03:00
GVariantDict * dict = g_variant_dict_new ( child ) ;
/* Takes ownership of the dictionary */
g_ptr_array_add ( deployment_dicts , dict ) ;
2015-06-13 03:19:16 +03:00
/* find lengths for use in column output */
if ( ! opt_pretty )
2014-06-12 18:32:21 +04:00
{
2015-07-16 21:11:35 +03:00
gchar * origin_refspec = NULL ; /* borrowed */
2015-02-11 21:06:43 +03:00
gchar * * origin_packages = NULL ; /* borrowed */
2015-07-16 21:11:35 +03:00
gchar * os_name = NULL ; /* borrowed */
gchar * version_string = NULL ; /* borrowed */
2014-06-13 21:16:43 +04:00
2015-09-08 17:31:22 +03:00
/* osname should always be present. */
if ( g_variant_dict_lookup ( dict , " osname " , " &s " , & os_name ) )
max_osname_len = MAX ( max_osname_len , strlen ( os_name ) ) ;
else
{
const char * id = NULL ;
g_variant_dict_lookup ( dict , " id " , " &s " , & id ) ;
g_critical ( " Deployment '%s' missing osname " , id ! = NULL ? id : " ? " ) ;
}
if ( g_variant_dict_lookup ( dict , " version " , " &s " , & version_string ) )
max_version_len = MAX ( max_version_len , strlen ( version_string ) ) ;
if ( g_variant_dict_lookup ( dict , " origin " , " &s " , & origin_refspec ) )
2015-02-11 21:06:43 +03:00
{
if ( g_variant_dict_lookup ( dict , " packages " , " ^a&s " , & origin_packages ) )
{
g_autofree gchar * origin_packages_plus =
format_layered_packages_plus ( origin_packages ) ;
max_refspec_len = MAX ( max_refspec_len , strlen ( origin_refspec ) + strlen ( origin_packages_plus ) ) ;
}
else
max_refspec_len = MAX ( max_refspec_len , strlen ( origin_refspec ) ) ;
}
2014-06-12 18:32:21 +04:00
}
2015-09-08 17:31:22 +03:00
g_variant_unref ( child ) ;
2015-06-13 03:19:16 +03:00
}
if ( ! opt_pretty )
{
2014-06-13 21:16:43 +04:00
/* print column headers */
2016-03-02 20:23:37 +03:00
g_print ( " %-*s " , max_timestamp_len + buffer , HEADER_TIMESTAMP ) ;
2014-12-12 00:29:12 +03:00
if ( max_version_len )
2016-03-02 20:23:37 +03:00
g_print ( " %-*s " , max_version_len + buffer , HEADER_VERSION ) ;
2014-12-12 00:29:12 +03:00
g_print ( " %-*s%-*s%-*s \n " ,
2016-03-02 20:23:37 +03:00
max_id_len + buffer , HEADER_ID ,
max_osname_len + buffer , HEADER_OSNAME ,
2016-03-02 20:28:12 +03:00
max_refspec_len , HEADER_REFSPEC ) ;
2014-06-11 21:47:10 +04:00
}
2014-06-13 21:16:43 +04:00
/* header for "pretty" row output */
else
printchar ( " = " , 60 ) ;
2014-06-12 18:32:21 +04:00
2015-09-08 17:31:22 +03:00
n = deployment_dicts - > len ;
2014-06-13 21:16:43 +04:00
/* print entries for each deployment */
2015-06-13 03:19:16 +03:00
for ( i = 0 ; i < n ; i + + )
2014-06-11 21:47:10 +04:00
{
2015-09-08 17:31:22 +03:00
GVariantDict * dict ;
2015-11-30 19:26:55 +03:00
g_autoptr ( GDateTime ) timestamp = NULL ;
2015-06-13 03:19:16 +03:00
g_autofree char * timestamp_string = NULL ;
g_autofree gchar * truncated_csum = NULL ;
2015-07-16 21:11:35 +03:00
g_autoptr ( GVariant ) signatures = NULL ;
gchar * id = NULL ; /* borrowed */
gchar * origin_refspec = NULL ; /* borrowed */
2015-02-11 21:06:43 +03:00
gchar * * origin_packages = NULL ; /* borrowed */
2015-07-16 21:11:35 +03:00
gchar * os_name = NULL ; /* borrowed */
gchar * version_string = NULL ; /* borrowed */
gchar * checksum = NULL ; /* borrowed */
2015-02-11 21:06:43 +03:00
g_autofree gchar * origin_refspec_description = NULL ;
2015-06-13 03:19:16 +03:00
2015-11-30 19:26:55 +03:00
guint64 t = 0 ;
2015-06-13 03:19:16 +03:00
gint serial ;
gboolean is_booted = FALSE ;
2015-09-08 17:31:22 +03:00
dict = g_ptr_array_index ( deployment_dicts , i ) ;
g_variant_dict_lookup ( dict , " id " , " &s " , & id ) ;
g_variant_dict_lookup ( dict , " osname " , " &s " , & os_name ) ;
g_variant_dict_lookup ( dict , " serial " , " i " , & serial ) ;
g_variant_dict_lookup ( dict , " checksum " , " s " , & checksum ) ;
g_variant_dict_lookup ( dict , " version " , " s " , & version_string ) ;
g_variant_dict_lookup ( dict , " timestamp " , " t " , & t ) ;
g_variant_dict_lookup ( dict , " origin " , " s " , & origin_refspec ) ;
2015-02-11 21:06:43 +03:00
g_variant_dict_lookup ( dict , " packages " , " ^a&s " , & origin_packages ) ;
2015-09-08 17:31:22 +03:00
signatures = g_variant_dict_lookup_value ( dict , " signatures " ,
G_VARIANT_TYPE ( " av " ) ) ;
2015-02-11 21:06:43 +03:00
if ( origin_packages )
{
g_autofree gchar * origin_packages_plus =
format_layered_packages_plus ( origin_packages ) ;
origin_refspec_description = g_strconcat ( origin_refspec , origin_packages_plus , NULL ) ;
}
else
{
origin_refspec_description = g_strdup ( origin_refspec ) ;
}
2015-06-13 03:19:16 +03:00
is_booted = g_strcmp0 ( booted_id , id ) = = 0 ;
timestamp = g_date_time_new_from_unix_utc ( t ) ;
2015-11-30 19:26:55 +03:00
if ( timestamp ! = NULL )
timestamp_string = g_date_time_format ( timestamp , " %Y-%m-%d %T " ) ;
else
timestamp_string = g_strdup_printf ( " (invalid) " ) ;
2014-06-13 21:16:43 +04:00
/* truncate checksum */
2015-06-13 03:19:16 +03:00
truncated_csum = g_strndup ( checksum , CSUM_DISP_LEN ) ;
2014-06-13 21:16:43 +04:00
/* print deployment info column */
if ( ! opt_pretty )
{
2016-05-29 21:15:29 +03:00
g_print ( " %s %-*s " ,
is_booted ? libsd_special_glyph ( BLACK_CIRCLE ) : " " ,
2014-12-12 00:29:12 +03:00
max_timestamp_len + buffer , timestamp_string ) ;
if ( max_version_len )
g_print ( " %-*s " ,
max_version_len + buffer , version_string ? version_string : " " ) ;
g_print ( " %-*s%-*s%-*s \n " ,
max_id_len + buffer , truncated_csum ,
2015-06-13 03:19:16 +03:00
max_osname_len + buffer , os_name ,
2015-02-11 21:06:43 +03:00
max_refspec_len , origin_refspec_description ) ;
2014-06-12 18:32:21 +04:00
}
2014-06-13 21:16:43 +04:00
/* print "pretty" row info */
else
{
guint tab = 11 ;
char * title = NULL ;
2016-02-25 20:42:56 +03:00
g_autofree char * packages_joined = NULL ;
if ( origin_packages & & g_strv_length ( origin_packages ) > 0 )
packages_joined = g_strjoinv ( " " , origin_packages ) ;
2015-02-11 21:06:43 +03:00
2014-06-13 21:16:43 +04:00
if ( i = = 0 )
title = " DEFAULT ON BOOT " ;
2015-06-13 03:19:16 +03:00
else if ( is_booted | | n < = 2 )
2014-06-13 21:16:43 +04:00
title = " NON-DEFAULT ROLLBACK TARGET " ;
else
title = " NON-DEFAULT DEPLOYMENT " ;
g_print ( " %c %s \n " ,
2015-06-13 03:19:16 +03:00
is_booted ? ' * ' : ' ' ,
2014-06-13 21:16:43 +04:00
title ) ;
printchar ( " - " , 40 ) ;
2014-10-24 10:03:08 +04:00
if ( version_string )
g_print ( " %-*s%-*s \n " , tab , " version " , tab , version_string ) ;
2015-06-13 03:19:16 +03:00
2016-02-25 20:42:56 +03:00
g_print ( " %-*s%-*s \n %-*s%-*s.%d \n %-*s%-*s \n %-*s%-*s \n " ,
2014-06-13 21:16:43 +04:00
tab , " timestamp " , tab , timestamp_string ,
2015-06-13 03:19:16 +03:00
tab , " id " , tab , checksum , serial ,
tab , " osname " , tab , os_name ,
2016-02-25 20:42:56 +03:00
tab , " refspec " , tab , origin_refspec ) ;
if ( packages_joined )
g_print ( " %-*s%-*s \n " , tab , " packages " , tab , packages_joined ) ;
2015-04-07 20:49:21 +03:00
2015-11-30 19:26:55 +03:00
if ( signatures ! = NULL )
rpmostree_print_signatures ( signatures , " GPG: " ) ;
2015-04-07 20:49:21 +03:00
2014-06-13 21:16:43 +04:00
printchar ( " = " , 60 ) ;
}
}
2014-06-11 21:47:10 +04:00
2015-04-07 20:49:21 +03:00
/* Print any signatures for the booted deployment, but only in NON-pretty
* mode . We save this for the end to preserve the tabular formatting for
* deployments . */
2015-06-13 03:19:16 +03:00
if ( ! opt_pretty & & booted_signatures ! = NULL )
2015-04-07 20:49:21 +03:00
{
2015-06-13 03:19:16 +03:00
guint n_sigs = g_variant_n_children ( booted_signatures ) ;
if ( n_sigs > 0 )
2015-04-07 20:49:21 +03:00
{
/* XXX If we ever add internationalization, use ngettext() here. */
g_print ( " \n GPG: Found %u signature%s on the booted deployment (*): \n " ,
n_sigs , n_sigs = = 1 ? " " : " s " ) ;
2015-06-13 03:19:16 +03:00
rpmostree_print_signatures ( booted_signatures , " " ) ;
2015-04-07 20:49:21 +03:00
}
}
2016-05-31 20:12:36 +03:00
return TRUE ;
}
int
rpmostree_builtin_status ( int argc ,
char * * argv ,
GCancellable * cancellable ,
GError * * error )
{
int exit_status = EXIT_FAILURE ;
GOptionContext * context = g_option_context_new ( " - Get the version of the booted system " ) ;
glnx_unref_object RPMOSTreeOS * os_proxy = NULL ;
glnx_unref_object RPMOSTreeSysroot * sysroot_proxy = NULL ;
g_autoptr ( GVariant ) booted_deployment = NULL ;
g_autoptr ( GVariant ) deployments = NULL ;
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 ,
RPM_OSTREE_BUILTIN_FLAG_NONE ,
cancellable ,
& sysroot_proxy ,
error ) )
goto out ;
if ( ! rpmostree_load_os_proxy ( sysroot_proxy , NULL ,
cancellable , & os_proxy , error ) )
goto out ;
deployments = rpmostree_sysroot_dup_deployments ( sysroot_proxy ) ;
booted_deployment = rpmostree_os_dup_booted_deployment ( os_proxy ) ;
if ( ! status_fallback ( sysroot_proxy , os_proxy , deployments ,
booted_deployment ,
cancellable , error ) )
goto out ;
exit_status = EXIT_SUCCESS ;
2015-06-13 03:19:16 +03:00
out :
2015-08-05 04:09:54 +03:00
/* Does nothing if using the message bus. */
rpmostree_cleanup_peer ( ) ;
2015-06-13 03:19:16 +03:00
2015-11-02 23:43:58 +03:00
return exit_status ;
2014-06-11 21:47:10 +04:00
}