[gnome-software] Allow the user to restart the currently running gnome-software instance
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Allow the user to restart the currently running gnome-software instance
- Date: Tue, 21 Feb 2017 13:18:28 +0000 (UTC)
commit b4012c274e1ef2c14183f8f81fbc9e83ef92c903
Author: Richard Hughes <richard hughsie com>
Date: Tue Feb 21 12:47:28 2017 +0000
Allow the user to restart the currently running gnome-software instance
This can happen if new plugins have been installed at runtime or if the user
has used something like jhbuild or rpm to live update a running instance.
To do this, use a new helper process that watches for the D-Bus name correctly.
Resolves: https://bugzilla.gnome.org/show_bug.cgi?id=778949
src/Makefile.am | 15 +++
src/gnome-software.ui | 11 +++
src/gs-application.c | 10 ++
src/gs-restarter.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++++
src/gs-shell.c | 21 +++++
5 files changed, 286 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 7ace0d5..f964836 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -110,6 +110,7 @@ gnome-software-service.desktop: gnome-software-service.desktop.in Makefile
$(AM_V_GEN) sed -e "s|\@bindir\@|$(bindir)|" $<> $@
libexec_PROGRAMS = \
+ gnome-software-restarter \
gnome-software-cmd
gnome_software_cmd_SOURCES = \
@@ -308,6 +309,20 @@ gnome_software_LDFLAGS = \
$(PIE_LDFLAGS) \
$(RELRO_LDFLAGS)
+gnome_software_restarter_SOURCES = \
+ gs-restarter.c
+
+gnome_software_restarter_LDADD = \
+ $(GLIB_LIBS)
+
+gnome_software_restarter_CFLAGS = \
+ -DLIBEXECDIR=\"$(libexecdir)\" \
+ $(WARN_CFLAGS)
+
+gnome_software_restarter_LDFLAGS = \
+ $(PIE_LDFLAGS) \
+ $(RELRO_LDFLAGS)
+
packagekit_built_sources = gs-packagekit-generated.c gs-packagekit-generated.h
$(packagekit_built_sources): Makefile.am org.freedesktop.PackageKit.xml
$(AM_V_GEN) gdbus-codegen \
diff --git a/src/gnome-software.ui b/src/gnome-software.ui
index 16d0a57..2e5e1a5 100644
--- a/src/gnome-software.ui
+++ b/src/gnome-software.ui
@@ -378,6 +378,17 @@
</packing>
</child>
<child>
+ <object class="GtkButton" id="button_events_restart_required">
+ <property name="label" translatable="yes" comments="button in the info
bar">Restart Now</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkButton" id="button_events_more_info">
<property name="label" translatable="yes" comments="button in the info bar">More
Information</property>
<property name="can_focus">True</property>
diff --git a/src/gs-application.c b/src/gs-application.c
index 0f6ace1..5abf7b1 100644
--- a/src/gs-application.c
+++ b/src/gs-application.c
@@ -446,6 +446,15 @@ reboot_activated (GSimpleAction *action,
}
static void
+shutdown_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ GsApplication *app = GS_APPLICATION (data);
+ g_application_quit (G_APPLICATION (app));
+}
+
+static void
reboot_and_install (GSimpleAction *action,
GVariant *parameter,
gpointer data)
@@ -718,6 +727,7 @@ static GActionEntry actions[] = {
{ "profile", profile_activated, NULL, NULL, NULL },
{ "reboot-and-install", reboot_and_install, NULL, NULL, NULL },
{ "reboot", reboot_activated, NULL, NULL, NULL },
+ { "shutdown", shutdown_activated, NULL, NULL, NULL },
{ "set-mode", set_mode_activated, "s", NULL, NULL },
{ "search", search_activated, "s", NULL, NULL },
{ "details", details_activated, "(ss)", NULL, NULL },
diff --git a/src/gs-restarter.c b/src/gs-restarter.c
new file mode 100644
index 0000000..f95e2fd
--- /dev/null
+++ b/src/gs-restarter.c
@@ -0,0 +1,229 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <stdlib.h>
+
+#define GS_BINARY_NAME "gnome-software"
+#define GS_DBUS_BUS_NAME "org.gnome.Software"
+#define GS_DBUS_OBJECT_PATH "/org/gnome/Software"
+#define GS_DBUS_INTERFACE_NAME "org.gtk.Actions"
+
+typedef struct {
+ GMainLoop *loop;
+ GDBusConnection *connection;
+ gboolean is_running;
+ gboolean timed_out;
+} GsRestarterPrivate;
+
+static void
+gs_restarter_on_name_appeared_cb (GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer user_data)
+{
+ GsRestarterPrivate *priv = (GsRestarterPrivate *) user_data;
+ priv->is_running = TRUE;
+ g_debug ("%s appeared", GS_DBUS_BUS_NAME);
+ if (g_main_loop_is_running (priv->loop))
+ g_main_loop_quit (priv->loop);
+}
+
+static void
+gs_restarter_on_name_vanished_cb (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GsRestarterPrivate *priv = (GsRestarterPrivate *) user_data;
+ priv->is_running = FALSE;
+ g_debug ("%s vanished", GS_DBUS_BUS_NAME);
+ if (g_main_loop_is_running (priv->loop))
+ g_main_loop_quit (priv->loop);
+}
+
+static gboolean
+gs_restarter_loop_timeout_cb (gpointer user_data)
+{
+ GsRestarterPrivate *priv = (GsRestarterPrivate *) user_data;
+ priv->timed_out = TRUE;
+ g_main_loop_quit (priv->loop);
+ return TRUE;
+}
+
+static gboolean
+gs_restarter_wait_for_timeout (GsRestarterPrivate *priv,
+ guint timeout_ms,
+ GError **error)
+{
+ guint timer_id;
+ priv->timed_out = FALSE;
+ timer_id = g_timeout_add (timeout_ms, gs_restarter_loop_timeout_cb, priv);
+ g_main_loop_run (priv->loop);
+ g_source_remove (timer_id);
+ if (priv->timed_out) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_TIMED_OUT,
+ "Waited for %ums", timeout_ms);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static GsRestarterPrivate *
+gs_restarter_private_new (void)
+{
+ GsRestarterPrivate *priv = g_new0 (GsRestarterPrivate, 1);
+ priv->loop = g_main_loop_new (NULL, FALSE);
+ return priv;
+}
+
+static void
+gs_restarter_private_free (GsRestarterPrivate *priv)
+{
+ if (priv->connection != NULL)
+ g_object_unref (priv->connection);
+ g_main_loop_unref (priv->loop);
+ g_free (priv);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GsRestarterPrivate, gs_restarter_private_free)
+
+static gboolean
+gs_restarter_create_new_process (GsRestarterPrivate *priv, GError **error)
+{
+ g_autofree gchar *binary_filename = NULL;
+
+ /* start executable */
+ binary_filename = g_build_filename (BINDIR, GS_BINARY_NAME, NULL);
+ g_debug ("starting new binary %s", binary_filename);
+ if (!g_spawn_command_line_async (binary_filename, error))
+ return FALSE;
+
+ /* wait for the bus name to appear */
+ if (!gs_restarter_wait_for_timeout (priv, 15000, error)) {
+ g_prefix_error (error, "%s did not appear: ", GS_DBUS_BUS_NAME);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gs_restarter_destroy_old_process (GsRestarterPrivate *priv, GError **error)
+{
+ GVariantBuilder args_params;
+ GVariantBuilder args_platform_data;
+ g_autoptr(GVariant) reply = NULL;
+
+ /* call a GtkAction */
+ g_variant_builder_init (&args_params, g_variant_type_new ("av"));
+ g_variant_builder_init (&args_platform_data, g_variant_type_new ("a{sv}"));
+ reply = g_dbus_connection_call_sync (priv->connection,
+ GS_DBUS_BUS_NAME,
+ GS_DBUS_OBJECT_PATH,
+ GS_DBUS_INTERFACE_NAME,
+ "Activate",
+ g_variant_new ("(sava{sv})",
+ "shutdown",
+ &args_params,
+ &args_platform_data),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 5000,
+ NULL,
+ error);
+ if (reply == NULL) {
+ g_prefix_error (error, "Failed to send RequestShutdown: ");
+ return FALSE;
+ }
+
+ /* wait for the name to disappear from the bus */
+ if (!gs_restarter_wait_for_timeout (priv, 30000, error)) {
+ g_prefix_error (error, "Failed to see %s vanish: ", GS_DBUS_BUS_NAME);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gs_restarter_setup_watcher (GsRestarterPrivate *priv, GError **error)
+{
+ /* watch the name appear and vanish */
+ priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+ if (priv->connection == NULL) {
+ g_prefix_error (error, "Failed to get D-Bus connection: ");
+ return FALSE;
+ }
+ g_bus_watch_name_on_connection (priv->connection,
+ GS_DBUS_BUS_NAME,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ gs_restarter_on_name_appeared_cb,
+ gs_restarter_on_name_vanished_cb,
+ priv,
+ NULL);
+
+ /* wait for one of the callbacks to be called */
+ if (!gs_restarter_wait_for_timeout (priv, 50, error)) {
+ g_prefix_error (error, "Failed to watch %s: ", GS_DBUS_BUS_NAME);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+ g_autoptr(GsRestarterPrivate) priv = NULL;
+ g_autoptr(GError) error = NULL;
+
+ /* show all debugging */
+ g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
+
+ /* set up the watcher */
+ priv = gs_restarter_private_new ();
+ if (!gs_restarter_setup_watcher (priv, &error)) {
+ g_warning ("Failed to set up: %s", error->message);
+ return EXIT_FAILURE;
+ }
+
+ /* kill the old process */
+ if (priv->is_running) {
+ if (!gs_restarter_destroy_old_process (priv, &error)) {
+ g_warning ("Failed to quit service: %s", error->message);
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* start a new process */
+ if (!gs_restarter_create_new_process (priv, &error)) {
+ g_warning ("Failed to start service: %s", error->message);
+ return EXIT_FAILURE;
+ }
+
+ /* success */
+ g_debug ("%s process successfully restarted", GS_DBUS_BUS_NAME);
+ return EXIT_SUCCESS;
+}
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 9744777..5a057c6 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -480,6 +480,14 @@ gs_shell_plugin_events_more_info_cb (GtkWidget *widget, GsShell *shell)
}
static void
+gs_shell_plugin_events_restart_required_cb (GtkWidget *widget, GsShell *shell)
+{
+ g_autoptr(GError) error = NULL;
+ if (!g_spawn_command_line_async (LIBEXECDIR "/gnome-software-restarter", &error))
+ g_warning ("failed to restart: %s", error->message);
+}
+
+static void
gs_shell_go_back (GsShell *shell)
{
GsShellPrivate *priv = gs_shell_get_instance_private (shell);
@@ -738,6 +746,7 @@ typedef enum {
GS_SHELL_EVENT_BUTTON_NO_SPACE = 1 << 1,
GS_SHELL_EVENT_BUTTON_NETWORK_SETTINGS = 1 << 2,
GS_SHELL_EVENT_BUTTON_MORE_INFO = 1 << 3,
+ GS_SHELL_EVENT_BUTTON_RESTART_REQUIRED = 1 << 4,
GS_SHELL_EVENT_BUTTON_LAST
} GsShellEventButtons;
@@ -765,10 +774,18 @@ gs_shell_show_event_app_notify (GsShell *shell,
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_events_network_settings"));
gtk_widget_set_visible (widget, (buttons & GS_SHELL_EVENT_BUTTON_NETWORK_SETTINGS) > 0);
+ /* restart button */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_events_restart_required"));
+ gtk_widget_set_visible (widget, (buttons & GS_SHELL_EVENT_BUTTON_RESTART_REQUIRED) > 0);
+
/* more-info button */
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_events_more_info"));
gtk_widget_set_visible (widget, (buttons & GS_SHELL_EVENT_BUTTON_MORE_INFO) > 0);
+ /* dismiss button */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_events_dismiss"));
+ gtk_widget_set_visible (widget, (buttons & GS_SHELL_EVENT_BUTTON_RESTART_REQUIRED) == 0);
+
/* set title */
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "label_events"));
gtk_label_set_markup (GTK_LABEL (widget), title);
@@ -1451,6 +1468,7 @@ gs_shell_show_event_fallback (GsShell *shell, GsPluginEvent *event)
g_string_append (str, _("This application needs to be "
"restarted to use new plugins."));
}
+ buttons |= GS_SHELL_EVENT_BUTTON_RESTART_REQUIRED;
break;
case GS_PLUGIN_ERROR_AC_POWER_REQUIRED:
/* TRANSLATORS: need to be connected to the AC power */
@@ -1669,6 +1687,9 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_events_more_info"));
g_signal_connect (widget, "clicked",
G_CALLBACK (gs_shell_plugin_events_more_info_cb), shell);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_events_restart_required"));
+ g_signal_connect (widget, "clicked",
+ G_CALLBACK (gs_shell_plugin_events_restart_required_cb), shell);
priv->shell_overview = GS_SHELL_OVERVIEW (gtk_builder_get_object (priv->builder, "shell_overview"));
gs_shell_overview_setup (priv->shell_overview,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]