f2cf3a09ed
Resolves: #1796190 Rework how subscription-manager plugin is conditionalized so it doens't get built on centos 8 stream.
1951 lines
65 KiB
Diff
1951 lines
65 KiB
Diff
From 13f7bc5958dc4ae7139e195098e5fbee58455f43 Mon Sep 17 00:00:00 2001
|
|
From: Richard Hughes <rhughes@redhat.com>
|
|
Date: Thu, 20 Aug 2020 11:16:09 -0400
|
|
Subject: [PATCH 01/16] subman: Add a new plugin to provide system subscription
|
|
registration
|
|
|
|
---
|
|
meson.build | 6 +
|
|
meson_options.txt | 1 +
|
|
plugins/dummy/meson.build | 4 +
|
|
plugins/meson.build | 4 +
|
|
plugins/subman/README.md | 56 +
|
|
plugins/subman/gsd-subman-common.c | 36 +
|
|
plugins/subman/gsd-subman-common.h | 40 +
|
|
plugins/subman/gsd-subman-helper.c | 378 +++++++
|
|
plugins/subman/gsd-subscription-manager.c | 982 ++++++++++++++++++
|
|
plugins/subman/gsd-subscription-manager.h | 63 ++
|
|
plugins/subman/main.c | 8 +
|
|
plugins/subman/meson.build | 56 +
|
|
...ome.SettingsDaemon.Subscription.desktop.in | 9 +
|
|
...ettings-daemon.plugins.subman.policy.in.in | 27 +
|
|
...gnome.settings-daemon.plugins.subman.rules | 7 +
|
|
15 files changed, 1677 insertions(+)
|
|
create mode 100644 plugins/subman/README.md
|
|
create mode 100644 plugins/subman/gsd-subman-common.c
|
|
create mode 100644 plugins/subman/gsd-subman-common.h
|
|
create mode 100644 plugins/subman/gsd-subman-helper.c
|
|
create mode 100644 plugins/subman/gsd-subscription-manager.c
|
|
create mode 100644 plugins/subman/gsd-subscription-manager.h
|
|
create mode 100644 plugins/subman/main.c
|
|
create mode 100644 plugins/subman/meson.build
|
|
create mode 100644 plugins/subman/org.gnome.SettingsDaemon.Subscription.desktop.in
|
|
create mode 100644 plugins/subman/org.gnome.settings-daemon.plugins.subman.policy.in.in
|
|
create mode 100644 plugins/subman/org.gnome.settings-daemon.plugins.subman.rules
|
|
|
|
diff --git a/meson.build b/meson.build
|
|
index 1632ea05..7a0be003 100644
|
|
--- a/meson.build
|
|
+++ b/meson.build
|
|
@@ -121,60 +121,66 @@ libgvc_dep = libgvc.get_variable('libgvc_dep')
|
|
# GUdev integration (default enabled)
|
|
enable_gudev = get_option('gudev')
|
|
if enable_gudev
|
|
gudev_dep = dependency('gudev-1.0')
|
|
endif
|
|
config_h.set10('HAVE_GUDEV', enable_gudev)
|
|
if host_is_linux
|
|
assert(enable_gudev, 'GUdev is not optional on Linux platforms')
|
|
endif
|
|
|
|
has_timerfd_create = cc.has_function('timerfd_create')
|
|
config_h.set10('HAVE_TIMERFD', has_timerfd_create)
|
|
|
|
# Check for wayland dependencies
|
|
enable_wayland = get_option('wayland')
|
|
if enable_wayland
|
|
assert(enable_gudev, 'GUDev support is required for wayland support.')
|
|
wayland_client_dep = dependency('wayland-client')
|
|
wayland_gdk_dep = dependency('gdk-wayland-3.0')
|
|
endif
|
|
config_h.set10('HAVE_WAYLAND', enable_wayland)
|
|
|
|
# wacom (disabled for s390/s390x and non Linux platforms)
|
|
enable_wacom = host_is_linux_not_s390
|
|
if enable_wacom
|
|
assert(enable_gudev, 'GUDev support is required for wacom support.')
|
|
libwacom_dep = dependency('libwacom', version: '>= 0.7')
|
|
endif
|
|
config_h.set10('HAVE_WACOM', enable_wacom)
|
|
|
|
+# subscription manager section
|
|
+enable_subman = get_option('subscription_manager')
|
|
+if enable_subman
|
|
+ jsonglib_dep = dependency('json-glib-1.0', version: '>= 1.1.1')
|
|
+endif
|
|
+
|
|
# smartcard section
|
|
enable_smartcard = get_option('smartcard')
|
|
if enable_smartcard
|
|
nss_dep = dependency('nss', version: '>= 3.11.2')
|
|
|
|
system_nssdb_dir = get_option('nssdb_dir')
|
|
if system_nssdb_dir == ''
|
|
system_nssdb_dir = join_paths(gsd_sysconfdir, 'pki', 'nssdb')
|
|
endif
|
|
endif
|
|
|
|
# CUPS
|
|
enable_cups = get_option('cups')
|
|
if enable_cups
|
|
cups_dep = dependency('cups', version : '>= 1.4', required: false)
|
|
assert(cups_dep.found(), 'CUPS 1.4 or newer not found')
|
|
|
|
# FIXME: 1.6 cflags generate a lot of errors
|
|
'''
|
|
cups_cflags = []
|
|
if cups_dep.version().version_compare('>= 1.6')
|
|
cups_cflags += '-D_PPD_DEPRECATED=""'
|
|
endif
|
|
|
|
cups_dep = declare_dependency(
|
|
dependencies: cups_dep,
|
|
compile_args: cups_cflags
|
|
)
|
|
'''
|
|
endif
|
|
diff --git a/meson_options.txt b/meson_options.txt
|
|
index 50bd1749..4327e07b 100644
|
|
--- a/meson_options.txt
|
|
+++ b/meson_options.txt
|
|
@@ -1,10 +1,11 @@
|
|
option('nssdb_dir', type: 'string', value: '', description: 'Absolute path to the system NSS database directory')
|
|
option('udev_dir', type: 'string', value: '', description: 'Absolute path of the udev base directory')
|
|
|
|
option('alsa', type: 'boolean', value: true, description: 'build with ALSA support (not optional on Linux platforms)')
|
|
option('gudev', type: 'boolean', value: true, description: 'build with gudev device support (not optional on Linux platforms)')
|
|
option('cups', type: 'boolean', value: true, description: 'build with CUPS support')
|
|
option('network_manager', type: 'boolean', value: true, description: 'build with NetworkManager support (not optional on Linux platforms)')
|
|
option('rfkill', type: 'boolean', value: true, description: 'build with rfkill support (not optional on Linux platforms)')
|
|
+option('subscription_manager', type: 'boolean', value: true, description: 'build with subscription-manager support')
|
|
option('smartcard', type: 'boolean', value: true, description: 'build with smartcard support')
|
|
option('wayland', type: 'boolean', value: true, description: 'build with Wayland support')
|
|
diff --git a/plugins/dummy/meson.build b/plugins/dummy/meson.build
|
|
index f563efa6..96105061 100644
|
|
--- a/plugins/dummy/meson.build
|
|
+++ b/plugins/dummy/meson.build
|
|
@@ -1,31 +1,35 @@
|
|
desktops = []
|
|
+if not enable_subman
|
|
+ desktops += ['org.gnome.SettingsDaemon.Subscription']
|
|
+endif
|
|
+
|
|
if not enable_smartcard
|
|
desktops += ['org.gnome.SettingsDaemon.Smartcard']
|
|
endif
|
|
|
|
if not enable_cups
|
|
desktops += ['org.gnome.SettingsDaemon.PrintNotifications']
|
|
endif
|
|
|
|
if not enable_rfkill
|
|
desktops += ['org.gnome.SettingsDaemon.Rfkill']
|
|
endif
|
|
|
|
if not enable_wacom
|
|
desktops += ['org.gnome.SettingsDaemon.Wacom']
|
|
endif
|
|
|
|
foreach desktop: desktops
|
|
dummy_conf = configuration_data()
|
|
dummy_conf.set('libexecdir', gsd_libexecdir)
|
|
dummy_conf.set('pluginname', desktop)
|
|
|
|
configure_file(
|
|
input: 'org.gnome.SettingsDaemon.Dummy.desktop.in',
|
|
output: desktop + '.desktop',
|
|
configuration: dummy_conf,
|
|
install: true,
|
|
install_dir: gsd_xdg_autostart
|
|
)
|
|
endforeach
|
|
|
|
diff --git a/plugins/meson.build b/plugins/meson.build
|
|
index 3c4d42ac..4fe46b68 100644
|
|
--- a/plugins/meson.build
|
|
+++ b/plugins/meson.build
|
|
@@ -1,48 +1,52 @@
|
|
enabled_plugins = [
|
|
['a11y-settings', 'A11ySettings'],
|
|
['account', 'Account'],
|
|
['clipboard', 'Clipboard'],
|
|
['color', 'Color'],
|
|
['datetime', 'Datetime'],
|
|
['dummy', ''],
|
|
['power', 'Power'],
|
|
['housekeeping', 'Housekeeping'],
|
|
['keyboard', 'Keyboard'],
|
|
['media-keys', 'MediaKeys'],
|
|
['mouse', 'Mouse'],
|
|
['screensaver-proxy', 'ScreensaverProxy'],
|
|
['sharing', 'Sharing'],
|
|
['sound', 'Sound'],
|
|
['xsettings', 'XSettings']
|
|
]
|
|
|
|
+if enable_subman
|
|
+ enabled_plugins += [['subman', 'Subscription']]
|
|
+endif
|
|
+
|
|
if enable_smartcard
|
|
enabled_plugins += [['smartcard', 'Smartcard']]
|
|
endif
|
|
|
|
if enable_wacom
|
|
enabled_plugins += [['wacom', 'Wacom']]
|
|
endif
|
|
|
|
if enable_cups
|
|
enabled_plugins += [['print-notifications', 'PrintNotifications']]
|
|
endif
|
|
|
|
if enable_rfkill
|
|
enabled_plugins += [['rfkill', 'Rfkill']]
|
|
endif
|
|
|
|
plugins_conf = configuration_data()
|
|
plugins_conf.set('libexecdir', gsd_libexecdir)
|
|
|
|
plugins_deps = [libgsd_dep]
|
|
|
|
plugins_cflags = ['-DGNOME_SETTINGS_LOCALEDIR="@0@"'.format(gsd_localedir)]
|
|
|
|
foreach plugin: [['common', '']] + enabled_plugins
|
|
plugin_name = plugin[0]
|
|
|
|
cflags = [
|
|
'-DG_LOG_DOMAIN="@0@-plugin"'.format(plugin_name),
|
|
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
|
|
] + plugins_cflags
|
|
diff --git a/plugins/subman/README.md b/plugins/subman/README.md
|
|
new file mode 100644
|
|
index 00000000..3e1cc3cd
|
|
--- /dev/null
|
|
+++ b/plugins/subman/README.md
|
|
@@ -0,0 +1,56 @@
|
|
+GNOME Settings Daemon: Subscription Manager Plugin
|
|
+==================================================
|
|
+
|
|
+Testing:
|
|
+
|
|
+To add a test acccount on subscription.rhsm.stage.redhat.com, use Ethel:
|
|
+http://account-manager-stage.app.eng.rdu2.redhat.com/#view
|
|
+
|
|
+Register with a username and password
|
|
+-------------------------------------
|
|
+
|
|
+ gdbus call \
|
|
+ --session \
|
|
+ --dest org.gnome.SettingsDaemon.Subscription \
|
|
+ --object-path /org/gnome/SettingsDaemon/Subscription \
|
|
+ --method org.gnome.SettingsDaemon.Subscription.Register "{'kind':<'username'>,'hostname':<'subscription.rhsm.stage.redhat.com'>,'username':<'rhughes_test'>,'password':<'barbaz'>}"
|
|
+
|
|
+To register with a certificate
|
|
+------------------------------
|
|
+
|
|
+ gdbus call \
|
|
+ --session \
|
|
+ --dest org.gnome.SettingsDaemon.Subscription \
|
|
+ --object-path /org/gnome/SettingsDaemon/Subscription \
|
|
+ --method org.gnome.SettingsDaemon.Subscription.Register "{'kind':<'key'>,'hostname':<'subscription.rhsm.stage.redhat.com'>,'organisation':<'foo'>,'activation-key':<'barbaz'>}"
|
|
+
|
|
+To unregister
|
|
+-------------
|
|
+
|
|
+ gdbus call \
|
|
+ --session \
|
|
+ --dest org.gnome.SettingsDaemon.Subscription \
|
|
+ --object-path /org/gnome/SettingsDaemon/Subscription \
|
|
+ --method org.gnome.SettingsDaemon.Subscription.Unregister
|
|
+
|
|
+Debugging
|
|
+---------
|
|
+
|
|
+Get the UNIX socket using `Subscription.Register` then call something like:
|
|
+
|
|
+ sudo G_MESSAGES_DEBUG=all ./plugins/subman/gsd-subman-helper \
|
|
+ --address="unix:abstract=/var/run/dbus-ulGB1wfnbn,guid=71e6bf329d861ce366df7a1d5d036a5b" \
|
|
+ --kind="register-with-username" \
|
|
+ --username="rhughes_test" \
|
|
+ --password="barbaz" \
|
|
+ --hostname="subscription.rhsm.stage.redhat.com" \
|
|
+ --organisation=""
|
|
+
|
|
+You can all see some basic debugging running `rhsmd` in the foreground:
|
|
+
|
|
+ sudo /usr/libexec/rhsmd -d -k
|
|
+
|
|
+Known Limitations
|
|
+=================
|
|
+
|
|
+Proxy servers are not supported, nor are custom host ports or prefixes.
|
|
diff --git a/plugins/subman/gsd-subman-common.c b/plugins/subman/gsd-subman-common.c
|
|
new file mode 100644
|
|
index 00000000..e515131e
|
|
--- /dev/null
|
|
+++ b/plugins/subman/gsd-subman-common.c
|
|
@@ -0,0 +1,36 @@
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
+ *
|
|
+ * Copyright (C) 2019 Richard Hughes <rhughes@redhat.com>
|
|
+ *
|
|
+ * 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, see <http://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "config.h"
|
|
+
|
|
+#include "gsd-subman-common.h"
|
|
+
|
|
+const gchar *
|
|
+gsd_subman_subscription_status_to_string (GsdSubmanSubscriptionStatus status)
|
|
+{
|
|
+ if (status == GSD_SUBMAN_SUBSCRIPTION_STATUS_VALID)
|
|
+ return "valid";
|
|
+ if (status == GSD_SUBMAN_SUBSCRIPTION_STATUS_INVALID)
|
|
+ return "invalid";
|
|
+ if (status == GSD_SUBMAN_SUBSCRIPTION_STATUS_DISABLED)
|
|
+ return "disabled";
|
|
+ if (status == GSD_SUBMAN_SUBSCRIPTION_STATUS_PARTIALLY_VALID)
|
|
+ return "partially-valid";
|
|
+ return "unknown";
|
|
+}
|
|
diff --git a/plugins/subman/gsd-subman-common.h b/plugins/subman/gsd-subman-common.h
|
|
new file mode 100644
|
|
index 00000000..fccf9f6a
|
|
--- /dev/null
|
|
+++ b/plugins/subman/gsd-subman-common.h
|
|
@@ -0,0 +1,40 @@
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
+ *
|
|
+ * Copyright (C) 2019 Richard Hughes <rhughes@redhat.com>
|
|
+ *
|
|
+ * 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, see <http://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __GSD_SUBMAN_COMMON_H
|
|
+#define __GSD_SUBMAN_COMMON_H
|
|
+
|
|
+#include <glib-object.h>
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+typedef enum {
|
|
+ GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN,
|
|
+ GSD_SUBMAN_SUBSCRIPTION_STATUS_VALID,
|
|
+ GSD_SUBMAN_SUBSCRIPTION_STATUS_INVALID,
|
|
+ GSD_SUBMAN_SUBSCRIPTION_STATUS_DISABLED,
|
|
+ GSD_SUBMAN_SUBSCRIPTION_STATUS_PARTIALLY_VALID,
|
|
+ GSD_SUBMAN_SUBSCRIPTION_STATUS_LAST
|
|
+} GsdSubmanSubscriptionStatus;
|
|
+
|
|
+const gchar *gsd_subman_subscription_status_to_string (GsdSubmanSubscriptionStatus status);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __GSD_SUBMAN_COMMON_H */
|
|
diff --git a/plugins/subman/gsd-subman-helper.c b/plugins/subman/gsd-subman-helper.c
|
|
new file mode 100644
|
|
index 00000000..182f7190
|
|
--- /dev/null
|
|
+++ b/plugins/subman/gsd-subman-helper.c
|
|
@@ -0,0 +1,378 @@
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
|
+ *
|
|
+ * Copyright (C) 2019 Richard Hughes <rhughes@redhat.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 <sys/types.h>
|
|
+#include <unistd.h>
|
|
+#include <stdlib.h>
|
|
+
|
|
+#include <gio/gio.h>
|
|
+#include <json-glib/json-glib.h>
|
|
+
|
|
+static void
|
|
+_helper_convert_error (const gchar *json_txt, GError **error)
|
|
+{
|
|
+ JsonNode *json_root;
|
|
+ JsonObject *json_obj;
|
|
+ const gchar *message;
|
|
+ g_autoptr(JsonParser) json_parser = json_parser_new ();
|
|
+
|
|
+ /* this may be plain text or JSON :| */
|
|
+ if (!json_parser_load_from_data (json_parser, json_txt, -1, NULL)) {
|
|
+ g_set_error_literal (error,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_NOT_SUPPORTED,
|
|
+ json_txt);
|
|
+ return;
|
|
+ }
|
|
+ json_root = json_parser_get_root (json_parser);
|
|
+ json_obj = json_node_get_object (json_root);
|
|
+ if (!json_object_has_member (json_obj, "message")) {
|
|
+ g_set_error (error,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_INVALID_DATA,
|
|
+ "no message' in %s", json_txt);
|
|
+ return;
|
|
+ }
|
|
+ message = json_object_get_string_member (json_obj, "message");
|
|
+ if (g_strstr_len (message, -1, "Invalid user credentials") != NULL) {
|
|
+ g_set_error_literal (error,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_PERMISSION_DENIED,
|
|
+ message);
|
|
+ return;
|
|
+ }
|
|
+ g_set_error_literal (error,
|
|
+ G_IO_ERROR,
|
|
+ G_IO_ERROR_NOT_SUPPORTED,
|
|
+ message);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_helper_unregister (GError **error)
|
|
+{
|
|
+ g_autoptr(GDBusProxy) proxy = NULL;
|
|
+ g_autoptr(GVariantBuilder) proxy_options = NULL;
|
|
+ g_autoptr(GVariant) res = NULL;
|
|
+
|
|
+ g_debug ("unregistering");
|
|
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
|
|
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
|
|
+ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
|
|
+ NULL,
|
|
+ "com.redhat.RHSM1",
|
|
+ "/com/redhat/RHSM1/Unregister",
|
|
+ "com.redhat.RHSM1.Unregister",
|
|
+ NULL, error);
|
|
+ if (proxy == NULL) {
|
|
+ g_prefix_error (error, "Failed to get proxy: ");
|
|
+ return FALSE;
|
|
+ }
|
|
+ proxy_options = g_variant_builder_new (G_VARIANT_TYPE_VARDICT);
|
|
+ res = g_dbus_proxy_call_sync (proxy,
|
|
+ "Unregister",
|
|
+ g_variant_new ("(a{sv}s)",
|
|
+ proxy_options,
|
|
+ ""), /* lang */
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ -1, NULL, error);
|
|
+ return res != NULL;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_helper_auto_attach (GError **error)
|
|
+{
|
|
+ const gchar *str = NULL;
|
|
+ g_autoptr(GDBusProxy) proxy = NULL;
|
|
+ g_autoptr(GVariantBuilder) proxy_options = NULL;
|
|
+ g_autoptr(GVariant) res = NULL;
|
|
+
|
|
+ g_debug ("auto-attaching subscriptions");
|
|
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
|
|
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
|
|
+ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
|
|
+ NULL,
|
|
+ "com.redhat.RHSM1",
|
|
+ "/com/redhat/RHSM1/Attach",
|
|
+ "com.redhat.RHSM1.Attach",
|
|
+ NULL, error);
|
|
+ if (proxy == NULL) {
|
|
+ g_prefix_error (error, "Failed to get proxy: ");
|
|
+ return FALSE;
|
|
+ }
|
|
+ proxy_options = g_variant_builder_new (G_VARIANT_TYPE_VARDICT);
|
|
+ res = g_dbus_proxy_call_sync (proxy,
|
|
+ "AutoAttach",
|
|
+ g_variant_new ("(sa{sv}s)",
|
|
+ "", /* now? */
|
|
+ proxy_options,
|
|
+ ""), /* lang */
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ -1, NULL, error);
|
|
+ if (res == NULL)
|
|
+ return FALSE;
|
|
+ g_variant_get (res, "(&s)", &str);
|
|
+ g_debug ("Attach.AutoAttach: %s", str);
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_helper_save_config (const gchar *key, const gchar *value, GError **error)
|
|
+{
|
|
+ g_autoptr(GDBusProxy) proxy = NULL;
|
|
+ g_autoptr(GVariant) res = NULL;
|
|
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
|
|
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
|
|
+ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
|
|
+ NULL,
|
|
+ "com.redhat.RHSM1",
|
|
+ "/com/redhat/RHSM1/Config",
|
|
+ "com.redhat.RHSM1.Config",
|
|
+ NULL, error);
|
|
+ if (proxy == NULL) {
|
|
+ g_prefix_error (error, "Failed to get proxy: ");
|
|
+ return FALSE;
|
|
+ }
|
|
+ res = g_dbus_proxy_call_sync (proxy, "Set",
|
|
+ g_variant_new ("(svs)",
|
|
+ key,
|
|
+ g_variant_new_string (value),
|
|
+ ""), /* lang */
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ -1, NULL, error);
|
|
+ return res != NULL;
|
|
+}
|
|
+
|
|
+int
|
|
+main (int argc, char *argv[])
|
|
+{
|
|
+ const gchar *userlang = ""; /* as root, so no translations */
|
|
+ g_autofree gchar *activation_key = NULL;
|
|
+ g_autofree gchar *address = NULL;
|
|
+ g_autofree gchar *hostname = NULL;
|
|
+ g_autofree gchar *kind = NULL;
|
|
+ g_autofree gchar *organisation = NULL;
|
|
+ g_autofree gchar *password = NULL;
|
|
+ g_autofree gchar *port = NULL;
|
|
+ g_autofree gchar *prefix = NULL;
|
|
+ g_autofree gchar *proxy_server = NULL;
|
|
+ g_autofree gchar *username = NULL;
|
|
+ g_autoptr(GDBusConnection) conn_private = NULL;
|
|
+ g_autoptr(GDBusProxy) proxy = NULL;
|
|
+ g_autoptr(GError) error = NULL;
|
|
+ g_autoptr(GOptionContext) context = g_option_context_new (NULL);
|
|
+ g_autoptr(GVariantBuilder) proxy_options = NULL;
|
|
+ g_autoptr(GVariantBuilder) subman_conopts = NULL;
|
|
+ g_autoptr(GVariantBuilder) subman_options = NULL;
|
|
+
|
|
+ const GOptionEntry options[] = {
|
|
+ { "kind", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING,
|
|
+ &kind, "Kind, e.g. 'username' or 'key'", NULL },
|
|
+ { "address", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING,
|
|
+ &address, "UNIX address", NULL },
|
|
+ { "username", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING,
|
|
+ &username, "Username", NULL },
|
|
+ { "password", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING,
|
|
+ &password, "Password", NULL },
|
|
+ { "organisation", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING,
|
|
+ &organisation, "Organisation", NULL },
|
|
+ { "activation-key", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING,
|
|
+ &activation_key, "Activation keys", NULL },
|
|
+ { "hostname", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING,
|
|
+ &hostname, "Registration server hostname", NULL },
|
|
+ { "prefix", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING,
|
|
+ &prefix, "Registration server prefix", NULL },
|
|
+ { "port", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING,
|
|
+ &port, "Registration server port", NULL },
|
|
+ { "proxy", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING,
|
|
+ &proxy_server, "Proxy settings", NULL },
|
|
+ { NULL}
|
|
+ };
|
|
+
|
|
+ /* check calling UID */
|
|
+ if (getuid () != 0 || geteuid () != 0) {
|
|
+ g_printerr ("This program can only be used by the root user\n");
|
|
+ return G_IO_ERROR_NOT_SUPPORTED;
|
|
+ }
|
|
+ g_option_context_add_main_entries (context, options, NULL);
|
|
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
|
|
+ g_printerr ("Failed to parse arguments: %s\n", error->message);
|
|
+ return G_IO_ERROR_NOT_SUPPORTED;
|
|
+ }
|
|
+
|
|
+ /* uncommon actions */
|
|
+ if (kind == NULL) {
|
|
+ g_printerr ("No --kind specified\n");
|
|
+ return G_IO_ERROR_INVALID_DATA;
|
|
+ }
|
|
+ if (g_strcmp0 (kind, "unregister") == 0) {
|
|
+ if (!_helper_unregister (&error)) {
|
|
+ g_printerr ("Failed to Unregister: %s\n", error->message);
|
|
+ return G_IO_ERROR_NOT_INITIALIZED;
|
|
+ }
|
|
+ return EXIT_SUCCESS;
|
|
+ }
|
|
+ if (g_strcmp0 (kind, "auto-attach") == 0) {
|
|
+ if (!_helper_auto_attach (&error)) {
|
|
+ g_printerr ("Failed to AutoAttach: %s\n", error->message);
|
|
+ return G_IO_ERROR_NOT_INITIALIZED;
|
|
+ }
|
|
+ return EXIT_SUCCESS;
|
|
+ }
|
|
+
|
|
+ /* connect to abstract socket for reasons */
|
|
+ if (address == NULL) {
|
|
+ g_printerr ("No --address specified\n");
|
|
+ return G_IO_ERROR_INVALID_DATA;
|
|
+ }
|
|
+ conn_private = g_dbus_connection_new_for_address_sync (address,
|
|
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
|
+ NULL, NULL,
|
|
+ &error);
|
|
+ if (conn_private == NULL) {
|
|
+ g_printerr ("Invalid --address specified: %s\n", error->message);
|
|
+ return G_IO_ERROR_INVALID_DATA;
|
|
+ }
|
|
+ proxy = g_dbus_proxy_new_sync (conn_private,
|
|
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
|
|
+ NULL, /* GDBusInterfaceInfo */
|
|
+ NULL, /* name */
|
|
+ "/com/redhat/RHSM1/Register",
|
|
+ "com.redhat.RHSM1.Register",
|
|
+ NULL, &error);
|
|
+ if (proxy == NULL) {
|
|
+ g_printerr ("Count not contact RHSM: %s\n", error->message);
|
|
+ return G_IO_ERROR_NOT_FOUND;
|
|
+ }
|
|
+
|
|
+ /* no options */
|
|
+ subman_options = g_variant_builder_new (G_VARIANT_TYPE("a{ss}"));
|
|
+
|
|
+ /* set registration server */
|
|
+ if (hostname == NULL || hostname[0] == '\0')
|
|
+ hostname = g_strdup ("subscription.rhsm.redhat.com");
|
|
+ if (prefix == NULL || prefix[0] == '\0')
|
|
+ prefix = g_strdup ("/subscription");
|
|
+ if (port == NULL || port[0] == '\0')
|
|
+ port = g_strdup ("443");
|
|
+ subman_conopts = g_variant_builder_new (G_VARIANT_TYPE("a{ss}"));
|
|
+ g_variant_builder_add (subman_conopts, "{ss}", "host", hostname);
|
|
+ g_variant_builder_add (subman_conopts, "{ss}", "handler", prefix);
|
|
+ g_variant_builder_add (subman_conopts, "{ss}", "port", port);
|
|
+
|
|
+ /* call into RHSM */
|
|
+ if (g_strcmp0 (kind, "register-with-key") == 0) {
|
|
+ g_auto(GStrv) activation_keys = NULL;
|
|
+ g_autoptr(GError) error_local = NULL;
|
|
+ g_autoptr(GVariant) res = NULL;
|
|
+
|
|
+ if (activation_key == NULL) {
|
|
+ g_printerr ("Required --activation-key\n");
|
|
+ return G_IO_ERROR_INVALID_DATA;
|
|
+ }
|
|
+ if (organisation == NULL) {
|
|
+ g_printerr ("Required --organisation\n");
|
|
+ return G_IO_ERROR_INVALID_DATA;
|
|
+ }
|
|
+
|
|
+ g_debug ("registering using activation key");
|
|
+ activation_keys = g_strsplit (activation_key, ",", -1);
|
|
+ res = g_dbus_proxy_call_sync (proxy,
|
|
+ "RegisterWithActivationKeys",
|
|
+ g_variant_new ("(s^asa{ss}a{ss}s)",
|
|
+ organisation,
|
|
+ activation_keys,
|
|
+ subman_options,
|
|
+ subman_conopts,
|
|
+ userlang),
|
|
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
+ -1, NULL, &error_local);
|
|
+ if (res == NULL) {
|
|
+ g_dbus_error_strip_remote_error (error_local);
|
|
+ _helper_convert_error (error_local->message, &error);
|
|
+ g_printerr ("Failed to RegisterWithActivationKeys: %s\n", error->message);
|
|
+ return error->code;
|
|
+ }
|
|
+ } else if (g_strcmp0 (kind, "register-with-username") == 0) {
|
|
+ g_autoptr(GError) error_local = NULL;
|
|
+ g_autoptr(GVariant) res = NULL;
|
|
+
|
|
+ g_debug ("registering using username and password");
|
|
+ if (username == NULL) {
|
|
+ g_printerr ("Required --username\n");
|
|
+ return G_IO_ERROR_INVALID_DATA;
|
|
+ }
|
|
+ if (password == NULL) {
|
|
+ g_printerr ("Required --password\n");
|
|
+ return G_IO_ERROR_INVALID_DATA;
|
|
+ }
|
|
+ if (organisation == NULL) {
|
|
+ g_printerr ("Required --organisation\n");
|
|
+ return G_IO_ERROR_INVALID_DATA;
|
|
+ }
|
|
+ res = g_dbus_proxy_call_sync (proxy,
|
|
+ "Register",
|
|
+ g_variant_new ("(sssa{ss}a{ss}s)",
|
|
+ organisation,
|
|
+ username,
|
|
+ password,
|
|
+ subman_options,
|
|
+ subman_conopts,
|
|
+ userlang),
|
|
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
+ -1, NULL, &error_local);
|
|
+ if (res == NULL) {
|
|
+ g_dbus_error_strip_remote_error (error_local);
|
|
+ _helper_convert_error (error_local->message, &error);
|
|
+ g_printerr ("Failed to Register: %s\n", error->message);
|
|
+ return error->code;
|
|
+ }
|
|
+ } else {
|
|
+ g_printerr ("Invalid --kind specified: %s\n", kind);
|
|
+ return G_IO_ERROR_INVALID_DATA;
|
|
+ }
|
|
+
|
|
+ /* set the new hostname */
|
|
+ if (!_helper_save_config ("server.hostname", hostname, &error)) {
|
|
+ g_printerr ("Failed to save hostname: %s\n", error->message);
|
|
+ return G_IO_ERROR_NOT_INITIALIZED;
|
|
+ }
|
|
+ if (!_helper_save_config ("server.prefix", prefix, &error)) {
|
|
+ g_printerr ("Failed to save prefix: %s\n", error->message);
|
|
+ return G_IO_ERROR_NOT_INITIALIZED;
|
|
+ }
|
|
+ if (!_helper_save_config ("server.port", port, &error)) {
|
|
+ g_printerr ("Failed to save port: %s\n", error->message);
|
|
+ return G_IO_ERROR_NOT_INITIALIZED;
|
|
+ }
|
|
+
|
|
+ /* wait for rhsmd to notice the new config */
|
|
+ g_usleep (G_USEC_PER_SEC * 5);
|
|
+
|
|
+ /* auto-attach */
|
|
+ if (!_helper_auto_attach (&error)) {
|
|
+ g_printerr ("Failed to AutoAttach: %s\n", error->message);
|
|
+ return G_IO_ERROR_NOT_INITIALIZED;
|
|
+ }
|
|
+
|
|
+ return EXIT_SUCCESS;
|
|
+}
|
|
diff --git a/plugins/subman/gsd-subscription-manager.c b/plugins/subman/gsd-subscription-manager.c
|
|
new file mode 100644
|
|
index 00000000..08b13fa6
|
|
--- /dev/null
|
|
+++ b/plugins/subman/gsd-subscription-manager.c
|
|
@@ -0,0 +1,982 @@
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
+ *
|
|
+ * Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
|
|
+ *
|
|
+ * 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, see <http://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "config.h"
|
|
+
|
|
+#include <glib/gi18n.h>
|
|
+#include <gdk/gdk.h>
|
|
+#include <gtk/gtk.h>
|
|
+#include <json-glib/json-glib.h>
|
|
+#include <libnotify/notify.h>
|
|
+
|
|
+#include "gnome-settings-profile.h"
|
|
+#include "gsd-subman-common.h"
|
|
+#include "gsd-subscription-manager.h"
|
|
+
|
|
+#define GSD_DBUS_NAME "org.gnome.SettingsDaemon"
|
|
+#define GSD_DBUS_PATH "/org/gnome/SettingsDaemon"
|
|
+#define GSD_DBUS_BASE_INTERFACE "org.gnome.SettingsDaemon"
|
|
+
|
|
+#define GSD_SUBSCRIPTION_DBUS_NAME GSD_DBUS_NAME ".Subscription"
|
|
+#define GSD_SUBSCRIPTION_DBUS_PATH GSD_DBUS_PATH "/Subscription"
|
|
+#define GSD_SUBSCRIPTION_DBUS_INTERFACE GSD_DBUS_BASE_INTERFACE ".Subscription"
|
|
+
|
|
+static const gchar introspection_xml[] =
|
|
+"<node>"
|
|
+" <interface name='org.gnome.SettingsDaemon.Subscription'>"
|
|
+" <method name='Register'>"
|
|
+" <arg type='a{sv}' name='options' direction='in'/>"
|
|
+" </method>"
|
|
+" <method name='Unregister'/>"
|
|
+" <property name='SubscriptionStatus' type='u' access='read'/>"
|
|
+" </interface>"
|
|
+"</node>";
|
|
+
|
|
+#define GSD_SUBSCRIPTION_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_SUBSCRIPTION_MANAGER, GsdSubscriptionManagerPrivate))
|
|
+
|
|
+typedef enum {
|
|
+ _RHSM_INTERFACE_CONFIG,
|
|
+ _RHSM_INTERFACE_REGISTER_SERVER,
|
|
+ _RHSM_INTERFACE_ATTACH,
|
|
+ _RHSM_INTERFACE_ENTITLEMENT,
|
|
+ _RHSM_INTERFACE_PRODUCTS,
|
|
+ _RHSM_INTERFACE_CONSUMER,
|
|
+ _RHSM_INTERFACE_SYSPURPOSE,
|
|
+ _RHSM_INTERFACE_LAST
|
|
+} _RhsmInterface;
|
|
+
|
|
+struct GsdSubscriptionManagerPrivate
|
|
+{
|
|
+ /* D-Bus */
|
|
+ guint name_id;
|
|
+ GDBusNodeInfo *introspection_data;
|
|
+ GDBusConnection *connection;
|
|
+ GCancellable *bus_cancellable;
|
|
+
|
|
+ GDBusProxy *proxies[_RHSM_INTERFACE_LAST];
|
|
+ const gchar *userlang; /* owned by GLib internally */
|
|
+ GHashTable *config; /* str:str */
|
|
+ gchar *address;
|
|
+
|
|
+ GTimer *timer_last_notified;
|
|
+ NotifyNotification *notification_expired;
|
|
+ NotifyNotification *notification_registered;
|
|
+ NotifyNotification *notification_registration_required;
|
|
+ GsdSubmanSubscriptionStatus subscription_status;
|
|
+ GsdSubmanSubscriptionStatus subscription_status_last;
|
|
+};
|
|
+
|
|
+enum {
|
|
+ PROP_0,
|
|
+};
|
|
+
|
|
+static void gsd_subscription_manager_class_init (GsdSubscriptionManagerClass *klass);
|
|
+static void gsd_subscription_manager_init (GsdSubscriptionManager *subscription_manager);
|
|
+static void gsd_subscription_manager_finalize (GObject *object);
|
|
+
|
|
+G_DEFINE_TYPE (GsdSubscriptionManager, gsd_subscription_manager, G_TYPE_OBJECT)
|
|
+
|
|
+static gpointer manager_object = NULL;
|
|
+
|
|
+GQuark
|
|
+gsd_subscription_manager_error_quark (void)
|
|
+{
|
|
+ static GQuark quark = 0;
|
|
+ if (!quark)
|
|
+ quark = g_quark_from_static_string ("gsd_subscription_manager_error");
|
|
+ return quark;
|
|
+}
|
|
+
|
|
+static GsdSubmanSubscriptionStatus
|
|
+_client_subscription_status_from_text (const gchar *status_txt)
|
|
+{
|
|
+ if (g_strcmp0 (status_txt, "Unknown") == 0)
|
|
+ return GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN;
|
|
+ if (g_strcmp0 (status_txt, "Current") == 0)
|
|
+ return GSD_SUBMAN_SUBSCRIPTION_STATUS_VALID;
|
|
+ if (g_strcmp0 (status_txt, "Invalid") == 0)
|
|
+ return GSD_SUBMAN_SUBSCRIPTION_STATUS_INVALID;
|
|
+ if (g_strcmp0 (status_txt, "Disabled") == 0)
|
|
+ return GSD_SUBMAN_SUBSCRIPTION_STATUS_DISABLED;
|
|
+ if (g_strcmp0 (status_txt, "Insufficient") == 0)
|
|
+ return GSD_SUBMAN_SUBSCRIPTION_STATUS_PARTIALLY_VALID;
|
|
+ g_warning ("Unknown subscription status: %s", status_txt); // 'Current'?
|
|
+ return GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN;
|
|
+}
|
|
+
|
|
+static void
|
|
+_emit_property_changed (GsdSubscriptionManager *manager,
|
|
+ const gchar *property_name,
|
|
+ GVariant *property_value)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ GVariantBuilder builder;
|
|
+ GVariantBuilder invalidated_builder;
|
|
+
|
|
+ /* not yet connected */
|
|
+ if (priv->connection == NULL)
|
|
+ return;
|
|
+
|
|
+ /* build the dict */
|
|
+ g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
|
|
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
|
+ g_variant_builder_add (&builder,
|
|
+ "{sv}",
|
|
+ property_name,
|
|
+ property_value);
|
|
+ g_dbus_connection_emit_signal (priv->connection,
|
|
+ NULL,
|
|
+ GSD_SUBSCRIPTION_DBUS_PATH,
|
|
+ "org.freedesktop.DBus.Properties",
|
|
+ "PropertiesChanged",
|
|
+ g_variant_new ("(sa{sv}as)",
|
|
+ GSD_SUBSCRIPTION_DBUS_INTERFACE,
|
|
+ &builder,
|
|
+ &invalidated_builder),
|
|
+ NULL);
|
|
+ g_variant_builder_clear (&builder);
|
|
+ g_variant_builder_clear (&invalidated_builder);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_client_subscription_status_update (GsdSubscriptionManager *manager, GError **error)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ JsonNode *json_root;
|
|
+ JsonObject *json_obj;
|
|
+ const gchar *json_txt = NULL;
|
|
+ const gchar *status_txt = NULL;
|
|
+ g_autoptr(GVariant) val = NULL;
|
|
+ g_autoptr(JsonParser) json_parser = json_parser_new ();
|
|
+
|
|
+ /* save old value */
|
|
+ priv->subscription_status_last = priv->subscription_status;
|
|
+
|
|
+ val = g_dbus_proxy_call_sync (priv->proxies[_RHSM_INTERFACE_ENTITLEMENT],
|
|
+ "GetStatus",
|
|
+ g_variant_new ("(ss)",
|
|
+ "", /* assumed as 'now' */
|
|
+ priv->userlang),
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ -1, NULL, error);
|
|
+ if (val == NULL)
|
|
+ return FALSE;
|
|
+ g_variant_get (val, "(&s)", &json_txt);
|
|
+ g_debug ("Entitlement.GetStatus JSON: %s", json_txt);
|
|
+ if (!json_parser_load_from_data (json_parser, json_txt, -1, error))
|
|
+ return FALSE;
|
|
+ json_root = json_parser_get_root (json_parser);
|
|
+ json_obj = json_node_get_object (json_root);
|
|
+ if (!json_object_has_member (json_obj, "status")) {
|
|
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
|
|
+ "no Entitlement.GetStatus status in %s", json_txt);
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ status_txt = json_object_get_string_member (json_obj, "status");
|
|
+ g_debug ("Entitlement.GetStatus: %s", status_txt);
|
|
+ priv->subscription_status = _client_subscription_status_from_text (status_txt);
|
|
+
|
|
+ /* emit notification for g-c-c */
|
|
+ if (priv->subscription_status != priv->subscription_status_last) {
|
|
+ _emit_property_changed (manager, "SubscriptionStatus",
|
|
+ g_variant_new_uint32 (priv->subscription_status));
|
|
+ }
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_client_syspurpose_update (GsdSubscriptionManager *manager, GError **error)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ JsonNode *json_root;
|
|
+ JsonObject *json_obj;
|
|
+ const gchar *json_txt = NULL;
|
|
+ g_autoptr(GVariant) val = NULL;
|
|
+ g_autoptr(JsonParser) json_parser = json_parser_new ();
|
|
+
|
|
+ val = g_dbus_proxy_call_sync (priv->proxies[_RHSM_INTERFACE_SYSPURPOSE],
|
|
+ "GetSyspurpose",
|
|
+ g_variant_new ("(s)", priv->userlang),
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ -1, NULL, error);
|
|
+ if (val == NULL)
|
|
+ return FALSE;
|
|
+ g_variant_get (val, "(&s)", &json_txt);
|
|
+ g_debug ("Syspurpose.GetSyspurpose JSON: %s", json_txt);
|
|
+ if (!json_parser_load_from_data (json_parser, json_txt, -1, error))
|
|
+ return FALSE;
|
|
+ json_root = json_parser_get_root (json_parser);
|
|
+ json_obj = json_node_get_object (json_root);
|
|
+ if (!json_object_has_member (json_obj, "status")) {
|
|
+ g_debug ("Syspurpose.GetSyspurpose: Unknown");
|
|
+ return TRUE;
|
|
+ }
|
|
+ g_debug ("Syspurpose.GetSyspurpose: '%s", json_object_get_string_member (json_obj, "status"));
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_client_register_start (GsdSubscriptionManager *manager, GError **error)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ const gchar *address = NULL;
|
|
+ g_autoptr(GDBusProxy) proxy = NULL;
|
|
+ g_autoptr(GVariant) val = NULL;
|
|
+
|
|
+ /* already started */
|
|
+ if (priv->address != NULL)
|
|
+ return TRUE;
|
|
+
|
|
+ /* apparently: "we can't send registration credentials over the regular
|
|
+ * system or session bus since those aren't really locked down..." */
|
|
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
|
|
+ G_DBUS_PROXY_FLAGS_NONE,
|
|
+ NULL,
|
|
+ "com.redhat.RHSM1",
|
|
+ "/com/redhat/RHSM1/RegisterServer",
|
|
+ "com.redhat.RHSM1.RegisterServer",
|
|
+ NULL, error);
|
|
+ if (proxy == NULL)
|
|
+ return FALSE;
|
|
+ val = g_dbus_proxy_call_sync (proxy, "Start",
|
|
+ g_variant_new ("(s)", priv->userlang),
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ -1, NULL, error);
|
|
+ if (val == NULL)
|
|
+ return FALSE;
|
|
+ g_variant_get (val, "(&s)", &address);
|
|
+ g_debug ("RegisterServer.Start: %s", address);
|
|
+ priv->address = g_strdup (address);
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_client_register_stop (GsdSubscriptionManager *manager, GError **error)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ g_autoptr(GDBusProxy) proxy = NULL;
|
|
+ g_autoptr(GVariant) val = NULL;
|
|
+
|
|
+ /* already started */
|
|
+ if (priv->address == NULL)
|
|
+ return TRUE;
|
|
+
|
|
+ /* stop registration server */
|
|
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
|
|
+ G_DBUS_PROXY_FLAGS_NONE,
|
|
+ NULL,
|
|
+ "com.redhat.RHSM1",
|
|
+ "/com/redhat/RHSM1/RegisterServer",
|
|
+ "com.redhat.RHSM1.RegisterServer",
|
|
+ NULL, error);
|
|
+ if (proxy == NULL)
|
|
+ return FALSE;
|
|
+ val = g_dbus_proxy_call_sync (proxy, "Stop",
|
|
+ g_variant_new ("(s)", priv->userlang),
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ -1, NULL, error);
|
|
+ if (val == NULL)
|
|
+ return FALSE;
|
|
+ g_clear_pointer (&priv->address, g_free);
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_client_subprocess_wait_check (GSubprocess *subprocess, GError **error)
|
|
+{
|
|
+ gint rc;
|
|
+ if (!g_subprocess_wait (subprocess, NULL, error)) {
|
|
+ g_prefix_error (error, "failed to run pkexec: ");
|
|
+ return FALSE;
|
|
+ }
|
|
+ rc = g_subprocess_get_exit_status (subprocess);
|
|
+ if (rc != 0) {
|
|
+ GInputStream *istream = g_subprocess_get_stderr_pipe (subprocess);
|
|
+ gchar buf[1024] = { 0x0 };
|
|
+ gsize sz = 0;
|
|
+ g_input_stream_read_all (istream, buf, sizeof(buf) - 1, &sz, NULL, NULL);
|
|
+ if (sz == 0) {
|
|
+ g_set_error_literal (error, G_IO_ERROR, rc,
|
|
+ "Failed to run helper without stderr");
|
|
+ return FALSE;
|
|
+ }
|
|
+ g_set_error_literal (error, G_IO_ERROR, rc, buf);
|
|
+ return FALSE;
|
|
+ }
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+typedef enum {
|
|
+ _NOTIFY_EXPIRED,
|
|
+ _NOTIFY_REGISTRATION_REQUIRED,
|
|
+ _NOTIFY_REGISTERED
|
|
+} _NotifyKind;
|
|
+
|
|
+static void
|
|
+_show_notification (GsdSubscriptionManager *manager, _NotifyKind notify_kind)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ switch (notify_kind) {
|
|
+ case _NOTIFY_EXPIRED:
|
|
+ notify_notification_close (priv->notification_registered, NULL);
|
|
+ notify_notification_close (priv->notification_registration_required, NULL);
|
|
+ notify_notification_show (priv->notification_expired, NULL);
|
|
+ break;
|
|
+ case _NOTIFY_REGISTRATION_REQUIRED:
|
|
+ notify_notification_close (priv->notification_registered, NULL);
|
|
+ notify_notification_close (priv->notification_expired, NULL);
|
|
+ notify_notification_show (priv->notification_registration_required, NULL);
|
|
+ break;
|
|
+ case _NOTIFY_REGISTERED:
|
|
+ notify_notification_close (priv->notification_expired, NULL);
|
|
+ notify_notification_close (priv->notification_registration_required, NULL);
|
|
+ notify_notification_show (priv->notification_registered, NULL);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ g_timer_reset (priv->timer_last_notified);
|
|
+}
|
|
+
|
|
+static void
|
|
+_client_maybe__show_notification (GsdSubscriptionManager *manager)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+
|
|
+ /* startup */
|
|
+ if (priv->subscription_status_last == GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN &&
|
|
+ priv->subscription_status == GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN) {
|
|
+ _show_notification (manager, _NOTIFY_REGISTRATION_REQUIRED);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* something changed */
|
|
+ if (priv->subscription_status_last != priv->subscription_status) {
|
|
+ g_debug ("transisition from subscription status '%s' to '%s'",
|
|
+ gsd_subman_subscription_status_to_string (priv->subscription_status_last),
|
|
+ gsd_subman_subscription_status_to_string (priv->subscription_status));
|
|
+
|
|
+ /* needs registration */
|
|
+ if (priv->subscription_status_last == GSD_SUBMAN_SUBSCRIPTION_STATUS_VALID &&
|
|
+ priv->subscription_status == GSD_SUBMAN_SUBSCRIPTION_STATUS_INVALID) {
|
|
+ _show_notification (manager, _NOTIFY_REGISTRATION_REQUIRED);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* was unregistered */
|
|
+ if (priv->subscription_status_last == GSD_SUBMAN_SUBSCRIPTION_STATUS_VALID &&
|
|
+ priv->subscription_status == GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN) {
|
|
+ _show_notification (manager, _NOTIFY_REGISTRATION_REQUIRED);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* registered */
|
|
+ if (priv->subscription_status_last == GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN &&
|
|
+ priv->subscription_status == GSD_SUBMAN_SUBSCRIPTION_STATUS_VALID &&
|
|
+ g_timer_elapsed (priv->timer_last_notified, NULL) > 60) {
|
|
+ _show_notification (manager, _NOTIFY_REGISTERED);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* nag again */
|
|
+ if (priv->subscription_status == GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN &&
|
|
+ g_timer_elapsed (priv->timer_last_notified, NULL) > 60 * 60 * 24) {
|
|
+ _show_notification (manager, _NOTIFY_REGISTRATION_REQUIRED);
|
|
+ return;
|
|
+ }
|
|
+ if (priv->subscription_status == GSD_SUBMAN_SUBSCRIPTION_STATUS_INVALID &&
|
|
+ g_timer_elapsed (priv->timer_last_notified, NULL) > 60 * 60 * 24) {
|
|
+ _show_notification (manager, _NOTIFY_EXPIRED);
|
|
+ return;
|
|
+ }
|
|
+ if (priv->subscription_status == GSD_SUBMAN_SUBSCRIPTION_STATUS_PARTIALLY_VALID &&
|
|
+ g_timer_elapsed (priv->timer_last_notified, NULL) > 60 * 60 * 24) {
|
|
+ _show_notification (manager, _NOTIFY_EXPIRED);
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_client_register_with_keys (GsdSubscriptionManager *manager,
|
|
+ const gchar *hostname,
|
|
+ const gchar *organisation,
|
|
+ const gchar *activation_key,
|
|
+ GError **error)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ g_autoptr(GSubprocess) subprocess = NULL;
|
|
+
|
|
+ /* apparently: "we can't send registration credentials over the regular
|
|
+ * system or session bus since those aren't really locked down..." */
|
|
+ if (!_client_register_start (manager, error))
|
|
+ return FALSE;
|
|
+ g_debug ("spawning %s", LIBEXECDIR "/gsd-subman-helper");
|
|
+ subprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_STDERR_PIPE, error,
|
|
+ "pkexec", LIBEXECDIR "/gsd-subman-helper",
|
|
+ "--kind", "register-with-key",
|
|
+ "--address", priv->address,
|
|
+ "--hostname", hostname,
|
|
+ "--organisation", organisation,
|
|
+ "--activation-key", activation_key,
|
|
+ NULL);
|
|
+ if (subprocess == NULL) {
|
|
+ g_prefix_error (error, "failed to find pkexec: ");
|
|
+ return FALSE;
|
|
+ }
|
|
+ if (!_client_subprocess_wait_check (subprocess, error))
|
|
+ return FALSE;
|
|
+
|
|
+ /* FIXME: also do on error? */
|
|
+ if (!_client_register_stop (manager, error))
|
|
+ return FALSE;
|
|
+ if (!_client_subscription_status_update (manager, error))
|
|
+ return FALSE;
|
|
+ _client_maybe__show_notification (manager);
|
|
+
|
|
+ /* success */
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_client_register (GsdSubscriptionManager *manager,
|
|
+ const gchar *hostname,
|
|
+ const gchar *organisation,
|
|
+ const gchar *username,
|
|
+ const gchar *password,
|
|
+ GError **error)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ g_autoptr(GSubprocess) subprocess = NULL;
|
|
+
|
|
+ /* fallback */
|
|
+ if (organisation == NULL)
|
|
+ organisation = "";
|
|
+
|
|
+ /* apparently: "we can't send registration credentials over the regular
|
|
+ * system or session bus since those aren't really locked down..." */
|
|
+ if (!_client_register_start (manager, error))
|
|
+ return FALSE;
|
|
+ g_debug ("spawning %s", LIBEXECDIR "/gsd-subman-helper");
|
|
+ subprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_STDERR_PIPE, error,
|
|
+ "pkexec", LIBEXECDIR "/gsd-subman-helper",
|
|
+ "--kind", "register-with-username",
|
|
+ "--address", priv->address,
|
|
+ "--hostname", hostname,
|
|
+ "--organisation", organisation,
|
|
+ "--username", username,
|
|
+ "--password", password,
|
|
+ NULL);
|
|
+ if (subprocess == NULL) {
|
|
+ g_prefix_error (error, "failed to find pkexec: ");
|
|
+ return FALSE;
|
|
+ }
|
|
+ if (!_client_subprocess_wait_check (subprocess, error))
|
|
+ return FALSE;
|
|
+
|
|
+ /* FIXME: also do on error? */
|
|
+ if (!_client_register_stop (manager, error))
|
|
+ return FALSE;
|
|
+ if (!_client_subscription_status_update (manager, error))
|
|
+ return FALSE;
|
|
+ _client_maybe__show_notification (manager);
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_client_unregister (GsdSubscriptionManager *manager, GError **error)
|
|
+{
|
|
+ g_autoptr(GSubprocess) subprocess = NULL;
|
|
+
|
|
+ /* apparently: "we can't send registration credentials over the regular
|
|
+ * system or session bus since those aren't really locked down..." */
|
|
+ if (!_client_register_start (manager, error))
|
|
+ return FALSE;
|
|
+ g_debug ("spawning %s", LIBEXECDIR "/gsd-subman-helper");
|
|
+ subprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_STDERR_PIPE, error,
|
|
+ "pkexec", LIBEXECDIR "/gsd-subman-helper",
|
|
+ "--kind", "unregister",
|
|
+ NULL);
|
|
+ if (subprocess == NULL) {
|
|
+ g_prefix_error (error, "failed to find pkexec: ");
|
|
+ return FALSE;
|
|
+ }
|
|
+ if (!_client_subprocess_wait_check (subprocess, error))
|
|
+ return FALSE;
|
|
+ if (!_client_subscription_status_update (manager, error))
|
|
+ return FALSE;
|
|
+ _client_maybe__show_notification (manager);
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_client_update_config (GsdSubscriptionManager *manager, GError **error)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ g_autoptr(GVariant) val = NULL;
|
|
+ g_autoptr(GVariant) val_server = NULL;
|
|
+ g_autoptr(GVariantDict) dict = NULL;
|
|
+ GVariantIter iter;
|
|
+ gchar *key;
|
|
+ gchar *value;
|
|
+
|
|
+ val = g_dbus_proxy_call_sync (priv->proxies[_RHSM_INTERFACE_CONFIG],
|
|
+ "GetAll",
|
|
+ g_variant_new ("(s)", priv->userlang),
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ -1, NULL, error);
|
|
+ if (val == NULL)
|
|
+ return FALSE;
|
|
+ dict = g_variant_dict_new (g_variant_get_child_value (val, 0));
|
|
+ val_server = g_variant_dict_lookup_value (dict, "server", G_VARIANT_TYPE("a{ss}"));
|
|
+ if (val_server != NULL) {
|
|
+ g_variant_iter_init (&iter, val_server);
|
|
+ while (g_variant_iter_next (&iter, "{ss}", &key, &value)) {
|
|
+ g_debug ("%s=%s", key, value);
|
|
+ g_hash_table_insert (priv->config,
|
|
+ g_steal_pointer (&key),
|
|
+ g_steal_pointer (&value));
|
|
+ }
|
|
+ }
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static void
|
|
+_subman_proxy_signal_cb (GDBusProxy *proxy,
|
|
+ const gchar *sender_name,
|
|
+ const gchar *signal_name,
|
|
+ GVariant *parameters,
|
|
+ GsdSubscriptionManager *manager)
|
|
+{
|
|
+ g_autoptr(GError) error = NULL;
|
|
+ if (!_client_syspurpose_update (manager, &error)) {
|
|
+ g_warning ("failed to update syspurpose: %s", error->message);
|
|
+ g_clear_error (&error);
|
|
+ }
|
|
+ if (!_client_subscription_status_update (manager, &error)) {
|
|
+ g_warning ("failed to update subscription status: %s", error->message);
|
|
+ g_clear_error (&error);
|
|
+ }
|
|
+ _client_maybe__show_notification (manager);
|
|
+}
|
|
+
|
|
+static void
|
|
+_client_unload (GsdSubscriptionManager *manager)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ for (guint i = 0; i < _RHSM_INTERFACE_LAST; i++)
|
|
+ g_clear_object (&priv->proxies[i]);
|
|
+ g_hash_table_unref (priv->config);
|
|
+}
|
|
+
|
|
+static const gchar *
|
|
+_rhsm_interface_to_string (_RhsmInterface kind)
|
|
+{
|
|
+ if (kind == _RHSM_INTERFACE_CONFIG)
|
|
+ return "Config";
|
|
+ if (kind == _RHSM_INTERFACE_REGISTER_SERVER)
|
|
+ return "RegisterServer";
|
|
+ if (kind == _RHSM_INTERFACE_ATTACH)
|
|
+ return "Attach";
|
|
+ if (kind == _RHSM_INTERFACE_ENTITLEMENT)
|
|
+ return "Entitlement";
|
|
+ if (kind == _RHSM_INTERFACE_PRODUCTS)
|
|
+ return "Products";
|
|
+ if (kind == _RHSM_INTERFACE_CONSUMER)
|
|
+ return "Consumer";
|
|
+ if (kind == _RHSM_INTERFACE_SYSPURPOSE)
|
|
+ return "Syspurpose";
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_client_load (GsdSubscriptionManager *manager, GError **error)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+
|
|
+ priv->config = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
+
|
|
+ /* connect to all the interfaces on the *different* objects :| */
|
|
+ for (guint i = 0; i < _RHSM_INTERFACE_LAST; i++) {
|
|
+ const gchar *kind = _rhsm_interface_to_string (i);
|
|
+ g_autofree gchar *opath = g_strdup_printf ("/com/redhat/RHSM1/%s", kind);
|
|
+ g_autofree gchar *iface = g_strdup_printf ("com.redhat.RHSM1.%s", kind);
|
|
+ priv->proxies[i] =
|
|
+ g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
|
|
+ G_DBUS_PROXY_FLAGS_NONE,
|
|
+ NULL,
|
|
+ "com.redhat.RHSM1",
|
|
+ opath, iface,
|
|
+ NULL,
|
|
+ error);
|
|
+ if (priv->proxies[i] == NULL)
|
|
+ return FALSE;
|
|
+ /* we want to get notified if the status of the system changes */
|
|
+ g_signal_connect (priv->proxies[i], "g-signal",
|
|
+ G_CALLBACK (_subman_proxy_signal_cb), manager);
|
|
+ }
|
|
+
|
|
+ /* get initial status */
|
|
+ priv->userlang = "";
|
|
+ if (!_client_update_config (manager, error))
|
|
+ return FALSE;
|
|
+ if (!_client_subscription_status_update (manager, error))
|
|
+ return FALSE;
|
|
+ if (!_client_syspurpose_update (manager, error))
|
|
+ return FALSE;
|
|
+
|
|
+ /* success */
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+gsd_subscription_manager_start (GsdSubscriptionManager *manager, GError **error)
|
|
+{
|
|
+ gboolean ret;
|
|
+ g_debug ("Starting subscription manager");
|
|
+ gnome_settings_profile_start (NULL);
|
|
+ ret = _client_load (manager, error);
|
|
+ _client_maybe__show_notification (manager);
|
|
+ gnome_settings_profile_end (NULL);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void
|
|
+gsd_subscription_manager_stop (GsdSubscriptionManager *manager)
|
|
+{
|
|
+ g_debug ("Stopping subscription manager");
|
|
+ _client_unload (manager);
|
|
+}
|
|
+
|
|
+static void
|
|
+gsd_subscription_manager_class_init (GsdSubscriptionManagerClass *klass)
|
|
+{
|
|
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
+ object_class->finalize = gsd_subscription_manager_finalize;
|
|
+ notify_init ("gnome-settings-daemon");
|
|
+ g_type_class_add_private (klass, sizeof (GsdSubscriptionManagerPrivate));
|
|
+}
|
|
+
|
|
+static void
|
|
+_launch_info_overview (void)
|
|
+{
|
|
+ const gchar *argv[] = { "gnome-control-center", "info-overview", NULL };
|
|
+ g_debug ("Running gnome-control-center info-overview");
|
|
+ g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
|
|
+ NULL, NULL, NULL, NULL);
|
|
+}
|
|
+
|
|
+static void
|
|
+_notify_closed_cb (NotifyNotification *notification, gpointer user_data)
|
|
+{
|
|
+ /* FIXME: only launch when clicking on the main body, not the window close */
|
|
+ if (notify_notification_get_closed_reason (notification) == 0x400)
|
|
+ _launch_info_overview ();
|
|
+}
|
|
+
|
|
+static void
|
|
+_notify_clicked_cb (NotifyNotification *notification, char *action, gpointer user_data)
|
|
+{
|
|
+ _launch_info_overview ();
|
|
+}
|
|
+
|
|
+static void
|
|
+gsd_subscription_manager_init (GsdSubscriptionManager *manager)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv = GSD_SUBSCRIPTION_MANAGER_GET_PRIVATE (manager);
|
|
+
|
|
+ priv->timer_last_notified = g_timer_new ();
|
|
+
|
|
+ /* expired */
|
|
+ priv->notification_expired =
|
|
+ notify_notification_new (_("Subscription Has Expired"),
|
|
+ _("Add or renew a subscription to continue receiving software updates."),
|
|
+ NULL);
|
|
+ notify_notification_set_app_name (priv->notification_expired, _("Subscription"));
|
|
+ notify_notification_set_hint_string (priv->notification_expired, "desktop-entry", "subman-panel");
|
|
+ notify_notification_set_hint_string (priv->notification_expired, "x-gnome-privacy-scope", "system");
|
|
+ notify_notification_set_urgency (priv->notification_expired, NOTIFY_URGENCY_CRITICAL);
|
|
+ notify_notification_add_action (priv->notification_expired,
|
|
+ "info-overview", _("Subscribe System…"),
|
|
+ _notify_clicked_cb,
|
|
+ manager, NULL);
|
|
+ g_signal_connect (priv->notification_expired, "closed",
|
|
+ G_CALLBACK (_notify_closed_cb), manager);
|
|
+
|
|
+ /* registered */
|
|
+ priv->notification_registered =
|
|
+ notify_notification_new (_("Registration Successful"),
|
|
+ _("The system has been registered and software updates have been enabled."),
|
|
+ NULL);
|
|
+ notify_notification_set_app_name (priv->notification_registered, _("Subscription"));
|
|
+ notify_notification_set_hint_string (priv->notification_registered, "desktop-entry", "subman-panel");
|
|
+ notify_notification_set_hint_string (priv->notification_registered, "x-gnome-privacy-scope", "system");
|
|
+ notify_notification_set_urgency (priv->notification_registered, NOTIFY_URGENCY_CRITICAL);
|
|
+ g_signal_connect (priv->notification_registered, "closed",
|
|
+ G_CALLBACK (_notify_closed_cb), manager);
|
|
+
|
|
+ /* registration required */
|
|
+ priv->notification_registration_required =
|
|
+ notify_notification_new (_("System Not Registered"),
|
|
+ _("Please register your system to receive software updates."),
|
|
+ NULL);
|
|
+ notify_notification_set_app_name (priv->notification_registration_required, _("Subscription"));
|
|
+ notify_notification_set_hint_string (priv->notification_registration_required, "desktop-entry", "subman-panel");
|
|
+ notify_notification_set_hint_string (priv->notification_registration_required, "x-gnome-privacy-scope", "system");
|
|
+ notify_notification_set_urgency (priv->notification_registration_required, NOTIFY_URGENCY_CRITICAL);
|
|
+ notify_notification_add_action (priv->notification_registration_required,
|
|
+ "info-overview", _("Register System…"),
|
|
+ _notify_clicked_cb,
|
|
+ manager, NULL);
|
|
+ g_signal_connect (priv->notification_registration_required, "closed",
|
|
+ G_CALLBACK (_notify_closed_cb), manager);
|
|
+}
|
|
+
|
|
+static void
|
|
+gsd_subscription_manager_finalize (GObject *object)
|
|
+{
|
|
+ GsdSubscriptionManager *manager;
|
|
+
|
|
+ g_return_if_fail (object != NULL);
|
|
+ g_return_if_fail (GSD_IS_SUBSCRIPTION_MANAGER (object));
|
|
+
|
|
+ manager = GSD_SUBSCRIPTION_MANAGER (object);
|
|
+
|
|
+ gsd_subscription_manager_stop (manager);
|
|
+
|
|
+ if (manager->priv->bus_cancellable != NULL) {
|
|
+ g_cancellable_cancel (manager->priv->bus_cancellable);
|
|
+ g_clear_object (&manager->priv->bus_cancellable);
|
|
+ }
|
|
+
|
|
+ g_clear_pointer (&manager->priv->introspection_data, g_dbus_node_info_unref);
|
|
+ g_clear_object (&manager->priv->connection);
|
|
+ g_clear_object (&manager->priv->notification_expired);
|
|
+ g_clear_object (&manager->priv->notification_registered);
|
|
+ g_timer_destroy (manager->priv->timer_last_notified);
|
|
+
|
|
+ if (manager->priv->name_id != 0) {
|
|
+ g_bus_unown_name (manager->priv->name_id);
|
|
+ manager->priv->name_id = 0;
|
|
+ }
|
|
+
|
|
+ G_OBJECT_CLASS (gsd_subscription_manager_parent_class)->finalize (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+handle_method_call (GDBusConnection *connection,
|
|
+ const gchar *sender,
|
|
+ const gchar *object_path,
|
|
+ const gchar *interface_name,
|
|
+ const gchar *method_name,
|
|
+ GVariant *parameters,
|
|
+ GDBusMethodInvocation *invocation,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GsdSubscriptionManager *manager = GSD_SUBSCRIPTION_MANAGER (user_data);
|
|
+ g_autoptr(GError) error = NULL;
|
|
+
|
|
+ if (g_strcmp0 (method_name, "Register") == 0) {
|
|
+ const gchar *organisation = NULL;
|
|
+ const gchar *hostname = NULL;
|
|
+
|
|
+ if (FALSE) {
|
|
+ g_dbus_method_invocation_return_error_literal (invocation,
|
|
+ G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
|
|
+ "Cannot register at this time");
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ g_autoptr(GVariantDict) dict = g_variant_dict_new (g_variant_get_child_value (parameters, 0));
|
|
+
|
|
+ const gchar *kind = NULL;
|
|
+ if (!g_variant_dict_lookup (dict, "kind", "&s", &kind)) {
|
|
+ g_dbus_method_invocation_return_error_literal (invocation,
|
|
+ G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
+ "No kind specified");
|
|
+
|
|
+ return;
|
|
+ }
|
|
+ if (g_strcmp0 (kind, "username") == 0) {
|
|
+ const gchar *username = NULL;
|
|
+ const gchar *password = NULL;
|
|
+ g_variant_dict_lookup (dict, "hostname", "&s", &hostname);
|
|
+ g_variant_dict_lookup (dict, "organisation", "&s", &organisation);
|
|
+ g_variant_dict_lookup (dict, "username", "&s", &username);
|
|
+ g_variant_dict_lookup (dict, "password", "&s", &password);
|
|
+ if (!_client_register (manager,
|
|
+ hostname,
|
|
+ organisation,
|
|
+ username,
|
|
+ password,
|
|
+ &error)) {
|
|
+ g_dbus_method_invocation_return_gerror (invocation, error);
|
|
+ return;
|
|
+ }
|
|
+ } else if (g_strcmp0 (kind, "key") == 0) {
|
|
+ const gchar *activation_key = NULL;
|
|
+ g_variant_dict_lookup (dict, "hostname", "&s", &hostname);
|
|
+ g_variant_dict_lookup (dict, "organisation", "&s", &organisation);
|
|
+ g_variant_dict_lookup (dict, "activation-key", "&s", &activation_key);
|
|
+ if (!_client_register_with_keys (manager,
|
|
+ hostname,
|
|
+ organisation,
|
|
+ activation_key,
|
|
+ &error)) {
|
|
+ g_dbus_method_invocation_return_gerror (invocation, error);
|
|
+ return;
|
|
+ }
|
|
+ } else {
|
|
+ g_dbus_method_invocation_return_error_literal (invocation,
|
|
+ G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
+ "Invalid kind specified");
|
|
+
|
|
+ return;
|
|
+ }
|
|
+ g_dbus_method_invocation_return_value (invocation, NULL);
|
|
+ } else if (g_strcmp0 (method_name, "Unregister") == 0) {
|
|
+ if (!_client_unregister (manager, &error)) {
|
|
+ g_dbus_method_invocation_return_gerror (invocation, error);
|
|
+ return;
|
|
+ }
|
|
+ g_dbus_method_invocation_return_value (invocation, NULL);
|
|
+ } else {
|
|
+ g_assert_not_reached ();
|
|
+ }
|
|
+}
|
|
+
|
|
+static GVariant *
|
|
+handle_get_property (GDBusConnection *connection,
|
|
+ const gchar *sender,
|
|
+ const gchar *object_path,
|
|
+ const gchar *interface_name,
|
|
+ const gchar *property_name,
|
|
+ GError **error, gpointer user_data)
|
|
+{
|
|
+ GsdSubscriptionManager *manager = GSD_SUBSCRIPTION_MANAGER (user_data);
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+
|
|
+ if (g_strcmp0 (interface_name, GSD_SUBSCRIPTION_DBUS_INTERFACE) != 0) {
|
|
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
|
|
+ "No such interface: %s", interface_name);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (g_strcmp0 (property_name, "SubscriptionStatus") == 0)
|
|
+ return g_variant_new_uint32 (priv->subscription_status);
|
|
+
|
|
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
|
|
+ "Failed to get property: %s", property_name);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+handle_set_property (GDBusConnection *connection,
|
|
+ const gchar *sender,
|
|
+ const gchar *object_path,
|
|
+ const gchar *interface_name,
|
|
+ const gchar *property_name,
|
|
+ GVariant *value,
|
|
+ GError **error, gpointer user_data)
|
|
+{
|
|
+ if (g_strcmp0 (interface_name, GSD_SUBSCRIPTION_DBUS_INTERFACE) != 0) {
|
|
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
|
|
+ "No such interface: %s", interface_name);
|
|
+ return FALSE;
|
|
+ }
|
|
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
|
|
+ "No such property: %s", property_name);
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static const GDBusInterfaceVTable interface_vtable =
|
|
+{
|
|
+ handle_method_call,
|
|
+ handle_get_property,
|
|
+ handle_set_property
|
|
+};
|
|
+
|
|
+static void
|
|
+name_lost_handler_cb (GDBusConnection *connection, const gchar *name, gpointer user_data)
|
|
+{
|
|
+ g_debug ("lost name, so exiting");
|
|
+ gtk_main_quit ();
|
|
+}
|
|
+
|
|
+static void
|
|
+on_bus_gotten (GObject *source_object, GAsyncResult *res, GsdSubscriptionManager *manager)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+ GDBusConnection *connection;
|
|
+ g_autoptr(GError) error = NULL;
|
|
+
|
|
+ connection = g_bus_get_finish (res, &error);
|
|
+ if (connection == NULL) {
|
|
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
+ g_warning ("Could not get session bus: %s", error->message);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ priv->connection = connection;
|
|
+ g_dbus_connection_register_object (connection,
|
|
+ GSD_SUBSCRIPTION_DBUS_PATH,
|
|
+ priv->introspection_data->interfaces[0],
|
|
+ &interface_vtable,
|
|
+ manager,
|
|
+ NULL,
|
|
+ NULL);
|
|
+ priv->name_id = g_bus_own_name_on_connection (connection,
|
|
+ GSD_SUBSCRIPTION_DBUS_NAME,
|
|
+ G_BUS_NAME_OWNER_FLAGS_NONE,
|
|
+ NULL,
|
|
+ name_lost_handler_cb,
|
|
+ manager,
|
|
+ NULL);
|
|
+}
|
|
+
|
|
+static void
|
|
+register_manager_dbus (GsdSubscriptionManager *manager)
|
|
+{
|
|
+ GsdSubscriptionManagerPrivate *priv = manager->priv;
|
|
+
|
|
+ priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
|
|
+ g_assert (priv->introspection_data != NULL);
|
|
+ priv->bus_cancellable = g_cancellable_new ();
|
|
+
|
|
+ g_bus_get (G_BUS_TYPE_SESSION, priv->bus_cancellable,
|
|
+ (GAsyncReadyCallback) on_bus_gotten, manager);
|
|
+}
|
|
+
|
|
+GsdSubscriptionManager *
|
|
+gsd_subscription_manager_new (void)
|
|
+{
|
|
+ if (manager_object != NULL) {
|
|
+ g_object_ref (manager_object);
|
|
+ } else {
|
|
+ manager_object = g_object_new (GSD_TYPE_SUBSCRIPTION_MANAGER, NULL);
|
|
+ g_object_add_weak_pointer (manager_object,
|
|
+ (gpointer *) &manager_object);
|
|
+ register_manager_dbus (manager_object);
|
|
+ }
|
|
+
|
|
+ return GSD_SUBSCRIPTION_MANAGER (manager_object);
|
|
+}
|
|
diff --git a/plugins/subman/gsd-subscription-manager.h b/plugins/subman/gsd-subscription-manager.h
|
|
new file mode 100644
|
|
index 00000000..6a524b1b
|
|
--- /dev/null
|
|
+++ b/plugins/subman/gsd-subscription-manager.h
|
|
@@ -0,0 +1,63 @@
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
+ *
|
|
+ * Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
|
|
+ *
|
|
+ * 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, see <http://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __GSD_SUBSCRIPTION_MANAGER_H
|
|
+#define __GSD_SUBSCRIPTION_MANAGER_H
|
|
+
|
|
+#include <glib-object.h>
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+#define GSD_TYPE_SUBSCRIPTION_MANAGER (gsd_subscription_manager_get_type ())
|
|
+#define GSD_SUBSCRIPTION_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_SUBSCRIPTION_MANAGER, GsdSubscriptionManager))
|
|
+#define GSD_SUBSCRIPTION_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_SUBSCRIPTION_MANAGER, GsdSubscriptionManagerClass))
|
|
+#define GSD_IS_SUBSCRIPTION_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_SUBSCRIPTION_MANAGER))
|
|
+#define GSD_IS_SUBSCRIPTION_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_SUBSCRIPTION_MANAGER))
|
|
+#define GSD_SUBSCRIPTION_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_SUBSCRIPTION_MANAGER, GsdSubscriptionManagerClass))
|
|
+#define GSD_SUBSCRIPTION_MANAGER_ERROR (gsd_subscription_manager_error_quark ())
|
|
+
|
|
+typedef struct GsdSubscriptionManagerPrivate GsdSubscriptionManagerPrivate;
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ GObject parent;
|
|
+ GsdSubscriptionManagerPrivate *priv;
|
|
+} GsdSubscriptionManager;
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ GObjectClass parent_class;
|
|
+} GsdSubscriptionManagerClass;
|
|
+
|
|
+enum
|
|
+{
|
|
+ GSD_SUBSCRIPTION_MANAGER_ERROR_FAILED
|
|
+};
|
|
+
|
|
+GType gsd_subscription_manager_get_type (void);
|
|
+GQuark gsd_subscription_manager_error_quark (void);
|
|
+
|
|
+GsdSubscriptionManager *gsd_subscription_manager_new (void);
|
|
+gboolean gsd_subscription_manager_start (GsdSubscriptionManager *manager,
|
|
+ GError **error);
|
|
+void gsd_subscription_manager_stop (GsdSubscriptionManager *manager);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __GSD_SUBSCRIPTION_MANAGER_H */
|
|
diff --git a/plugins/subman/main.c b/plugins/subman/main.c
|
|
new file mode 100644
|
|
index 00000000..28ac995b
|
|
--- /dev/null
|
|
+++ b/plugins/subman/main.c
|
|
@@ -0,0 +1,8 @@
|
|
+#define NEW gsd_subscription_manager_new
|
|
+#define START gsd_subscription_manager_start
|
|
+#define STOP gsd_subscription_manager_stop
|
|
+#define MANAGER GsdSubscriptionManager
|
|
+#define GDK_BACKEND "x11"
|
|
+#include "gsd-subscription-manager.h"
|
|
+
|
|
+#include "daemon-skeleton-gtk.h"
|
|
diff --git a/plugins/subman/meson.build b/plugins/subman/meson.build
|
|
new file mode 100644
|
|
index 00000000..bfd073b6
|
|
--- /dev/null
|
|
+++ b/plugins/subman/meson.build
|
|
@@ -0,0 +1,56 @@
|
|
+sources = files(
|
|
+ 'gsd-subscription-manager.c',
|
|
+ 'gsd-subman-common.c',
|
|
+ 'main.c'
|
|
+)
|
|
+
|
|
+deps = plugins_deps + [
|
|
+ libnotify_dep,
|
|
+ gtk_dep,
|
|
+ jsonglib_dep,
|
|
+ m_dep,
|
|
+]
|
|
+
|
|
+cflags += ['-DBINDIR="@0@"'.format(gsd_bindir)]
|
|
+cflags += ['-DLIBEXECDIR="@0@"'.format(gsd_libexecdir)]
|
|
+
|
|
+executable(
|
|
+ 'gsd-' + plugin_name,
|
|
+ sources,
|
|
+ include_directories: [top_inc, common_inc],
|
|
+ dependencies: deps,
|
|
+ c_args: cflags,
|
|
+ install: true,
|
|
+ install_rpath: gsd_pkglibdir,
|
|
+ install_dir: gsd_libexecdir
|
|
+)
|
|
+
|
|
+# .Register needs to be called from root as subman can't do PolicyKit...
|
|
+policy = 'org.gnome.settings-daemon.plugins.subman.policy'
|
|
+policy_in = configure_file(
|
|
+ input: policy + '.in.in',
|
|
+ output: policy + '.in',
|
|
+ configuration: plugins_conf
|
|
+)
|
|
+
|
|
+i18n.merge_file(
|
|
+ policy,
|
|
+ input: policy_in,
|
|
+ output: policy,
|
|
+ po_dir: po_dir,
|
|
+ install: true,
|
|
+ install_dir: join_paths(gsd_datadir, 'polkit-1', 'actions')
|
|
+)
|
|
+
|
|
+install_data('org.gnome.settings-daemon.plugins.subman.rules',
|
|
+ install_dir : join_paths(gsd_datadir, 'polkit-1', 'rules.d'))
|
|
+
|
|
+executable(
|
|
+ 'gsd-subman-helper',
|
|
+ 'gsd-subman-helper.c',
|
|
+ include_directories: top_inc,
|
|
+ dependencies: [gio_dep, jsonglib_dep],
|
|
+ install: true,
|
|
+ install_rpath: gsd_pkglibdir,
|
|
+ install_dir: gsd_libexecdir
|
|
+)
|
|
diff --git a/plugins/subman/org.gnome.SettingsDaemon.Subscription.desktop.in b/plugins/subman/org.gnome.SettingsDaemon.Subscription.desktop.in
|
|
new file mode 100644
|
|
index 00000000..14fe5915
|
|
--- /dev/null
|
|
+++ b/plugins/subman/org.gnome.SettingsDaemon.Subscription.desktop.in
|
|
@@ -0,0 +1,9 @@
|
|
+[Desktop Entry]
|
|
+Type=Application
|
|
+Name=GNOME Settings Daemon's subscription manager plugin
|
|
+Exec=@libexecdir@/gsd-subman
|
|
+OnlyShowIn=GNOME;
|
|
+NoDisplay=true
|
|
+X-GNOME-Autostart-Phase=Initialization
|
|
+X-GNOME-Autostart-Notify=true
|
|
+X-GNOME-AutoRestart=true
|
|
diff --git a/plugins/subman/org.gnome.settings-daemon.plugins.subman.policy.in.in b/plugins/subman/org.gnome.settings-daemon.plugins.subman.policy.in.in
|
|
new file mode 100644
|
|
index 00000000..59e9fdd4
|
|
--- /dev/null
|
|
+++ b/plugins/subman/org.gnome.settings-daemon.plugins.subman.policy.in.in
|
|
@@ -0,0 +1,27 @@
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
+<!DOCTYPE policyconfig PUBLIC
|
|
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
|
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
|
|
+<policyconfig>
|
|
+
|
|
+ <!--
|
|
+ Policy definitions for gnome-settings-daemon system-wide actions.
|
|
+ Copyright (c) 2019 Richard Hughes <richard@hughsie.com>
|
|
+ -->
|
|
+
|
|
+ <vendor>GNOME Settings Daemon</vendor>
|
|
+ <vendor_url>http://git.gnome.org/browse/gnome-settings-daemon</vendor_url>
|
|
+ <icon_name>emblem-synchronizing</icon_name>
|
|
+
|
|
+ <action id="org.gnome.settings-daemon.plugins.subman.register">
|
|
+ <description>Register the system</description>
|
|
+ <message>Authentication is required to register the system</message>
|
|
+ <defaults>
|
|
+ <allow_any>no</allow_any>
|
|
+ <allow_inactive>no</allow_inactive>
|
|
+ <allow_active>auth_admin_keep</allow_active>
|
|
+ </defaults>
|
|
+ <annotate key="org.freedesktop.policykit.exec.path">@libexecdir@/gsd-subman-helper</annotate>
|
|
+ </action>
|
|
+
|
|
+</policyconfig>
|
|
diff --git a/plugins/subman/org.gnome.settings-daemon.plugins.subman.rules b/plugins/subman/org.gnome.settings-daemon.plugins.subman.rules
|
|
new file mode 100644
|
|
index 00000000..1ed3a0ea
|
|
--- /dev/null
|
|
+++ b/plugins/subman/org.gnome.settings-daemon.plugins.subman.rules
|
|
@@ -0,0 +1,7 @@
|
|
+polkit.addRule(function(action, subject) {
|
|
+ if (action.id == "org.gnome.settings-daemon.plugins.subman.register" &&
|
|
+ subject.active == true && subject.local == true &&
|
|
+ subject.isInGroup("wheel")) {
|
|
+ return polkit.Result.YES;
|
|
+ }
|
|
+});
|
|
--
|
|
2.41.0.rc2
|
|
|