2014-03-22 23:05:41 +04:00
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright ( C ) 2014 Colin Walters < walters @ verbum . org >
*
* This library 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 License , 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 <gio/gio.h>
2015-06-13 03:19:16 +03:00
# include <glib-unix.h>
2014-03-22 23:05:41 +04:00
# include <errno.h>
2014-11-24 20:34:45 +03:00
# include <stdlib.h>
2014-03-22 23:05:41 +04:00
# include <string.h>
# include <unistd.h>
# include <locale.h>
# include "rpmostree-builtins.h"
# include "libgsystem.h"
static RpmOstreeCommand commands [ ] = {
2014-09-17 05:45:30 +04:00
# ifdef HAVE_COMPOSE_TOOLING
2014-11-26 23:02:00 +03:00
{ " compose " , rpmostree_builtin_compose } ,
2014-09-17 05:45:30 +04:00
# endif
2015-07-15 21:53:26 +03:00
{ " db " , rpmostree_builtin_db } ,
2015-10-21 16:50:26 +03:00
{ " deploy " , rpmostree_builtin_deploy } ,
2014-11-26 23:02:00 +03:00
{ " rebase " , rpmostree_builtin_rebase } ,
{ " rollback " , rpmostree_builtin_rollback } ,
{ " status " , rpmostree_builtin_status } ,
2015-07-15 21:53:26 +03:00
{ " upgrade " , rpmostree_builtin_upgrade } ,
2016-01-11 01:00:46 +03:00
{ " internals " , rpmostree_builtin_internals } ,
2016-01-26 22:40:51 +03:00
{ " container " , rpmostree_builtin_container } ,
2014-03-22 23:05:41 +04:00
{ NULL }
} ;
2014-11-24 20:34:45 +03:00
static gboolean opt_version ;
2015-08-05 19:39:07 +03:00
static gboolean opt_force_peer ;
static char * opt_sysroot ;
2014-11-24 20:34:45 +03:00
static GOptionEntry global_entries [ ] = {
{ " version " , 0 , 0 , G_OPTION_ARG_NONE , & opt_version , " Print version information and exit " , NULL } ,
{ NULL }
} ;
2015-08-05 19:39:07 +03:00
static GOptionEntry daemon_entries [ ] = {
{ " sysroot " , 0 , 0 , G_OPTION_ARG_STRING , & opt_sysroot , " Use system root SYSROOT (default: /) " , " SYSROOT " } ,
{ " peer " , 0 , 0 , G_OPTION_ARG_NONE , & opt_force_peer , " Force a peer-to-peer connection instead of using the system message bus " , NULL } ,
{ NULL }
} ;
2014-11-24 20:34:45 +03:00
static GOptionContext *
option_context_new_with_commands ( void )
2014-03-22 23:05:41 +04:00
{
RpmOstreeCommand * command = commands ;
2014-11-24 20:34:45 +03:00
GOptionContext * context ;
GString * summary ;
2014-03-22 23:05:41 +04:00
2014-11-24 20:34:45 +03:00
context = g_option_context_new ( " COMMAND " ) ;
2014-03-22 23:05:41 +04:00
2014-11-24 20:34:45 +03:00
summary = g_string_new ( " Builtin Commands: " ) ;
2014-03-22 23:05:41 +04:00
2014-11-24 20:34:45 +03:00
while ( command - > name ! = NULL )
2014-03-22 23:05:41 +04:00
{
2016-03-24 04:46:45 +03:00
/* Internals will remain hidden always, container will possibly
* get promoted at some point . For now , it ' s an easter egg .
*/
if ( ! g_str_equal ( command - > name , " internals " )
& & ! g_str_equal ( command - > name , " container " ) )
2016-01-11 01:00:46 +03:00
g_string_append_printf ( summary , " \n %s " , command - > name ) ;
2014-03-22 23:05:41 +04:00
command + + ;
}
2014-11-24 20:34:45 +03:00
g_option_context_set_summary ( context , summary - > str ) ;
g_string_free ( summary , TRUE ) ;
return context ;
}
gboolean
rpmostree_option_context_parse ( GOptionContext * context ,
const GOptionEntry * main_entries ,
int * argc ,
char * * * argv ,
2015-08-05 19:39:07 +03:00
RpmOstreeBuiltinFlags flags ,
GCancellable * cancellable ,
RPMOSTreeSysroot * * out_sysroot_proxy ,
2014-11-24 20:34:45 +03:00
GError * * error )
{
2015-08-05 19:39:07 +03:00
gboolean use_daemon ;
gboolean ret = FALSE ;
use_daemon = ( ( flags & RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD ) = = 0 ) ;
2014-11-24 20:34:45 +03:00
if ( main_entries ! = NULL )
g_option_context_add_main_entries ( context , main_entries , NULL ) ;
2015-08-05 19:39:07 +03:00
if ( use_daemon )
g_option_context_add_main_entries ( context , daemon_entries , NULL ) ;
2014-11-24 20:34:45 +03:00
g_option_context_add_main_entries ( context , global_entries , NULL ) ;
if ( ! g_option_context_parse ( context , argc , argv , error ) )
2015-08-05 19:39:07 +03:00
goto out ;
2014-11-24 20:34:45 +03:00
if ( opt_version )
{
2015-11-19 22:22:48 +03:00
g_print ( " %s \n %s \n " , PACKAGE_STRING , RPM_OSTREE_FEATURES ) ;
2014-11-24 20:34:45 +03:00
exit ( EXIT_SUCCESS ) ;
}
2015-08-05 19:39:07 +03:00
if ( use_daemon )
{
if ( ! rpmostree_load_sysroot ( opt_sysroot ,
opt_force_peer ,
cancellable ,
out_sysroot_proxy ,
error ) )
goto out ;
}
ret = TRUE ;
out :
return ret ;
2014-03-22 23:05:41 +04:00
}
2015-04-06 20:09:37 +03:00
void
rpmostree_print_gpg_verify_result ( OstreeGpgVerifyResult * result )
{
GString * buffer ;
guint n_sigs , ii ;
n_sigs = ostree_gpg_verify_result_count_all ( result ) ;
/* XXX If we ever add internationalization, use ngettext() here. */
g_print ( " GPG: Verification enabled, found %u signature%s: \n " ,
n_sigs , n_sigs = = 1 ? " " : " s " ) ;
buffer = g_string_sized_new ( 256 ) ;
for ( ii = 0 ; ii < n_sigs ; ii + + )
{
g_string_append_c ( buffer , ' \n ' ) ;
ostree_gpg_verify_result_describe ( result , ii , buffer , " " ,
OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT ) ;
}
g_print ( " %s \n " , buffer - > str ) ;
g_string_free ( buffer , TRUE ) ;
}
2015-06-13 03:19:16 +03:00
static gboolean
on_sigint ( gpointer user_data )
{
GCancellable * cancellable = user_data ;
g_debug ( " Caught signal. Canceling " ) ;
g_cancellable_cancel ( cancellable ) ;
return FALSE ;
}
2014-03-22 23:05:41 +04:00
int
main ( int argc ,
char * * argv )
{
2015-06-13 03:19:16 +03:00
GCancellable * cancellable = g_cancellable_new ( ) ;
2014-03-22 23:05:41 +04:00
RpmOstreeCommand * command ;
2015-11-02 23:43:58 +03:00
int exit_status = EXIT_SUCCESS ;
2014-11-24 20:34:45 +03:00
int in , out ;
const char * command_name = NULL ;
gs_free char * prgname = NULL ;
2015-11-02 23:12:22 +03:00
GError * local_error = NULL ;
2015-06-13 03:19:16 +03:00
2014-03-22 23:05:41 +04:00
/* avoid gvfs (http://bugzilla.gnome.org/show_bug.cgi?id=526454) */
g_setenv ( " GIO_USE_VFS " , " local " , TRUE ) ;
g_set_prgname ( argv [ 0 ] ) ;
setlocale ( LC_ALL , " " ) ;
/*
* Parse the global options . We rearrange the options as
* necessary , in order to pass relevant options through
* to the commands , but also have them take effect globally .
*/
for ( in = 1 , out = 1 ; in < argc ; in + + , out + + )
{
/* The non-option is the command, take it out of the arguments */
if ( argv [ in ] [ 0 ] ! = ' - ' )
{
2014-11-24 20:34:45 +03:00
if ( command_name = = NULL )
2014-03-22 23:05:41 +04:00
{
2014-11-24 20:34:45 +03:00
command_name = argv [ in ] ;
out - - ;
continue ;
2014-03-22 23:05:41 +04:00
}
}
2014-11-24 20:34:45 +03:00
else if ( g_str_equal ( argv [ in ] , " -- " ) )
2014-03-22 23:05:41 +04:00
{
2014-11-24 20:34:45 +03:00
break ;
2014-03-22 23:05:41 +04:00
}
2014-11-24 20:34:45 +03:00
argv [ out ] = argv [ in ] ;
2014-03-22 23:05:41 +04:00
}
argc = out ;
2015-06-13 03:19:16 +03:00
g_unix_signal_add ( SIGINT , on_sigint , cancellable ) ;
g_unix_signal_add ( SIGTERM , on_sigint , cancellable ) ;
g_unix_signal_add ( SIGHUP , on_sigint , cancellable ) ;
2014-12-02 01:38:08 +03:00
/* Keep the "rpm" command working for backward-compatibility. */
if ( g_strcmp0 ( command_name , " rpm " ) = = 0 )
command_name = " db " ;
2014-03-22 23:05:41 +04:00
command = commands ;
while ( command - > name )
{
2014-11-24 20:34:45 +03:00
if ( g_strcmp0 ( command_name , command - > name ) = = 0 )
2014-03-22 23:05:41 +04:00
break ;
command + + ;
}
if ( ! command - > fn )
{
2014-11-24 20:34:45 +03:00
GOptionContext * context ;
2015-11-23 20:03:11 +03:00
gs_free char * help = NULL ;
2014-11-24 20:34:45 +03:00
context = option_context_new_with_commands ( ) ;
/* This will not return for some options (e.g. --version). */
2016-04-27 15:11:06 +03:00
( void ) rpmostree_option_context_parse ( context , NULL , & argc , & argv ,
RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD ,
NULL , NULL , NULL ) ;
if ( command_name = = NULL )
2014-11-24 20:34:45 +03:00
{
2016-04-27 15:11:06 +03:00
local_error = g_error_new_literal ( G_IO_ERROR , G_IO_ERROR_FAILED ,
" No command specified " ) ;
}
else
{
local_error = g_error_new ( G_IO_ERROR , G_IO_ERROR_FAILED ,
" Unknown command '%s' " , command_name ) ;
2014-11-24 20:34:45 +03:00
}
help = g_option_context_get_help ( context , FALSE , NULL ) ;
g_printerr ( " %s " , help ) ;
2016-04-08 23:48:29 +03:00
exit_status = EXIT_FAILURE ;
2014-11-24 20:34:45 +03:00
g_option_context_free ( context ) ;
2014-03-22 23:05:41 +04:00
goto out ;
}
2014-11-24 20:34:45 +03:00
prgname = g_strdup_printf ( " %s %s " , g_get_prgname ( ) , command_name ) ;
g_set_prgname ( prgname ) ;
2014-03-22 23:05:41 +04:00
2015-11-02 23:43:58 +03:00
exit_status = command - > fn ( argc , argv , cancellable , & local_error ) ;
2014-03-22 23:05:41 +04:00
out :
2015-11-02 23:12:22 +03:00
if ( local_error ! = NULL )
2014-03-22 23:05:41 +04:00
{
int is_tty = isatty ( 1 ) ;
const char * prefix = " " ;
const char * suffix = " " ;
if ( is_tty )
{
prefix = " \x1b [31m \x1b [1m " ; /* red, bold */
suffix = " \x1b [22m \x1b [0m " ; /* bold off, color reset */
}
2015-11-02 23:12:22 +03:00
g_dbus_error_strip_remote_error ( local_error ) ;
g_printerr ( " %serror: %s%s \n " , prefix , suffix , local_error - > message ) ;
g_error_free ( local_error ) ;
2015-11-02 23:43:58 +03:00
/* Print a warning if the exit status indicates success when we
* actually had an error , so it gets reported and fixed quickly . */
g_warn_if_fail ( exit_status ! = EXIT_SUCCESS ) ;
2014-03-22 23:05:41 +04:00
}
2015-11-02 23:43:58 +03:00
return exit_status ;
2014-03-22 23:05:41 +04:00
}