diff --git a/app/Makefile.am.inc b/app/Makefile.am.inc index 7b8839fc..4345864d 100644 --- a/app/Makefile.am.inc +++ b/app/Makefile.am.inc @@ -17,6 +17,7 @@ xdg_app_SOURCES = \ app/xdg-app-builtins-list.c \ app/xdg-app-builtins-run.c \ app/xdg-app-builtins-enter.c \ + app/xdg-app-builtins-dump.c \ app/xdg-app-builtins-build-init.c \ app/xdg-app-builtins-build.c \ app/xdg-app-builtins-build-finish.c \ @@ -26,6 +27,6 @@ xdg_app_SOURCES = \ $(xdp_dbus_built_sources) \ $(NULL) -xdg_app_LDADD = $(BASE_LIBS) $(OSTREE_LIBS) $(SOUP_LIBS) libglnx.la libxdgapp.la -xdg_app_CFLAGS = $(BASE_CFLAGS) $(OSTREE_CFLAGS) $(SOUP_CFLAGS) +xdg_app_LDADD = $(BASE_LIBS) $(OSTREE_LIBS) $(SOUP_LIBS) $(LIBARCHIVE_LIBS) libglnx.la libxdgapp.la +xdg_app_CFLAGS = $(BASE_CFLAGS) $(OSTREE_CFLAGS) $(SOUP_CFLAGS) $(LIBARCHIVE_CFLAGS) diff --git a/app/xdg-app-builtins-dump.c b/app/xdg-app-builtins-dump.c new file mode 100644 index 00000000..cb3378ca --- /dev/null +++ b/app/xdg-app-builtins-dump.c @@ -0,0 +1,309 @@ +/* + * Copyright © 2014 Red Hat, Inc + * + * 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 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, see . + * + * Authors: + * Alexander Larsson + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "libgsystem.h" +#include "libglnx/libglnx.h" + +#include "xdg-app-builtins.h" +#include "xdg-app-utils.h" + +static char *opt_arch; +static char *opt_file; + +static GOptionEntry options[] = { + { "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, "Arch to make current for", "ARCH" }, + { "file", 0, 0, G_OPTION_ARG_STRING, &opt_file, "Write to file instead of stdout", "PATH" }, + { NULL } +}; + +#ifdef HAVE_LIBARCHIVE +#include +#include + +typedef struct archive write_archive_t; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(write_archive_t, archive_write_free); + +typedef struct archive_entry archive_entry_t; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(archive_entry_t, archive_entry_free); + +static gboolean +dump_data (GFile *file, + struct archive *archive, + GCancellable *cancellable, + GError **error) +{ + char buffer[32*1024]; + g_autoptr(GFileInputStream) in = NULL; + gssize in_buffer; + + in = g_file_read (file, cancellable, error); + if (in == NULL) + return FALSE; + + while (TRUE) + { + in_buffer = g_input_stream_read (G_INPUT_STREAM (in), buffer, sizeof (buffer), cancellable, error); + if (in_buffer == -1) + return FALSE; + + if (in_buffer == 0) + break; + + if (archive_write_data (archive, buffer, in_buffer) < ARCHIVE_OK) + return xdg_app_fail (error, "Can't write tar data"); + } + + return TRUE; +} + +static gboolean +dump_files (GFile *dir, + struct archive *archive, + GCancellable *cancellable, + char *parent, + GError **error) +{ + g_autoptr(GFileEnumerator) fe = NULL; + gboolean ret = TRUE; + GFileType type; + + fe = g_file_enumerate_children (dir, + "standard::name,standard::type,standard::is-symlink,standard::symlink-target,unix::mode,time::*", + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); + if (fe == NULL) + return FALSE; + + + while (TRUE) + { + g_autoptr(GFileInfo) info = g_file_enumerator_next_file (fe, cancellable, error); + g_autofree char *path = NULL; + g_autoptr(GFile) child = NULL; + guint32 mode; + g_autoptr(archive_entry_t) entry = archive_entry_new2 (archive); + + if (!info) + { + if (error && *error != NULL) + ret = FALSE; + break; + } + + type = g_file_info_get_file_type (info); + mode = g_file_info_get_attribute_uint32 (info, "unix::mode"); + path = g_build_filename (parent, g_file_info_get_name (info), NULL); + child = g_file_enumerator_get_child (fe, info); + + archive_entry_set_pathname (entry, path); + archive_entry_set_uid(entry, 0); + archive_entry_set_gid(entry, 0); + archive_entry_set_perm(entry, mode & 0777); + archive_entry_set_mtime(entry, 0, 0); + + switch (type) + { + case G_FILE_TYPE_SYMBOLIC_LINK: + archive_entry_set_filetype (entry, AE_IFLNK); + archive_entry_set_symlink (entry, g_file_info_get_symlink_target (info)); + break; + + case G_FILE_TYPE_REGULAR: + archive_entry_set_filetype (entry, AE_IFREG); + archive_entry_set_size(entry, g_file_info_get_size (info)); + break; + + case G_FILE_TYPE_DIRECTORY: + archive_entry_set_filetype (entry, AE_IFDIR); + break; + + default: + g_error ("Unhandled type %d\n", type); + break; + } + + if (archive_write_header (archive, entry) < ARCHIVE_OK) + return xdg_app_fail (error, "Can't write tar header"); + + if (type == G_FILE_TYPE_REGULAR) + { + if (!dump_data (child, archive, cancellable, error)) + return FALSE; + } + + if (archive_write_finish_entry (archive) < ARCHIVE_OK) + return xdg_app_fail (error, "Can't finish tar entry"); + + if (type == G_FILE_TYPE_DIRECTORY) + { + if (!dump_files (child, archive, cancellable, path, error)) + return FALSE; + } + } + + return ret; +} + +static const char *extra_dirs[] = { + "app", + "dev", + "home", + "proc", + "run", + "run/host", + "run/dbus", + "run/media", + "run/user", + "sys", + "usr", + "tmp", + "var", +}; + +static struct {const char *path; const char *target; } extra_symlinks[] = { + {"bin", "usr/bin" }, + {"sbin", "usr/sbin" }, + {"etc", "usr/etc" }, + {"lib", "usr/lib" }, + {"lib32", "usr/lib32" }, + {"lib64", "usr/lib64" }, + {"var/run", "/run" }, + {"var/tmp", "/tmp" }, +}; + +static gboolean +dump_runtime (GFile *root, GCancellable *cancellable, GError **error) +{ + int i; + + g_autoptr(write_archive_t) archive = NULL; + g_autoptr(GFile) files = g_file_get_child (root, "files"); + + archive = archive_write_new (); + if (archive == NULL) + return xdg_app_fail (error, "Can't allocate archive"); + + if (archive_write_set_format_gnutar (archive) < ARCHIVE_OK) + return xdg_app_fail (error, "Can't set tar format"); + + if (archive_write_open_FILE (archive, stdout) < ARCHIVE_OK) + return xdg_app_fail (error, "can't open stdout"); + + for (i = 0; i < G_N_ELEMENTS(extra_dirs); i++) + { + g_autoptr(archive_entry_t) entry = archive_entry_new2 (archive); + + archive_entry_set_pathname (entry, extra_dirs[i]); + archive_entry_set_uid(entry, 0); + archive_entry_set_gid(entry, 0); + archive_entry_set_perm(entry, 0755); + archive_entry_set_mtime(entry, 0, 0); + archive_entry_set_filetype (entry, AE_IFDIR); + + if (archive_write_header (archive, entry) < ARCHIVE_OK) + return xdg_app_fail (error, "Can't write tar header"); + } + + for (i = 0; i < G_N_ELEMENTS(extra_symlinks); i++) + { + g_autoptr(archive_entry_t) entry = NULL; + g_autoptr(GFile) dest = g_file_resolve_relative_path (files, extra_symlinks[i].target); + + if (g_str_has_prefix (extra_symlinks[i].target, "usr/")) + { + g_autoptr(GFile) dest = g_file_resolve_relative_path (files, extra_symlinks[i].target + 4); + + if (!g_file_query_exists (dest, cancellable)) + continue; + } + + entry = archive_entry_new2 (archive); + + archive_entry_set_pathname (entry, extra_symlinks[i].path); + archive_entry_set_uid(entry, 0); + archive_entry_set_gid(entry, 0); + archive_entry_set_perm(entry, 0755); + archive_entry_set_mtime(entry, 0, 0); + archive_entry_set_filetype (entry, AE_IFLNK); + archive_entry_set_symlink (entry, extra_symlinks[i].target); + + if (archive_write_header (archive, entry) < ARCHIVE_OK) + return xdg_app_fail (error, "Can't write tar header"); + } + + if (!dump_files (files, archive, cancellable, "usr", error)) + return FALSE; + + if (archive_write_close (archive) < ARCHIVE_OK) + return xdg_app_fail (error, "can't close archive"); + + return TRUE; +} +#endif + +gboolean +xdg_app_builtin_dump_runtime (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(XdgAppDir) dir = NULL; + const char *runtime; + const char *branch = "master"; + g_autofree char *ref = NULL; + OstreeRepo *repo; + g_autofree char *commit = NULL; + g_autoptr(GFile) root = NULL; + + context = g_option_context_new ("RUNTIME BRANCH - Make branch of application current"); + + if (!xdg_app_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error)) + return FALSE; + + if (argc < 3) + return usage_error (context, "RUNTIME and BRANCH must be specified", error); + + runtime = argv[1]; + branch = argv[2]; + + if (!xdg_app_is_valid_name (runtime)) + return xdg_app_fail (error, "'%s' is not a valid name", runtime); + + if (!xdg_app_is_valid_branch (branch)) + return xdg_app_fail (error, "'%s' is not a valid branch name", branch); + + ref = xdg_app_build_runtime_ref (runtime, branch, opt_arch); + + repo = xdg_app_dir_get_repo (dir); + + if (!ostree_repo_read_commit (repo, ref, + &root, &commit, cancellable, error)) + return FALSE; + +#ifdef HAVE_LIBARCHIVE + return dump_runtime (root, cancellable, error); +#else + return xdg_app_fail (error, "Build without libarchive"); +#endif +} diff --git a/app/xdg-app-builtins.h b/app/xdg-app-builtins.h index 9cfe4fec..2a5ff63a 100644 --- a/app/xdg-app-builtins.h +++ b/app/xdg-app-builtins.h @@ -71,6 +71,7 @@ BUILTINPROTO(build_export); BUILTINPROTO(repo_update); BUILTINPROTO(export_file); BUILTINPROTO(override); +BUILTINPROTO(dump_runtime); #undef BUILTINPROTO diff --git a/app/xdg-app-main.c b/app/xdg-app-main.c index 2ab0748e..b882199c 100644 --- a/app/xdg-app-main.c +++ b/app/xdg-app-main.c @@ -63,6 +63,7 @@ static XdgAppCommand commands[] = { { "build", xdg_app_builtin_build }, { "build-finish", xdg_app_builtin_build_finish }, { "build-export", xdg_app_builtin_build_export }, + { "dump-runtime", xdg_app_builtin_dump_runtime }, { "repo-update", xdg_app_builtin_repo_update }, { NULL } }; diff --git a/completion/xdg-app b/completion/xdg-app index 8899ee6a..45b3dcc0 100755 --- a/completion/xdg-app +++ b/completion/xdg-app @@ -23,8 +23,8 @@ _xdg-app() { local dir cmd sdk loc local -A VERBS=( - [ALL]='add-remote modify-remote delete-remote ls-remote list-remotes install-runtime update-runtime uninstall-runtime list-runtimes install-app update-app uninstall-app list-apps run override enter export-file build-init build build-finish build-export repo-update make-app-current' - [MODE]='add-remote modify-remote delete-remote ls-remote list-remotes install-runtime update-runtime uninstall-runtime list-runtimes install-app update-app uninstall-app list-apps make-app-current' + [ALL]='add-remote modify-remote delete-remote ls-remote list-remotes install-runtime update-runtime uninstall-runtime list-runtimes install-app update-app uninstall-app list-apps run override enter export-file build-init build build-finish build-export repo-update make-app-current dump-runtime' + [MODE]='add-remote modify-remote delete-remote ls-remote list-remotes install-runtime update-runtime uninstall-runtime list-runtimes install-app update-app uninstall-app list-apps make-app-current dump-runtime' [PERMS]='run override build build-finish' [UNINSTALL]='uninstall-runtime uninstall-app' [ARCH]='build-init install-runtime install-app run uninstall-runtime uninstall-app update-runtime update-app make-app-current' @@ -240,7 +240,7 @@ _xdg-app() { comps='' ;; - update-runtime|uninstall-runtime) + update-runtime|uninstall-runtime|dump-runtime) if [[ -z $name ]]; then comps=$(xdg-app $mode list-runtimes) else diff --git a/configure.ac b/configure.ac index 039ba114..4f16cb58 100644 --- a/configure.ac +++ b/configure.ac @@ -108,6 +108,34 @@ AC_ARG_ENABLE(sudo, [SUDO_BIN="sudo"], [SUDO_BIN=""]) AC_SUBST([SUDO_BIN]) +LIBARCHIVE_DEPENDENCY="libarchive >= 2.8.0" + +AC_ARG_WITH(libarchive, + AS_HELP_STRING([--without-libarchive], [Do not use libarchive]), + :, with_libarchive=maybe) + +AS_IF([ test x$with_libarchive != xno ], [ + AC_MSG_CHECKING([for $LIBARCHIVE_DEPENDENCY]) + PKG_CHECK_EXISTS($LIBARCHIVE_DEPENDENCY, have_libarchive=yes, have_libarchive=no) + AC_MSG_RESULT([$have_libarchive]) + AS_IF([ test x$have_libarchive = xno && test x$with_libarchive != xmaybe ], [ + AC_MSG_ERROR([libarchive is enabled but could not be found]) + ]) + AS_IF([ test x$have_libarchive = xyes], [ + AC_DEFINE([HAVE_LIBARCHIVE], 1, [Define if we have libarchive.pc]) + PKG_CHECK_MODULES(LIBARCHIVE, $LIBARCHIVE_DEPENDENCY) + save_LIBS=$LIBS + LIBS=$LIBARCHIVE_LIBS + AC_CHECK_FUNCS(archive_read_support_filter_all) + LIBS=$save_LIBS + with_libarchive=yes + ], [ + with_libarchive=no + ]) +], [ with_libarchive=no ]) +AM_CONDITIONAL(USE_LIBARCHIVE, test $with_libarchive != no) + + AC_ARG_ENABLE(documentation, AC_HELP_STRING([--enable-documentation], [Build documentation]),, enable_documentation=yes)