summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2015-09-25 17:37:16 +0200
committerAlexander Larsson <alexl@redhat.com>2015-09-28 10:48:47 +0200
commitb25830d8f9e8fbb8fb5f74299a3b24adca220471 (patch)
tree8e81f23e452a2ebca1ab919a99eb4d1a6b7ff4fa
parent7af7d61fa0fd43da01f76c14dd7858e1feca1136 (diff)
downloadxdg-app-b25830d8f9e8fbb8fb5f74299a3b24adca220471.tar.gz
Add xdg-app dump-runtime command
This creates a tar of the runtime as set up by xdg-app
-rw-r--r--app/Makefile.am.inc5
-rw-r--r--app/xdg-app-builtins-dump.c309
-rw-r--r--app/xdg-app-builtins.h1
-rw-r--r--app/xdg-app-main.c1
-rwxr-xr-xcompletion/xdg-app6
-rw-r--r--configure.ac28
6 files changed, 345 insertions, 5 deletions
diff --git a/app/Makefile.am.inc b/app/Makefile.am.inc
index 7b8839f..4345864 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 0000000..cb3378c
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alexander Larsson <alexl@redhat.com>
+ */
+
+#include "config.h"
+
+#include <locale.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#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 <archive.h>
+#include <archive_entry.h>
+
+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 9cfe4fe..2a5ff63 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 2ab0748..b882199 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 8899ee6..45b3dcc 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 039ba11..4f16cb5 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)