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-05-03 18:05:43 +04:00
# include <gio/gunixoutputstream.h>
2014-11-10 04:28:10 +03:00
# include <stdio.h>
2014-01-24 22:34:19 +04:00
2014-05-26 23:05:08 +04:00
# include "rpmostree-compose-builtins.h"
2014-05-16 01:46:51 +04:00
# include "rpmostree-util.h"
2014-11-13 22:53:43 +03:00
# include "rpmostree-json-parsing.h"
2014-11-12 02:40:58 +03:00
# include "rpmostree-cleanup.h"
2014-10-31 06:49:18 +03:00
# include "rpmostree-treepkgdiff.h"
compose: Introduce a little 'libcontainer', use it for the post script
The current motivation for this is that
https://github.com/fedora-infra/fedmsg-atomic-composer
started using mock --new-chroot (which uses systemd-nspawn) to run
rpm-ostree, which in turn uses systemd-nspawn to run the post script.
Now systemd-nspawn is not really nestable (it wants to link up
journald, resolv.conf handling, etc).
First, dropping nspawn and going to raw containers fixes the nesting
problem.
Second, we don't need all the features of systemd-nspawn. We are ok
with log messages going to stdout, and we don't use networking, so no
resolv.conf is needed.
Third, this sets a bit of a stage for more sandboxing internally when
run on real systems. I already have a prototype branch which runs
librepo as an unprivileged user, that could be combined with this for
even stronger security.
Why not use systemd? Well...I'm still debating that. But the core
problem is systemd isn't a library in the C sense - to use its
sandboxing features we have to use unit files. It's harder to have a
daemon that looks like a single service from a management perspective,
but uses sandboxing internally.
2014-11-20 02:30:00 +03:00
# include "rpmostree-libcontainer.h"
2014-01-29 23:37:44 +04:00
# include "rpmostree-postprocess.h"
2014-01-24 22:34:19 +04:00
# include "libgsystem.h"
static char * opt_workdir ;
2014-07-02 06:07:56 +04:00
static gboolean opt_workdir_tmpfs ;
2014-05-03 14:55:35 +04:00
static char * opt_cachedir ;
static char * opt_proxy ;
2014-10-24 01:14:14 +04:00
static char * opt_output_repodata_dir ;
2014-10-14 17:32:29 +04:00
static char * * opt_metadata_strings ;
2014-05-03 14:55:35 +04:00
static char * opt_repo ;
2014-07-11 22:02:45 +04:00
static char * * opt_override_pkg_repos ;
2014-05-03 18:05:43 +04:00
static gboolean opt_print_only ;
2014-01-24 22:34:19 +04:00
static GOptionEntry option_entries [ ] = {
2014-10-14 17:32:29 +04:00
{ " add-metadata-string " , 0 , 0 , G_OPTION_ARG_STRING_ARRAY , & opt_metadata_strings , " Append given key and value (in string format) to metadata " , " KEY=VALUE " } ,
2014-05-03 14:55:35 +04:00
{ " workdir " , 0 , 0 , G_OPTION_ARG_STRING , & opt_workdir , " Working directory " , " WORKDIR " } ,
2014-07-02 06:07:56 +04:00
{ " workdir-tmpfs " , 0 , 0 , G_OPTION_ARG_NONE , & opt_workdir_tmpfs , " Use tmpfs for working state " , NULL } ,
2014-10-24 01:14:14 +04:00
{ " output-repodata-dir " , 0 , 0 , G_OPTION_ARG_STRING , & opt_output_repodata_dir , " Save downloaded repodata in DIR " , " DIR " } ,
2014-05-03 14:55:35 +04:00
{ " cachedir " , 0 , 0 , G_OPTION_ARG_STRING , & opt_cachedir , " Cached state " , " CACHEDIR " } ,
{ " repo " , ' r ' , 0 , G_OPTION_ARG_STRING , & opt_repo , " Path to OSTree repository " , " REPO " } ,
2014-07-11 22:02:45 +04:00
{ " add-override-pkg-repo " , 0 , 0 , G_OPTION_ARG_STRING_ARRAY , & opt_override_pkg_repos , " Include an additional package repository from DIRECTORY " , " DIRECTORY " } ,
2014-05-03 14:55:35 +04:00
{ " proxy " , 0 , 0 , G_OPTION_ARG_STRING , & opt_proxy , " HTTP proxy " , " PROXY " } ,
2014-05-03 18:05:43 +04:00
{ " print-only " , 0 , 0 , G_OPTION_ARG_NONE , & opt_print_only , " Just expand any includes and print treefile " , NULL } ,
2014-01-24 22:34:19 +04:00
{ NULL }
} ;
2014-05-18 22:13:31 +04:00
typedef struct {
GPtrArray * treefile_context_dirs ;
2014-11-17 04:05:47 +03:00
GFile * workdir ;
2014-09-07 20:38:34 +04:00
GBytes * serialized_treefile ;
2014-05-18 22:13:31 +04:00
} RpmOstreeTreeComposeContext ;
2014-02-26 02:07:59 +04:00
static char *
2014-11-10 04:28:10 +03:00
strv_join_shell_quote ( char * * argv )
2014-02-26 02:07:59 +04:00
{
GString * ret = g_string_new ( " " ) ;
char * * strviter ;
for ( strviter = argv ; strviter & & * strviter ; strviter + + )
{
gs_free char * quoted = g_shell_quote ( * strviter ) ;
2014-05-06 01:57:16 +04:00
g_string_append_c ( ret , ' ' ) ;
2014-02-26 02:07:59 +04:00
g_string_append ( ret , quoted ) ;
}
return g_string_free ( ret , FALSE ) ;
}
2014-01-26 19:13:45 +04:00
typedef struct {
2014-11-10 04:28:10 +03:00
gboolean running ;
pid_t pid ;
2014-01-26 19:13:45 +04:00
GFile * tmp_reposdir_path ;
GDataOutputStream * stdin ;
} 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-11-10 04:28:10 +03:00
if ( yumctx - > running )
2014-01-26 19:13:45 +04:00
{
if ( yumctx - > stdin )
{
if ( ! g_output_stream_close ( ( GOutputStream * ) yumctx - > stdin , cancellable , error ) )
goto out ;
g_clear_object ( & yumctx - > stdin ) ;
}
2014-11-10 04:28:10 +03:00
2014-11-20 01:37:15 +03:00
g_print ( " Waiting for yum... \n " ) ;
if ( ! _rpmostree_sync_wait_on_pid ( yumctx - > pid , error ) )
goto out ;
2014-11-10 04:28:10 +03:00
2014-11-20 01:37:15 +03:00
g_print ( " Waiting for yum [OK] \n " ) ;
2014-01-26 19:13:45 +04:00
}
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
2014-05-18 22:13:31 +04:00
append_repo_and_cache_opts ( RpmOstreeTreeComposeContext * self ,
JsonObject * treedata ,
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 ;
2014-05-18 22:13:31 +04:00
guint i ;
2014-07-11 22:02:45 +04:00
char * * iter ;
2014-02-26 02:07:59 +04:00
gs_unref_object GFile * yumcache_lookaside = NULL ;
gs_unref_object GFile * repos_tmpdir = NULL ;
2014-07-11 22:02:45 +04:00
gs_unref_ptrarray GPtrArray * reposdir_args = g_ptr_array_new_with_free_func ( g_free ) ;
2014-02-26 02:07:59 +04:00
2014-10-24 01:14:14 +04:00
if ( opt_output_repodata_dir )
yumcache_lookaside = g_file_new_for_path ( opt_output_repodata_dir ) ;
else
{
2014-11-17 04:05:47 +03:00
yumcache_lookaside = g_file_resolve_relative_path ( self - > workdir , " yum-cache " ) ;
2014-10-24 01:14:14 +04:00
if ( ! gs_file_ensure_directory ( yumcache_lookaside , TRUE , cancellable , error ) )
goto out ;
}
2014-11-17 04:05:47 +03:00
repos_tmpdir = g_file_resolve_relative_path ( self - > workdir , " tmp-repos " ) ;
2014-02-26 02:07:59 +04:00
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 " ) ) ;
2014-05-18 17:29:51 +04:00
{
const char * proxy ;
if ( opt_proxy )
proxy = opt_proxy ;
else
proxy = g_getenv ( " http_proxy " ) ;
2014-07-11 22:02:45 +04:00
2014-05-18 17:29:51 +04:00
if ( proxy )
g_ptr_array_add ( args , g_strconcat ( " --setopt=proxy= " , proxy , NULL ) ) ;
}
2014-05-03 14:55:35 +04:00
2014-07-11 22:02:45 +04:00
g_ptr_array_add ( args , g_strdup ( " --disablerepo=* " ) ) ;
/* Add the directory for each treefile to the reposdir argument */
for ( i = 0 ; i < self - > treefile_context_dirs - > len ; i + + )
{
GFile * contextdir = self - > treefile_context_dirs - > pdata [ i ] ;
g_ptr_array_add ( reposdir_args , g_file_get_path ( contextdir ) ) ;
}
/* Process local overrides */
for ( iter = opt_override_pkg_repos ; iter & & * iter ; iter + + )
{
const char * repodir = * iter ;
gs_free char * bn = g_path_get_basename ( repodir ) ;
gs_free char * reponame = g_strconcat ( " rpm-ostree-override- " , repodir , NULL ) ;
gs_free char * baseurl = g_strconcat ( " file:// " , repodir , NULL ) ;
gs_free char * tmprepo_filename = g_strconcat ( reponame , " .repo " , NULL ) ;
gs_unref_object GFile * tmprepo_path = g_file_get_child ( repos_tmpdir , tmprepo_filename ) ;
__attribute__ ( ( cleanup ( cleanup_keyfile_unref ) ) ) GKeyFile * keyfile = NULL ;
gs_free char * data = NULL ;
gsize len ;
keyfile = g_key_file_new ( ) ;
g_key_file_set_string ( keyfile , reponame , " name " , reponame ) ;
g_key_file_set_string ( keyfile , reponame , " baseurl " , baseurl ) ;
data = g_key_file_to_data ( keyfile , & len , NULL ) ;
if ( ! g_file_replace_contents ( tmprepo_path , data , len , NULL , FALSE , 0 , NULL ,
cancellable , error ) )
goto out ;
g_ptr_array_add ( args , g_strconcat ( " --enablerepo= " , reponame , NULL ) ) ;
}
if ( opt_override_pkg_repos )
g_ptr_array_add ( reposdir_args , g_file_get_path ( repos_tmpdir ) ) ;
2014-05-18 22:13:31 +04:00
{
gboolean first = TRUE ;
2014-07-11 22:02:45 +04:00
GString * reposdir_value = g_string_new ( " --setopt=reposdir= " ) ;
for ( i = 0 ; i < reposdir_args - > len ; i + + )
2014-05-18 22:13:31 +04:00
{
2014-07-11 22:02:45 +04:00
const char * reponame = reposdir_args - > pdata [ i ] ;
2014-05-18 22:13:31 +04:00
if ( first )
first = FALSE ;
else
g_string_append_c ( reposdir_value , ' , ' ) ;
2014-07-11 22:02:45 +04:00
g_string_append ( reposdir_value , reponame ) ;
2014-05-18 22:13:31 +04:00
}
g_ptr_array_add ( args , g_string_free ( reposdir_value , FALSE ) ) ;
}
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 + + )
{
2014-11-13 22:53:43 +03:00
const char * reponame = _rpmostree_jsonutil_array_require_string_element ( enable_repos , i , error ) ;
2014-02-13 03:26:31 +04:00
if ( ! reponame )
goto out ;
g_ptr_array_add ( args , g_strconcat ( " --enablerepo= " , reponame , NULL ) ) ;
}
}
2014-01-31 04:04:58 +04:00
2014-06-19 19:57:33 +04:00
g_ptr_array_add ( args , g_strdup ( " --setopt=keepcache=0 " ) ) ;
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-05-18 22:13:31 +04:00
yum_context_new ( RpmOstreeTreeComposeContext * self ,
JsonObject * treedata ,
2014-02-13 03:26:31 +04:00
GFile * yumroot ,
2014-01-26 19:13:45 +04:00
GCancellable * cancellable ,
GError * * error )
{
gboolean success = FALSE ;
YumContext * yumctx = NULL ;
2014-11-14 04:39:42 +03:00
JsonNode * install_langs_n ;
2014-01-26 19:13:45 +04:00
GPtrArray * yum_argv = g_ptr_array_new_with_free_func ( g_free ) ;
2014-11-10 04:28:10 +03:00
pid_t child ;
int clone_flags = SIGCHLD | CLONE_NEWNS | CLONE_NEWPID ;
int pipefds [ 2 ] ;
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-11-17 04:05:47 +03:00
if ( ! append_repo_and_cache_opts ( self , treedata , yum_argv ,
2014-02-26 02:07:59 +04:00
cancellable , error ) )
2014-02-13 03:26:31 +04:00
goto out ;
2014-01-31 04:04:58 +04:00
2014-11-14 04:39:42 +03:00
install_langs_n = json_object_get_member ( treedata , " install-langs " ) ;
if ( install_langs_n ! = NULL )
{
JsonArray * instlangs_a = json_node_get_array ( install_langs_n ) ;
guint len = json_array_get_length ( instlangs_a ) ;
guint i ;
GString * opt = g_string_new ( " --setopt=override_install_langs= " ) ;
for ( i = 0 ; i < len ; i + + )
{
g_string_append ( opt , json_array_get_string_element ( instlangs_a , i ) ) ;
if ( i < len - 1 )
g_string_append_c ( opt , ' , ' ) ;
}
g_ptr_array_add ( yum_argv , opt - > str ) ;
g_string_free ( opt , FALSE ) ;
}
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 ) ;
2014-11-10 04:28:10 +03:00
if ( ! g_unix_open_pipe ( pipefds , FD_CLOEXEC , error ) )
goto out ;
2014-01-24 22:34:19 +04:00
2014-11-10 04:28:10 +03:00
if ( ( child = syscall ( __NR_clone , clone_flags , NULL ) ) < 0 )
{
2014-11-20 02:32:08 +03:00
_rpmostree_set_error_from_errno ( error , errno ) ;
2014-11-10 04:28:10 +03:00
goto out ;
}
if ( child = = 0 )
{
if ( dup2 ( pipefds [ 0 ] , 0 ) ! = 0 )
2014-11-20 05:39:17 +03:00
_rpmostree_perror_fatal ( " dup2() " ) ;
2014-11-10 04:28:10 +03:00
/* This is used at the moment, but eventually I'd like to teach
* Fedora ' s kernel . spec to e . g . skip making an initramfs ,
* because we ' re going to be making one .
*/
setenv ( " OSTREE_KERNEL_INSTALL_NOOP " , " 1 " , TRUE ) ;
/* See fedora's kernel.spec; we don't need this because ostree
* itself takes care of dedup - via - hardlink .
*/
setenv ( " HARDLINK " , " no " , TRUE ) ;
/* Turn off setuid binaries, we shouldn't need them */
compose: Introduce a little 'libcontainer', use it for the post script
The current motivation for this is that
https://github.com/fedora-infra/fedmsg-atomic-composer
started using mock --new-chroot (which uses systemd-nspawn) to run
rpm-ostree, which in turn uses systemd-nspawn to run the post script.
Now systemd-nspawn is not really nestable (it wants to link up
journald, resolv.conf handling, etc).
First, dropping nspawn and going to raw containers fixes the nesting
problem.
Second, we don't need all the features of systemd-nspawn. We are ok
with log messages going to stdout, and we don't use networking, so no
resolv.conf is needed.
Third, this sets a bit of a stage for more sandboxing internally when
run on real systems. I already have a prototype branch which runs
librepo as an unprivileged user, that could be combined with this for
even stronger security.
Why not use systemd? Well...I'm still debating that. But the core
problem is systemd isn't a library in the C sense - to use its
sandboxing features we have to use unit files. It's harder to have a
daemon that looks like a single service from a management perspective,
but uses sandboxing internally.
2014-11-20 02:30:00 +03:00
if ( _rpmostree_libcontainer_get_available ( ) )
{
if ( mount ( NULL , " / " , " none " , MS_PRIVATE | MS_REMOUNT | MS_NOSUID , NULL ) < 0 )
_rpmostree_perror_fatal ( " mount(/, MS_PRIVATE | MS_NOSUID) " ) ;
}
2014-11-10 04:28:10 +03:00
if ( execvp ( " yum " , ( char * * ) yum_argv - > pdata ) < 0 )
2014-11-20 05:39:17 +03:00
_rpmostree_perror_fatal ( " execvp " ) ;
2014-11-10 04:28:10 +03:00
}
2014-01-24 22:34:19 +04:00
2014-11-10 04:28:10 +03:00
( void ) close ( pipefds [ 0 ] ) ;
2014-01-24 22:34:19 +04:00
2014-11-10 04:28:10 +03:00
{
gs_free char * cmdline = strv_join_shell_quote ( ( char * * ) yum_argv - > pdata ) ;
g_print ( " Starting %s \n " , cmdline ) ;
}
2014-05-05 16:34:45 +04:00
yumctx = g_new0 ( YumContext , 1 ) ;
2014-11-10 04:28:10 +03:00
yumctx - > running = TRUE ;
yumctx - > pid = child ;
2014-05-05 16:34:45 +04:00
2014-02-26 02:07:59 +04:00
{
2014-11-10 04:28:10 +03:00
gs_unref_object GOutputStream * yumproc_stdin = g_unix_output_stream_new ( pipefds [ 1 ] , TRUE ) ;
yumctx - > stdin = ( GDataOutputStream * ) g_data_output_stream_new ( yumproc_stdin ) ;
2014-02-26 02:07:59 +04:00
}
2014-01-26 19:13:45 +04:00
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 ;
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-05-18 22:13:31 +04:00
yuminstall ( RpmOstreeTreeComposeContext * self ,
JsonObject * treedata ,
2014-02-13 03:26:31 +04:00
GFile * yumroot ,
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-11-17 04:05:47 +03:00
yumctx = yum_context_new ( self , treedata , yumroot , 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 ;
}
2014-05-03 18:05:43 +04:00
static gboolean
2014-05-18 22:13:31 +04:00
process_includes ( RpmOstreeTreeComposeContext * self ,
GFile * treefile_path ,
2014-05-03 18:05:43 +04:00
guint depth ,
JsonObject * root ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
const char * include_path ;
const guint maxdepth = 50 ;
if ( depth > maxdepth )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Exceeded maximum include depth of %u " , maxdepth ) ;
goto out ;
}
2014-05-18 22:13:31 +04:00
{
gs_unref_object GFile * parent = g_file_get_parent ( treefile_path ) ;
gboolean existed = FALSE ;
if ( self - > treefile_context_dirs - > len > 0 )
{
GFile * prev = self - > treefile_context_dirs - > pdata [ self - > treefile_context_dirs - > len - 1 ] ;
if ( g_file_equal ( parent , prev ) )
existed = TRUE ;
}
if ( ! existed )
{
g_ptr_array_add ( self - > treefile_context_dirs , parent ) ;
parent = NULL ; /* Transfer ownership */
}
}
2014-11-13 22:53:43 +03:00
if ( ! _rpmostree_jsonutil_object_get_optional_string_member ( root , " include " , & include_path , error ) )
2014-05-03 18:05:43 +04:00
goto out ;
if ( include_path )
{
gs_unref_object GFile * treefile_dirpath = g_file_get_parent ( treefile_path ) ;
gs_unref_object GFile * parent_path = g_file_resolve_relative_path ( treefile_dirpath , include_path ) ;
gs_unref_object JsonParser * parent_parser = json_parser_new ( ) ;
JsonNode * parent_rootval ;
JsonObject * parent_root ;
GList * members ;
GList * iter ;
if ( ! json_parser_load_from_file ( parent_parser ,
gs_file_get_path_cached ( parent_path ) ,
error ) )
goto out ;
parent_rootval = json_parser_get_root ( parent_parser ) ;
if ( ! JSON_NODE_HOLDS_OBJECT ( parent_rootval ) )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Treefile root is not an object " ) ;
goto out ;
}
parent_root = json_node_get_object ( parent_rootval ) ;
2014-05-18 22:13:31 +04:00
if ( ! process_includes ( self , parent_path , depth + 1 , parent_root ,
2014-05-03 18:05:43 +04:00
cancellable , error ) )
goto out ;
members = json_object_get_members ( parent_root ) ;
for ( iter = members ; iter ; iter = iter - > next )
{
const char * name = iter - > data ;
JsonNode * parent_val = json_object_get_member ( parent_root , name ) ;
JsonNode * val = json_object_get_member ( root , name ) ;
g_assert ( parent_val ) ;
if ( ! val )
json_object_set_member ( root , name , json_node_copy ( parent_val ) ) ;
else
{
JsonNodeType parent_type =
json_node_get_node_type ( parent_val ) ;
JsonNodeType child_type =
json_node_get_node_type ( val ) ;
if ( parent_type ! = child_type )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Conflicting element type of '%s' " ,
name ) ;
goto out ;
}
if ( child_type = = JSON_NODE_ARRAY )
{
JsonArray * parent_array = json_node_get_array ( parent_val ) ;
JsonArray * child_array = json_node_get_array ( val ) ;
JsonArray * new_child = json_array_new ( ) ;
guint i , len ;
len = json_array_get_length ( parent_array ) ;
for ( i = 0 ; i < len ; i + + )
json_array_add_element ( new_child , json_node_copy ( json_array_get_element ( parent_array , i ) ) ) ;
len = json_array_get_length ( child_array ) ;
for ( i = 0 ; i < len ; i + + )
json_array_add_element ( new_child , json_node_copy ( json_array_get_element ( child_array , i ) ) ) ;
json_object_set_array_member ( root , name , new_child ) ;
}
}
}
2014-09-07 20:38:34 +04:00
json_object_remove_member ( root , " include " ) ;
2014-05-03 18:05:43 +04:00
}
ret = TRUE ;
out :
return ret ;
}
2014-05-16 01:46:51 +04:00
static char *
cachedir_fssafe_key ( const char * primary_key )
{
GString * ret = g_string_new ( " " ) ;
for ( ; * primary_key ; primary_key + + )
{
const char c = * primary_key ;
if ( ! g_ascii_isprint ( c ) | | c = = ' - ' )
g_string_append_printf ( ret , " \\ %02x " , c ) ;
else if ( c = = ' / ' )
g_string_append_c ( ret , ' - ' ) ;
else
g_string_append_c ( ret , c ) ;
}
return g_string_free ( ret , FALSE ) ;
}
static GFile *
cachedir_keypath ( GFile * cachedir ,
const char * primary_key )
{
gs_free char * fssafe_key = cachedir_fssafe_key ( primary_key ) ;
return g_file_get_child ( cachedir , fssafe_key ) ;
}
static gboolean
cachedir_lookup_string ( GFile * cachedir ,
const char * key ,
char * * out_value ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
gs_free char * ret_value = NULL ;
if ( cachedir )
{
gs_unref_object GFile * keypath = cachedir_keypath ( cachedir , key ) ;
if ( ! _rpmostree_file_load_contents_utf8_allow_noent ( keypath , & ret_value ,
cancellable , error ) )
goto out ;
}
ret = TRUE ;
gs_transfer_out_value ( out_value , & ret_value ) ;
out :
return ret ;
}
static gboolean
cachedir_set_string ( GFile * cachedir ,
const char * key ,
const char * value ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
gs_unref_object GFile * keypath = NULL ;
if ( ! cachedir )
return TRUE ;
keypath = cachedir_keypath ( cachedir , key ) ;
if ( ! g_file_replace_contents ( keypath , value , strlen ( value ) , NULL ,
FALSE , 0 , NULL ,
cancellable , error ) )
goto out ;
ret = TRUE ;
out :
return ret ;
}
static gboolean
2014-09-07 20:38:34 +04:00
compute_checksum_for_compose ( RpmOstreeTreeComposeContext * self ,
JsonObject * treefile_rootval ,
2014-05-16 01:46:51 +04:00
GFile * yumroot ,
char * * out_checksum ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
gs_free char * ret_checksum = NULL ;
GChecksum * checksum = g_checksum_new ( G_CHECKSUM_SHA256 ) ;
{
gsize len ;
2014-09-07 20:38:34 +04:00
const guint8 * buf = g_bytes_get_data ( self - > serialized_treefile , & len ) ;
2014-05-16 01:46:51 +04:00
2014-09-07 20:38:34 +04:00
g_checksum_update ( checksum , buf , len ) ;
2014-05-16 01:46:51 +04:00
}
2014-06-06 22:51:18 +04:00
/* Query the generated rpmdb, to see if anything has changed. */
2014-05-16 01:46:51 +04:00
{
2014-10-31 06:49:18 +03:00
_cleanup_hysack_ HySack sack = NULL ;
_cleanup_hypackagelist_ HyPackageList pkglist = NULL ;
HyPackage pkg ;
guint i ;
if ( ! rpmostree_get_pkglist_for_root ( yumroot , & sack , & pkglist ,
cancellable , error ) )
2014-06-06 22:51:18 +04:00
{
2014-10-31 06:49:18 +03:00
g_prefix_error ( error , " Reading package set: " ) ;
2014-06-06 22:51:18 +04:00
goto out ;
}
2014-05-29 22:57:55 +04:00
2014-10-31 06:49:18 +03:00
FOR_PACKAGELIST ( pkg , pkglist , i )
2014-05-29 22:57:55 +04:00
{
2014-10-31 06:49:18 +03:00
gs_free char * nevra = hy_package_get_nevra ( pkg ) ;
g_checksum_update ( checksum , ( guint8 * ) nevra , strlen ( nevra ) ) ;
2014-05-29 22:57:55 +04:00
}
2014-05-16 01:46:51 +04:00
}
ret_checksum = g_strdup ( g_checksum_get_string ( checksum ) ) ;
ret = TRUE ;
gs_transfer_out_value ( out_checksum , & ret_checksum ) ;
out :
if ( checksum ) g_checksum_free ( checksum ) ;
return ret ;
}
2014-10-14 17:32:29 +04:00
static gboolean
parse_keyvalue_strings ( char * * strings ,
GVariant * * out_metadata ,
GError * * error )
{
gboolean ret = FALSE ;
char * * iter ;
gs_unref_variant_builder GVariantBuilder * builder = NULL ;
builder = g_variant_builder_new ( G_VARIANT_TYPE ( " a{sv} " ) ) ;
for ( iter = strings ; * iter ; iter + + )
{
const char * s ;
const char * eq ;
gs_free char * key = NULL ;
s = * iter ;
eq = strchr ( s , ' = ' ) ;
if ( ! eq )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Missing '=' in KEY=VALUE metadata '%s' " , s ) ;
goto out ;
}
key = g_strndup ( s , eq - s ) ;
g_variant_builder_add ( builder , " {sv} " , key ,
g_variant_new_string ( eq + 1 ) ) ;
}
ret = TRUE ;
* out_metadata = g_variant_builder_end ( builder ) ;
g_variant_ref_sink ( * out_metadata ) ;
out :
return ret ;
}
2014-03-22 23:05:41 +04:00
gboolean
2014-05-26 23:05:08 +04:00
rpmostree_compose_builtin_tree ( int argc ,
char * * argv ,
GCancellable * cancellable ,
GError * * error )
2014-01-24 22:34:19 +04:00
{
2014-03-22 23:05:41 +04:00
gboolean ret = FALSE ;
2014-01-24 22:34:19 +04:00
GOptionContext * context = g_option_context_new ( " - Run yum and commit the result to an OSTree repository " ) ;
const char * ref ;
2014-05-18 22:13:31 +04:00
RpmOstreeTreeComposeContext selfdata = { NULL , } ;
RpmOstreeTreeComposeContext * self = & selfdata ;
2014-05-03 18:05:43 +04:00
JsonNode * treefile_rootval = NULL ;
2014-02-13 03:26:31 +04:00
JsonObject * treefile = NULL ;
2014-01-24 22:34:19 +04:00
gs_free char * ref_unix = NULL ;
2014-05-16 01:46:51 +04:00
gs_free char * cachekey = NULL ;
gs_free char * cached_compose_checksum = NULL ;
gs_free char * new_compose_checksum = NULL ;
2014-01-24 22:34:19 +04:00
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 ;
2014-05-03 14:55:35 +04:00
gs_unref_object GFile * repo_path = NULL ;
2014-02-13 03:26:31 +04:00
gs_unref_object JsonParser * treefile_parser = NULL ;
2014-10-14 17:32:29 +04:00
gs_unref_variant GVariant * metadata = NULL ;
2014-05-03 14:55:35 +04:00
gboolean workdir_is_tmp = FALSE ;
2014-05-18 22:13:31 +04:00
self - > treefile_context_dirs = g_ptr_array_new_with_free_func ( ( GDestroyNotify ) g_object_unref ) ;
2014-01-24 22:34:19 +04:00
2014-11-24 20:34:45 +03:00
if ( ! rpmostree_option_context_parse ( context , option_entries , & argc , & argv , error ) )
2014-01-24 22:34:19 +04:00
goto out ;
2014-03-22 23:05:41 +04:00
if ( argc < 2 )
2014-01-24 22:34:19 +04:00
{
2014-05-26 23:05:08 +04:00
g_printerr ( " usage: rpm-ostree compose tree TREEFILE \n " ) ;
2014-01-24 22:34:19 +04:00
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Option processing failed " ) ;
goto out ;
}
2014-05-03 14:55:35 +04:00
if ( ! opt_repo )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" --repo must be specified " ) ;
goto out ;
}
2014-11-10 04:28:10 +03:00
/* Use a private mount namespace to avoid polluting the global
* namespace , and to ensure any tmpfs mounts get cleaned up if we
* exit unexpectedly .
*
* We also rely on this for the yum confinement .
*/
if ( unshare ( CLONE_NEWNS ) ! = 0 )
{
2014-11-20 02:32:08 +03:00
_rpmostree_set_prefix_error_from_errno ( error , errno , " unshare(CLONE_NEWNS): " ) ;
2014-11-10 04:28:10 +03:00
goto out ;
}
if ( mount ( NULL , " / " , " none " , MS_PRIVATE | MS_REC , NULL ) = = - 1 )
{
compose: Introduce a little 'libcontainer', use it for the post script
The current motivation for this is that
https://github.com/fedora-infra/fedmsg-atomic-composer
started using mock --new-chroot (which uses systemd-nspawn) to run
rpm-ostree, which in turn uses systemd-nspawn to run the post script.
Now systemd-nspawn is not really nestable (it wants to link up
journald, resolv.conf handling, etc).
First, dropping nspawn and going to raw containers fixes the nesting
problem.
Second, we don't need all the features of systemd-nspawn. We are ok
with log messages going to stdout, and we don't use networking, so no
resolv.conf is needed.
Third, this sets a bit of a stage for more sandboxing internally when
run on real systems. I already have a prototype branch which runs
librepo as an unprivileged user, that could be combined with this for
even stronger security.
Why not use systemd? Well...I'm still debating that. But the core
problem is systemd isn't a library in the C sense - to use its
sandboxing features we have to use unit files. It's harder to have a
daemon that looks like a single service from a management perspective,
but uses sandboxing internally.
2014-11-20 02:30:00 +03:00
/* This happens on RHEL6, not going to debug it further right now... */
if ( errno = = EINVAL )
_rpmostree_libcontainer_set_not_available ( ) ;
else
{
_rpmostree_set_prefix_error_from_errno ( error , errno , " mount(/, MS_PRIVATE): " ) ;
goto out ;
}
2014-11-10 04:28:10 +03:00
}
/* Mount several directories read only for protection from librpm
* and any stray code in yum / hawkey .
*/
compose: Introduce a little 'libcontainer', use it for the post script
The current motivation for this is that
https://github.com/fedora-infra/fedmsg-atomic-composer
started using mock --new-chroot (which uses systemd-nspawn) to run
rpm-ostree, which in turn uses systemd-nspawn to run the post script.
Now systemd-nspawn is not really nestable (it wants to link up
journald, resolv.conf handling, etc).
First, dropping nspawn and going to raw containers fixes the nesting
problem.
Second, we don't need all the features of systemd-nspawn. We are ok
with log messages going to stdout, and we don't use networking, so no
resolv.conf is needed.
Third, this sets a bit of a stage for more sandboxing internally when
run on real systems. I already have a prototype branch which runs
librepo as an unprivileged user, that could be combined with this for
even stronger security.
Why not use systemd? Well...I'm still debating that. But the core
problem is systemd isn't a library in the C sense - to use its
sandboxing features we have to use unit files. It's harder to have a
daemon that looks like a single service from a management perspective,
but uses sandboxing internally.
2014-11-20 02:30:00 +03:00
if ( _rpmostree_libcontainer_get_available ( ) )
{
struct stat stbuf ;
/* Protect /var/lib/rpm if (and only if) it's a regular directory.
This happens when you ' re running compose - tree from inside a
" mainline " system . On an rpm - ostree based system ,
/ var / lib / rpm - > / usr / share / rpm , which is already protected by a read - only
bind mount . */
if ( lstat ( " /var/lib/rpm " , & stbuf ) = = 0 & & S_ISDIR ( stbuf . st_mode ) )
{
if ( ! _rpmostree_libcontainer_bind_mount_readonly ( " /var/lib/rpm " , error ) )
goto out ;
}
2014-11-10 04:28:10 +03:00
compose: Introduce a little 'libcontainer', use it for the post script
The current motivation for this is that
https://github.com/fedora-infra/fedmsg-atomic-composer
started using mock --new-chroot (which uses systemd-nspawn) to run
rpm-ostree, which in turn uses systemd-nspawn to run the post script.
Now systemd-nspawn is not really nestable (it wants to link up
journald, resolv.conf handling, etc).
First, dropping nspawn and going to raw containers fixes the nesting
problem.
Second, we don't need all the features of systemd-nspawn. We are ok
with log messages going to stdout, and we don't use networking, so no
resolv.conf is needed.
Third, this sets a bit of a stage for more sandboxing internally when
run on real systems. I already have a prototype branch which runs
librepo as an unprivileged user, that could be combined with this for
even stronger security.
Why not use systemd? Well...I'm still debating that. But the core
problem is systemd isn't a library in the C sense - to use its
sandboxing features we have to use unit files. It's harder to have a
daemon that looks like a single service from a management perspective,
but uses sandboxing internally.
2014-11-20 02:30:00 +03:00
/* Protect the system's /etc and /usr */
if ( ! _rpmostree_libcontainer_bind_mount_readonly ( " /etc " , error ) )
goto out ;
if ( ! _rpmostree_libcontainer_bind_mount_readonly ( " /usr " , error ) )
goto out ;
}
2014-11-10 04:28:10 +03:00
2014-05-03 14:55:35 +04:00
repo_path = g_file_new_for_path ( opt_repo ) ;
repo = ostree_repo_new ( repo_path ) ;
if ( ! ostree_repo_open ( repo , cancellable , error ) )
goto out ;
2014-10-14 17:32:29 +04:00
2014-03-22 23:05:41 +04:00
treefile_path = g_file_new_for_path ( argv [ 1 ] ) ;
2014-01-24 22:34:19 +04:00
2014-05-03 14:55:35 +04:00
if ( opt_workdir )
{
2014-11-17 04:05:47 +03:00
self - > workdir = g_file_new_for_path ( opt_workdir ) ;
2014-05-03 14:55:35 +04:00
}
else
{
gs_free char * tmpd = g_mkdtemp ( g_strdup ( " /var/tmp/rpm-ostree.XXXXXX " ) ) ;
2014-11-17 04:05:47 +03:00
self - > workdir = g_file_new_for_path ( tmpd ) ;
2014-05-03 14:55:35 +04:00
workdir_is_tmp = TRUE ;
2014-07-02 06:07:56 +04:00
if ( opt_workdir_tmpfs )
{
if ( mount ( " tmpfs " , tmpd , " tmpfs " , 0 , ( const void * ) " mode=755 " ) ! = 0 )
{
2014-11-20 02:32:08 +03:00
_rpmostree_set_prefix_error_from_errno ( error , errno ,
" mount(tmpfs): " ) ;
2014-07-02 06:07:56 +04:00
goto out ;
}
}
2014-05-03 14:55:35 +04:00
}
2014-05-16 01:46:51 +04:00
if ( opt_cachedir )
2014-06-07 02:25:08 +04:00
{
cachedir = g_file_new_for_path ( opt_cachedir ) ;
if ( ! gs_file_ensure_directory ( cachedir , FALSE , cancellable , error ) )
goto out ;
}
2014-05-16 01:46:51 +04:00
2014-10-14 17:32:29 +04:00
if ( opt_metadata_strings )
{
if ( ! parse_keyvalue_strings ( opt_metadata_strings ,
& metadata , error ) )
goto out ;
}
2014-11-17 04:05:47 +03:00
if ( chdir ( gs_file_get_path_cached ( self - > 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 " ,
2014-11-17 04:05:47 +03:00
gs_file_get_path_cached ( self - > workdir ) ,
strerror ( errno ) ) ;
2014-02-13 03:26:31 +04:00
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 ;
2014-05-03 18:05:43 +04:00
treefile_rootval = json_parser_get_root ( treefile_parser ) ;
if ( ! JSON_NODE_HOLDS_OBJECT ( treefile_rootval ) )
2014-02-13 03:26:31 +04:00
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Treefile root is not an object " ) ;
goto out ;
}
2014-05-03 18:05:43 +04:00
treefile = json_node_get_object ( treefile_rootval ) ;
2014-05-18 22:13:31 +04:00
if ( ! process_includes ( self , treefile_path , 0 , treefile ,
2014-05-03 18:05:43 +04:00
cancellable , error ) )
goto out ;
if ( opt_print_only )
{
gs_unref_object JsonGenerator * generator = json_generator_new ( ) ;
gs_unref_object GOutputStream * stdout = g_unix_output_stream_new ( 1 , FALSE ) ;
json_generator_set_pretty ( generator , TRUE ) ;
json_generator_set_root ( generator , treefile_rootval ) ;
( void ) json_generator_to_stream ( generator , stdout , NULL , NULL ) ;
ret = TRUE ;
goto out ;
}
2014-02-13 03:26:31 +04:00
2014-11-17 04:05:47 +03:00
yumroot = g_file_get_child ( self - > workdir , " rootfs.tmp " ) ;
2014-01-24 22:34:19 +04:00
if ( ! gs_shutil_rm_rf ( yumroot , cancellable , error ) )
goto out ;
2014-11-17 04:05:47 +03:00
targetroot = g_file_get_child ( self - > workdir , " rootfs " ) ;
2014-01-24 22:34:19 +04:00
2014-11-13 22:53:43 +03:00
ref = _rpmostree_jsonutil_object_require_string_member ( treefile , " ref " , error ) ;
2014-02-13 03:26:31 +04:00
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-11-17 23:48:08 +03:00
if ( json_object_has_member ( treefile , " bootstrap_packages " ) )
{
2014-11-18 00:08:03 +03:00
if ( ! _rpmostree_jsonutil_append_string_array_to ( treefile , " bootstrap_packages " , packages , error ) )
2014-11-17 23:48:08 +03:00
goto out ;
}
2014-11-18 00:08:03 +03:00
if ( ! _rpmostree_jsonutil_append_string_array_to ( treefile , " packages " , packages , error ) )
2014-02-13 03:26:31 +04:00
goto out ;
g_ptr_array_add ( packages , NULL ) ;
2014-01-24 22:34:19 +04:00
2014-09-30 00:24:53 +04:00
{
gs_unref_object JsonGenerator * generator = json_generator_new ( ) ;
char * treefile_buf = NULL ;
gsize len ;
json_generator_set_root ( generator , treefile_rootval ) ;
json_generator_set_pretty ( generator , TRUE ) ;
treefile_buf = json_generator_to_data ( generator , & len ) ;
self - > serialized_treefile = g_bytes_new_take ( treefile_buf , len ) ;
}
2014-11-17 04:05:47 +03:00
if ( ! yuminstall ( self , treefile , yumroot ,
2014-07-11 02:28:22 +04:00
( char * * ) packages - > pdata ,
2014-05-16 01:47:47 +04:00
cancellable , error ) )
goto out ;
2014-05-16 01:46:51 +04:00
cachekey = g_strconcat ( " treecompose/ " , ref , NULL ) ;
if ( ! cachedir_lookup_string ( cachedir , cachekey ,
& cached_compose_checksum ,
cancellable , error ) )
goto out ;
2014-09-07 20:38:34 +04:00
if ( ! compute_checksum_for_compose ( self , treefile , yumroot ,
2014-05-16 01:46:51 +04:00
& new_compose_checksum ,
cancellable , error ) )
goto out ;
if ( g_strcmp0 ( cached_compose_checksum , new_compose_checksum ) = = 0 )
{
g_print ( " No changes to input, reusing cached commit \n " ) ;
ret = TRUE ;
goto out ;
}
2014-05-16 01:47:47 +04:00
ref_unix = g_strdelimit ( g_strdup ( ref ) , " / " , ' _ ' ) ;
2014-01-26 19:13:45 +04:00
2014-05-16 01:47:47 +04:00
if ( g_strcmp0 ( g_getenv ( " RPM_OSTREE_BREAK " ) , " post-yum " ) = = 0 )
goto out ;
2014-01-26 19:13:45 +04:00
2014-11-14 19:53:21 +03:00
if ( ! rpmostree_treefile_postprocessing ( yumroot , self - > treefile_context_dirs - > pdata [ 0 ] ,
self - > serialized_treefile , treefile ,
2014-11-13 22:54:33 +03:00
cancellable , error ) )
goto out ;
2014-11-17 17:18:02 +03:00
if ( ! rpmostree_prepare_rootfs_for_commit ( yumroot , treefile , cancellable , error ) )
goto out ;
2014-03-29 04:15:43 +04:00
2014-05-16 01:47:47 +04:00
{
const char * gpgkey ;
2014-11-13 22:53:43 +03:00
if ( ! _rpmostree_jsonutil_object_get_optional_string_member ( treefile , " gpg_key " , & gpgkey , error ) )
2014-05-16 01:47:47 +04:00
goto out ;
2014-03-29 04:15:43 +04:00
2014-10-14 17:32:29 +04:00
if ( ! rpmostree_commit ( yumroot , repo , ref , metadata , gpgkey ,
2014-05-16 01:47:47 +04:00
json_object_get_boolean_member ( treefile , " selinux " ) ,
cancellable , error ) )
2014-01-29 23:37:44 +04:00
goto out ;
2014-01-24 22:34:19 +04:00
}
2014-05-16 01:46:51 +04:00
if ( ! cachedir_set_string ( cachedir , cachekey ,
new_compose_checksum ,
cancellable , error ) )
goto out ;
2014-01-24 22:34:19 +04:00
g_print ( " Complete \n " ) ;
out :
2014-07-02 06:07:56 +04:00
2014-06-11 15:27:31 +04:00
if ( workdir_is_tmp )
2014-07-02 06:07:56 +04:00
{
if ( opt_workdir_tmpfs )
2014-11-17 04:05:47 +03:00
( void ) umount ( gs_file_get_path_cached ( self - > workdir ) ) ;
( void ) gs_shutil_rm_rf ( self - > workdir , NULL , NULL ) ;
2014-07-02 06:07:56 +04:00
}
2014-05-18 22:13:31 +04:00
if ( self )
{
2014-11-17 04:05:47 +03:00
g_clear_object ( & self - > workdir ) ;
2014-09-07 20:38:34 +04:00
g_clear_pointer ( & self - > serialized_treefile , g_bytes_unref ) ;
2014-05-18 22:13:31 +04:00
g_ptr_array_unref ( self - > treefile_context_dirs ) ;
}
2014-03-22 23:05:41 +04:00
return ret ;
2014-01-24 22:34:19 +04:00
}