diff --git a/Makefile.am b/Makefile.am index f2ac4aed..98d823a1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,6 +2,7 @@ NULL = AM_CPPFLAGS = \ -DXDG_APP_BASEDIR=\"$(datadir)/xdg-app\" \ + -DHELPER=\"$(bindir)/xdg-app-helper\" \ $(NULL) bin_PROGRAMS = \ @@ -17,6 +18,7 @@ xdg_app_SOURCES = \ xdg-app-builtins-add-repo.c \ xdg-app-builtins-install.c \ xdg-app-builtins-update.c \ + xdg-app-builtins-run.c \ xdg-app-dir.c \ xdg-app-dir.h \ xdg-app-utils.h \ diff --git a/xdg-app-builtins-run.c b/xdg-app-builtins-run.c new file mode 100644 index 00000000..391446ef --- /dev/null +++ b/xdg-app-builtins-run.c @@ -0,0 +1,174 @@ +#include "config.h" + +#include +#include +#include +#include + +#include "libgsystem.h" + +#include "xdg-app-builtins.h" +#include "xdg-app-utils.h" + +static char *opt_arch; +static char *opt_branch; +static gchar **opt_rest = NULL; + +static GOptionEntry options[] = { + { "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, "Arch to install for", NULL }, + { "branch", 0, 0, G_OPTION_ARG_STRING, &opt_branch, "Branch to run", NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_rest, + "Special option that collects any remaining arguments for us" }, + { NULL } +}; + +static void +usage_error (GOptionContext *context, const char *message, GError **error) +{ + gs_free gchar *help = g_option_context_get_help (context, TRUE, NULL); + g_printerr ("%s", help); + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message); +} + +#define _gs_unref_keyfile __attribute__ ((cleanup(gs_local_keyfile_unref))) + +gboolean +xdg_app_builtin_run (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + GOptionContext *context; + gboolean ret = FALSE; + gs_unref_object XdgAppDir *user_dir = NULL; + gs_unref_object XdgAppDir *system_dir = NULL; + gs_unref_variant_builder GVariantBuilder *optbuilder = NULL; + gs_unref_object GFile *deploy_base = NULL; + gs_unref_object GFile *var = NULL; + gs_unref_object GFile *var_tmp = NULL; + gs_unref_object GFile *var_run = NULL; + gs_unref_object GFile *app_deploy = NULL; + gs_unref_object GFile *app_files = NULL; + gs_unref_object GFile *runtime_deploy = NULL; + gs_unref_object GFile *runtime_files = NULL; + gs_unref_object GFile *metadata = NULL; + gs_free char *metadata_contents = NULL; + gs_free char *runtime = NULL; + gs_free char *runtime_ref = NULL; + gs_free char *app_ref = NULL; + _gs_unref_keyfile GKeyFile *metakey = NULL; + gs_free_error GError *my_error = NULL; + gs_free_error GError *my_error2 = NULL; + gs_unref_ptrarray GPtrArray *argv_array = NULL; + gsize metadata_size; + const char *app; + const char *branch = "master"; + int i; + + context = g_option_context_new ("APP COMMAND - Run an app"); + + if (!xdg_app_option_context_parse (context, options, &argc, &argv, XDG_APP_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) + goto out; + + if (g_strv_length (opt_rest) < 2) + { + usage_error (context, "APP and COMMAND must be specified", error); + goto out; + } + + app = opt_rest[0]; + + if (opt_branch) + branch = opt_branch; + + app_ref = xdg_app_build_app_ref (app, branch, opt_arch); + + user_dir = xdg_app_dir_get_user (); + system_dir = xdg_app_dir_get_system (); + + app_deploy = xdg_app_dir_get_if_deployed (user_dir, app_ref, NULL, cancellable); + if (app_deploy == NULL) + app_deploy = xdg_app_dir_get_if_deployed (system_dir, app_ref, NULL, cancellable); + if (app_deploy == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "App %s branch %s not installed", app, branch); + goto out; + } + + metadata = g_file_get_child (app_deploy, "metadata"); + if (!g_file_load_contents (metadata, cancellable, &metadata_contents, &metadata_size, NULL, error)) + goto out; + + metakey = g_key_file_new (); + if (!g_key_file_load_from_data (metakey, metadata_contents, metadata_size, 0, error)) + goto out; + + runtime = g_key_file_get_string (metakey, "Application", "runtime", error); + if (runtime == NULL) + goto out; + + runtime_ref = g_build_filename ("runtime", runtime, NULL); + + runtime_deploy = xdg_app_dir_get_if_deployed (user_dir, runtime_ref, NULL, cancellable); + if (runtime_deploy == NULL) + runtime_deploy = xdg_app_dir_get_if_deployed (system_dir, runtime_ref, NULL, cancellable); + if (runtime_deploy == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Required runtime %s not installed", runtime); + goto out; + } + + if (!xdg_app_dir_ensure_path (user_dir, cancellable, error)) + goto out; + + var = xdg_app_dir_get_app_data (user_dir, app); + if (!gs_file_ensure_directory (var, TRUE, cancellable, error)) + goto out; + + var_tmp = g_file_get_child (var, "tmp"); + var_run = g_file_get_child (var, "run"); + + if (!g_file_make_symbolic_link (var_tmp, "/tmp", cancellable, &my_error) && + !g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) + { + g_propagate_error (error, my_error); + my_error = NULL; + goto out; + } + + if (!g_file_make_symbolic_link (var_run, "/run", cancellable, &my_error2) && + !g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) + { + g_propagate_error (error, my_error2); + my_error2 = NULL; + goto out; + } + + app_files = g_file_get_child (app_deploy, "files"); + runtime_files = g_file_get_child (runtime_deploy, "files"); + + argv_array = g_ptr_array_new (); + g_ptr_array_add (argv_array, HELPER); + g_ptr_array_add (argv_array, "-i"); + g_ptr_array_add (argv_array, "-a"); + g_ptr_array_add (argv_array, (char *)gs_file_get_path_cached (app_files)); + g_ptr_array_add (argv_array, "-v"); + g_ptr_array_add (argv_array, (char *)gs_file_get_path_cached (var)); + g_ptr_array_add (argv_array, (char *)gs_file_get_path_cached (runtime_files)); + + for (i = 1; opt_rest[i] != NULL; i++) + g_ptr_array_add (argv_array, opt_rest[i]); + + g_ptr_array_add (argv_array, NULL); + + if (!execve (HELPER, (char **)argv_array->pdata, NULL)) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Unable to start app"); + goto out; + } + + /* Not actually reached... */ + ret = TRUE; + + out: + if (context) + g_option_context_free (context); + return ret; +} diff --git a/xdg-app-builtins.h b/xdg-app-builtins.h index a51174c2..3b9b1e44 100644 --- a/xdg-app-builtins.h +++ b/xdg-app-builtins.h @@ -29,6 +29,7 @@ BUILTINPROTO(install_runtime); BUILTINPROTO(update_runtime); BUILTINPROTO(install_app); BUILTINPROTO(update_app); +BUILTINPROTO(run); #undef BUILTINPROTO diff --git a/xdg-app-dir.c b/xdg-app-dir.c index 7edbe601..950aacff 100644 --- a/xdg-app-dir.c +++ b/xdg-app-dir.c @@ -388,6 +388,23 @@ xdg_app_dir_deploy (XdgAppDir *self, return ret; } +GFile * +xdg_app_dir_get_if_deployed (XdgAppDir *self, + const char *ref, + const char *hash, + GCancellable *cancellable) +{ + gs_unref_object GFile *deploy_base = NULL; + gs_unref_object GFile *deploy_dir = NULL; + + deploy_base = xdg_app_dir_get_deploy_dir (self, ref); + deploy_dir = g_file_get_child (deploy_base, hash ? hash : "latest"); + + if (g_file_query_file_type (deploy_dir, G_FILE_QUERY_INFO_NONE, cancellable) == G_FILE_TYPE_DIRECTORY) + return g_object_ref (deploy_dir); + return NULL; +} + XdgAppDir* xdg_app_dir_new (GFile *path, gboolean user) { diff --git a/xdg-app-dir.h b/xdg-app-dir.h index e8d0a3b3..179331db 100644 --- a/xdg-app-dir.h +++ b/xdg-app-dir.h @@ -31,6 +31,10 @@ gboolean xdg_app_dir_is_user (XdgAppDir *self); GFile * xdg_app_dir_get_path (XdgAppDir *self); GFile * xdg_app_dir_get_deploy_dir (XdgAppDir *self, const char *ref); +GFile * xdg_app_dir_get_if_deployed(XdgAppDir *self, + const char *ref, + const char *hash, + GCancellable *cancellable); GFile * xdg_app_dir_get_app_data (XdgAppDir *self, const char *app); OstreeRepo *xdg_app_dir_get_repo (XdgAppDir *self); diff --git a/xdg-app-main.c b/xdg-app-main.c index e1846c94..a22f61b2 100644 --- a/xdg-app-main.c +++ b/xdg-app-main.c @@ -24,6 +24,7 @@ static XdgAppCommand commands[] = { { "update-runtime", xdg_app_builtin_update_runtime }, { "install-app", xdg_app_builtin_install_app }, { "update-app", xdg_app_builtin_update_app }, + { "run", xdg_app_builtin_run }, { NULL } }; @@ -180,10 +181,6 @@ xdg_app_run (int argc, continue; } } - else if (g_str_equal (argv[in], "--")) - { - break; - } argv[out] = argv[in]; }