/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* 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.1 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
#include
#include
#include "libglnx.h"
#ifdef USE_SYSTEM_HELPER
#include
#include "flatpak-polkit-agent-text-listener.h"
/* Work with polkit before and after autoptr support was added */
typedef PolkitSubject AutoPolkitSubject;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (AutoPolkitSubject, g_object_unref)
#endif
#include "flatpak-builtins.h"
#include "flatpak-builtins-utils.h"
#include "flatpak-utils-private.h"
static int opt_verbose;
static gboolean opt_ostree_verbose;
static gboolean opt_version;
static gboolean opt_default_arch;
static gboolean opt_supported_arches;
static gboolean opt_gl_drivers;
static gboolean opt_list_installations;
static gboolean opt_print_updated_env;
static gboolean opt_print_system_only;
static gboolean opt_user;
static gboolean opt_system;
static char **opt_installations;
static gboolean opt_help;
static gboolean is_in_complete;
typedef struct
{
const char *name;
const char *description;
gboolean (*fn)(int argc,
char **argv,
GCancellable *cancellable,
GError **error);
gboolean (*complete)(FlatpakCompletion *completion);
gboolean deprecated;
} FlatpakCommand;
static FlatpakCommand commands[] = {
/* translators: please keep the leading space */
{ N_(" Manage installed applications and runtimes") },
{ "install", N_("Install an application or runtime"), flatpak_builtin_install, flatpak_complete_install },
{ "update", N_("Update an installed application or runtime"), flatpak_builtin_update, flatpak_complete_update },
/* Alias upgrade to update to help users of yum/dnf */
{ "upgrade", NULL, flatpak_builtin_update, flatpak_complete_update, TRUE },
{ "uninstall", N_("Uninstall an installed application or runtime"), flatpak_builtin_uninstall, flatpak_complete_uninstall },
/* Alias remove to uninstall to help users of yum/dnf/apt */
{ "remove", NULL, flatpak_builtin_uninstall, flatpak_complete_uninstall, TRUE },
{ "mask", N_("Mask out updates and automatic installation"), flatpak_builtin_mask, flatpak_complete_mask },
{ "pin", N_("Pin a runtime to prevent automatic removal"), flatpak_builtin_pin, flatpak_complete_pin },
{ "list", N_("List installed apps and/or runtimes"), flatpak_builtin_list, flatpak_complete_list },
{ "info", N_("Show info for installed app or runtime"), flatpak_builtin_info, flatpak_complete_info },
{ "history", N_("Show history"), flatpak_builtin_history, flatpak_complete_history },
{ "config", N_("Configure flatpak"), flatpak_builtin_config, flatpak_complete_config },
{ "repair", N_("Repair flatpak installation"), flatpak_builtin_repair, flatpak_complete_repair },
{ "create-usb", N_("Put applications or runtimes onto removable media"), flatpak_builtin_create_usb, flatpak_complete_create_usb },
/* translators: please keep the leading newline and space */
{ N_("\n Find applications and runtimes") },
{ "search", N_("Search for remote apps/runtimes"), flatpak_builtin_search, flatpak_complete_search },
/* translators: please keep the leading newline and space */
{ N_("\n Manage running applications") },
{ "run", N_("Run an application"), flatpak_builtin_run, flatpak_complete_run },
{ "override", N_("Override permissions for an application"), flatpak_builtin_override, flatpak_complete_override },
{ "make-current", N_("Specify default version to run"), flatpak_builtin_make_current_app, flatpak_complete_make_current_app },
{ "enter", N_("Enter the namespace of a running application"), flatpak_builtin_enter, flatpak_complete_enter },
{ "ps", N_("Enumerate running applications"), flatpak_builtin_ps, flatpak_complete_ps },
{ "kill", N_("Stop a running application"), flatpak_builtin_kill, flatpak_complete_kill },
/* translators: please keep the leading newline and space */
{ N_("\n Manage file access") },
{ "documents", N_("List exported files"), flatpak_builtin_document_list, flatpak_complete_document_list },
{ "document-export", N_("Grant an application access to a specific file"), flatpak_builtin_document_export, flatpak_complete_document_export },
{ "document-unexport", N_("Revoke access to a specific file"), flatpak_builtin_document_unexport, flatpak_complete_document_unexport },
{ "document-info", N_("Show information about a specific file"), flatpak_builtin_document_info, flatpak_complete_document_info },
{ "document-list", NULL, flatpak_builtin_document_list, flatpak_complete_document_list, TRUE },
/* translators: please keep the leading newline and space */
{ N_("\n Manage dynamic permissions") },
{ "permissions", N_("List permissions"), flatpak_builtin_permission_list, flatpak_complete_permission_list },
{ "permission-remove", N_("Remove item from permission store"), flatpak_builtin_permission_remove, flatpak_complete_permission_remove },
{ "permission-list", NULL, flatpak_builtin_permission_list, flatpak_complete_permission_list, TRUE },
{ "permission-set", N_("Set permissions"), flatpak_builtin_permission_set, flatpak_complete_permission_set },
{ "permission-show", N_("Show app permissions"), flatpak_builtin_permission_show, flatpak_complete_permission_show },
{ "permission-reset", N_("Reset app permissions"), flatpak_builtin_permission_reset, flatpak_complete_permission_reset },
/* translators: please keep the leading newline and space */
{ N_("\n Manage remote repositories") },
{ "remotes", N_("List all configured remotes"), flatpak_builtin_remote_list, flatpak_complete_remote_list },
{ "remote-add", N_("Add a new remote repository (by URL)"), flatpak_builtin_remote_add, flatpak_complete_remote_add },
{ "remote-modify", N_("Modify properties of a configured remote"), flatpak_builtin_remote_modify, flatpak_complete_remote_modify },
{ "remote-delete", N_("Delete a configured remote"), flatpak_builtin_remote_delete, flatpak_complete_remote_delete },
{ "remote-list", NULL, flatpak_builtin_remote_list, flatpak_complete_remote_list, TRUE },
{ "remote-ls", N_("List contents of a configured remote"), flatpak_builtin_remote_ls, flatpak_complete_remote_ls },
{ "remote-info", N_("Show information about a remote app or runtime"), flatpak_builtin_remote_info, flatpak_complete_remote_info },
/* translators: please keep the leading newline and space */
{ N_("\n Build applications") },
{ "build-init", N_("Initialize a directory for building"), flatpak_builtin_build_init, flatpak_complete_build_init },
{ "build", N_("Run a build command inside the build dir"), flatpak_builtin_build, flatpak_complete_build },
{ "build-finish", N_("Finish a build dir for export"), flatpak_builtin_build_finish, flatpak_complete_build_finish },
{ "build-export", N_("Export a build dir to a repository"), flatpak_builtin_build_export, flatpak_complete_build_export },
{ "build-bundle", N_("Create a bundle file from a ref in a local repository"), flatpak_builtin_build_bundle, flatpak_complete_build_bundle },
{ "build-import-bundle", N_("Import a bundle file"), flatpak_builtin_build_import, flatpak_complete_build_import },
{ "build-sign", N_("Sign an application or runtime"), flatpak_builtin_build_sign, flatpak_complete_build_sign },
{ "build-update-repo", N_("Update the summary file in a repository"), flatpak_builtin_build_update_repo, flatpak_complete_build_update_repo },
{ "build-commit-from", N_("Create new commit based on existing ref"), flatpak_builtin_build_commit_from, flatpak_complete_build_commit_from },
{ "repo", N_("Show information about a repo"), flatpak_builtin_repo, flatpak_complete_repo },
{ NULL }
};
static gboolean
opt_verbose_cb (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error)
{
opt_verbose++;
return TRUE;
}
GOptionEntry global_entries[] = {
{ "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, &opt_verbose_cb, N_("Show debug information, -vv for more detail"), NULL },
{ "ostree-verbose", 0, 0, G_OPTION_ARG_NONE, &opt_ostree_verbose, N_("Show OSTree debug information"), NULL },
{ "help", '?', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_help, NULL, NULL },
{ NULL }
};
static GOptionEntry empty_entries[] = {
{ "version", 0, 0, G_OPTION_ARG_NONE, &opt_version, N_("Print version information and exit"), NULL },
{ "default-arch", 0, 0, G_OPTION_ARG_NONE, &opt_default_arch, N_("Print default arch and exit"), NULL },
{ "supported-arches", 0, 0, G_OPTION_ARG_NONE, &opt_supported_arches, N_("Print supported arches and exit"), NULL },
{ "gl-drivers", 0, 0, G_OPTION_ARG_NONE, &opt_gl_drivers, N_("Print active gl drivers and exit"), NULL },
{ "installations", 0, 0, G_OPTION_ARG_NONE, &opt_list_installations, N_("Print paths for system installations and exit"), NULL },
{ "print-updated-env", 0, 0, G_OPTION_ARG_NONE, &opt_print_updated_env, N_("Print the updated environment needed to run flatpaks"), NULL },
{ "print-system-only", 0, 0, G_OPTION_ARG_NONE, &opt_print_system_only, N_("Only include the system installation with --print-updated-env"), NULL },
{ NULL }
};
GOptionEntry user_entries[] = {
{ "user", 'u', 0, G_OPTION_ARG_NONE, &opt_user, N_("Work on the user installation"), NULL },
{ "system", 0, 0, G_OPTION_ARG_NONE, &opt_system, N_("Work on the system-wide installation (default)"), NULL },
{ "installation", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_installations, N_("Work on a non-default system-wide installation"), N_("NAME") },
{ NULL }
};
static void
message_handler (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *message,
gpointer user_data)
{
g_printerr ("F: %s\n", message);
}
static void
no_message_handler (const char *log_domain,
GLogLevelFlags log_level,
const char *message,
gpointer user_data)
{
}
static GOptionContext *
flatpak_option_context_new_with_commands (FlatpakCommand *f_commands)
{
GOptionContext *context;
GString *summary;
context = g_option_context_new (_("COMMAND"));
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
summary = g_string_new (_("Builtin Commands:"));
while (f_commands->name != NULL)
{
if (!f_commands->deprecated)
{
if (f_commands->fn != NULL)
{
g_string_append_printf (summary, "\n %s", f_commands->name);
/* Note: the 23 is there to align command descriptions with
* the option descriptions produced by GOptionContext.
*/
if (f_commands->description)
g_string_append_printf (summary, "%*s%s", (int) (23 - strlen (f_commands->name)), "", _(f_commands->description));
}
else
{
g_string_append_printf (summary, "\n%s", _(f_commands->name));
}
}
f_commands++;
}
g_option_context_set_summary (context, summary->str);
g_string_free (summary, TRUE);
return context;
}
static void
check_environment (void)
{
const char * const *dirs;
gboolean has_system = FALSE;
gboolean has_user = FALSE;
g_autofree char *system_exports = NULL;
g_autofree char *user_exports = NULL;
int i;
int rows, cols;
/* Only print warnings on ttys */
if (!flatpak_fancy_output ())
return;
/* Don't recommend restarting the session when we're not in one */
if (!g_getenv ("DBUS_SESSION_BUS_ADDRESS"))
return;
/* Avoid interfering with tests */
if (g_getenv ("FLATPAK_SYSTEM_DIR") || g_getenv ("FLATPAK_USER_DIR"))
return;
system_exports = g_build_filename (FLATPAK_SYSTEMDIR, "exports/share", NULL);
user_exports = g_build_filename (g_get_user_data_dir (), "flatpak/exports/share", NULL);
dirs = g_get_system_data_dirs ();
for (i = 0; dirs[i]; i++)
{
/* There should never be a relative path but just in case we don't want
* g_file_new_for_path() to take the current directory into account.
*/
if (!g_str_has_prefix (dirs[i], "/"))
continue;
/* Normalize the path using GFile to e.g. replace // with / */
g_autoptr(GFile) dir_file = g_file_new_for_path (dirs[i]);
g_autofree char *dir_path = g_file_get_path (dir_file);
if (g_str_has_prefix (dir_path, system_exports))
has_system = TRUE;
if (g_str_has_prefix (dir_path, user_exports))
has_user = TRUE;
}
flatpak_get_window_size (&rows, &cols);
if (cols > 80)
cols = 80;
if (!has_system && !has_user)
{
g_autofree char *missing = NULL;
missing = g_strdup_printf ("\n\n '%s'\n '%s'\n\n", system_exports, user_exports);
g_print ("\n");
/* Translators: this text is automatically wrapped, don't insert line breaks */
print_wrapped (cols,
_("Note that the directories %s are not in the search path "
"set by the XDG_DATA_DIRS environment variable, so applications "
"installed by Flatpak may not appear on your desktop until the "
"session is restarted."),
missing);
g_print ("\n");
}
else if (!has_system || !has_user)
{
g_autofree char *missing = NULL;
missing = g_strdup_printf ("\n\n '%s'\n\n", !has_system ? system_exports : user_exports);
g_print ("\n");
/* Translators: this text is automatically wrapped, don't insert line breaks */
print_wrapped (cols,
_("Note that the directory %s is not in the search path "
"set by the XDG_DATA_DIRS environment variable, so applications "
"installed by Flatpak may not appear on your desktop until the "
"session is restarted."),
missing);
g_print ("\n");
}
}
gboolean
flatpak_option_context_parse (GOptionContext *context,
const GOptionEntry *main_entries,
int *argc,
char ***argv,
FlatpakBuiltinFlags flags,
GPtrArray **out_dirs,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GPtrArray) dirs = NULL;
if (__builtin_popcount (flags & (FLATPAK_BUILTIN_FLAG_NO_DIR |
FLATPAK_BUILTIN_FLAG_ONE_DIR |
FLATPAK_BUILTIN_FLAG_STANDARD_DIRS |
FLATPAK_BUILTIN_FLAG_ALL_DIRS)) != 1)
g_assert_not_reached ();
if (!(flags & FLATPAK_BUILTIN_FLAG_NO_DIR))
g_option_context_add_main_entries (context, user_entries, NULL);
if (main_entries != NULL)
g_option_context_add_main_entries (context, main_entries, NULL);
g_option_context_add_main_entries (context, global_entries, NULL);
/* We never want help output to interrupt completion */
if (is_in_complete)
g_option_context_set_help_enabled (context, FALSE);
if (!g_option_context_parse (context, argc, argv, error))
return FALSE;
/* We never want verbose output in the complete case, that breaks completion */
if (is_in_complete)
{
g_log_set_default_handler (no_message_handler, NULL);
}
else
{
if (opt_verbose > 0)
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, message_handler, NULL);
if (opt_verbose > 1)
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, message_handler, NULL);
if (opt_ostree_verbose)
g_log_set_handler ("OSTree", G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO, message_handler, NULL);
if (opt_verbose > 0 || opt_ostree_verbose)
flatpak_disable_fancy_output ();
}
/* sudo flatpak --user ... would operate on the root user's installation,
* which is almost certainly not what the user intended so just consider it
* an error.
*/
if (opt_user && running_under_sudo ())
return flatpak_fail_error (error, FLATPAK_ERROR,
_("Refusing to operate under sudo with --user. "
"Omit sudo to operate on the user installation, "
"or use a root shell to operate on the root user's installation."));
if (!(flags & FLATPAK_BUILTIN_FLAG_NO_DIR))
{
dirs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
int i;
if (!(flags & FLATPAK_BUILTIN_FLAG_ONE_DIR))
{
/*
* FLATPAK_BUILTIN_FLAG_STANDARD_DIRS or FLATPAK_BUILTIN_FLAG_ALL_DIRS
* must be set.
*/
/* If nothing is set, then we put the system dir first, which can be used as the default */
if (opt_system || (!opt_user && opt_installations == NULL))
g_ptr_array_add (dirs, flatpak_dir_get_system_default ());
if (opt_user || (!opt_system && opt_installations == NULL))
g_ptr_array_add (dirs, flatpak_dir_get_user ());
if (opt_installations != NULL)
{
for (i = 0; opt_installations[i] != NULL; i++)
{
FlatpakDir *installation_dir = NULL;
/* Already included the default system installation */
if (opt_system && g_strcmp0 (opt_installations[i], "default") == 0)
continue;
installation_dir = flatpak_dir_get_system_by_id (opt_installations[i], cancellable, error);
if (installation_dir == NULL)
return FALSE;
g_ptr_array_add (dirs, installation_dir);
}
}
if (flags & FLATPAK_BUILTIN_FLAG_ALL_DIRS &&
opt_installations == NULL && !opt_user && !opt_system)
{
g_autoptr(GPtrArray) system_dirs = NULL;
g_ptr_array_set_size (dirs, 0);
/* The first dir should be the default */
g_ptr_array_add (dirs, flatpak_dir_get_system_default ());
g_ptr_array_add (dirs, flatpak_dir_get_user ());
system_dirs = flatpak_dir_get_system_list (cancellable, error);
if (system_dirs == NULL)
return FALSE;
for (i = 0; i < system_dirs->len; i++)
{
FlatpakDir *dir = g_ptr_array_index (system_dirs, i);
const char *id = flatpak_dir_get_id (dir);
if (g_strcmp0 (id, SYSTEM_DIR_DEFAULT_ID) != 0)
g_ptr_array_add (dirs, g_object_ref (dir));
}
}
}
else /* FLATPAK_BUILTIN_FLAG_ONE_DIR */
{
FlatpakDir *dir;
if ((opt_system && opt_user) ||
(opt_system && opt_installations != NULL) ||
(opt_user && opt_installations != NULL) ||
(opt_installations != NULL && opt_installations[1] != NULL))
return usage_error (context, _("Multiple installations specified for a command "
"that works on one installation"), error);
if (opt_system || (!opt_user && opt_installations == NULL))
dir = flatpak_dir_get_system_default ();
else if (opt_user)
dir = flatpak_dir_get_user ();
else if (opt_installations != NULL)
{
dir = flatpak_dir_get_system_by_id (opt_installations[0], cancellable, error);
if (dir == NULL)
return FALSE;
}
else
g_assert_not_reached ();
g_ptr_array_add (dirs, dir);
}
for (i = 0; i < dirs->len; i++)
{
FlatpakDir *dir = g_ptr_array_index (dirs, i);
if (flags & (FLATPAK_BUILTIN_FLAG_OPTIONAL_REPO |
FLATPAK_BUILTIN_FLAG_ALL_DIRS |
FLATPAK_BUILTIN_FLAG_STANDARD_DIRS))
{
if (!flatpak_dir_maybe_ensure_repo (dir, cancellable, error))
return FALSE;
}
else
{
if (!flatpak_dir_ensure_repo (dir, cancellable, error))
return FALSE;
}
flatpak_log_dir_access (dir);
}
}
if (out_dirs)
*out_dirs = g_steal_pointer (&dirs);
return TRUE;
}
gboolean
usage_error (GOptionContext *context, const char *message, GError **error)
{
g_autofree char *hint = NULL;
hint = g_strdup_printf (_("See '%s --help'"), g_get_prgname ());
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s\n\n%s", message, hint);
return FALSE;
}
static FlatpakCommand *
extract_command (int *argc,
char **argv,
const char **command_name_out)
{
FlatpakCommand *command;
const char *command_name = NULL;
int in, out;
/*
* Parse the global options. We rearrange the options as
* necessary, in order to pass relevant options through
* to the commands, but also have them take effect globally.
*/
for (in = 1, out = 1; in < *argc; in++, out++)
{
/* The non-option is the command, take it out of the arguments */
if (argv[in][0] != '-')
{
if (command_name == NULL)
{
command_name = argv[in];
out--;
continue;
}
}
argv[out] = argv[in];
}
*argc = out;
argv[out] = NULL;
command = commands;
while (command->name)
{
if (command->fn != NULL &&
g_strcmp0 (command_name, command->name) == 0)
break;
command++;
}
*command_name_out = command_name;
return command;
}
static const char *
find_similar_command (const char *word,
gboolean *option)
{
int i, d, k;
const char *suggestion;
GOptionEntry *entries[3] = { global_entries, empty_entries, user_entries };
d = G_MAXINT;
suggestion = NULL;
for (i = 0; commands[i].name; i++)
{
if (!commands[i].fn)
continue;
int d1 = flatpak_levenshtein_distance (word, -1, commands[i].name, -1);
if (d1 < d)
{
d = d1;
suggestion = commands[i].name;
*option = FALSE;
}
}
for (k = 0; k < 3; k++)
{
for (i = 0; entries[k][i].long_name; i++)
{
int d1 = flatpak_levenshtein_distance (word, -1, entries[k][i].long_name, -1);
if (d1 < d)
{
d = d1;
suggestion = entries[k][i].long_name;
*option = TRUE;
}
}
}
return suggestion;
}
static gpointer
install_polkit_agent (void)
{
gpointer agent = NULL;
#ifdef USE_SYSTEM_HELPER
PolkitAgentListener *listener = NULL;
g_autoptr(GError) local_error = NULL;
g_autoptr(GDBusConnection) bus = NULL;
const char *on_session;
on_session = g_getenv ("FLATPAK_SYSTEM_HELPER_ON_SESSION");
if (on_session != NULL)
return NULL;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &local_error);
if (bus == NULL)
{
g_info ("Unable to connect to system bus: %s", local_error->message);
return NULL;
}
/* Install a polkit agent as fallback, in case we're running on a console */
listener = flatpak_polkit_agent_text_listener_new (NULL, &local_error);
if (listener == NULL)
{
g_info ("Failed to create polkit agent listener: %s", local_error->message);
}
else
{
g_autoptr(AutoPolkitSubject) subject = NULL;
GVariantBuilder opt_builder;
g_autoptr(GVariant) options = NULL;
subject = polkit_unix_process_new_for_owner (getpid (), 0, getuid ());
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
if (g_strcmp0 (g_getenv ("FLATPAK_FORCE_TEXT_AUTH"), "1") != 0)
g_variant_builder_add (&opt_builder, "{sv}", "fallback", g_variant_new_boolean (TRUE));
options = g_variant_ref_sink (g_variant_builder_end (&opt_builder));
agent = polkit_agent_listener_register_with_options (listener,
POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD,
subject,
NULL,
options,
NULL,
&local_error);
if (agent == NULL)
{
g_info ("Failed to register polkit agent listener: %s", local_error->message);
}
g_object_unref (listener);
}
#endif
return agent;
}
static void
uninstall_polkit_agent (gpointer *agent)
{
#ifdef USE_SYSTEM_HELPER
if (*agent)
polkit_agent_listener_unregister (*agent);
#endif
}
static int
flatpak_run (int argc,
char **argv,
GError **res_error)
{
FlatpakCommand *command;
GError *error = NULL;
GCancellable *cancellable = NULL;
g_autofree char *prgname = NULL;
gboolean success = FALSE;
const char *command_name = NULL;
__attribute__((cleanup (uninstall_polkit_agent))) gpointer polkit_agent = NULL;
command = extract_command (&argc, argv, &command_name);
if (!command->fn)
{
g_autoptr(GOptionContext) context = NULL;
g_autofree char *hint = NULL;
g_autofree char *msg = NULL;
context = flatpak_option_context_new_with_commands (commands);
hint = g_strdup_printf (_("See '%s --help'"), g_get_prgname ());
if (command_name != NULL)
{
const char *similar;
gboolean option;
similar = find_similar_command (command_name, &option);
if (similar)
msg = g_strdup_printf (_("'%s' is not a flatpak command. Did you mean '%s%s'?"),
command_name, option ? "--" : "", similar);
else
msg = g_strdup_printf (_("'%s' is not a flatpak command"),
command_name);
}
else
{
g_autoptr(GError) local_error = NULL;
g_option_context_add_main_entries (context, empty_entries, NULL);
g_option_context_add_main_entries (context, global_entries, NULL);
if (g_option_context_parse (context, &argc, &argv, &local_error))
{
if (opt_version)
{
g_print ("%s\n", PACKAGE_STRING);
return EXIT_SUCCESS;
}
if (opt_default_arch)
{
g_print ("%s\n", flatpak_get_arch ());
return EXIT_SUCCESS;
}
if (opt_supported_arches)
{
const char **arches = flatpak_get_arches ();
int i;
for (i = 0; arches[i] != NULL; i++)
g_print ("%s\n", arches[i]);
return EXIT_SUCCESS;
}
if (opt_gl_drivers)
{
const char **drivers = flatpak_get_gl_drivers ();
int i;
for (i = 0; drivers[i] != NULL; i++)
g_print ("%s\n", drivers[i]);
return EXIT_SUCCESS;
}
if (opt_list_installations)
{
GPtrArray *paths;
paths = flatpak_get_system_base_dir_locations (NULL, &local_error);
if (paths)
{
guint i;
for (i = 0; i < paths->len; i++)
{
GFile *file = paths->pdata[i];
g_print ("%s\n", flatpak_file_get_path_cached (file));
}
return EXIT_SUCCESS;
}
}
/* systemd environment generator for system and user installations.
* Intended to be used only from the scripts in env.d/.
* See `man systemd.environment-generator`. */
if (opt_print_updated_env)
{
g_autoptr(GPtrArray) installations = g_ptr_array_new_with_free_func (g_free); /* (element-type GFile) */
GPtrArray *system_installation_locations; /* (element-type GFile) */
const gchar * const *xdg_data_dirs = g_get_system_data_dirs ();
g_autoptr(GPtrArray) new_dirs = g_ptr_array_new_with_free_func (g_free); /* (element-type filename) */
g_autofree gchar *new_dirs_joined = NULL;
/* Work out the set of installations we want in the environment. */
if (!opt_print_system_only)
{
g_autoptr(GFile) home_installation_location = flatpak_get_user_base_dir_location ();
g_ptr_array_add (installations, g_file_get_path (home_installation_location));
}
system_installation_locations = flatpak_get_system_base_dir_locations (NULL, &local_error);
if (local_error != NULL)
{
g_printerr ("%s\n", local_error->message);
return 1;
}
for (gsize i = 0; i < system_installation_locations->len; i++)
g_ptr_array_add (installations, g_file_get_path (system_installation_locations->pdata[i]));
/* Get the export path for each installation, and filter out
* ones which are already listed in @xdg_data_dirs. */
for (gsize i = 0; i < installations->len; i++)
{
g_autofree gchar *share_path = g_build_filename (installations->pdata[i], "exports", "share", NULL);
g_autofree gchar *share_path_with_slash = g_strconcat (share_path, "/", NULL);
if (g_strv_contains (xdg_data_dirs, share_path) || g_strv_contains (xdg_data_dirs, share_path_with_slash))
continue;
g_ptr_array_add (new_dirs, g_steal_pointer (&share_path));
}
/* Add the rest of the existing @xdg_data_dirs to the new list. */
for (gsize i = 0; xdg_data_dirs[i] != NULL; i++)
g_ptr_array_add (new_dirs, g_strdup (xdg_data_dirs[i]));
g_ptr_array_add (new_dirs, NULL);
/* Print in a format suitable for a system environment generator. */
new_dirs_joined = g_strjoinv (":", (gchar **) new_dirs->pdata);
g_print ("XDG_DATA_DIRS=%s\n", new_dirs_joined);
return EXIT_SUCCESS;
}
}
if (local_error)
msg = g_strdup (local_error->message);
else
msg = g_strdup (_("No command specified"));
}
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s\n\n%s", msg, hint);
goto out;
}
prgname = g_strdup_printf ("%s %s", g_get_prgname (), command_name);
g_set_prgname (prgname);
/* Only print environment warnings in some commonly used interactive operations so we
avoid messing up output in commands where you might parse the output. */
if (g_strcmp0 (command->name, "install") == 0 ||
g_strcmp0 (command->name, "update") == 0 ||
g_strcmp0 (command->name, "remote-add") == 0 ||
g_strcmp0 (command->name, "run") == 0)
check_environment ();
/* Don't talk to dbus in enter, as it must be thread-free to setns, also
skip run/build/history for performance reasons (no need to connect to dbus). */
if (g_strcmp0 (command->name, "enter") != 0 &&
g_strcmp0 (command->name, "run") != 0 &&
g_strcmp0 (command->name, "build") != 0 &&
g_strcmp0 (command->name, "history") != 0)
polkit_agent = install_polkit_agent ();
/* g_vfs_get_default can spawn threads */
if (g_strcmp0 (command->name, "enter") != 0)
{
g_autofree const char *old_env = NULL;
/* avoid gvfs (http://bugzilla.gnome.org/show_bug.cgi?id=526454) */
old_env = g_strdup (g_getenv ("GIO_USE_VFS"));
g_setenv ("GIO_USE_VFS", "local", TRUE);
g_vfs_get_default ();
if (old_env)
g_setenv ("GIO_USE_VFS", old_env, TRUE);
else
g_unsetenv ("GIO_USE_VFS");
}
if (!command->fn (argc, argv, cancellable, &error))
goto out;
success = TRUE;
out:
/* Note: We allow failures with NULL error (it means don't print anything), useful when e.g. the user aborted */
g_assert (!success || error == NULL);
if (error)
{
g_propagate_error (res_error, error);
}
return success ? 0 : 1;
}
static int
complete (int argc,
char **argv)
{
g_autoptr(FlatpakCompletion) completion = NULL;
FlatpakCommand *command;
const char *command_name = NULL;
is_in_complete = TRUE;
completion = flatpak_completion_new (argv[2], argv[3], argv[4]);
if (completion == NULL)
return 1;
command = extract_command (&completion->argc, completion->argv, &command_name);
flatpak_completion_debug ("command=%p '%s'", command->fn, command->name);
if (!command->fn)
{
FlatpakCommand *c = commands;
while (c->name)
{
if (c->fn != NULL && !c->deprecated)
flatpak_complete_word (completion, "%s ", c->name);
c++;
}
flatpak_complete_options (completion, global_entries);
flatpak_complete_options (completion, empty_entries);
flatpak_complete_options (completion, user_entries);
}
else if (command->complete)
{
if (!command->complete (completion))
return 1;
}
else
{
flatpak_complete_options (completion, global_entries);
}
return 0;
}
static void
handle_sigterm (int signum)
{
flatpak_disable_raw_mode ();
flatpak_show_cursor ();
_exit (1);
}
int
main (int argc,
char **argv)
{
g_autoptr(GError) error = NULL;
int ret;
struct sigaction action;
/* The child repo shared between the client process and the
system-helper really needs to support creating files that
are readable by others, so override the umask to 022.
Ideally this should be set when needed, but umask is thread-unsafe
so there is really no local way to fix this.
*/
umask(022);
memset (&action, 0, sizeof (struct sigaction));
action.sa_handler = handle_sigterm;
sigaction (SIGTERM, &action, NULL);
sigaction (SIGHUP, &action, NULL);
sigaction (SIGINT, &action, NULL);
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_WARNING, message_handler, NULL);
g_set_prgname (argv[0]);
/* Avoid weird recursive type initialization deadlocks from libsoup */
g_type_ensure (G_TYPE_SOCKET);
if (argc >= 4 && strcmp (argv[1], "complete") == 0)
return complete (argc, argv);
ret = flatpak_run (argc, argv, &error);
if (error != NULL)
{
const char *prefix = "";
const char *suffix = "";
if (flatpak_fancy_output ())
{
prefix = FLATPAK_ANSI_RED FLATPAK_ANSI_BOLD_ON;
suffix = FLATPAK_ANSI_BOLD_OFF FLATPAK_ANSI_COLOR_RESET;
}
g_dbus_error_strip_remote_error (error);
g_printerr ("%s%s %s%s\n", prefix, _("error:"), suffix, error->message);
}
return ret;
}