2014-01-24 22:34:19 +04:00
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright ( C ) 2013 , 2014 Colin Walters < walters @ verbum . org >
*
* 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>
2014-02-13 03:26:31 +04:00
# include <json-glib/json-glib.h>
2014-01-24 22:34:19 +04:00
2014-01-29 23:37:44 +04:00
# include "rpmostree-postprocess.h"
2014-01-24 22:34:19 +04:00
# include "libgsystem.h"
2014-02-26 02:07:59 +04:00
static char * opt_workdir ;
2014-01-24 22:34:19 +04:00
static char * opt_workdir ;
static GOptionEntry option_entries [ ] = {
{ " workdir " , 0 , 0 , G_OPTION_ARG_STRING , & opt_workdir , " Working directory " , " REPO " } ,
{ NULL }
} ;
2014-02-26 02:07:59 +04:00
static char *
subprocess_context_print_args ( GSSubprocessContext * ctx )
{
GString * ret = g_string_new ( " " ) ;
gs_strfreev char * * argv = NULL ;
char * * strviter ;
g_object_get ( ( GObject * ) ctx , " argv " , & argv , NULL ) ;
for ( strviter = argv ; strviter & & * strviter ; strviter + + )
{
gs_free char * quoted = g_shell_quote ( * strviter ) ;
g_string_append ( ret , quoted ) ;
}
return g_string_free ( ret , FALSE ) ;
}
2014-02-13 03:26:31 +04:00
static const char *
object_require_string_member ( JsonObject * object ,
const char * member_name ,
GError * * error )
2014-01-26 19:13:45 +04:00
{
2014-02-13 03:26:31 +04:00
const char * ret = json_object_get_string_member ( object , member_name ) ;
if ( ! ret )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" No member '%s' found " , member_name ) ;
return NULL ;
}
return ret ;
}
static const char *
array_require_string_element ( JsonArray * array ,
guint i ,
GError * * error )
{
const char * ret = json_array_get_string_element ( array , i ) ;
if ( ! ret )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Element at index %u is not a string " , i ) ;
return NULL ;
}
return ret ;
}
static gboolean
append_string_array_to ( JsonObject * object ,
const char * member_name ,
GPtrArray * array ,
GError * * error )
{
JsonArray * jarray = json_object_get_array_member ( object , member_name ) ;
guint i , len ;
if ( ! jarray )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" No member '%s' found " , member_name ) ;
return FALSE ;
}
2014-01-26 19:13:45 +04:00
2014-02-13 03:26:31 +04:00
len = json_array_get_length ( jarray ) ;
2014-01-26 19:13:45 +04:00
for ( i = 0 ; i < len ; i + + )
{
2014-02-13 03:26:31 +04:00
const char * v = array_require_string_element ( jarray , i , error ) ;
if ( ! v )
return FALSE ;
g_ptr_array_add ( array , g_strdup ( v ) ) ;
2014-01-26 19:13:45 +04:00
}
2014-02-13 03:26:31 +04:00
return TRUE ;
2014-01-26 19:13:45 +04:00
}
2014-01-24 22:34:19 +04:00
static gboolean
replace_nsswitch ( GFile * target_usretc ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
gs_unref_object GFile * nsswitch_conf =
g_file_get_child ( target_usretc , " nsswitch.conf " ) ;
gs_free char * nsswitch_contents = NULL ;
gs_free char * new_nsswitch_contents = NULL ;
static gsize regex_initialized ;
static GRegex * passwd_regex ;
if ( g_once_init_enter ( & regex_initialized ) )
{
passwd_regex = g_regex_new ( " ^(passwd|group): \\ s+files(.*)$ " ,
G_REGEX_MULTILINE , 0 , NULL ) ;
g_assert ( passwd_regex ) ;
g_once_init_leave ( & regex_initialized , 1 ) ;
}
nsswitch_contents = gs_file_load_contents_utf8 ( nsswitch_conf , cancellable , error ) ;
if ( ! nsswitch_contents )
goto out ;
new_nsswitch_contents = g_regex_replace ( passwd_regex ,
nsswitch_contents , - 1 , 0 ,
" \\ 1: files altfiles \\ 2 " ,
0 , error ) ;
if ( ! new_nsswitch_contents )
goto out ;
if ( ! g_file_replace_contents ( nsswitch_conf , new_nsswitch_contents ,
strlen ( new_nsswitch_contents ) ,
NULL , FALSE , 0 , NULL ,
cancellable , error ) )
goto out ;
ret = TRUE ;
out :
return ret ;
}
2014-01-26 19:13:45 +04:00
typedef struct {
GSSubprocess * process ;
GFile * reposdir_path ;
GFile * tmp_reposdir_path ;
GDataOutputStream * stdin ;
/* GDataInputStream *stdout; */
} YumContext ;
2014-01-24 22:34:19 +04:00
2014-01-26 19:13:45 +04:00
static gboolean
yum_context_close ( YumContext * yumctx ,
GCancellable * cancellable ,
GError * * error )
2014-01-24 22:34:19 +04:00
{
2014-01-26 19:13:45 +04:00
gboolean ret = FALSE ;
2014-01-24 22:34:19 +04:00
2014-01-26 19:13:45 +04:00
if ( ! yumctx )
return TRUE ;
2014-01-24 22:34:19 +04:00
2014-01-26 19:13:45 +04:00
if ( yumctx - > tmp_reposdir_path )
{
if ( ! gs_file_rename ( yumctx - > tmp_reposdir_path , yumctx - > reposdir_path ,
cancellable , error ) )
goto out ;
g_clear_object ( & yumctx - > reposdir_path ) ;
g_clear_object ( & yumctx - > tmp_reposdir_path ) ;
}
if ( yumctx - > process )
{
if ( yumctx - > stdin )
{
if ( ! g_output_stream_close ( ( GOutputStream * ) yumctx - > stdin , cancellable , error ) )
goto out ;
g_clear_object ( & yumctx - > stdin ) ;
}
/*
if ( yumctx - > stdout )
{
if ( ! g_input_stream_close ( ( GInputStream * ) yumctx - > stdout , cancellable , error ) )
goto out ;
g_clear_object ( & yumctx - > stdout ) ;
}
*/
g_print ( " Waiting for yum... \n " ) ;
if ( ! gs_subprocess_wait_sync_check ( yumctx - > process , cancellable , error ) )
goto out ;
g_print ( " Waiting for yum [OK] \n " ) ;
g_clear_object ( & yumctx - > process ) ;
}
2014-01-24 22:34:19 +04:00
2014-01-26 19:13:45 +04:00
ret = TRUE ;
out :
2014-01-24 22:34:19 +04:00
return ret ;
}
2014-01-26 19:13:45 +04:00
static void
yum_context_free ( YumContext * yumctx )
{
if ( ! yumctx )
return ;
( void ) yum_context_close ( yumctx , NULL , NULL ) ;
g_free ( yumctx ) ;
}
2014-02-26 02:07:59 +04:00
static inline
void cleanup_keyfile_unref ( void * loc )
{
GKeyFile * locp = * ( ( GKeyFile * * ) loc ) ;
if ( locp )
g_key_file_unref ( locp ) ;
}
2014-02-13 03:26:31 +04:00
static gboolean
append_repo_and_cache_opts ( JsonObject * treedata ,
2014-02-26 02:07:59 +04:00
GFile * cachedir ,
2014-02-13 03:26:31 +04:00
GPtrArray * args ,
2014-02-26 02:07:59 +04:00
GCancellable * cancellable ,
2014-02-13 03:26:31 +04:00
GError * * error )
2014-01-24 22:34:19 +04:00
{
2014-02-13 03:26:31 +04:00
gboolean ret = FALSE ;
2014-02-26 02:07:59 +04:00
JsonArray * enable_repos = NULL ;
JsonArray * repos_data = NULL ;
gs_unref_object GFile * yumcache_lookaside = NULL ;
gs_unref_object GFile * repos_tmpdir = NULL ;
yumcache_lookaside = g_file_resolve_relative_path ( cachedir , " yum-cache " ) ;
if ( ! gs_file_ensure_directory ( yumcache_lookaside , TRUE , cancellable , error ) )
goto out ;
repos_tmpdir = g_file_resolve_relative_path ( cachedir , " tmp-repos " ) ;
if ( ! gs_shutil_rm_rf ( repos_tmpdir , cancellable , error ) )
goto out ;
if ( ! gs_file_ensure_directory ( repos_tmpdir , TRUE , cancellable , error ) )
goto out ;
2014-01-26 19:13:45 +04:00
if ( g_getenv ( " RPM_OSTREE_OFFLINE " ) )
g_ptr_array_add ( args , g_strdup ( " -C " ) ) ;
g_ptr_array_add ( args , g_strdup ( " --disablerepo=* " ) ) ;
2014-02-13 03:26:31 +04:00
2014-02-26 02:07:59 +04:00
if ( json_object_has_member ( treedata , " repos " ) )
enable_repos = json_object_get_array_member ( treedata , " repos " ) ;
2014-02-13 03:26:31 +04:00
if ( enable_repos )
{
guint i ;
guint n = json_array_get_length ( enable_repos ) ;
for ( i = 0 ; i < n ; i + + )
{
const char * reponame = array_require_string_element ( enable_repos , i , error ) ;
if ( ! reponame )
goto out ;
g_ptr_array_add ( args , g_strconcat ( " --enablerepo= " , reponame , NULL ) ) ;
}
}
2014-01-31 04:04:58 +04:00
2014-02-26 02:07:59 +04:00
if ( json_object_has_member ( treedata , " repos_data " ) )
repos_data = json_object_get_array_member ( treedata , " repos_data " ) ;
else
repos_data = NULL ;
if ( repos_data )
{
guint i ;
guint n = json_array_get_length ( repos_data ) ;
if ( n > 0 )
g_ptr_array_add ( args , g_strconcat ( " --setopt=reposdir=/etc/yum.repos.d, " ,
gs_file_get_path_cached ( repos_tmpdir ) ,
NULL ) ) ;
for ( i = 0 ; i < n ; i + + )
{
const char * repodata = array_require_string_element ( repos_data , i , error ) ;
__attribute__ ( ( cleanup ( cleanup_keyfile_unref ) ) ) GKeyFile * keyfile = NULL ;
gs_strfreev char * * groups = NULL ;
const char * reponame ;
gs_free char * rpmostree_reponame = NULL ;
gs_free char * rpmostree_repo_filename = NULL ;
gs_unref_object GFile * repo_tmp_file = NULL ;
gsize len ;
if ( ! repodata )
goto out ;
keyfile = g_key_file_new ( ) ;
if ( ! g_key_file_load_from_data ( keyfile , repodata , - 1 , 0 , error ) )
{
g_prefix_error ( error , " Parsing keyfile data in repos_data: " ) ;
goto out ;
}
groups = g_key_file_get_groups ( keyfile , & len ) ;
if ( len = = 0 )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" No groups found in keyfile data in repos_data " ) ;
goto out ;
}
reponame = groups [ 0 ] ;
g_assert ( strchr ( reponame , ' / ' ) = = NULL ) ;
rpmostree_reponame = g_strconcat ( reponame , NULL ) ;
rpmostree_repo_filename = g_strconcat ( rpmostree_reponame , " .repo " , NULL ) ;
repo_tmp_file = g_file_get_child ( repos_tmpdir , rpmostree_repo_filename ) ;
if ( ! g_file_replace_contents ( repo_tmp_file , repodata , strlen ( repodata ) ,
NULL , FALSE , 0 ,
NULL ,
cancellable , error ) )
goto out ;
g_ptr_array_add ( args , g_strconcat ( " --enablerepo= " , rpmostree_reponame , NULL ) ) ;
}
}
2014-01-31 04:04:58 +04:00
g_ptr_array_add ( args , g_strdup ( " --setopt=keepcache=1 " ) ) ;
2014-02-26 02:07:59 +04:00
g_ptr_array_add ( args , g_strconcat ( " --setopt=cachedir= " ,
gs_file_get_path_cached ( yumcache_lookaside ) ,
NULL ) ) ;
2014-02-13 03:26:31 +04:00
ret = TRUE ;
out :
return ret ;
2014-01-26 19:13:45 +04:00
}
static YumContext *
2014-02-13 03:26:31 +04:00
yum_context_new ( JsonObject * treedata ,
GFile * yumroot ,
2014-01-31 04:04:58 +04:00
GFile * cachedir ,
2014-01-26 19:13:45 +04:00
GCancellable * cancellable ,
GError * * error )
{
gboolean success = FALSE ;
YumContext * yumctx = NULL ;
GPtrArray * yum_argv = g_ptr_array_new_with_free_func ( g_free ) ;
2014-01-24 22:34:19 +04:00
gs_unref_object GSSubprocessContext * context = NULL ;
gs_unref_object GSSubprocess * yum_process = NULL ;
gs_unref_object GFile * reposdir_path = NULL ;
2014-01-26 19:13:45 +04:00
g_ptr_array_add ( yum_argv , g_strdup ( " yum " ) ) ;
g_ptr_array_add ( yum_argv , g_strdup ( " -y " ) ) ;
2014-01-31 04:04:58 +04:00
2014-02-26 02:07:59 +04:00
if ( ! append_repo_and_cache_opts ( treedata , cachedir , yum_argv ,
cancellable , error ) )
2014-02-13 03:26:31 +04:00
goto out ;
2014-01-31 04:04:58 +04:00
2014-01-26 19:13:45 +04:00
g_ptr_array_add ( yum_argv , g_strconcat ( " --installroot= " ,
gs_file_get_path_cached ( yumroot ) ,
NULL ) ) ;
2014-01-24 22:34:19 +04:00
2014-01-26 19:13:45 +04:00
g_ptr_array_add ( yum_argv , g_strdup ( " shell " ) ) ;
2014-01-24 22:34:19 +04:00
g_ptr_array_add ( yum_argv , NULL ) ;
context = gs_subprocess_context_new ( ( char * * ) yum_argv - > pdata ) ;
{
gs_strfreev char * * duped_environ = g_get_environ ( ) ;
duped_environ = g_environ_setenv ( duped_environ , " OSTREE_KERNEL_INSTALL_NOOP " , " 1 " , TRUE ) ;
2014-02-21 23:56:12 +04:00
/* See fedora's kernel.spec */
duped_environ = g_environ_setenv ( duped_environ , " HARDLINK " , " no " , TRUE ) ;
2014-01-24 22:34:19 +04:00
gs_subprocess_context_set_environment ( context , duped_environ ) ;
}
reposdir_path = g_file_resolve_relative_path ( yumroot , " etc/yum.repos.d " ) ;
/* Hideous workaround for the fact that as soon as yum.repos.d
exists in the install root , yum will prefer it . */
if ( g_file_query_exists ( reposdir_path , NULL ) )
{
2014-01-26 19:13:45 +04:00
yumctx - > reposdir_path = g_object_ref ( reposdir_path ) ;
yumctx - > tmp_reposdir_path = g_file_resolve_relative_path ( yumroot , " etc/yum.repos.d.tmp " ) ;
if ( ! gs_file_rename ( reposdir_path , yumctx - > tmp_reposdir_path ,
2014-01-24 22:34:19 +04:00
cancellable , error ) )
goto out ;
}
2014-01-26 19:13:45 +04:00
gs_subprocess_context_set_stdin_disposition ( context , GS_SUBPROCESS_STREAM_DISPOSITION_PIPE ) ;
/* gs_subprocess_context_set_stdout_disposition (context, GS_SUBPROCESS_STREAM_DISPOSITION_PIPE); */
yumctx = g_new0 ( YumContext , 1 ) ;
2014-01-24 22:34:19 +04:00
2014-02-26 02:07:59 +04:00
{
gs_free char * cmdline = subprocess_context_print_args ( context ) ;
g_print ( " Starting %s \n " , cmdline ) ;
}
2014-01-26 19:13:45 +04:00
yumctx - > process = gs_subprocess_new ( context , cancellable , error ) ;
if ( ! yumctx - > process )
2014-01-24 22:34:19 +04:00
goto out ;
2014-01-26 19:13:45 +04:00
yumctx - > stdin = ( GDataOutputStream * ) g_data_output_stream_new ( gs_subprocess_get_stdin_pipe ( yumctx - > process ) ) ;
/* yumctx->stdout = (GDataInputStream*)g_data_input_stream_new (gs_subprocess_get_stdout_pipe (yumctx->process)); */
success = TRUE ;
out :
if ( ! success )
2014-01-24 22:34:19 +04:00
{
2014-01-26 19:13:45 +04:00
yum_context_free ( yumctx ) ;
return NULL ;
2014-01-24 22:34:19 +04:00
}
2014-01-26 19:13:45 +04:00
return yumctx ;
}
2014-01-24 22:34:19 +04:00
2014-01-26 19:13:45 +04:00
static gboolean
yum_context_command ( YumContext * yumctx ,
const char * cmd ,
GPtrArray * * out_lines ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
gsize bytes_written ;
gs_unref_ptrarray GPtrArray * lines = g_ptr_array_new_with_free_func ( g_free ) ;
gs_free char * cmd_nl = g_strconcat ( cmd , " \n " , NULL ) ;
g_print ( " yum> %s " , cmd_nl ) ;
if ( ! g_output_stream_write_all ( ( GOutputStream * ) yumctx - > stdin ,
cmd_nl , strlen ( cmd_nl ) , & bytes_written ,
cancellable , error ) )
2014-01-24 22:34:19 +04:00
goto out ;
2014-01-26 19:13:45 +04:00
/*
do
2014-01-24 22:34:19 +04:00
{
2014-01-26 19:13:45 +04:00
const guint8 * buf ;
gsize len ;
GBytes * bytes ;
const guint8 * nl ;
bytes = g_input_stream_read_bytes ( ( GInputStream * ) yumctx - > stdout , 8192 ,
cancellable , error ) ;
if ( ! bytes )
2014-01-24 22:34:19 +04:00
goto out ;
2014-01-26 19:13:45 +04:00
buf = g_bytes_get_data ( bytes , & len ) ;
if ( g_bytes_get_size ( bytes ) = = 0 )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Received unexpected EOF from yum! " ) ;
goto out ;
}
while ( ( nl = memchr ( buf , ' \n ' , len ) ) ! = NULL )
{
gsize linelen = nl - buf ;
char * line ;
if ( ! g_utf8_validate ( ( char * ) buf , linelen , NULL ) )
{
gs_free char * cstr = c_stringify ( buf , linelen ) ;
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Invalid UTF-8 from yum: %s " , cstr ) ;
goto out ;
}
line = g_strndup ( ( char * ) buf , linelen ) ;
g_print ( " yum< %s \n " , line ) ;
g_ptr_array_add ( lines , line ) ;
len - = ( linelen + 1 ) ;
buf = nl + 1 ;
}
if ( len < 2 | | memcmp ( buf , " > " , 2 ) ! = 0 )
{
gs_free char * cstr = c_stringify ( buf , len ) ;
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Unexpected end of data '%s' " , cstr ) ;
goto out ;
}
2014-01-24 22:34:19 +04:00
}
2014-01-26 19:13:45 +04:00
while ( TRUE ) ;
*/
2014-01-24 22:34:19 +04:00
ret = TRUE ;
2014-01-26 19:13:45 +04:00
gs_transfer_out_value ( out_lines , & lines ) ;
2014-01-24 22:34:19 +04:00
out :
return ret ;
}
2014-01-26 19:13:45 +04:00
2014-01-24 22:34:19 +04:00
static gboolean
2014-02-13 03:26:31 +04:00
yuminstall ( JsonObject * treedata ,
GFile * yumroot ,
2014-01-31 04:04:58 +04:00
GFile * cachedir ,
2014-01-26 19:13:45 +04:00
char * * packages ,
GCancellable * cancellable ,
GError * * error )
2014-01-24 22:34:19 +04:00
{
gboolean ret = FALSE ;
char * * strviter ;
2014-01-26 19:13:45 +04:00
YumContext * yumctx ;
2014-01-24 22:34:19 +04:00
2014-02-13 03:26:31 +04:00
yumctx = yum_context_new ( treedata , yumroot , cachedir , cancellable , error ) ;
2014-01-26 19:13:45 +04:00
if ( ! yumctx )
goto out ;
2014-01-24 22:34:19 +04:00
for ( strviter = packages ; strviter & & * strviter ; strviter + + )
{
2014-01-26 19:13:45 +04:00
gs_free char * cmd = NULL ;
2014-01-24 22:34:19 +04:00
const char * package = * strviter ;
2014-01-26 19:13:45 +04:00
gs_unref_ptrarray GPtrArray * lines = NULL ;
2014-01-24 22:34:19 +04:00
if ( g_str_has_prefix ( package , " @ " ) )
2014-01-26 19:13:45 +04:00
cmd = g_strconcat ( " group install " , package , NULL ) ;
2014-01-24 22:34:19 +04:00
else
2014-01-26 19:13:45 +04:00
cmd = g_strconcat ( " install " , package , NULL ) ;
if ( ! yum_context_command ( yumctx , cmd , & lines ,
cancellable , error ) )
goto out ;
2014-01-24 22:34:19 +04:00
}
2014-01-26 19:13:45 +04:00
{
gs_unref_ptrarray GPtrArray * lines = NULL ;
if ( ! yum_context_command ( yumctx , " run " , & lines ,
cancellable , error ) )
goto out ;
}
if ( ! yum_context_close ( yumctx , cancellable , error ) )
2014-01-24 22:34:19 +04:00
goto out ;
ret = TRUE ;
out :
return ret ;
}
int
main ( int argc ,
char * * argv )
{
GError * local_error = NULL ;
GError * * error = & local_error ;
GCancellable * cancellable = NULL ;
GOptionContext * context = g_option_context_new ( " - Run yum and commit the result to an OSTree repository " ) ;
const char * cmd ;
const char * ref ;
2014-02-13 03:26:31 +04:00
JsonNode * treefile_root = NULL ;
JsonObject * treefile = NULL ;
2014-02-26 02:07:59 +04:00
JsonArray * internal_postprocessing = NULL ;
JsonArray * units = NULL ;
2014-02-13 03:26:31 +04:00
guint len ;
2014-01-24 22:34:19 +04:00
gs_free char * ref_unix = NULL ;
gs_unref_object GFile * cachedir = NULL ;
gs_unref_object GFile * yumroot = NULL ;
gs_unref_object GFile * targetroot = NULL ;
gs_unref_object GFile * yumroot_varcache = NULL ;
2014-01-29 23:37:44 +04:00
gs_unref_object OstreeRepo * repo = NULL ;
2014-02-13 03:26:31 +04:00
gs_unref_ptrarray GPtrArray * bootstrap_packages = NULL ;
gs_unref_ptrarray GPtrArray * packages = NULL ;
gs_unref_object GFile * treefile_path = NULL ;
gs_unref_object JsonParser * treefile_parser = NULL ;
2014-01-24 22:34:19 +04:00
g_option_context_add_main_entries ( context , option_entries , NULL ) ;
if ( ! g_option_context_parse ( context , & argc , & argv , error ) )
goto out ;
2014-02-13 03:26:31 +04:00
if ( argc < 3 )
2014-01-24 22:34:19 +04:00
{
2014-02-13 03:26:31 +04:00
g_printerr ( " usage: %s create treefile.json \n " , argv [ 0 ] ) ;
2014-01-24 22:34:19 +04:00
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Option processing failed " ) ;
goto out ;
}
cmd = argv [ 1 ] ;
if ( strcmp ( cmd , " create " ) ! = 0 )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Unknown command '%s' " , cmd ) ;
goto out ;
}
2014-02-13 03:26:31 +04:00
treefile_path = g_file_new_for_path ( argv [ 2 ] ) ;
2014-01-24 22:34:19 +04:00
2014-02-13 03:26:31 +04:00
if ( opt_workdir & & chdir ( opt_workdir ) ! = 0 )
2014-01-24 22:34:19 +04:00
{
2014-02-13 03:26:31 +04:00
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Failed to chdir to '%s': %s " ,
opt_workdir , strerror ( errno ) ) ;
goto out ;
2014-01-24 22:34:19 +04:00
}
2014-02-13 03:26:31 +04:00
treefile_parser = json_parser_new ( ) ;
if ( ! json_parser_load_from_file ( treefile_parser ,
gs_file_get_path_cached ( treefile_path ) ,
error ) )
goto out ;
treefile_root = json_parser_get_root ( treefile_parser ) ;
if ( ! JSON_NODE_HOLDS_OBJECT ( treefile_root ) )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Treefile root is not an object " ) ;
goto out ;
}
treefile = json_node_get_object ( treefile_root ) ;
2014-01-24 22:34:19 +04:00
cachedir = g_file_new_for_path ( " cache " ) ;
if ( ! gs_file_ensure_directory ( cachedir , TRUE , cancellable , error ) )
goto out ;
yumroot = g_file_get_child ( cachedir , " yum " ) ;
if ( ! gs_shutil_rm_rf ( yumroot , cancellable , error ) )
goto out ;
2014-02-26 02:07:59 +04:00
targetroot = g_file_resolve_relative_path ( cachedir , " rootfs " ) ;
2014-01-24 22:34:19 +04:00
2014-02-13 03:26:31 +04:00
ref = object_require_string_member ( treefile , " ref " , error ) ;
if ( ! ref )
goto out ;
2014-01-26 19:13:45 +04:00
ref_unix = g_strdelimit ( g_strdup ( ref ) , " / " , ' _ ' ) ;
2014-01-24 22:34:19 +04:00
2014-02-13 03:26:31 +04:00
bootstrap_packages = g_ptr_array_new ( ) ;
packages = g_ptr_array_new ( ) ;
2014-01-24 22:34:19 +04:00
2014-02-13 03:26:31 +04:00
if ( ! append_string_array_to ( treefile , " bootstrap_packages " , bootstrap_packages , error ) )
goto out ;
g_ptr_array_add ( bootstrap_packages , NULL ) ;
if ( ! append_string_array_to ( treefile , " packages " , packages , error ) )
goto out ;
g_ptr_array_add ( packages , NULL ) ;
2014-01-26 19:13:45 +04:00
2014-01-24 22:34:19 +04:00
{
gs_free char * cached_packageset_name =
g_strconcat ( " packageset- " , ref_unix , " .txt " , NULL ) ;
gs_unref_object GFile * rpmtextlist_path =
g_file_resolve_relative_path ( cachedir , cached_packageset_name ) ;
gs_free char * cached_packageset_name_new =
g_strconcat ( cached_packageset_name , " .new " , NULL ) ;
gs_unref_object GFile * rpmtextlist_path_new =
g_file_resolve_relative_path ( cachedir , cached_packageset_name_new ) ;
2014-01-26 19:13:45 +04:00
GPtrArray * repoquery_argv = g_ptr_array_new_with_free_func ( g_free ) ;
gs_unref_object GSSubprocessContext * repoquery_proc_ctx = NULL ;
gs_unref_object GSSubprocess * repoquery_proc = NULL ;
guint i ;
2014-01-31 04:04:58 +04:00
GString * repoquery_arg_string = NULL ;
gboolean first = TRUE ;
2014-01-26 19:13:45 +04:00
g_ptr_array_add ( repoquery_argv , g_strdup ( PKGLIBDIR " /repoquery-sorted " ) ) ;
2014-02-26 02:07:59 +04:00
if ( ! append_repo_and_cache_opts ( treefile , cachedir , repoquery_argv ,
cancellable , error ) )
2014-02-13 03:26:31 +04:00
goto out ;
2014-01-26 19:13:45 +04:00
g_ptr_array_add ( repoquery_argv , g_strdup ( " --recursive " ) ) ;
g_ptr_array_add ( repoquery_argv , g_strdup ( " --requires " ) ) ;
g_ptr_array_add ( repoquery_argv , g_strdup ( " --resolve " ) ) ;
2014-01-31 04:04:58 +04:00
repoquery_arg_string = g_string_new ( " " ) ;
2014-02-13 03:26:31 +04:00
for ( i = 0 ; i < packages - > len ; i + + )
2014-01-26 19:13:45 +04:00
{
2014-02-13 03:26:31 +04:00
const char * package = packages - > pdata [ i ] ;
2014-01-26 19:13:45 +04:00
if ( ! package )
continue ;
g_ptr_array_add ( repoquery_argv , g_strdup ( package ) ) ;
2014-01-31 04:04:58 +04:00
if ( first )
first = FALSE ;
else
g_string_append_c ( repoquery_arg_string , ' ' ) ;
g_string_append ( repoquery_arg_string , package ) ;
2014-01-26 19:13:45 +04:00
}
2014-01-31 04:04:58 +04:00
g_print ( " Running repoquery: %s \n " , repoquery_arg_string - > str ) ;
g_string_free ( repoquery_arg_string , TRUE ) ;
2014-01-26 19:13:45 +04:00
g_ptr_array_add ( repoquery_argv , NULL ) ;
2014-01-24 22:34:19 +04:00
2014-01-26 19:13:45 +04:00
repoquery_proc_ctx = gs_subprocess_context_new ( ( char * * ) repoquery_argv - > pdata ) ;
gs_subprocess_context_set_stdout_file_path ( repoquery_proc_ctx , gs_file_get_path_cached ( rpmtextlist_path_new ) ) ;
gs_subprocess_context_set_stderr_disposition ( repoquery_proc_ctx , GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT ) ;
g_print ( " Resolving dependencies... \n " ) ;
repoquery_proc = gs_subprocess_new ( repoquery_proc_ctx , cancellable , error ) ;
if ( ! repoquery_proc )
goto out ;
if ( ! gs_subprocess_wait_sync_check ( repoquery_proc , cancellable , error ) )
2014-01-24 22:34:19 +04:00
goto out ;
if ( g_file_query_exists ( rpmtextlist_path , NULL ) )
{
GError * temp_error = NULL ;
gs_unref_object GSSubprocess * diff_proc = NULL ;
2014-01-25 19:37:48 +04:00
gboolean differs = FALSE ;
2014-01-24 22:34:19 +04:00
g_print ( " Comparing diff of previous tree \n " ) ;
diff_proc =
gs_subprocess_new_simple_argl ( GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT ,
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT ,
cancellable ,
error ,
" diff " , " -u " ,
gs_file_get_path_cached ( rpmtextlist_path ) ,
gs_file_get_path_cached ( rpmtextlist_path_new ) ,
NULL ) ;
if ( ! gs_subprocess_wait_sync_check ( diff_proc , cancellable , & temp_error ) )
{
int ecode ;
if ( temp_error - > domain ! = G_SPAWN_EXIT_ERROR )
{
g_propagate_error ( error , temp_error ) ;
goto out ;
}
ecode = temp_error - > code ;
g_assert ( ecode ! = 0 ) ;
if ( ecode = = 1 )
differs = TRUE ;
else
{
g_propagate_error ( error , temp_error ) ;
goto out ;
}
}
if ( ! differs )
{
g_print ( " No changes in package set \n " ) ;
2014-01-25 19:37:48 +04:00
if ( ! gs_file_unlink ( rpmtextlist_path_new , cancellable , error ) )
goto out ;
2014-01-24 22:34:19 +04:00
goto out ;
}
}
2014-01-25 19:37:48 +04:00
else
{
g_print ( " No previous diff file found at '%s' \n " ,
gs_file_get_path_cached ( rpmtextlist_path ) ) ;
}
2014-01-24 22:34:19 +04:00
2014-01-26 19:13:45 +04:00
/* Ensure we have enough to modify NSS */
2014-02-26 02:07:59 +04:00
if ( ! yuminstall ( treefile , yumroot , cachedir ,
2014-02-13 03:26:31 +04:00
( char * * ) bootstrap_packages - > pdata ,
cancellable , error ) )
goto out ;
2014-01-26 19:13:45 +04:00
/* Prepare NSS configuration; this needs to be done
before any invocations of " useradd " in % post */
{
gs_unref_object GFile * yumroot_passwd =
g_file_resolve_relative_path ( yumroot , " usr/lib/passwd " ) ;
gs_unref_object GFile * yumroot_group =
g_file_resolve_relative_path ( yumroot , " usr/lib/group " ) ;
gs_unref_object GFile * yumroot_etc =
g_file_resolve_relative_path ( yumroot , " etc " ) ;
if ( ! g_file_replace_contents ( yumroot_passwd , " " , 0 , NULL , FALSE , 0 ,
NULL , cancellable , error ) )
goto out ;
if ( ! g_file_replace_contents ( yumroot_group , " " , 0 , NULL , FALSE , 0 ,
NULL , cancellable , error ) )
goto out ;
if ( ! replace_nsswitch ( yumroot_etc , cancellable , error ) )
goto out ;
}
{
2014-02-26 02:07:59 +04:00
if ( ! yuminstall ( treefile , yumroot , cachedir ,
2014-02-13 03:26:31 +04:00
( char * * ) packages - > pdata ,
2014-01-26 19:13:45 +04:00
cancellable , error ) )
goto out ;
}
ref_unix = g_strdelimit ( g_strdup ( ref ) , " / " , ' _ ' ) ;
2014-01-30 03:37:05 +04:00
if ( g_strcmp0 ( g_getenv ( " RPM_OSTREE_BREAK " ) , " post-yum " ) = = 0 )
goto out ;
2014-01-30 03:12:50 +04:00
if ( ! rpmostree_postprocess ( yumroot , cancellable , error ) )
goto out ;
2014-02-26 02:07:59 +04:00
if ( json_object_has_member ( treefile , " postprocess " ) )
internal_postprocessing = json_object_get_array_member ( treefile , " postprocess " ) ;
2014-02-13 03:26:31 +04:00
if ( internal_postprocessing )
len = json_array_get_length ( internal_postprocessing ) ;
else
len = 0 ;
for ( i = 0 ; i < len ; i + + )
2014-01-30 03:12:50 +04:00
{
gs_unref_object GFile * pkglibdir = g_file_new_for_path ( PKGLIBDIR ) ;
gs_unref_object GFile * pkglibdir_posts = g_file_get_child ( pkglibdir , " postprocessing " ) ;
2014-02-13 03:26:31 +04:00
gs_unref_object GFile * post_path = NULL ;
const char * post_name = array_require_string_element ( internal_postprocessing , i , error ) ;
if ( ! post_name )
goto out ;
post_path = g_file_get_child ( pkglibdir_posts , post_name ) ;
2014-01-30 03:12:50 +04:00
g_print ( " Running internal postprocessing command '%s' \n " ,
gs_file_get_path_cached ( post_path ) ) ;
if ( ! gs_subprocess_simple_run_sync ( gs_file_get_path_cached ( yumroot ) ,
GS_SUBPROCESS_STREAM_DISPOSITION_NULL ,
cancellable , error ,
gs_file_get_path_cached ( post_path ) ,
NULL ) )
goto out ;
}
2014-02-26 02:07:59 +04:00
if ( json_object_has_member ( treefile , " units " ) )
units = json_object_get_array_member ( treefile , " units " ) ;
2014-01-30 03:12:50 +04:00
2014-02-13 03:26:31 +04:00
if ( units )
len = json_array_get_length ( units ) ;
else
len = 0 ;
{
gs_unref_object GFile * multiuser_wants_dir =
g_file_resolve_relative_path ( yumroot , " usr/etc/systemd/system/multi-user.target.wants " ) ;
2014-02-28 03:16:12 +04:00
if ( ! gs_file_ensure_directory ( multiuser_wants_dir , TRUE , cancellable , error ) )
goto out ;
2014-02-13 03:26:31 +04:00
for ( i = 0 ; i < len ; i + + )
{
const char * unitname = array_require_string_element ( units , i , error ) ;
gs_unref_object GFile * unit_link_target = NULL ;
gs_free char * symlink_target = NULL ;
2014-01-30 03:12:50 +04:00
2014-02-13 03:26:31 +04:00
if ( ! unitname )
goto out ;
g_print ( " Adding %s to multi-user.target.wants \n " , unitname ) ;
symlink_target = g_strconcat ( " /usr/lib/systemd/system/ " , unitname , NULL ) ;
unit_link_target = g_file_get_child ( multiuser_wants_dir , unitname ) ;
if ( ! g_file_make_symbolic_link ( unit_link_target , symlink_target ,
cancellable , error ) )
goto out ;
}
}
{
gs_unref_object GFile * target_treefile_dir_path =
g_file_resolve_relative_path ( yumroot , " usr/share/rpm-ostree " ) ;
gs_unref_object GFile * target_treefile_path =
g_file_get_child ( target_treefile_dir_path , " treefile.json " ) ;
if ( ! gs_file_ensure_directory ( target_treefile_dir_path , TRUE ,
cancellable , error ) )
goto out ;
g_print ( " Copying '%s' to '%s' \n " ,
gs_file_get_path_cached ( treefile_path ) ,
gs_file_get_path_cached ( target_treefile_path ) ) ;
if ( ! g_file_copy ( treefile_path , target_treefile_path ,
G_FILE_COPY_TARGET_DEFAULT_PERMS ,
cancellable , NULL , NULL , error ) )
goto out ;
}
2014-01-26 19:13:45 +04:00
{
2014-01-29 23:37:44 +04:00
gs_unref_object GFile * repo_path = g_file_new_for_path ( " repo " ) ;
repo = ostree_repo_new ( repo_path ) ;
2014-01-26 19:13:45 +04:00
}
2014-01-29 23:37:44 +04:00
if ( ! ostree_repo_open ( repo , cancellable , error ) )
goto out ;
2014-01-26 19:13:45 +04:00
2014-02-13 03:26:31 +04:00
if ( ! rpmostree_commit ( yumroot , repo , ref , json_object_get_string_member ( treefile , " gpg_key " ) ,
json_object_get_boolean_member ( treefile , " selinux " ) ,
2014-01-30 03:12:50 +04:00
cancellable , error ) )
2014-01-29 23:37:44 +04:00
goto out ;
2014-01-30 03:12:50 +04:00
2014-01-26 19:13:45 +04:00
if ( ! gs_file_rename ( rpmtextlist_path_new , rpmtextlist_path ,
cancellable , error ) )
2014-01-24 22:34:19 +04:00
goto out ;
}
g_print ( " Complete \n " ) ;
out :
if ( local_error ! = NULL )
{
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 */
}
g_printerr ( " %serror: %s%s \n " , prefix , suffix , local_error - > message ) ;
g_error_free ( local_error ) ;
return 2 ;
}
else
return 0 ;
}