From 2b528edce044642edcd388b851f52012b7e09e38 Mon Sep 17 00:00:00 2001 From: Zhang Xianwei Date: Thu, 18 Oct 2018 17:09:38 +0800 Subject: rfkill: Add RFKill support plugin on Linux systems from https://github.com/GNOME/gnome-settings-daemon/commit/444af32e343a79ca3db0f957bf73687a0f0df9ec Signed-off-by: Zhang Xianwei --- configure.ac | 18 + data/Makefile.am | 1 + ...te.SettingsDaemon.plugins.rfkill.gschema.xml.in | 15 + mate-settings-daemon/Makefile.am | 2 + mate-settings-daemon/mate-settings-bus.c | 77 +++ mate-settings-daemon/mate-settings-bus.h | 33 + plugins/Makefile.am | 6 + .../rfkill/61-mate-settings-daemon-rfkill.rules | 8 + plugins/rfkill/Makefile.am | 42 ++ plugins/rfkill/msd-rfkill-manager.c | 739 +++++++++++++++++++++ plugins/rfkill/msd-rfkill-manager.h | 58 ++ plugins/rfkill/msd-rfkill-plugin.c | 112 ++++ plugins/rfkill/msd-rfkill-plugin.h | 62 ++ plugins/rfkill/rfkill-glib.c | 517 ++++++++++++++ plugins/rfkill/rfkill-glib.h | 85 +++ plugins/rfkill/rfkill.h | 107 +++ plugins/rfkill/rfkill.mate-settings-plugin.in | 10 + po/POTFILES.in | 2 + 18 files changed, 1894 insertions(+) create mode 100644 data/org.mate.SettingsDaemon.plugins.rfkill.gschema.xml.in create mode 100644 mate-settings-daemon/mate-settings-bus.c create mode 100644 mate-settings-daemon/mate-settings-bus.h create mode 100644 plugins/rfkill/61-mate-settings-daemon-rfkill.rules create mode 100644 plugins/rfkill/Makefile.am create mode 100644 plugins/rfkill/msd-rfkill-manager.c create mode 100644 plugins/rfkill/msd-rfkill-manager.h create mode 100644 plugins/rfkill/msd-rfkill-plugin.c create mode 100644 plugins/rfkill/msd-rfkill-plugin.h create mode 100644 plugins/rfkill/rfkill-glib.c create mode 100644 plugins/rfkill/rfkill-glib.h create mode 100644 plugins/rfkill/rfkill.h create mode 100644 plugins/rfkill/rfkill.mate-settings-plugin.in diff --git a/configure.ac b/configure.ac index 63d1c62..b3b77c1 100644 --- a/configure.ac +++ b/configure.ac @@ -355,6 +355,21 @@ AM_CONDITIONAL(HAVE_POLKIT, test "x$HAVE_POLKIT" = "xyes") AC_SUBST(POLKIT_CFLAGS) AC_SUBST(POLKIT_LIBS) +# --------------------------------------------------------------------------- +# Rfkill +# --------------------------------------------------------------------------- + +AC_ARG_ENABLE(rfkill, + AS_HELP_STRING([--disable-rfkill], [disable rfkill support (default: enabled)]),, + enable_rfkill=yes, enabled_rfkill=no) + +if test x"$enable_rfkill" != x"no" ; then + AC_CHECK_HEADERS([linux/rfkill.h],, + AC_MSG_ERROR([RFKill headers not found but rfkill support requested])) +fi + +AM_CONDITIONAL(BUILD_RFKILL, [test x"$enable_rfkill" = x"yes"]) + # --------------------------------------------------------------------------- # Enable Profiling # --------------------------------------------------------------------------- @@ -454,6 +469,7 @@ plugins/keyboard/Makefile plugins/media-keys/Makefile plugins/mpris/Makefile plugins/mouse/Makefile +plugins/rfkill/Makefile plugins/smartcard/Makefile plugins/sound/Makefile plugins/typing-break/Makefile @@ -480,6 +496,7 @@ data/org.mate.SettingsDaemon.plugins.keyboard.gschema.xml data/org.mate.SettingsDaemon.plugins.media-keys.gschema.xml data/org.mate.SettingsDaemon.plugins.mpris.gschema.xml data/org.mate.SettingsDaemon.plugins.mouse.gschema.xml +data/org.mate.SettingsDaemon.plugins.rfkill.gschema.xml data/org.mate.SettingsDaemon.plugins.smartcard.gschema.xml data/org.mate.SettingsDaemon.plugins.sound.gschema.xml data/org.mate.SettingsDaemon.plugins.typing-break.gschema.xml @@ -521,6 +538,7 @@ echo " Libcanberra support: ${have_libcanberra} Libmatemixer support: ${have_libmatemixer} Smartcard support: ${have_smartcard_support} + RFKill support: ${enable_rfkill} ${NSS_DATABASE:+\ System nssdb: ${NSS_DATABASE} }\ diff --git a/data/Makefile.am b/data/Makefile.am index 34cf52e..5f40975 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -16,6 +16,7 @@ msd_gschemas_in = \ org.mate.SettingsDaemon.plugins.media-keys.gschema.xml.in \ org.mate.SettingsDaemon.plugins.mpris.gschema.xml.in \ org.mate.SettingsDaemon.plugins.mouse.gschema.xml.in \ + org.mate.SettingsDaemon.plugins.rfkill.gschema.xml.in \ org.mate.SettingsDaemon.plugins.smartcard.gschema.xml.in \ org.mate.SettingsDaemon.plugins.sound.gschema.xml.in \ org.mate.SettingsDaemon.plugins.typing-break.gschema.xml.in \ diff --git a/data/org.mate.SettingsDaemon.plugins.rfkill.gschema.xml.in b/data/org.mate.SettingsDaemon.plugins.rfkill.gschema.xml.in new file mode 100644 index 0000000..9292832 --- /dev/null +++ b/data/org.mate.SettingsDaemon.plugins.rfkill.gschema.xml.in @@ -0,0 +1,15 @@ + + + + true + Activation of this plugin + Whether this plugin would be activated by mate-settings-daemon or not + + + 0 + Priority to use for this plugin + Priority to use for this plugin in mate-settings-daemon startup queue + + + + diff --git a/mate-settings-daemon/Makefile.am b/mate-settings-daemon/Makefile.am index 91e4c78..ed0f1b1 100644 --- a/mate-settings-daemon/Makefile.am +++ b/mate-settings-daemon/Makefile.am @@ -59,6 +59,8 @@ api_DATA = \ mate_settings_daemon_SOURCES = \ main.c \ + mate-settings-bus.c \ + mate-settings-bus.h \ mate-settings-manager.c \ mate-settings-manager.h \ mate-settings-plugin.c \ diff --git a/mate-settings-daemon/mate-settings-bus.c b/mate-settings-daemon/mate-settings-bus.c new file mode 100644 index 0000000..806ca04 --- /dev/null +++ b/mate-settings-daemon/mate-settings-bus.c @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2006-2011 Richard Hughes + * + * 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 +#include +#include +#include +#include + +#include "mate-settings-bus.h" + +char * +mate_settings_get_chassis_type (void) +{ + char *ret = NULL; + GError *error = NULL; + GVariant *inner; + GVariant *variant = NULL; + GDBusConnection *connection; + + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, + NULL, + &error); + if (connection == NULL) { + g_warning ("system bus not available: %s", error->message); + g_error_free (error); + goto out; + } + + variant = g_dbus_connection_call_sync (connection, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", + "org.freedesktop.hostname1", + "Chassis"), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (variant == NULL) { + g_debug ("Failed to get property '%s': %s", "Chassis", error->message); + g_error_free (error); + goto out; + } + + g_variant_get (variant, "(v)", &inner); + ret = g_variant_dup_string (inner, NULL); + g_debug ("Get property '%s': %s", "Chassis", ret); + g_variant_unref (inner); +out: + g_clear_object (&connection); + g_clear_pointer (&variant, g_variant_unref); + return ret; +} diff --git a/mate-settings-daemon/mate-settings-bus.h b/mate-settings-daemon/mate-settings-bus.h new file mode 100644 index 0000000..e97f08d --- /dev/null +++ b/mate-settings-daemon/mate-settings-bus.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2010-2011 Richard Hughes + * + * 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. + */ + +#ifndef __MATE_SETTINGS_BUS_H +#define __MATE_SETTINGS_BUS_H + +#include + +G_BEGIN_DECLS + +char * mate_settings_get_chassis_type (void); + +G_END_DECLS + +#endif /* __MATE_SETTINGS_BUS_H */ diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 05d04c7..3e14a03 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -28,5 +28,11 @@ else disabled_plugins += smartcard endif +if BUILD_RFKILL +enabled_plugins += rfkill +else +disabled_plugins += rfkill +endif + SUBDIRS = common $(enabled_plugins) DIST_SUBDIRS = $(SUBDIRS) $(disabled_plugins) diff --git a/plugins/rfkill/61-mate-settings-daemon-rfkill.rules b/plugins/rfkill/61-mate-settings-daemon-rfkill.rules new file mode 100644 index 0000000..87eabff --- /dev/null +++ b/plugins/rfkill/61-mate-settings-daemon-rfkill.rules @@ -0,0 +1,8 @@ +# Get access to /dev/rfkill for users +# See https://bugzilla.redhat.com/show_bug.cgi?id=514798 +# +# Simplified by Kay Sievers +# https://bugzilla.redhat.com/show_bug.cgi?id=733326 +# See also https://bugzilla.gnome.org/show_bug.cgi?id=711373 + +KERNEL=="rfkill", SUBSYSTEM=="misc", TAG+="uaccess" diff --git a/plugins/rfkill/Makefile.am b/plugins/rfkill/Makefile.am new file mode 100644 index 0000000..9cc6a99 --- /dev/null +++ b/plugins/rfkill/Makefile.am @@ -0,0 +1,42 @@ +plugin_LTLIBRARIES = librfkill.la + +librfkill_la_SOURCES = \ + msd-rfkill-plugin.c \ + msd-rfkill-manager.h \ + msd-rfkill-manager.c \ + rfkill-glib.c \ + rfkill-glib.h \ + rfkill.h + +librfkill_la_CPPFLAGS = \ + -I$(top_srcdir)/mate-settings-daemon \ + -I$(top_srcdir)/data/ \ + -DMATE_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + $(AM_CPPFLAGS) + +librfkill_la_CFLAGS = \ + $(PLUGIN_CFLAGS) \ + $(RFKILL_CFLAGS) \ + $(SETTINGS_PLUGIN_CFLAGS) \ + $(AM_CFLAGS) + +librfkill_la_LDFLAGS = \ + $(MSD_PLUGIN_LDFLAGS) + +librfkill_la_LIBADD = \ + $(RFKILL_LIBS) \ + $(SETTINGS_PLUGIN_LIBS) + +plugin_in_files = rfkill.mate-settings-plugin.in + +plugin_DATA = $(plugin_in_files:.mate-settings-plugin.in=.mate-settings-plugin) + +udevrulesdir = $(prefix)/lib/udev/rules.d +udevrules_DATA = 61-mate-settings-daemon-rfkill.rules + +EXTRA_DIST = $(plugin_in_files) $(udevrules_DATA) +CLEANFILES = $(plugin_DATA) +DISTCLEANFILES = $(plugin_DATA) + +@MSD_INTLTOOL_PLUGIN_RULE@ diff --git a/plugins/rfkill/msd-rfkill-manager.c b/plugins/rfkill/msd-rfkill-manager.c new file mode 100644 index 0000000..4c1ec19 --- /dev/null +++ b/plugins/rfkill/msd-rfkill-manager.c @@ -0,0 +1,739 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann + * Copyright (C) 2010,2011 Red Hat, Inc. + * + * Author: Bastien Nocera + * + * 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 St, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include "config.h" + +#include +#include + +#include "mate-settings-profile.h" +#include "msd-rfkill-manager.h" +#include "rfkill-glib.h" +#include "mate-settings-bus.h" + +#define MSD_RFKILL_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MSD_TYPE_RFKILL_MANAGER, MsdRfkillManagerPrivate)) + +struct MsdRfkillManagerPrivate +{ + GDBusNodeInfo *introspection_data; + guint name_id; + GDBusConnection *connection; + GCancellable *cancellable; + + CcRfkillGlib *rfkill; + GHashTable *killswitches; + GHashTable *bt_killswitches; + + /* In addition to using the rfkill kernel subsystem + (which is exposed by wlan, wimax, bluetooth, nfc, + some platform drivers and some usb modems), we + need to go through NetworkManager, which in turn + will tell ModemManager to write the right commands + in the USB bus to take external modems down, all + from userspace. + */ + GDBusProxy *nm_client; + gboolean wwan_enabled; + GDBusObjectManager *mm_client; + gboolean wwan_interesting; + + gchar *chassis_type; +}; + +#define MSD_DBUS_NAME "org.mate.SettingsDaemon" +#define MSD_DBUS_PATH "/org/mate/SettingsDaemon" + +#define MSD_RFKILL_DBUS_NAME MSD_DBUS_NAME ".Rfkill" +#define MSD_RFKILL_DBUS_PATH MSD_DBUS_PATH "/Rfkill" + +static const gchar introspection_xml[] = +"" +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +""; + +static void msd_rfkill_manager_class_init (MsdRfkillManagerClass *klass); +static void msd_rfkill_manager_init (MsdRfkillManager *rfkill_manager); +static void msd_rfkill_manager_finalize (GObject *object); + +G_DEFINE_TYPE (MsdRfkillManager, msd_rfkill_manager, G_TYPE_OBJECT) + +static gpointer manager_object = NULL; + +static void +msd_rfkill_manager_class_init (MsdRfkillManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = msd_rfkill_manager_finalize; + + g_type_class_add_private (klass, sizeof (MsdRfkillManagerPrivate)); +} + +static void +msd_rfkill_manager_init (MsdRfkillManager *manager) +{ + manager->priv = MSD_RFKILL_MANAGER_GET_PRIVATE (manager); +} + +static gboolean +engine_get_airplane_mode_helper (GHashTable *killswitches) +{ + GHashTableIter iter; + gpointer key, value; + + if (g_hash_table_size (killswitches) == 0) + return FALSE; + + g_hash_table_iter_init (&iter, killswitches); + while (g_hash_table_iter_next (&iter, &key, &value)) { + int state; + + state = GPOINTER_TO_INT (value); + + /* A single rfkill switch that's unblocked? Airplane mode is off */ + if (state == RFKILL_STATE_UNBLOCKED) + return FALSE; + } + + return TRUE; +} + +static gboolean +engine_get_bluetooth_airplane_mode (MsdRfkillManager *manager) +{ + return engine_get_airplane_mode_helper (manager->priv->bt_killswitches); +} + +static gboolean +engine_get_bluetooth_hardware_airplane_mode (MsdRfkillManager *manager) +{ + GHashTableIter iter; + gpointer key, value; + + /* If we have no killswitches, hw airplane mode is off. */ + if (g_hash_table_size (manager->priv->bt_killswitches) == 0) + return FALSE; + + g_hash_table_iter_init (&iter, manager->priv->bt_killswitches); + while (g_hash_table_iter_next (&iter, &key, &value)) { + int state; + + state = GPOINTER_TO_INT (value); + + /* A single rfkill switch that's not hw blocked? Hw airplane mode is off */ + if (state != RFKILL_STATE_HARD_BLOCKED) { + return FALSE; + } + } + + return TRUE; +} + +static gboolean +engine_get_has_bluetooth_airplane_mode (MsdRfkillManager *manager) +{ + return (g_hash_table_size (manager->priv->bt_killswitches) > 0); +} + +static gboolean +engine_get_airplane_mode (MsdRfkillManager *manager) +{ + if (!manager->priv->wwan_interesting) + return engine_get_airplane_mode_helper (manager->priv->killswitches); + /* wwan enabled? then airplane mode is off (because an USB modem + could be on in this state) */ + return engine_get_airplane_mode_helper (manager->priv->killswitches) && !manager->priv->wwan_enabled; +} + +static gboolean +engine_get_hardware_airplane_mode (MsdRfkillManager *manager) +{ + GHashTableIter iter; + gpointer key, value; + + /* If we have no killswitches, hw airplane mode is off. */ + if (g_hash_table_size (manager->priv->killswitches) == 0) + return FALSE; + + g_hash_table_iter_init (&iter, manager->priv->killswitches); + while (g_hash_table_iter_next (&iter, &key, &value)) { + int state; + + state = GPOINTER_TO_INT (value); + + /* A single rfkill switch that's not hw blocked? Hw airplane mode is off */ + if (state != RFKILL_STATE_HARD_BLOCKED) { + return FALSE; + } + } + + return TRUE; +} + +static gboolean +engine_get_has_airplane_mode (MsdRfkillManager *manager) +{ + return (g_hash_table_size (manager->priv->killswitches) > 0) || + manager->priv->wwan_interesting; +} + +static gboolean +engine_get_should_show_airplane_mode (MsdRfkillManager *manager) +{ + return (g_strcmp0 (manager->priv->chassis_type, "desktop") != 0) && + (g_strcmp0 (manager->priv->chassis_type, "server") != 0) && + (g_strcmp0 (manager->priv->chassis_type, "vm") != 0) && + (g_strcmp0 (manager->priv->chassis_type, "container") != 0); +} + +static void +engine_properties_changed (MsdRfkillManager *manager) +{ + GVariantBuilder props_builder; + GVariant *props_changed = NULL; + + /* not yet connected to the session bus */ + if (manager->priv->connection == NULL) + return; + + g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}")); + + g_variant_builder_add (&props_builder, "{sv}", "AirplaneMode", + g_variant_new_boolean (engine_get_airplane_mode (manager))); + g_variant_builder_add (&props_builder, "{sv}", "HardwareAirplaneMode", + g_variant_new_boolean (engine_get_hardware_airplane_mode (manager))); + g_variant_builder_add (&props_builder, "{sv}", "HasAirplaneMode", + g_variant_new_boolean (engine_get_has_airplane_mode (manager))); + g_variant_builder_add (&props_builder, "{sv}", "ShouldShowAirplaneMode", + g_variant_new_boolean (engine_get_should_show_airplane_mode (manager))); + g_variant_builder_add (&props_builder, "{sv}", "BluetoothAirplaneMode", + g_variant_new_boolean (engine_get_bluetooth_airplane_mode (manager))); + g_variant_builder_add (&props_builder, "{sv}", "BluetoothHardwareAirplaneMode", + g_variant_new_boolean (engine_get_bluetooth_hardware_airplane_mode (manager))); + g_variant_builder_add (&props_builder, "{sv}", "BluetoothHasAirplaneMode", + g_variant_new_boolean (engine_get_has_bluetooth_airplane_mode (manager))); + + props_changed = g_variant_new ("(s@a{sv}@as)", MSD_RFKILL_DBUS_NAME, + g_variant_builder_end (&props_builder), + g_variant_new_strv (NULL, 0)); + + g_dbus_connection_emit_signal (manager->priv->connection, + NULL, + MSD_RFKILL_DBUS_PATH, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + props_changed, NULL); +} + +static void +rfkill_changed (CcRfkillGlib *rfkill, + GList *events, + MsdRfkillManager *manager) +{ + GList *l; + int value; + + for (l = events; l != NULL; l = l->next) { + struct rfkill_event *event = l->data; + + switch (event->op) { + case RFKILL_OP_ADD: + case RFKILL_OP_CHANGE: + if (event->hard) + value = RFKILL_STATE_HARD_BLOCKED; + else if (event->soft) + value = RFKILL_STATE_SOFT_BLOCKED; + else + value = RFKILL_STATE_UNBLOCKED; + + g_hash_table_insert (manager->priv->killswitches, + GINT_TO_POINTER (event->idx), + GINT_TO_POINTER (value)); + if (event->type == RFKILL_TYPE_BLUETOOTH) + g_hash_table_insert (manager->priv->bt_killswitches, + GINT_TO_POINTER (event->idx), + GINT_TO_POINTER (value)); + g_debug ("%s %srfkill with ID %d", + event->op == RFKILL_OP_ADD ? "Added" : "Changed", + event->type == RFKILL_TYPE_BLUETOOTH ? "Bluetooth " : "", + event->idx); + break; + case RFKILL_OP_DEL: + g_hash_table_remove (manager->priv->killswitches, + GINT_TO_POINTER (event->idx)); + if (event->type == RFKILL_TYPE_BLUETOOTH) + g_hash_table_remove (manager->priv->bt_killswitches, + GINT_TO_POINTER (event->idx)); + g_debug ("Removed %srfkill with ID %d", event->type == RFKILL_TYPE_BLUETOOTH ? "Bluetooth " : "", + event->idx); + break; + } + } + + engine_properties_changed (manager); +} + +static void +rfkill_set_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + gboolean ret; + GError *error = NULL; + + ret = cc_rfkill_glib_send_change_all_event_finish (CC_RFKILL_GLIB (source_object), res, &error); + if (!ret) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) + g_debug ("Timed out waiting for blocked rfkills"); + else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to set RFKill: %s", error->message); + g_error_free (error); + } +} + +static void +set_wwan_complete (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GError *error; + GVariant *variant; + + error = NULL; + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), result, &error); + + if (variant == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to set WWAN power status: %s", error->message); + + g_error_free (error); + } else { + g_variant_unref (variant); + } +} + +static gboolean +engine_set_bluetooth_airplane_mode (MsdRfkillManager *manager, + gboolean enable) +{ + cc_rfkill_glib_send_change_all_event (manager->priv->rfkill, RFKILL_TYPE_BLUETOOTH, + enable, manager->priv->cancellable, rfkill_set_cb, manager); + + return TRUE; +} + +static gboolean +engine_set_airplane_mode (MsdRfkillManager *manager, + gboolean enable) +{ + cc_rfkill_glib_send_change_all_event (manager->priv->rfkill, RFKILL_TYPE_ALL, + enable, manager->priv->cancellable, rfkill_set_cb, manager); + + /* Note: we set the the NM property even if there are no modems, so we don't + need to resync when one is plugged in */ + if (manager->priv->nm_client) { + g_dbus_proxy_call (manager->priv->nm_client, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + "org.freedesktop.NetworkManager", + "WwanEnabled", + g_variant_new_boolean (!enable)), + G_DBUS_CALL_FLAGS_NONE, + -1, /* timeout */ + manager->priv->cancellable, + set_wwan_complete, NULL); + } + + return TRUE; +} + +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) +{ + MsdRfkillManager *manager = MSD_RFKILL_MANAGER (user_data); + + if (g_strcmp0 (property_name, "AirplaneMode") == 0) { + gboolean airplane_mode; + g_variant_get (value, "b", &airplane_mode); + return engine_set_airplane_mode (manager, airplane_mode); + } else if (g_strcmp0 (property_name, "BluetoothAirplaneMode") == 0) { + gboolean airplane_mode; + g_variant_get (value, "b", &airplane_mode); + return engine_set_bluetooth_airplane_mode (manager, airplane_mode); + } + + return FALSE; +} + +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) +{ + MsdRfkillManager *manager = MSD_RFKILL_MANAGER (user_data); + + /* Check session pointer as a proxy for whether the manager is in the + start or stop state */ + if (manager->priv->connection == NULL) { + return NULL; + } + + if (g_strcmp0 (property_name, "AirplaneMode") == 0) { + gboolean airplane_mode; + airplane_mode = engine_get_airplane_mode (manager); + return g_variant_new_boolean (airplane_mode); + } + + if (g_strcmp0 (property_name, "HardwareAirplaneMode") == 0) { + gboolean hw_airplane_mode; + hw_airplane_mode = engine_get_hardware_airplane_mode (manager); + return g_variant_new_boolean (hw_airplane_mode); + } + + if (g_strcmp0 (property_name, "ShouldShowAirplaneMode") == 0) { + gboolean should_show_airplane_mode; + should_show_airplane_mode = engine_get_should_show_airplane_mode (manager); + return g_variant_new_boolean (should_show_airplane_mode); + } + + if (g_strcmp0 (property_name, "HasAirplaneMode") == 0) { + gboolean has_airplane_mode; + has_airplane_mode = engine_get_has_airplane_mode (manager); + return g_variant_new_boolean (has_airplane_mode); + } + + if (g_strcmp0 (property_name, "BluetoothAirplaneMode") == 0) { + gboolean airplane_mode; + airplane_mode = engine_get_bluetooth_airplane_mode (manager); + return g_variant_new_boolean (airplane_mode); + } + + if (g_strcmp0 (property_name, "BluetoothHardwareAirplaneMode") == 0) { + gboolean hw_airplane_mode; + hw_airplane_mode = engine_get_bluetooth_hardware_airplane_mode (manager); + return g_variant_new_boolean (hw_airplane_mode); + } + + if (g_strcmp0 (property_name, "BluetoothHasAirplaneMode") == 0) { + gboolean has_airplane_mode; + has_airplane_mode = engine_get_has_bluetooth_airplane_mode (manager); + return g_variant_new_boolean (has_airplane_mode); + } + + return NULL; +} + +static const GDBusInterfaceVTable interface_vtable = +{ + NULL, + handle_get_property, + handle_set_property +}; + +static void +on_bus_gotten (GObject *source_object, + GAsyncResult *res, + MsdRfkillManager *manager) +{ + GDBusConnection *connection; + 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); + g_error_free (error); + return; + } + manager->priv->connection = connection; + + g_dbus_connection_register_object (connection, + MSD_RFKILL_DBUS_PATH, + manager->priv->introspection_data->interfaces[0], + &interface_vtable, + manager, + NULL, + NULL); + + manager->priv->name_id = g_bus_own_name_on_connection (connection, + MSD_RFKILL_DBUS_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + NULL, + NULL, + NULL, + NULL); +} + +static void +sync_wwan_enabled (MsdRfkillManager *manager) +{ + GVariant *property; + + property = g_dbus_proxy_get_cached_property (manager->priv->nm_client, + "WwanEnabled"); + + if (property == NULL) { + /* GDBus telling us NM went down */ + return; + } + + manager->priv->wwan_enabled = g_variant_get_boolean (property); + engine_properties_changed (manager); + + g_variant_unref (property); +} + +static void +nm_signal (GDBusProxy *proxy, + char *sender_name, + char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + MsdRfkillManager *manager = user_data; + GVariant *changed; + GVariant *property; + + if (g_strcmp0 (signal_name, "PropertiesChanged") == 0) { + changed = g_variant_get_child_value (parameters, 0); + property = g_variant_lookup_value (changed, "WwanEnabled", G_VARIANT_TYPE ("b")); + g_dbus_proxy_set_cached_property (proxy, "WwanEnabled", property); + + if (property != NULL) { + sync_wwan_enabled (manager); + g_variant_unref (property); + } + + g_variant_unref (changed); + } +} + +static void +on_nm_proxy_gotten (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + MsdRfkillManager *manager = user_data; + GDBusProxy *proxy; + GError *error; + + error = NULL; + proxy = g_dbus_proxy_new_for_bus_finish (result, &error); + + if (proxy == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) + g_warning ("Failed to acquire NetworkManager proxy: %s", error->message); + + g_error_free (error); + goto out; + } + + manager->priv->nm_client = proxy; + + g_signal_connect (manager->priv->nm_client, "g-signal", + G_CALLBACK (nm_signal), manager); + sync_wwan_enabled (manager); + + out: + g_object_unref (manager); +} + +static void +sync_wwan_interesting (GDBusObjectManager *object_manager, + GDBusObject *object, + GDBusInterface *interface, + gpointer user_data) +{ + MsdRfkillManager *manager = user_data; + GList *objects; + + objects = g_dbus_object_manager_get_objects (object_manager); + manager->priv->wwan_interesting = (objects != NULL); + engine_properties_changed (manager); + + g_list_free_full (objects, g_object_unref); +} + +static void +on_mm_proxy_gotten (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + MsdRfkillManager *manager = user_data; + GDBusObjectManager *proxy; + GError *error; + + error = NULL; + proxy = g_dbus_object_manager_client_new_for_bus_finish (result, &error); + + if (proxy == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) + g_warning ("Failed to acquire ModemManager proxy: %s", error->message); + + g_error_free (error); + goto out; + } + + manager->priv->mm_client = proxy; + + g_signal_connect (manager->priv->mm_client, "interface-added", + G_CALLBACK (sync_wwan_interesting), manager); + g_signal_connect (manager->priv->mm_client, "interface-removed", + G_CALLBACK (sync_wwan_interesting), manager); + sync_wwan_interesting (manager->priv->mm_client, NULL, NULL, manager); + + out: + g_object_unref (manager); +} + +gboolean +msd_rfkill_manager_start (MsdRfkillManager *manager, + GError **error) +{ + mate_settings_profile_start (NULL); + + manager->priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (manager->priv->introspection_data != NULL); + + manager->priv->killswitches = g_hash_table_new (g_direct_hash, g_direct_equal); + manager->priv->bt_killswitches = g_hash_table_new (g_direct_hash, g_direct_equal); + manager->priv->rfkill = cc_rfkill_glib_new (); + g_signal_connect (G_OBJECT (manager->priv->rfkill), "changed", + G_CALLBACK (rfkill_changed), manager); + cc_rfkill_glib_open (manager->priv->rfkill); + + manager->priv->cancellable = g_cancellable_new (); + + manager->priv->chassis_type = mate_settings_get_chassis_type (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, /* g-interface-info */ + "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "org.freedesktop.NetworkManager", + manager->priv->cancellable, + on_nm_proxy_gotten, g_object_ref (manager)); + + g_dbus_object_manager_client_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + "org.freedesktop.ModemManager1", + "/org/freedesktop/ModemManager1", + NULL, NULL, NULL, /* get_proxy_type and closure */ + manager->priv->cancellable, + on_mm_proxy_gotten, g_object_ref (manager)); + + /* Start process of owning a D-Bus name */ + g_bus_get (G_BUS_TYPE_SESSION, + manager->priv->cancellable, + (GAsyncReadyCallback) on_bus_gotten, + manager); + + mate_settings_profile_end (NULL); + + return TRUE; +} + +void +msd_rfkill_manager_stop (MsdRfkillManager *manager) +{ + MsdRfkillManagerPrivate *p = manager->priv; + + g_debug ("Stopping rfkill manager"); + + if (manager->priv->name_id != 0) { + g_bus_unown_name (manager->priv->name_id); + manager->priv->name_id = 0; + } + + g_clear_pointer (&p->introspection_data, g_dbus_node_info_unref); + g_clear_object (&p->connection); + g_clear_object (&p->rfkill); + g_clear_pointer (&p->killswitches, g_hash_table_destroy); + g_clear_pointer (&p->bt_killswitches, g_hash_table_destroy); + + if (p->cancellable) { + g_cancellable_cancel (p->cancellable); + g_clear_object (&p->cancellable); + } + + g_clear_object (&p->nm_client); + g_clear_object (&p->mm_client); + p->wwan_enabled = FALSE; + p->wwan_interesting = FALSE; + + g_clear_pointer (&p->chassis_type, g_free); +} + +static void +msd_rfkill_manager_finalize (GObject *object) +{ + MsdRfkillManager *manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (MSD_IS_RFKILL_MANAGER (object)); + + manager = MSD_RFKILL_MANAGER (object); + + g_return_if_fail (manager->priv != NULL); + + msd_rfkill_manager_stop (manager); + + G_OBJECT_CLASS (msd_rfkill_manager_parent_class)->finalize (object); +} + +MsdRfkillManager * +msd_rfkill_manager_new (void) +{ + if (manager_object != NULL) { + g_object_ref (manager_object); + } else { + manager_object = g_object_new (MSD_TYPE_RFKILL_MANAGER, NULL); + g_object_add_weak_pointer (manager_object, + (gpointer *) &manager_object); + } + + return MSD_RFKILL_MANAGER (manager_object); +} diff --git a/plugins/rfkill/msd-rfkill-manager.h b/plugins/rfkill/msd-rfkill-manager.h new file mode 100644 index 0000000..6ca862f --- /dev/null +++ b/plugins/rfkill/msd-rfkill-manager.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#ifndef __MSD_RFKILL_MANAGER_H +#define __MSD_RFKILL_MANAGER_H + +#include + +G_BEGIN_DECLS + +#define MSD_TYPE_RFKILL_MANAGER (msd_rfkill_manager_get_type ()) +#define MSD_RFKILL_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MSD_TYPE_RFKILL_MANAGER, MsdRfkillManager)) +#define MSD_RFKILL_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MSD_TYPE_RFKILL_MANAGER, MsdRfkillManagerClass)) +#define MSD_IS_RFKILL_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MSD_TYPE_RFKILL_MANAGER)) +#define MSD_IS_RFKILL_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MSD_TYPE_RFKILL_MANAGER)) +#define MSD_RFKILL_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MSD_TYPE_RFKILL_MANAGER, MsdRfkillManagerClass)) + +typedef struct MsdRfkillManagerPrivate MsdRfkillManagerPrivate; + +typedef struct +{ + GObject parent; + MsdRfkillManagerPrivate *priv; +} MsdRfkillManager; + +typedef struct +{ + GObjectClass parent_class; +} MsdRfkillManagerClass; + +GType msd_rfkill_manager_get_type (void); + +MsdRfkillManager * msd_rfkill_manager_new (void); +gboolean msd_rfkill_manager_start (MsdRfkillManager *manager, + GError **error); +void msd_rfkill_manager_stop (MsdRfkillManager *manager); + +G_END_DECLS + +#endif /* __MSD_RFKILL_MANAGER_H */ diff --git a/plugins/rfkill/msd-rfkill-plugin.c b/plugins/rfkill/msd-rfkill-plugin.c new file mode 100644 index 0000000..ce8a870 --- /dev/null +++ b/plugins/rfkill/msd-rfkill-plugin.c @@ -0,0 +1,112 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann + * Copyright (C) 2010 Red Hat, Inc. + * + * 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, 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 St, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include "config.h" + +#include +#include + +#include "mate-settings-plugin.h" +#include "msd-rfkill-manager.h" +#include "msd-rfkill-plugin.h" + +struct _MsdRfkillPluginPrivate +{ + MsdRfkillManager *manager; +}; + +#define MSD_RFKILL_PLUGIN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), MSD_TYPE_RFKILL_PLUGIN, MsdRfkillPluginPrivate)) + +MATE_SETTINGS_PLUGIN_REGISTER (MsdRfkillPlugin, msd_rfkill_plugin) + +static void +msd_rfkill_plugin_init (MsdRfkillPlugin *plugin) +{ + plugin->priv = MSD_RFKILL_PLUGIN_GET_PRIVATE (plugin); + + g_debug ("MsdRfkillPlugin initializing"); + + plugin->priv->manager = msd_rfkill_manager_new (); +} + +static void +msd_rfkill_plugin_finalize (GObject *object) +{ + MsdRfkillPlugin *plugin; + + g_return_if_fail (object != NULL); + g_return_if_fail (MSD_IS_RFKILL_PLUGIN (object)); + + g_debug ("MsdRfkillPlugin finalizing"); + + plugin = MSD_RFKILL_PLUGIN (object); + + g_return_if_fail (plugin->priv != NULL); + + if (plugin->priv->manager != NULL) { + g_object_unref (plugin->priv->manager); + } + + G_OBJECT_CLASS (msd_rfkill_plugin_parent_class)->finalize (object); +} + +static void +impl_activate (MateSettingsPlugin *plugin) +{ + gboolean res; + GError *error; + + g_debug ("Activating rfkill plugin"); + + error = NULL; + res = msd_rfkill_manager_start (MSD_RFKILL_PLUGIN (plugin)->priv->manager, &error); + if (! res) { + g_warning ("Unable to start rfkill manager: %s", error->message); + g_error_free (error); + } +} + +static void +impl_deactivate (MateSettingsPlugin *plugin) +{ + g_debug ("Deactivating rfkill plugin"); + msd_rfkill_manager_stop (MSD_RFKILL_PLUGIN (plugin)->priv->manager); +} + +static void +msd_rfkill_plugin_class_init (MsdRfkillPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MateSettingsPluginClass *plugin_class = MATE_SETTINGS_PLUGIN_CLASS (klass); + + object_class->finalize = msd_rfkill_plugin_finalize; + + plugin_class->activate = impl_activate; + plugin_class->deactivate = impl_deactivate; + + g_type_class_add_private (klass, sizeof (MsdRfkillPluginPrivate)); +} + +static void +msd_rfkill_plugin_class_finalize (MsdRfkillPluginClass *klass) +{ +} + diff --git a/plugins/rfkill/msd-rfkill-plugin.h b/plugins/rfkill/msd-rfkill-plugin.h new file mode 100644 index 0000000..1887d62 --- /dev/null +++ b/plugins/rfkill/msd-rfkill-plugin.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky + * + * 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, 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 St, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#ifndef __MSD_RFKILL_PLUGIN_H__ +#define __MSD_RFKILL_PLUGIN_H__ + +#include +#include +#include + +#include "mate-settings-plugin.h" + +G_BEGIN_DECLS + +#define MSD_TYPE_RFKILL_PLUGIN (msd_rfkill_plugin_get_type ()) +#define MSD_RFKILL_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MSD_TYPE_RFKILL_PLUGIN, MsdRfkillPlugin)) +#define MSD_RFKILL_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MSD_TYPE_RFKILL_PLUGIN, MsdRfkillPluginClass)) +#define MSD_IS_RFKILL_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MSD_TYPE_RFKILL_PLUGIN)) +#define MSD_IS_RFKILL_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MSD_TYPE_RFKILL_PLUGIN)) +#define MSD_RFKILL_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MSD_TYPE_RFKILL_PLUGIN, MsdRfkillPluginClass)) + +typedef struct _MsdRfkillPlugin MsdRfkillPlugin; +typedef struct _MsdRfkillPluginClass MsdRfkillPluginClass; +typedef struct _MsdRfkillPluginPrivate MsdRfkillPluginPrivate; + +struct _MsdRfkillPlugin +{ + MateSettingsPlugin parent; + MsdRfkillPluginPrivate *priv; +}; + +struct _MsdRfkillPluginClass +{ + MateSettingsPluginClass parent_class; +}; + +GType msd_rfkill_plugin_get_type (void) G_GNUC_CONST; + +/* All the plugins must implement this function */ +G_MODULE_EXPORT GType register_mate_settings_plugin (GTypeModule *module); + +G_END_DECLS + +#endif /* __MSD_RFKILL_PLUGIN_H__ */ diff --git a/plugins/rfkill/rfkill-glib.c b/plugins/rfkill/rfkill-glib.c new file mode 100644 index 0000000..9686cdf --- /dev/null +++ b/plugins/rfkill/rfkill-glib.c @@ -0,0 +1,517 @@ +/* + * + * gnome-bluetooth - Bluetooth integration for GNOME + * + * Copyright (C) 2012 Bastien Nocera + * Copyright © 2017 Endless Mobile, Inc. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "rfkill-glib.h" + +enum { + CHANGED, + LAST_SIGNAL +}; + +static int signals[LAST_SIGNAL] = { 0 }; + +#define CC_RFKILL_GLIB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \ + CC_RFKILL_TYPE_GLIB, CcRfkillGlibPrivate)) + +struct CcRfkillGlibPrivate { + GOutputStream *stream; + GIOChannel *channel; + guint watch_id; + + /* Pending Bluetooth enablement */ + guint change_all_timeout_id; + struct rfkill_event *event; + GTask *task; + GCancellable *cancellable; +}; + +G_DEFINE_TYPE(CcRfkillGlib, cc_rfkill_glib, G_TYPE_OBJECT) + +#define CHANGE_ALL_TIMEOUT 500 + +static const char *type_to_string (unsigned int type); + +/* Note that this can return %FALSE without setting @error. */ +gboolean +cc_rfkill_glib_send_event_finish (CcRfkillGlib *rfkill, + GAsyncResult *res, + GError **error) +{ + g_return_val_if_fail (RFKILL_IS_GLIB (rfkill), FALSE); + g_return_val_if_fail (g_task_is_valid (res, rfkill), FALSE); + g_return_val_if_fail (g_async_result_is_tagged (res, cc_rfkill_glib_send_event), FALSE); + + return (g_task_propagate_int (G_TASK (res), error) >= 0); +} + +static void +write_done_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK (user_data); + g_autoptr(GError) error = NULL; + gssize ret; + + ret = g_output_stream_write_finish (G_OUTPUT_STREAM (source_object), res, &error); + if (ret < 0) + g_task_return_error (task, g_steal_pointer (&error)); + else + g_task_return_int (task, ret); +} + +void +cc_rfkill_glib_send_event (CcRfkillGlib *rfkill, + struct rfkill_event *event, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (RFKILL_IS_GLIB (rfkill)); + g_return_if_fail (rfkill->priv->stream); + + task = g_task_new (rfkill, cancellable, callback, user_data); + g_task_set_source_tag (task, cc_rfkill_glib_send_event); + + g_output_stream_write_async (rfkill->priv->stream, + event, sizeof(struct rfkill_event), + G_PRIORITY_DEFAULT, + cancellable, write_done_cb, + g_object_ref (task)); +} + +/* Note that this can return %FALSE without setting @error. */ +gboolean +cc_rfkill_glib_send_change_all_event_finish (CcRfkillGlib *rfkill, + GAsyncResult *res, + GError **error) +{ + g_return_val_if_fail (RFKILL_IS_GLIB (rfkill), FALSE); + g_return_val_if_fail (g_task_is_valid (res, rfkill), FALSE); + g_return_val_if_fail (g_async_result_is_tagged (res, cc_rfkill_glib_send_change_all_event), FALSE); + + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +write_change_all_again_done_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CcRfkillGlib *rfkill = user_data; + g_autoptr(GError) error = NULL; + gssize ret; + + g_debug ("Finished writing second RFKILL_OP_CHANGE_ALL event"); + + ret = g_output_stream_write_finish (G_OUTPUT_STREAM (source_object), res, &error); + if (ret < 0) + g_task_return_error (rfkill->priv->task, g_steal_pointer (&error)); + else + g_task_return_boolean (rfkill->priv->task, ret >= 0); + + g_clear_object (&rfkill->priv->task); + g_clear_pointer (&rfkill->priv->event, g_free); +} + +static gboolean +write_change_all_timeout_cb (CcRfkillGlib *rfkill) +{ + g_assert (rfkill->priv->event); + + g_debug ("Sending second RFKILL_OP_CHANGE_ALL timed out"); + + g_task_return_new_error (rfkill->priv->task, + G_IO_ERROR, G_IO_ERROR_TIMED_OUT, + "Enabling rfkill for %s timed out", + type_to_string (rfkill->priv->event->type)); + + g_clear_object (&rfkill->priv->task); + g_clear_pointer (&rfkill->priv->event, g_free); + g_clear_object (&rfkill->priv->cancellable); + rfkill->priv->change_all_timeout_id = 0; + + return G_SOURCE_REMOVE; +} + +static void +write_change_all_done_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CcRfkillGlib *rfkill = user_data; + g_autoptr(GError) error = NULL; + gssize ret; + + g_debug ("Sending original RFKILL_OP_CHANGE_ALL event done"); + + ret = g_output_stream_write_finish (G_OUTPUT_STREAM (source_object), res, &error); + if (ret < 0) { + g_task_return_error (rfkill->priv->task, g_steal_pointer (&error)); + goto bail; + } else if (rfkill->priv->event->soft == 1 || + rfkill->priv->event->type != RFKILL_TYPE_BLUETOOTH) { + g_task_return_boolean (rfkill->priv->task, ret >= 0); + goto bail; + } + + rfkill->priv->change_all_timeout_id = g_timeout_add (CHANGE_ALL_TIMEOUT, + (GSourceFunc) write_change_all_timeout_cb, + rfkill); + + return; + +bail: + g_clear_object (&rfkill->priv->task); + g_clear_pointer (&rfkill->priv->event, g_free); + g_clear_object (&rfkill->priv->cancellable); +} + +void +cc_rfkill_glib_send_change_all_event (CcRfkillGlib *rfkill, + guint rfkill_type, + gboolean enable, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + struct rfkill_event *event; + + g_return_if_fail (RFKILL_IS_GLIB (rfkill)); + g_return_if_fail (rfkill->priv->stream); + + task = g_task_new (rfkill, cancellable, callback, user_data); + g_task_set_source_tag (task, cc_rfkill_glib_send_change_all_event); + + if (rfkill->priv->change_all_timeout_id > 0) { + g_source_remove (rfkill->priv->change_all_timeout_id); + rfkill->priv->change_all_timeout_id = 0; + write_change_all_timeout_cb (rfkill); + } + + event = g_new0 (struct rfkill_event, 1); + event->op = RFKILL_OP_CHANGE_ALL; + event->type = rfkill_type; + event->soft = enable ? 1 : 0; + + rfkill->priv->event = event; + rfkill->priv->task = g_object_ref (task); + rfkill->priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + rfkill->priv->change_all_timeout_id = 0; + + g_output_stream_write_async (rfkill->priv->stream, + event, sizeof(struct rfkill_event), + G_PRIORITY_DEFAULT, + cancellable, write_change_all_done_cb, rfkill); +} + +static const char * +type_to_string (unsigned int type) +{ + switch (type) { + case RFKILL_TYPE_ALL: + return "ALL"; + case RFKILL_TYPE_WLAN: + return "WLAN"; + case RFKILL_TYPE_BLUETOOTH: + return "BLUETOOTH"; + case RFKILL_TYPE_UWB: + return "UWB"; + case RFKILL_TYPE_WIMAX: + return "WIMAX"; + case RFKILL_TYPE_WWAN: + return "WWAN"; + default: + return "UNKNOWN"; + } +} + +static const char * +op_to_string (unsigned int op) +{ + switch (op) { + case RFKILL_OP_ADD: + return "ADD"; + case RFKILL_OP_DEL: + return "DEL"; + case RFKILL_OP_CHANGE: + return "CHANGE"; + case RFKILL_OP_CHANGE_ALL: + return "CHANGE_ALL"; + default: + g_assert_not_reached (); + } +} + +static void +print_event (struct rfkill_event *event) +{ + g_debug ("RFKILL event: idx %u type %u (%s) op %u (%s) soft %u hard %u", + event->idx, + event->type, type_to_string (event->type), + event->op, op_to_string (event->op), + event->soft, event->hard); +} + +static gboolean +got_change_event (GList *events) +{ + GList *l; + + g_assert (events != NULL); + + for (l = events ; l != NULL; l = l->next) { + struct rfkill_event *event = l->data; + + if (event->op == RFKILL_OP_CHANGE) + return TRUE; + } + + return FALSE; +} + +static void +emit_changed_signal_and_free (CcRfkillGlib *rfkill, + GList *events) +{ + if (events == NULL) + return; + + g_signal_emit (G_OBJECT (rfkill), + signals[CHANGED], + 0, events); + + if (rfkill->priv->change_all_timeout_id > 0 && + got_change_event (events)) { + g_debug ("Received a change event after a RFKILL_OP_CHANGE_ALL event, re-sending RFKILL_OP_CHANGE_ALL"); + + g_output_stream_write_async (rfkill->priv->stream, + rfkill->priv->event, sizeof(struct rfkill_event), + G_PRIORITY_DEFAULT, + rfkill->priv->cancellable, write_change_all_again_done_cb, rfkill); + + g_source_remove (rfkill->priv->change_all_timeout_id); + rfkill->priv->change_all_timeout_id = 0; + } + + g_list_free_full (events, g_free); +} + +static gboolean +event_cb (GIOChannel *source, + GIOCondition condition, + CcRfkillGlib *rfkill) +{ + GList *events; + + events = NULL; + + if (condition & G_IO_IN) { + GIOStatus status; + struct rfkill_event event; + gsize read; + + status = g_io_channel_read_chars (source, + (char *) &event, + sizeof(event), + &read, + NULL); + + while (status == G_IO_STATUS_NORMAL && read == sizeof(event)) { + struct rfkill_event *event_ptr; + + print_event (&event); + + event_ptr = g_memdup (&event, sizeof(event)); + events = g_list_prepend (events, event_ptr); + + status = g_io_channel_read_chars (source, + (char *) &event, + sizeof(event), + &read, + NULL); + } + events = g_list_reverse (events); + } else { + g_debug ("Something unexpected happened on rfkill fd"); + return FALSE; + } + + emit_changed_signal_and_free (rfkill, events); + + return TRUE; +} + +static void +cc_rfkill_glib_init (CcRfkillGlib *rfkill) +{ + CcRfkillGlibPrivate *priv; + + priv = CC_RFKILL_GLIB_GET_PRIVATE (rfkill); + rfkill->priv = priv; +} + +int +cc_rfkill_glib_open (CcRfkillGlib *rfkill) +{ + CcRfkillGlibPrivate *priv; + int fd; + int ret; + GList *events; + + g_return_val_if_fail (RFKILL_IS_GLIB (rfkill), -1); + g_return_val_if_fail (rfkill->priv->stream == NULL, -1); + + priv = rfkill->priv; + + fd = open("/dev/rfkill", O_RDWR); + if (fd < 0) { + if (errno == EACCES) + g_warning ("Could not open RFKILL control device, please verify your installation"); + return fd; + } + + ret = fcntl(fd, F_SETFL, O_NONBLOCK); + if (ret < 0) { + g_debug ("Can't set RFKILL control device to non-blocking"); + close(fd); + return ret; + } + + events = NULL; + + while (1) { + struct rfkill_event event; + struct rfkill_event *event_ptr; + ssize_t len; + + len = read(fd, &event, sizeof(event)); + if (len < 0) { + if (errno == EAGAIN) + break; + g_debug ("Reading of RFKILL events failed"); + break; + } + + if (len != RFKILL_EVENT_SIZE_V1) { + g_warning ("Wrong size of RFKILL event\n"); + continue; + } + + if (event.op != RFKILL_OP_ADD) + continue; + + g_debug ("Read killswitch of type '%s' (idx=%d): soft %d hard %d", + type_to_string (event.type), + event.idx, event.soft, event.hard); + + event_ptr = g_memdup (&event, sizeof(event)); + events = g_list_prepend (events, event_ptr); + } + + /* Setup monitoring */ + priv->channel = g_io_channel_unix_new (fd); + priv->watch_id = g_io_add_watch (priv->channel, + G_IO_IN | G_IO_HUP | G_IO_ERR, + (GIOFunc) event_cb, + rfkill); + + if (events) { + events = g_list_reverse (events); + emit_changed_signal_and_free (rfkill, events); + } else { + g_debug ("No rfkill device available on startup"); + } + + /* Setup write stream */ + priv->stream = g_unix_output_stream_new (fd, TRUE); + + return fd; +} + +static void +cc_rfkill_glib_finalize (GObject *object) +{ + CcRfkillGlib *rfkill; + CcRfkillGlibPrivate *priv; + + rfkill = CC_RFKILL_GLIB (object); + priv = rfkill->priv; + + if (priv->change_all_timeout_id > 0) + write_change_all_timeout_cb (rfkill); + + /* cleanup monitoring */ + if (priv->watch_id > 0) { + g_source_remove (priv->watch_id); + priv->watch_id = 0; + g_io_channel_shutdown (priv->channel, FALSE, NULL); + g_io_channel_unref (priv->channel); + } + g_clear_object (&priv->stream); + + G_OBJECT_CLASS(cc_rfkill_glib_parent_class)->finalize(object); +} + +static void +cc_rfkill_glib_class_init(CcRfkillGlibClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + g_type_class_add_private(klass, sizeof(CcRfkillGlibPrivate)); + object_class->finalize = cc_rfkill_glib_finalize; + + signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CcRfkillGlibClass, changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); + +} + +CcRfkillGlib * +cc_rfkill_glib_new (void) +{ + return CC_RFKILL_GLIB (g_object_new (CC_RFKILL_TYPE_GLIB, NULL)); +} diff --git a/plugins/rfkill/rfkill-glib.h b/plugins/rfkill/rfkill-glib.h new file mode 100644 index 0000000..4734154 --- /dev/null +++ b/plugins/rfkill/rfkill-glib.h @@ -0,0 +1,85 @@ +/* + * + * gnome-bluetooth - Bluetooth integration for GNOME + * + * Copyright (C) 2012 Bastien Nocera + * + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __CC_RFKILL_GLIB_H +#define __CC_RFKILL_GLIB_H + +#include +#include +#include "rfkill.h" + +G_BEGIN_DECLS + +#define CC_RFKILL_TYPE_GLIB (cc_rfkill_glib_get_type()) +#define CC_RFKILL_GLIB(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + CC_RFKILL_TYPE_GLIB, CcRfkillGlib)) +#define CC_RFKILL_GLIB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \ + CC_RFKILL_TYPE_GLIB, CcRfkillGlibClass)) +#define RFKILL_IS_GLIB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + CC_RFKILL_TYPE_GLIB)) +#define RFKILL_IS_GLIB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \ + CC_RFKILL_TYPE_GLIB)) +#define RFKILL_GET_GLIB_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + CC_RFKILL_TYPE_GLIB, CcRfkillGlibClass)) + +typedef struct CcRfkillGlibPrivate CcRfkillGlibPrivate; + +typedef struct _CcRfkillGlib { + GObject parent; + CcRfkillGlibPrivate *priv; +} CcRfkillGlib; + +typedef struct _CcRfkillGlibClass { + GObjectClass parent_class; + + void (*changed) (CcRfkillGlib *rfkill, GList *events); +} CcRfkillGlibClass; + +GType cc_rfkill_glib_get_type (void); +CcRfkillGlib *cc_rfkill_glib_new (void); +int cc_rfkill_glib_open (CcRfkillGlib *rfkill); + +void cc_rfkill_glib_send_event (CcRfkillGlib *rfkill, + struct rfkill_event *event, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean cc_rfkill_glib_send_event_finish (CcRfkillGlib *rfkill, + GAsyncResult *res, + GError **error); + +void cc_rfkill_glib_send_change_all_event (CcRfkillGlib *rfkill, + guint rfkill_type, + gboolean enable, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean cc_rfkill_glib_send_change_all_event_finish (CcRfkillGlib *rfkill, + GAsyncResult *res, + GError **error); + +G_END_DECLS + +#endif /* __CC_RFKILL_GLIB_H */ diff --git a/plugins/rfkill/rfkill.h b/plugins/rfkill/rfkill.h new file mode 100644 index 0000000..abb2c66 --- /dev/null +++ b/plugins/rfkill/rfkill.h @@ -0,0 +1,107 @@ +#ifndef __RFKILL_H +#define __RFKILL_H + +/* + * Copyright (C) 2006 - 2007 Ivo van Doorn + * Copyright (C) 2007 Dmitry Torokhov + * Copyright 2009 Johannes Berg + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +/* define userspace visible states */ +#define RFKILL_STATE_SOFT_BLOCKED 0 +#define RFKILL_STATE_UNBLOCKED 1 +#define RFKILL_STATE_HARD_BLOCKED 2 + +/** + * enum rfkill_type - type of rfkill switch. + * + * @RFKILL_TYPE_ALL: toggles all switches (requests only - not a switch type) + * @RFKILL_TYPE_WLAN: switch is on a 802.11 wireless network device. + * @RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device. + * @RFKILL_TYPE_UWB: switch is on a ultra wideband device. + * @RFKILL_TYPE_WIMAX: switch is on a WiMAX device. + * @RFKILL_TYPE_WWAN: switch is on a wireless WAN device. + * @RFKILL_TYPE_GPS: switch is on a GPS device. + * @RFKILL_TYPE_FM: switch is on a FM radio device. + * @NUM_RFKILL_TYPES: number of defined rfkill types + */ +enum rfkill_type { + RFKILL_TYPE_ALL = 0, + RFKILL_TYPE_WLAN, + RFKILL_TYPE_BLUETOOTH, + RFKILL_TYPE_UWB, + RFKILL_TYPE_WIMAX, + RFKILL_TYPE_WWAN, + RFKILL_TYPE_GPS, + RFKILL_TYPE_FM, + NUM_RFKILL_TYPES, +}; + +/** + * enum rfkill_operation - operation types + * @RFKILL_OP_ADD: a device was added + * @RFKILL_OP_DEL: a device was removed + * @RFKILL_OP_CHANGE: a device's state changed -- userspace changes one device + * @RFKILL_OP_CHANGE_ALL: userspace changes all devices (of a type, or all) + */ +enum rfkill_operation { + RFKILL_OP_ADD = 0, + RFKILL_OP_DEL, + RFKILL_OP_CHANGE, + RFKILL_OP_CHANGE_ALL, +}; + +/** + * struct rfkill_event - events for userspace on /dev/rfkill + * @idx: index of dev rfkill + * @type: type of the rfkill struct + * @op: operation code + * @hard: hard state (0/1) + * @soft: soft state (0/1) + * + * Structure used for userspace communication on /dev/rfkill, + * used for events from the kernel and control to the kernel. + */ +struct rfkill_event { + __u32 idx; + __u8 type; + __u8 op; + __u8 soft, hard; +} __attribute__((packed)); + +/* + * We are planning to be backward and forward compatible with changes + * to the event struct, by adding new, optional, members at the end. + * When reading an event (whether the kernel from userspace or vice + * versa) we need to accept anything that's at least as large as the + * version 1 event size, but might be able to accept other sizes in + * the future. + * + * One exception is the kernel -- we already have two event sizes in + * that we've made the 'hard' member optional since our only option + * is to ignore it anyway. + */ +#define RFKILL_EVENT_SIZE_V1 8 + +/* ioctl for turning off rfkill-input (if present) */ +#define RFKILL_IOC_MAGIC 'R' +#define RFKILL_IOC_NOINPUT 1 +#define RFKILL_IOCTL_NOINPUT _IO(RFKILL_IOC_MAGIC, RFKILL_IOC_NOINPUT) + +/* and that's all userspace gets */ + +#endif /* RFKILL_H */ diff --git a/plugins/rfkill/rfkill.mate-settings-plugin.in b/plugins/rfkill/rfkill.mate-settings-plugin.in new file mode 100644 index 0000000..579b106 --- /dev/null +++ b/plugins/rfkill/rfkill.mate-settings-plugin.in @@ -0,0 +1,10 @@ +[MATE Settings Plugin] +Module=rfkill +IAge=0 +# Default Priority +# Priority=100 +_Name=Rfkill +_Description=Rfkill plugin +Authors=Bastien Nocera +Copyright=Copyright © 2013 +Website= diff --git a/po/POTFILES.in b/po/POTFILES.in index db8c45e..89a6f92 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -16,6 +16,7 @@ data/50-accessibility.xml.in [type: gettext/gsettings]data/org.mate.SettingsDaemon.plugins.media-keys.gschema.xml.in [type: gettext/gsettings]data/org.mate.SettingsDaemon.plugins.mouse.gschema.xml.in [type: gettext/gsettings]data/org.mate.SettingsDaemon.plugins.mpris.gschema.xml.in +[type: gettext/gsettings]data/org.mate.SettingsDaemon.plugins.rfkill.gschema.xml.in [type: gettext/gsettings]data/org.mate.SettingsDaemon.plugins.smartcard.gschema.xml.in [type: gettext/gsettings]data/org.mate.SettingsDaemon.plugins.sound.gschema.xml.in [type: gettext/gsettings]data/org.mate.SettingsDaemon.plugins.typing-break.gschema.xml.in @@ -50,6 +51,7 @@ plugins/xrdb/msd-xrdb-manager.c [type: gettext/ini]plugins/xrdb/xrdb.mate-settings-plugin.in plugins/xsettings/msd-xsettings-manager.c [type: gettext/ini]plugins/xsettings/xsettings.mate-settings-plugin.in +[type: gettext/ini]plugins/rfkill/rfkill.mate-settings-plugin.in plugins/smartcard/msd-smartcard-manager.c plugins/smartcard/msd-smartcard.c plugins/datetime/org.mate.settingsdaemon.datetimemechanism.policy.in -- cgit v1.2.1