diff options
-rw-r--r-- | cli/Makefile.am | 2 | ||||
-rw-r--r-- | cli/mmcli-manager.c | 148 | ||||
-rw-r--r-- | data/tests/org.freedesktop.ModemManager1.service.in | 2 | ||||
-rw-r--r-- | docs/reference/libmm-glib/libmm-glib-docs.xml | 1 | ||||
-rw-r--r-- | docs/reference/libmm-glib/libmm-glib-sections.txt | 38 | ||||
-rw-r--r-- | introspection/org.freedesktop.ModemManager1.xml | 74 | ||||
-rw-r--r-- | libmm-glib/Makefile.am | 3 | ||||
-rw-r--r-- | libmm-glib/libmm-glib.h | 1 | ||||
-rw-r--r-- | libmm-glib/mm-kernel-event-properties.c | 454 | ||||
-rw-r--r-- | libmm-glib/mm-kernel-event-properties.h | 97 | ||||
-rw-r--r-- | libmm-glib/mm-manager.c | 124 | ||||
-rw-r--r-- | libmm-glib/mm-manager.h | 14 | ||||
-rw-r--r-- | src/kerneldevice/mm-kernel-device-udev.c | 221 | ||||
-rw-r--r-- | src/kerneldevice/mm-kernel-device-udev.h | 9 | ||||
-rw-r--r-- | src/main.c | 3 | ||||
-rw-r--r-- | src/mm-base-manager.c | 266 | ||||
-rw-r--r-- | src/mm-base-manager.h | 2 | ||||
-rw-r--r-- | src/mm-context.c | 32 | ||||
-rw-r--r-- | src/mm-context.h | 19 |
19 files changed, 1437 insertions, 73 deletions
diff --git a/cli/Makefile.am b/cli/Makefile.am index d2573dbc..7915f289 100644 --- a/cli/Makefile.am +++ b/cli/Makefile.am @@ -2,6 +2,7 @@ bin_PROGRAMS = mmcli mmcli_CPPFLAGS = \ $(MMCLI_CFLAGS) \ + $(GUDEV_CFLAGS) \ -I$(top_srcdir) \ -I$(top_srcdir)/include \ -I$(top_builddir)/include \ @@ -34,6 +35,7 @@ mmcli_SOURCES = \ $(NULL) mmcli_LDADD = \ + $(GUDEV_LIBS) \ $(MMCLI_LIBS) \ $(top_builddir)/libmm-glib/libmm-glib.la \ $(NULL) diff --git a/cli/mmcli-manager.c b/cli/mmcli-manager.c index 9960412e..4e078f17 100644 --- a/cli/mmcli-manager.c +++ b/cli/mmcli-manager.c @@ -15,8 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org> * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2011-2016 Aleksander Morgado <aleksander@aleksander.es> */ #include "config.h" @@ -29,6 +29,8 @@ #include <glib.h> #include <gio/gio.h> +#include <gudev/gudev.h> + #define _LIBMM_INSIDE_MMCLI #include "libmm-glib.h" @@ -39,6 +41,7 @@ typedef struct { MMManager *manager; GCancellable *cancellable; + GUdevClient *udev; } Context; static Context *ctx; @@ -47,6 +50,8 @@ static gboolean list_modems_flag; static gboolean monitor_modems_flag; static gboolean scan_modems_flag; static gchar *set_logging_str; +static gchar *report_kernel_event_str; +static gboolean report_kernel_event_auto_scan; static GOptionEntry entries[] = { { "set-logging", 'G', 0, G_OPTION_ARG_STRING, &set_logging_str, @@ -65,6 +70,14 @@ static GOptionEntry entries[] = { "Request to re-scan looking for modems", NULL }, + { "report-kernel-event", 'K', 0, G_OPTION_ARG_STRING, &report_kernel_event_str, + "Report kernel event", + "[\"key=value,...\"]" + }, + { "report-kernel-event-auto-scan", 0, 0, G_OPTION_ARG_NONE, &report_kernel_event_auto_scan, + "Automatically report kernel events based on udev notifications", + NULL + }, { NULL } }; @@ -96,7 +109,9 @@ mmcli_manager_options_enabled (void) n_actions = (list_modems_flag + monitor_modems_flag + scan_modems_flag + - !!set_logging_str); + !!set_logging_str + + !!report_kernel_event_str + + report_kernel_event_auto_scan); if (n_actions > 1) { g_printerr ("error: too many manager actions requested\n"); @@ -106,6 +121,9 @@ mmcli_manager_options_enabled (void) if (monitor_modems_flag) mmcli_force_async_operation (); + if (report_kernel_event_auto_scan) + mmcli_force_async_operation (); + checked = TRUE; return !!n_actions; } @@ -116,6 +134,8 @@ context_free (Context *ctx) if (!ctx) return; + if (ctx->udev) + g_object_unref (ctx->udev); if (ctx->manager) g_object_unref (ctx->manager); if (ctx->cancellable) @@ -130,6 +150,47 @@ mmcli_manager_shutdown (void) } static void +report_kernel_event_process_reply (gboolean result, + const GError *error) +{ + if (!result) { + g_printerr ("error: couldn't report kernel event: '%s'\n", + error ? error->message : "unknown error"); + exit (EXIT_FAILURE); + } + + g_print ("successfully reported kernel event\n"); +} + +static void +report_kernel_event_ready (MMManager *manager, + GAsyncResult *result) +{ + gboolean operation_result; + GError *error = NULL; + + operation_result = mm_manager_report_kernel_event_finish (manager, result, &error); + report_kernel_event_process_reply (operation_result, error); + + mmcli_async_operation_done (); +} + +static MMKernelEventProperties * +build_kernel_event_properties_from_input (const gchar *properties_string) +{ + GError *error = NULL; + MMKernelEventProperties *properties; + + properties = mm_kernel_event_properties_new_from_string (properties_string, &error); + if (!properties) { + g_printerr ("error: cannot parse properties string: '%s'\n", error->message); + exit (EXIT_FAILURE); + } + + return properties; +} + +static void set_logging_process_reply (gboolean result, const GError *error) { @@ -248,6 +309,22 @@ cancelled (GCancellable *cancellable) } static void +handle_uevent (GUdevClient *client, + const char *action, + GUdevDevice *device, + gpointer none) +{ + MMKernelEventProperties *properties; + + properties = mm_kernel_event_properties_new (); + mm_kernel_event_properties_set_action (properties, action); + mm_kernel_event_properties_set_subsystem (properties, g_udev_device_get_subsystem (device)); + mm_kernel_event_properties_set_name (properties, g_udev_device_get_name (device)); + mm_manager_report_kernel_event (ctx->manager, properties, NULL, NULL, NULL); + g_object_unref (properties); +} + +static void get_manager_ready (GObject *source, GAsyncResult *result, gpointer none) @@ -276,6 +353,54 @@ get_manager_ready (GObject *source, return; } + /* Request to report kernel event? */ + if (report_kernel_event_str) { + MMKernelEventProperties *properties; + + properties = build_kernel_event_properties_from_input (report_kernel_event_str); + mm_manager_report_kernel_event (ctx->manager, + properties, + ctx->cancellable, + (GAsyncReadyCallback)report_kernel_event_ready, + NULL); + g_object_unref (properties); + return; + } + + if (report_kernel_event_auto_scan) { + const gchar *subsys[] = { "tty", "usbmisc", "net", NULL }; + guint i; + + ctx->udev = g_udev_client_new (subsys); + g_signal_connect (ctx->udev, "uevent", G_CALLBACK (handle_uevent), NULL); + + for (i = 0; subsys[i]; i++) { + GList *list, *iter; + + list = g_udev_client_query_by_subsystem (ctx->udev, subsys[i]); + for (iter = list; iter; iter = g_list_next (iter)) { + MMKernelEventProperties *properties; + GUdevDevice *device; + + device = G_UDEV_DEVICE (iter->data); + properties = mm_kernel_event_properties_new (); + mm_kernel_event_properties_set_action (properties, "add"); + mm_kernel_event_properties_set_subsystem (properties, subsys[i]); + mm_kernel_event_properties_set_name (properties, g_udev_device_get_name (device)); + mm_manager_report_kernel_event (ctx->manager, properties, NULL, NULL, NULL); + g_object_unref (properties); + } + g_list_free_full (list, (GDestroyNotify) g_object_unref); + } + + /* If we get cancelled, operation done */ + g_cancellable_connect (ctx->cancellable, + G_CALLBACK (cancelled), + NULL, + NULL); + return; + } + /* Request to monitor modems? */ if (monitor_modems_flag) { g_signal_connect (ctx->manager, @@ -332,6 +457,11 @@ mmcli_manager_run_synchronous (GDBusConnection *connection) exit (EXIT_FAILURE); } + if (report_kernel_event_auto_scan) { + g_printerr ("error: monitoring udev events cannot be done synchronously\n"); + exit (EXIT_FAILURE); + } + /* Initialize context */ ctx = g_new0 (Context, 1); ctx->manager = mmcli_get_manager_sync (connection); @@ -362,6 +492,20 @@ mmcli_manager_run_synchronous (GDBusConnection *connection) return; } + /* Request to report kernel event? */ + if (report_kernel_event_str) { + MMKernelEventProperties *properties; + gboolean result; + + properties = build_kernel_event_properties_from_input (report_kernel_event_str); + result = mm_manager_report_kernel_event_sync (ctx->manager, + properties, + NULL, + &error); + report_kernel_event_process_reply (result, error); + return; + } + /* Request to list modems? */ if (list_modems_flag) { list_current_modems (ctx->manager); diff --git a/data/tests/org.freedesktop.ModemManager1.service.in b/data/tests/org.freedesktop.ModemManager1.service.in index ee8bdead..d7c1a007 100644 --- a/data/tests/org.freedesktop.ModemManager1.service.in +++ b/data/tests/org.freedesktop.ModemManager1.service.in @@ -2,4 +2,4 @@ [D-BUS Service] Name=org.freedesktop.ModemManager1 -Exec=@abs_top_builddir@/src/ModemManager --test-session --test-no-auto-scan --test-enable --test-plugin-dir="@abs_top_builddir@/plugins/.libs" --debug +Exec=@abs_top_builddir@/src/ModemManager --test-session --no-auto-scan --test-enable --test-plugin-dir="@abs_top_builddir@/plugins/.libs" --debug diff --git a/docs/reference/libmm-glib/libmm-glib-docs.xml b/docs/reference/libmm-glib/libmm-glib-docs.xml index 4e1d7fec..f611b72d 100644 --- a/docs/reference/libmm-glib/libmm-glib-docs.xml +++ b/docs/reference/libmm-glib/libmm-glib-docs.xml @@ -72,6 +72,7 @@ <chapter> <title>The Manager object</title> <xi:include href="xml/mm-manager.xml"/> + <xi:include href="xml/mm-kernel-event-properties.xml"/> </chapter> <chapter> diff --git a/docs/reference/libmm-glib/libmm-glib-sections.txt b/docs/reference/libmm-glib/libmm-glib-sections.txt index 27a98cda..250ae712 100644 --- a/docs/reference/libmm-glib/libmm-glib-sections.txt +++ b/docs/reference/libmm-glib/libmm-glib-sections.txt @@ -20,6 +20,9 @@ mm_manager_scan_devices_sync mm_manager_set_logging mm_manager_set_logging_finish mm_manager_set_logging_sync +mm_manager_report_kernel_event +mm_manager_report_kernel_event_finish +mm_manager_report_kernel_event_sync <SUBSECTION Standard> MMManagerClass MMManagerPrivate @@ -33,6 +36,37 @@ mm_manager_get_type </SECTION> <SECTION> +<FILE>mm-kernel-event-properties</FILE> +<TITLE>MMKernelEventProperties</TITLE> +MMKernelEventProperties +<SUBSECTION Methods> +mm_kernel_event_properties_new +mm_kernel_event_properties_get_action +mm_kernel_event_properties_set_action +mm_kernel_event_properties_get_name +mm_kernel_event_properties_set_name +mm_kernel_event_properties_get_subsystem +mm_kernel_event_properties_set_subsystem +mm_kernel_event_properties_get_uid +mm_kernel_event_properties_set_uid +<SUBSECTION Private> +mm_kernel_event_properties_new_from_string +mm_kernel_event_properties_new_from_dictionary +mm_kernel_event_properties_dup +mm_kernel_event_properties_get_dictionary +<SUBSECTION Standard> +MMKernelEventPropertiesClass +MMKernelEventPropertiesPrivate +MM_KERNEL_EVENT_PROPERTIES +MM_KERNEL_EVENT_PROPERTIES_CLASS +MM_KERNEL_EVENT_PROPERTIES_GET_CLASS +MM_IS_KERNEL_EVENT_PROPERTIES +MM_IS_KERNEL_EVENT_PROPERTIES_CLASS +MM_TYPE_KERNEL_EVENT_PROPERTIES +mm_kernel_event_properties_get_type +</SECTION> + +<SECTION> <FILE>mm-object</FILE> <TITLE>MMObject</TITLE> MMObject @@ -1555,10 +1589,14 @@ mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_sync mm_gdbus_org_freedesktop_modem_manager1_call_set_logging mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_finish mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_sync +mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event +mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_finish +mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_sync <SUBSECTION Private> mm_gdbus_org_freedesktop_modem_manager1_override_properties mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging +mm_gdbus_org_freedesktop_modem_manager1_complete_report_kernel_event mm_gdbus_org_freedesktop_modem_manager1_interface_info <SUBSECTION Standard> MM_GDBUS_IS_ORG_FREEDESKTOP_MODEM_MANAGER1 diff --git a/introspection/org.freedesktop.ModemManager1.xml b/introspection/org.freedesktop.ModemManager1.xml index 2ecd026e..4eccbc97 100644 --- a/introspection/org.freedesktop.ModemManager1.xml +++ b/introspection/org.freedesktop.ModemManager1.xml @@ -37,5 +37,79 @@ <arg name="level" type="s" direction="in" /> </method> + <!-- + ReportKernelEvent: + @properties: event properties. + + Reports a kernel event to ModemManager. + + This method is only available if udev is not being used to report kernel + events. + + The @properties dictionary is composed of key/value string pairs. The + possible keys are: + + <variablelist> + + <varlistentry><term><literal>action</literal></term> + <listitem> + <para> + The type of action, given as a string value (signature + <literal>"s"</literal>). + This parameter is MANDATORY. + </para> + <variablelist> + <varlistentry><term><literal>add</literal></term> + <listitem> + A new kernel device has been added. + </listitem> + </varlistentry> + <varlistentry><term><literal>remove</literal></term> + <listitem> + An existing kernel device has been removed. + </listitem> + </varlistentry> + </variablelist> + </listitem> + </varlistentry> + + <varlistentry><term><literal>name</literal></term> + <listitem> + <para> + The device name, given as a string value (signature + <literal>"s"</literal>). + This parameter is MANDATORY. + </para> + </listitem> + </varlistentry> + + <varlistentry><term><literal>subsystem</literal></term> + <listitem> + <para> + The device subsystem, given as a string value (signature + <literal>"s"</literal>). + This parameter is MANDATORY. + </para> + </listitem> + </varlistentry> + + <varlistentry><term><literal>uid</literal></term> + <listitem> + <para> + The unique ID of the physical device, given as a string value + (signature <literal>"s"</literal>). + This parameter is OPTIONAL, if not given the sysfs path of the + physical device will be used. This parameter must be the same + for all devices exposed by the same physical device. + </para> + </listitem> + </varlistentry> + + </variablelist> + --> + <method name="ReportKernelEvent"> + <arg name="properties" type="a{sv}" direction="in" /> + </method> + </interface> </node> diff --git a/libmm-glib/Makefile.am b/libmm-glib/Makefile.am index e257d6f0..64bca40e 100644 --- a/libmm-glib/Makefile.am +++ b/libmm-glib/Makefile.am @@ -81,6 +81,8 @@ libmm_glib_la_SOURCES = \ mm-cdma-manual-activation-properties.c \ mm-signal.h \ mm-signal.c \ + mm-kernel-event-properties.h \ + mm-kernel-event-properties.c \ $(NULL) libmm_glib_la_CPPFLAGS = \ @@ -149,6 +151,7 @@ include_HEADERS = \ mm-firmware-properties.h \ mm-cdma-manual-activation-properties.h \ mm-signal.h \ + mm-kernel-event-properties.h \ $(NULL) CLEANFILES = diff --git a/libmm-glib/libmm-glib.h b/libmm-glib/libmm-glib.h index fa4c7e2f..53f0a196 100644 --- a/libmm-glib/libmm-glib.h +++ b/libmm-glib/libmm-glib.h @@ -72,6 +72,7 @@ #include <mm-firmware-properties.h> #include <mm-cdma-manual-activation-properties.h> #include <mm-signal.h> +#include <mm-kernel-event-properties.h> /* generated */ #include <mm-errors-types.h> diff --git a/libmm-glib/mm-kernel-event-properties.c b/libmm-glib/mm-kernel-event-properties.c new file mode 100644 index 00000000..6782fbf7 --- /dev/null +++ b/libmm-glib/mm-kernel-event-properties.c @@ -0,0 +1,454 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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: + * + * Copyright (C) 2016 Velocloud, Inc. + */ + +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +#include "mm-errors-types.h" +#include "mm-enums-types.h" +#include "mm-common-helpers.h" +#include "mm-kernel-event-properties.h" + +/** + * SECTION: mm-kernel-event-properties + * @title: MMKernelEventProperties + * @short_description: Helper object to handle kernel event properties. + * + * The #MMKernelEventProperties is an object handling the properties to be set + * in reported kernel events. + * + * This object is created by the user and passed to ModemManager with either + * mm_manager_report_kernel_event() or mm_manager_report_kernel_event_sync(). + */ + +G_DEFINE_TYPE (MMKernelEventProperties, mm_kernel_event_properties, G_TYPE_OBJECT) + +#define PROPERTY_ACTION "action" +#define PROPERTY_SUBSYSTEM "subsystem" +#define PROPERTY_NAME "name" +#define PROPERTY_UID "uid" + +struct _MMKernelEventPropertiesPrivate { + gchar *action; + gchar *subsystem; + gchar *name; + gchar *uid; +}; + +/*****************************************************************************/ + +/** + * mm_kernel_event_properties_set_action: + * @self: A #MMKernelEventProperties. + * @action: The action to set. + * + * Sets the action. + */ +void +mm_kernel_event_properties_set_action (MMKernelEventProperties *self, + const gchar *action) +{ + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self)); + + g_free (self->priv->action); + self->priv->action = g_strdup (action); +} + +/** + * mm_kernel_event_properties_get_action: + * @self: A #MMKernelEventProperties. + * + * Gets the action. + * + * Returns: (transfer none): The action. Do not free the returned value, it is owned by @self. + */ +const gchar * +mm_kernel_event_properties_get_action (MMKernelEventProperties *self) +{ + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL); + + return self->priv->action; +} + +/*****************************************************************************/ + +/** + * mm_kernel_event_properties_set_subsystem: + * @self: A #MMKernelEventProperties. + * @subsystem: The subsystem to set. + * + * Sets the subsystem. + */ +void +mm_kernel_event_properties_set_subsystem (MMKernelEventProperties *self, + const gchar *subsystem) +{ + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self)); + + g_free (self->priv->subsystem); + self->priv->subsystem = g_strdup (subsystem); +} + +/** + * mm_kernel_event_properties_get_subsystem: + * @self: A #MMKernelEventProperties. + * + * Gets the subsystem. + * + * Returns: (transfer none): The subsystem. Do not free the returned value, it is owned by @self. + */ +const gchar * +mm_kernel_event_properties_get_subsystem (MMKernelEventProperties *self) +{ + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL); + + return self->priv->subsystem; +} + +/*****************************************************************************/ + +/** + * mm_kernel_event_properties_set_name: + * @self: A #MMKernelEventProperties. + * @name: The name to set. + * + * Sets the name. + */ +void +mm_kernel_event_properties_set_name (MMKernelEventProperties *self, + const gchar *name) +{ + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self)); + + g_free (self->priv->name); + self->priv->name = g_strdup (name); +} + +/** + * mm_kernel_event_properties_get_name: + * @self: A #MMKernelEventProperties. + * + * Gets the name. + * + * Returns: (transfer none): The name. Do not free the returned value, it is owned by @self. + */ +const gchar * +mm_kernel_event_properties_get_name (MMKernelEventProperties *self) +{ + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL); + + return self->priv->name; +} + +/*****************************************************************************/ + +/** + * mm_kernel_event_properties_set_uid: + * @self: A #MMKernelEventProperties. + * @uid: The uid to set. + * + * Sets the unique ID of the physical device. + */ +void +mm_kernel_event_properties_set_uid (MMKernelEventProperties *self, + const gchar *uid) +{ + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self)); + + g_free (self->priv->uid); + self->priv->uid = g_strdup (uid); +} + +/** + * mm_kernel_event_properties_get_uid: + * @self: A #MMKernelEventProperties. + * + * Gets the unique ID of the physical device. + * + * Returns: (transfer none): The uid. Do not free the returned value, it is owned by @self. + */ +const gchar * +mm_kernel_event_properties_get_uid (MMKernelEventProperties *self) +{ + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL); + + return self->priv->uid; +} + +/*****************************************************************************/ + +GVariant * +mm_kernel_event_properties_get_dictionary (MMKernelEventProperties *self) +{ + GVariantBuilder builder; + + /* We do allow NULL */ + if (!self) + return NULL; + + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + + if (self->priv->action) + g_variant_builder_add (&builder, + "{sv}", + PROPERTY_ACTION, + g_variant_new_string (self->priv->action)); + + if (self->priv->subsystem) + g_variant_builder_add (&builder, + "{sv}", + PROPERTY_SUBSYSTEM, + g_variant_new_string (self->priv->subsystem)); + + if (self->priv->name) + g_variant_builder_add (&builder, + "{sv}", + PROPERTY_NAME, + g_variant_new_string (self->priv->name)); + + if (self->priv->uid) + g_variant_builder_add (&builder, + "{sv}", + PROPERTY_UID, + g_variant_new_string (self->priv->uid)); + + return g_variant_ref_sink (g_variant_builder_end (&builder)); +} + +/*****************************************************************************/ + +static gboolean +consume_string (MMKernelEventProperties *self, + const gchar *key, + const gchar *value, + GError **error) +{ + if (g_str_equal (key, PROPERTY_ACTION)) + mm_kernel_event_properties_set_action (self, value); + else if (g_str_equal (key, PROPERTY_SUBSYSTEM)) + mm_kernel_event_properties_set_subsystem (self, value); + else if (g_str_equal (key, PROPERTY_NAME)) + mm_kernel_event_properties_set_name (self, value); + else if (g_str_equal (key, PROPERTY_UID)) + mm_kernel_event_properties_set_uid (self, value); + else { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Invalid properties string, unexpected key '%s'", + key); + return FALSE; + } + + return TRUE; +} + +typedef struct { + MMKernelEventProperties *properties; + GError *error; +} ParseKeyValueContext; + +static gboolean +key_value_foreach (const gchar *key, + const gchar *value, + ParseKeyValueContext *ctx) +{ + return consume_string (ctx->properties, + key, + value, + &ctx->error); +} + +MMKernelEventProperties * +mm_kernel_event_properties_new_from_string (const gchar *str, + GError **error) +{ + ParseKeyValueContext ctx; + + ctx.properties = mm_kernel_event_properties_new (); + ctx.error = NULL; + + mm_common_parse_key_value_string (str, + &ctx.error, + (MMParseKeyValueForeachFn) key_value_foreach, + &ctx); + + /* If error, destroy the object */ + if (ctx.error) { + g_propagate_error (error, ctx.error); + g_object_unref (ctx.properties); + ctx.properties = NULL; + } + + return ctx.properties; +} + +/*****************************************************************************/ + +static gboolean +consume_variant (MMKernelEventProperties *properties, + const gchar *key, + GVariant *value, + GError **error) +{ + if (g_str_equal (key, PROPERTY_ACTION)) + mm_kernel_event_properties_set_action ( + properties, + g_variant_get_string (value, NULL)); + else if (g_str_equal (key, PROPERTY_SUBSYSTEM)) + mm_kernel_event_properties_set_subsystem ( + properties, + g_variant_get_string (value, NULL)); + else if (g_str_equal (key, PROPERTY_NAME)) + mm_kernel_event_properties_set_name ( + properties, + g_variant_get_string (value, NULL)); + else if (g_str_equal (key, PROPERTY_UID)) + mm_kernel_event_properties_set_uid ( + properties, + g_variant_get_string (value, NULL)); + else { + /* Set error */ + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Invalid properties dictionary, unexpected key '%s'", + key); + return FALSE; + } + + return TRUE; +} + +MMKernelEventProperties * +mm_kernel_event_properties_new_from_dictionary (GVariant *dictionary, + GError **error) +{ + GError *inner_error = NULL; + GVariantIter iter; + gchar *key; + GVariant *value; + MMKernelEventProperties *properties; + + properties = mm_kernel_event_properties_new (); + if (!dictionary) + return properties; + + if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Cannot create kernel event properties from dictionary: " + "invalid variant type received"); + g_object_unref (properties); + return NULL; + } + + g_variant_iter_init (&iter, dictionary); + while (!inner_error && + g_variant_iter_next (&iter, "{sv}", &key, &value)) { + consume_variant (properties, + key, + value, + &inner_error); + g_free (key); + g_variant_unref (value); + } + + /* If error, destroy the object */ + if (inner_error) { + g_propagate_error (error, inner_error); + g_object_unref (properties); + properties = NULL; + } + + return properties; +} + +/*****************************************************************************/ + +/** + * mm_kernel_event_properties_dup: + * @orig: a #MMKernelEventProperties + * + * Returns a copy of @orig. + * + * Returns: (transfer full): a #MMKernelEventProperties + */ +MMKernelEventProperties * +mm_kernel_event_properties_dup (MMKernelEventProperties *orig) +{ + GVariant *dict; + MMKernelEventProperties *copy; + GError *error = NULL; + + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (orig), NULL); + + dict = mm_kernel_event_properties_get_dictionary (orig); + copy = mm_kernel_event_properties_new_from_dictionary (dict, &error); + g_assert_no_error (error); + g_variant_unref (dict); + + return copy; +} + +/*****************************************************************************/ + +/** + * mm_kernel_event_properties_new: + * + * Creates a new empty #MMKernelEventProperties. + * + * Returns: (transfer full): a #MMKernelEventProperties. The returned value should be freed with g_object_unref(). + */ +MMKernelEventProperties * +mm_kernel_event_properties_new (void) +{ + return (MM_KERNEL_EVENT_PROPERTIES (g_object_new (MM_TYPE_KERNEL_EVENT_PROPERTIES, NULL))); +} + +static void +mm_kernel_event_properties_init (MMKernelEventProperties *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + MM_TYPE_KERNEL_EVENT_PROPERTIES, + MMKernelEventPropertiesPrivate); +} + +static void +finalize (GObject *object) +{ + MMKernelEventProperties *self = MM_KERNEL_EVENT_PROPERTIES (object); + + g_free (self->priv->action); + g_free (self->priv->subsystem); + g_free (self->priv->name); + g_free (self->priv->uid); + + G_OBJECT_CLASS (mm_kernel_event_properties_parent_class)->finalize (object); +} + +static void +mm_kernel_event_properties_class_init (MMKernelEventPropertiesClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMKernelEventPropertiesPrivate)); + + object_class->finalize = finalize; +} diff --git a/libmm-glib/mm-kernel-event-properties.h b/libmm-glib/mm-kernel-event-properties.h new file mode 100644 index 00000000..576e356b --- /dev/null +++ b/libmm-glib/mm-kernel-event-properties.h @@ -0,0 +1,97 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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: + * + * Copyright (C) 2016 Velocloud, Inc. + */ + +#ifndef MM_KERNEL_EVENT_PROPERTIES_H +#define MM_KERNEL_EVENT_PROPERTIES_H + +#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION) +#error "Only <libmm-glib.h> can be included directly." +#endif + +#include <ModemManager.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define MM_TYPE_KERNEL_EVENT_PROPERTIES (mm_kernel_event_properties_get_type ()) +#define MM_KERNEL_EVENT_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventProperties)) +#define MM_KERNEL_EVENT_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventPropertiesClass)) +#define MM_IS_KERNEL_EVENT_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES)) +#define MM_IS_KERNEL_EVENT_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_EVENT_PROPERTIES)) +#define MM_KERNEL_EVENT_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventPropertiesClass)) + +typedef struct _MMKernelEventProperties MMKernelEventProperties; +typedef struct _MMKernelEventPropertiesClass MMKernelEventPropertiesClass; +typedef struct _MMKernelEventPropertiesPrivate MMKernelEventPropertiesPrivate; + +/** + * MMKernelEventProperties: + * + * The #MMKernelEventProperties structure contains private data and should only be + * accessed using the provided API. + */ +struct _MMKernelEventProperties { + /*< private >*/ + GObject parent; + MMKernelEventPropertiesPrivate *priv; +}; + +struct _MMKernelEventPropertiesClass { + /*< private >*/ + GObjectClass parent; +}; + +GType mm_kernel_event_properties_get_type (void); + +MMKernelEventProperties *mm_kernel_event_properties_new (void); + +void mm_kernel_event_properties_set_action (MMKernelEventProperties *self, + const gchar *action); +const gchar *mm_kernel_event_properties_get_action (MMKernelEventProperties *self); + +void mm_kernel_event_properties_set_subsystem (MMKernelEventProperties *self, + const gchar *subsystem); +const gchar *mm_kernel_event_properties_get_subsystem (MMKernelEventProperties *self); + +void mm_kernel_event_properties_set_name (MMKernelEventProperties *self, + const gchar *name); +const gchar *mm_kernel_event_properties_get_name (MMKernelEventProperties *self); + +void mm_kernel_event_properties_set_uid (MMKernelEventProperties *self, + const gchar *uid); +const gchar *mm_kernel_event_properties_get_uid (MMKernelEventProperties *self); + +/*****************************************************************************/ +/* ModemManager/libmm-glib/mmcli specific methods */ + +#if defined (_LIBMM_INSIDE_MM) || \ + defined (_LIBMM_INSIDE_MMCLI) || \ + defined (LIBMM_GLIB_COMPILATION) + +MMKernelEventProperties *mm_kernel_event_properties_new_from_string (const gchar *str, + GError **error); + +MMKernelEventProperties *mm_kernel_event_properties_new_from_dictionary (GVariant *dictionary, + GError **error); + +MMKernelEventProperties *mm_kernel_event_properties_dup (MMKernelEventProperties *orig); + +GVariant *mm_kernel_event_properties_get_dictionary (MMKernelEventProperties *self); + +#endif + +G_END_DECLS + +#endif /* MM_KERNEL_EVENT_PROPERTIES_H */ diff --git a/libmm-glib/mm-manager.c b/libmm-glib/mm-manager.c index cb196b3f..6a9ce5dd 100644 --- a/libmm-glib/mm-manager.c +++ b/libmm-glib/mm-manager.c @@ -497,6 +497,130 @@ mm_manager_scan_devices_sync (MMManager *manager, /*****************************************************************************/ +/** + * mm_manager_report_kernel_event_finish: + * @manager: A #MMManager. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_manager_report_kernel_event(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with mm_manager_report_kernel_event(). + * + * Returns: %TRUE if the operation succeded, %FALSE if @error is set. + */ +gboolean +mm_manager_report_kernel_event_finish (MMManager *manager, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +report_kernel_event_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (!mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_finish ( + manager_iface_proxy, + res, + &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +/** + * mm_manager_report_kernel_event: + * @manager: A #MMManager. + * @properties: the properties of the kernel event. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously report kernel event. + * + * When the operation is finished, @callback will be invoked in the + * <link linkend="g-main-context-push-thread-default">thread-default main loop</link> + * of the thread you are calling this method from. You can then call + * mm_manager_report_kernel_event_finish() to get the result of the operation. + * + * See mm_manager_report_kernel_event_sync() for the synchronous, blocking version of this method. + */ +void +mm_manager_report_kernel_event (MMManager *manager, + MMKernelEventProperties *properties, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + GError *inner_error = NULL; + GVariant *dictionary; + + g_return_if_fail (MM_IS_MANAGER (manager)); + + task = g_task_new (manager, cancellable, callback, user_data); + + if (!ensure_modem_manager1_proxy (manager, &inner_error)) { + g_task_return_error (task, inner_error); + g_object_unref (task); + return; + } + + dictionary = mm_kernel_event_properties_get_dictionary (properties); + mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event ( + manager->priv->manager_iface_proxy, + dictionary, + cancellable, + (GAsyncReadyCallback)report_kernel_event_ready, + task); + g_variant_unref (dictionary); +} + +/** + * mm_manager_report_kernel_event_sync: + * @manager: A #MMManager. + * @properties: the properties of the kernel event. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously report kernel event. + * + * The calling thread is blocked until a reply is received. + * + * See mm_manager_report_kernel_event() for the asynchronous version of this method. + * + * Returns: %TRUE if the operation succeded, %FALSE if @error is set. + */ +gboolean +mm_manager_report_kernel_event_sync (MMManager *manager, + MMKernelEventProperties *properties, + GCancellable *cancellable, + GError **error) +{ + GVariant *dictionary; + gboolean result; + + g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE); + + if (!ensure_modem_manager1_proxy (manager, error)) + return FALSE; + + dictionary = mm_kernel_event_properties_get_dictionary (properties); + result = (mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_sync ( + manager->priv->manager_iface_proxy, + dictionary, + cancellable, + error)); + g_variant_unref (dictionary); + return result; +} + +/*****************************************************************************/ + static void register_dbus_errors (void) { diff --git a/libmm-glib/mm-manager.h b/libmm-glib/mm-manager.h index 96c3066a..57a129f5 100644 --- a/libmm-glib/mm-manager.h +++ b/libmm-glib/mm-manager.h @@ -33,6 +33,7 @@ #include <ModemManager.h> #include "mm-gdbus-modem.h" +#include "mm-kernel-event-properties.h" G_BEGIN_DECLS @@ -108,6 +109,19 @@ gboolean mm_manager_scan_devices_sync (MMManager *manager, GCancellable *cancellable, GError **error); +void mm_manager_report_kernel_event (MMManager *manager, + MMKernelEventProperties *properties, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_manager_report_kernel_event_finish (MMManager *manager, + GAsyncResult *res, + GError **error); +gboolean mm_manager_report_kernel_event_sync (MMManager *manager, + MMKernelEventProperties *properties, + GCancellable *cancellable, + GError **error); + G_END_DECLS #endif /* _MM_MANAGER_H_ */ diff --git a/src/kerneldevice/mm-kernel-device-udev.c b/src/kerneldevice/mm-kernel-device-udev.c index 4953f48f..f4b47d88 100644 --- a/src/kerneldevice/mm-kernel-device-udev.c +++ b/src/kerneldevice/mm-kernel-device-udev.c @@ -21,11 +21,15 @@ #include "mm-kernel-device-udev.h" #include "mm-log.h" -G_DEFINE_TYPE (MMKernelDeviceUdev, mm_kernel_device_udev, MM_TYPE_KERNEL_DEVICE) +static void initable_iface_init (GInitableIface *iface); + +G_DEFINE_TYPE_EXTENDED (MMKernelDeviceUdev, mm_kernel_device_udev, MM_TYPE_KERNEL_DEVICE, 0, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) enum { PROP_0, PROP_UDEV_DEVICE, + PROP_PROPERTIES, PROP_LAST }; @@ -37,6 +41,8 @@ struct _MMKernelDeviceUdevPrivate { GUdevDevice *physdev; guint16 vendor; guint16 product; + + MMKernelEventProperties *properties; }; /*****************************************************************************/ @@ -153,8 +159,11 @@ ensure_device_ids (MMKernelDeviceUdev *self) if (self->priv->vendor || self->priv->product) return; + if (!self->priv->device) + return; + if (!get_device_ids (self->priv->device, &self->priv->vendor, &self->priv->product)) - mm_dbg ("(%s/%s) could not get vendor/product ID", + mm_dbg ("(%s/%s) could not get vendor/product id", g_udev_device_get_subsystem (self->priv->device), g_udev_device_get_name (self->priv->device)); } @@ -240,25 +249,42 @@ ensure_physdev (MMKernelDeviceUdev *self) { if (self->priv->physdev) return; - self->priv->physdev = find_physical_gudevdevice (self->priv->device); + if (self->priv->device) + self->priv->physdev = find_physical_gudevdevice (self->priv->device); } /*****************************************************************************/ static const gchar * -kernel_device_get_subsystem (MMKernelDevice *self) +kernel_device_get_subsystem (MMKernelDevice *_self) { - g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (self), NULL); + MMKernelDeviceUdev *self; + + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), NULL); + + self = MM_KERNEL_DEVICE_UDEV (_self); + + if (self->priv->device) + return g_udev_device_get_subsystem (self->priv->device); - return g_udev_device_get_subsystem (MM_KERNEL_DEVICE_UDEV (self)->priv->device); + g_assert (self->priv->properties); + return mm_kernel_event_properties_get_subsystem (self->priv->properties); } static const gchar * -kernel_device_get_name (MMKernelDevice *self) +kernel_device_get_name (MMKernelDevice *_self) { - g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (self), NULL); + MMKernelDeviceUdev *self; + + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), NULL); + + self = MM_KERNEL_DEVICE_UDEV (_self); - return g_udev_device_get_name (MM_KERNEL_DEVICE_UDEV (self)->priv->device); + if (self->priv->device) + return g_udev_device_get_name (self->priv->device); + + g_assert (self->priv->properties); + return mm_kernel_event_properties_get_name (self->priv->properties); } static const gchar * @@ -271,6 +297,9 @@ kernel_device_get_driver (MMKernelDevice *_self) self = MM_KERNEL_DEVICE_UDEV (_self); + if (!self->priv->device) + return NULL; + driver = g_udev_device_get_driver (self->priv->device); if (!driver) { GUdevDevice *parent; @@ -308,6 +337,9 @@ kernel_device_get_sysfs_path (MMKernelDevice *self) { g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL); + if (!MM_KERNEL_DEVICE_UDEV (self)->priv->device) + return NULL; + return g_udev_device_get_sysfs_path (MM_KERNEL_DEVICE_UDEV (self)->priv->device); } @@ -320,14 +352,20 @@ kernel_device_get_physdev_uid (MMKernelDevice *_self) g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), NULL); self = MM_KERNEL_DEVICE_UDEV (_self); - ensure_physdev (self); - if (!self->priv->physdev) - return NULL; + /* Prefer the one coming in the properties, if any */ + if (self->priv->properties) + uid = mm_kernel_event_properties_get_uid (MM_KERNEL_DEVICE_UDEV (self)->priv->properties); - uid = g_udev_device_get_property (self->priv->physdev, "ID_MM_PHYSDEV_UID"); - if (!uid) - uid = g_udev_device_get_sysfs_path (self->priv->physdev); + if (!uid) { + ensure_physdev (self); + if (!self->priv->physdev) + return NULL; + + uid = g_udev_device_get_property (self->priv->physdev, "ID_MM_PHYSDEV_UID"); + if (!uid) + uid = g_udev_device_get_sysfs_path (self->priv->physdev); + } return uid; } @@ -364,7 +402,8 @@ kernel_device_get_parent_sysfs_path (MMKernelDevice *_self) g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), 0); self = MM_KERNEL_DEVICE_UDEV (_self); - if (!self->priv->parent) + + if (!self->priv->parent && self->priv->device) self->priv->parent = g_udev_device_get_parent (self->priv->device); return (self->priv->parent ? g_udev_device_get_sysfs_path (self->priv->parent) : NULL); } @@ -382,6 +421,9 @@ kernel_device_is_candidate (MMKernelDevice *_self, self = MM_KERNEL_DEVICE_UDEV (_self); + if (!self->priv->device) + return FALSE; + name = g_udev_device_get_name (self->priv->device); subsys = g_udev_device_get_subsystem (self->priv->device); @@ -455,17 +497,22 @@ kernel_device_cmp (MMKernelDevice *_a, a = MM_KERNEL_DEVICE_UDEV (_a); b = MM_KERNEL_DEVICE_UDEV (_b); - if (g_udev_device_has_property (a->priv->device, "DEVPATH_OLD") && - g_str_has_suffix (g_udev_device_get_sysfs_path (b->priv->device), - g_udev_device_get_property (a->priv->device, "DEVPATH_OLD"))) - return TRUE; + if (a->priv->device && b->priv->device) { + if (g_udev_device_has_property (a->priv->device, "DEVPATH_OLD") && + g_str_has_suffix (g_udev_device_get_sysfs_path (b->priv->device), + g_udev_device_get_property (a->priv->device, "DEVPATH_OLD"))) + return TRUE; - if (g_udev_device_has_property (b->priv->device, "DEVPATH_OLD") && - g_str_has_suffix (g_udev_device_get_sysfs_path (a->priv->device), - g_udev_device_get_property (b->priv->device, "DEVPATH_OLD"))) - return TRUE; + if (g_udev_device_has_property (b->priv->device, "DEVPATH_OLD") && + g_str_has_suffix (g_udev_device_get_sysfs_path (a->priv->device), + g_udev_device_get_property (b->priv->device, "DEVPATH_OLD"))) + return TRUE; + + return !g_strcmp0 (g_udev_device_get_sysfs_path (a->priv->device), g_udev_device_get_sysfs_path (b->priv->device)); + } - return !g_strcmp0 (g_udev_device_get_sysfs_path (a->priv->device), g_udev_device_get_sysfs_path (b->priv->device)); + return (!g_strcmp0 (mm_kernel_device_get_subsystem (_a), mm_kernel_device_get_subsystem (_b)) && + !g_strcmp0 (mm_kernel_device_get_name (_a), mm_kernel_device_get_name (_b))); } static gboolean @@ -477,6 +524,10 @@ kernel_device_has_property (MMKernelDevice *_self, g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), FALSE); self = MM_KERNEL_DEVICE_UDEV (_self); + + if (!self->priv->device) + return FALSE; + return g_udev_device_has_property (self->priv->device, property); } @@ -489,6 +540,10 @@ kernel_device_get_property (MMKernelDevice *_self, g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), NULL); self = MM_KERNEL_DEVICE_UDEV (_self); + + if (!self->priv->device) + return NULL; + return g_udev_device_get_property (self->priv->device, property); } @@ -501,6 +556,10 @@ kernel_device_get_property_as_boolean (MMKernelDevice *_self, g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), FALSE); self = MM_KERNEL_DEVICE_UDEV (_self); + + if (!self->priv->device) + return FALSE; + return g_udev_device_get_property_as_boolean (self->priv->device, property); } @@ -513,6 +572,10 @@ kernel_device_get_property_as_int (MMKernelDevice *_self, g_return_val_if_fail (MM_IS_KERNEL_DEVICE_UDEV (_self), -1); self = MM_KERNEL_DEVICE_UDEV (_self); + + if (!self->priv->device) + return -1; + return g_udev_device_get_property_as_int (self->priv->device, property); } @@ -521,11 +584,31 @@ kernel_device_get_property_as_int (MMKernelDevice *_self, MMKernelDevice * mm_kernel_device_udev_new (GUdevDevice *udev_device) { + GError *error = NULL; + MMKernelDevice *self; + g_return_val_if_fail (G_UDEV_IS_DEVICE (udev_device), NULL); - return MM_KERNEL_DEVICE (g_object_new (MM_TYPE_KERNEL_DEVICE_UDEV, - "udev-device", udev_device, - NULL)); + self = MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_UDEV, + NULL, + &error, + "udev-device", udev_device, + NULL)); + g_assert_no_error (error); + return self; +} + +/*****************************************************************************/ + +MMKernelDevice * +mm_kernel_device_udev_new_from_properties (MMKernelEventProperties *properties, + GError **error) +{ + return MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_UDEV, + NULL, + error, + "properties", properties, + NULL)); } /*****************************************************************************/ @@ -550,6 +633,10 @@ set_property (GObject *object, g_assert (!self->priv->device); self->priv->device = g_value_dup_object (value); break; + case PROP_PROPERTIES: + g_assert (!self->priv->properties); + self->priv->properties = g_value_dup_object (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -568,12 +655,73 @@ get_property (GObject *object, case PROP_UDEV_DEVICE: g_value_set_object (value, self->priv->device); break; + case PROP_PROPERTIES: + g_value_set_object (value, self->priv->properties); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static gboolean +initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (initable); + const gchar *subsystem; + const gchar *name; + + /* When created from a GUdevDevice, we're done */ + if (self->priv->device) + return TRUE; + + /* Otherwise, we do need properties with subsystem and name */ + if (!self->priv->properties) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, + "missing properties in kernel device"); + return FALSE; + } + + subsystem = mm_kernel_event_properties_get_subsystem (self->priv->properties); + if (!subsystem) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, + "subsystem is mandatory in kernel device"); + return FALSE; + } + + name = mm_kernel_event_properties_get_name (self->priv->properties); + if (!mm_kernel_device_get_name (MM_KERNEL_DEVICE (self))) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, + "name is mandatory in kernel device"); + return FALSE; + } + + /* On remove events, we don't look for the GUdevDevice */ + if (g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove")) { + GUdevClient *client; + GUdevDevice *device; + + client = g_udev_client_new (NULL); + device = g_udev_client_query_by_subsystem_and_name (client, subsystem, name); + if (!device) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, + "device %s/%s not found", + subsystem, + name); + g_object_unref (client); + return FALSE; + } + + /* Store device */ + self->priv->device = device; + g_object_unref (client); + } + + return TRUE; +} + static void dispose (GObject *object) { @@ -582,11 +730,18 @@ dispose (GObject *object) g_clear_object (&self->priv->physdev); g_clear_object (&self->priv->parent); g_clear_object (&self->priv->device); + g_clear_object (&self->priv->properties); G_OBJECT_CLASS (mm_kernel_device_udev_parent_class)->dispose (object); } static void +initable_iface_init (GInitableIface *iface) +{ + iface->init = initable_init; +} + +static void mm_kernel_device_udev_class_init (MMKernelDeviceUdevClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); @@ -618,6 +773,14 @@ mm_kernel_device_udev_class_init (MMKernelDeviceUdevClass *klass) "udev device", "Device object as reported by GUdev", G_UDEV_TYPE_DEVICE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_UDEV_DEVICE, properties[PROP_UDEV_DEVICE]); + + properties[PROP_PROPERTIES] = + g_param_spec_object ("properties", + "Properties", + "Generic kernel event properties", + MM_TYPE_KERNEL_EVENT_PROPERTIES, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_PROPERTIES, properties[PROP_PROPERTIES]); } diff --git a/src/kerneldevice/mm-kernel-device-udev.h b/src/kerneldevice/mm-kernel-device-udev.h index ed83159b..9096ca71 100644 --- a/src/kerneldevice/mm-kernel-device-udev.h +++ b/src/kerneldevice/mm-kernel-device-udev.h @@ -20,6 +20,9 @@ #include <glib-object.h> #include <gudev/gudev.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + #include "mm-kernel-device.h" #define MM_TYPE_KERNEL_DEVICE_UDEV (mm_kernel_device_udev_get_type ()) @@ -42,7 +45,9 @@ struct _MMKernelDeviceUdevClass { MMKernelDeviceClass parent; }; -GType mm_kernel_device_udev_get_type (void); -MMKernelDevice *mm_kernel_device_udev_new (GUdevDevice *udev_device); +GType mm_kernel_device_udev_get_type (void); +MMKernelDevice *mm_kernel_device_udev_new (GUdevDevice *udev_device); +MMKernelDevice *mm_kernel_device_udev_new_from_properties (MMKernelEventProperties *properties, + GError **error); #endif /* MM_KERNEL_DEVICE_UDEV_H */ @@ -86,7 +86,8 @@ bus_acquired_cb (GDBusConnection *connection, g_assert (!manager); manager = mm_base_manager_new (connection, mm_context_get_test_plugin_dir (), - !mm_context_get_test_no_auto_scan (), + !mm_context_get_no_auto_scan (), + mm_context_get_initial_kernel_events (), mm_context_get_test_enable (), &error); if (!manager) { diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c index 9da38960..3e03d552 100644 --- a/src/mm-base-manager.c +++ b/src/mm-base-manager.c @@ -47,6 +47,7 @@ enum { PROP_AUTO_SCAN, PROP_ENABLE_TEST, PROP_PLUGIN_DIR, + PROP_INITIAL_KERNEL_EVENTS, LAST_PROP }; @@ -59,6 +60,8 @@ struct _MMBaseManagerPrivate { gboolean enable_test; /* Path to look for plugins */ gchar *plugin_dir; + /* Path to the list of initial kernel events */ + gchar *initial_kernel_events; /* The UDev client */ GUdevClient *udev; /* The authorization provider */ @@ -238,6 +241,11 @@ device_added (MMBaseManager *manager, g_return_if_fail (port != NULL); + mm_dbg ("(%s/%s): adding device at sysfs path: %s", + mm_kernel_device_get_subsystem (port), + mm_kernel_device_get_name (port), + mm_kernel_device_get_sysfs_path (port)); + if (!mm_kernel_device_is_candidate (port, manual_scan)) { /* This could mean that device changed, losing its ID_MM_CANDIDATE * flags (such as Bluetooth RFCOMM devices upon disconnect. @@ -267,6 +275,11 @@ device_added (MMBaseManager *manager, if (!device) { FindDeviceSupportContext *ctx; + mm_dbg ("(%s/%s): first port in device %s", + mm_kernel_device_get_subsystem (port), + mm_kernel_device_get_name (port), + physdev_uid); + /* Keep the device listed in the Manager */ device = mm_device_new (physdev_uid, hotplugged, FALSE); g_hash_table_insert (manager->priv->devices, @@ -282,12 +295,73 @@ device_added (MMBaseManager *manager, device, (GAsyncReadyCallback) device_support_check_ready, ctx); - } + } else + mm_dbg ("(%s/%s): additional port in device %s", + mm_kernel_device_get_subsystem (port), + mm_kernel_device_get_name (port), + physdev_uid); /* Grab the port in the existing device. */ mm_device_grab_port (device, port); } +static gboolean +handle_kernel_event (MMBaseManager *self, + MMKernelEventProperties *properties, + GError **error) +{ + MMKernelDevice *kernel_device; + const gchar *action; + const gchar *subsystem; + const gchar *name; + const gchar *uid; + + action = mm_kernel_event_properties_get_action (properties); + if (!action) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'action'"); + return FALSE; + } + if (g_strcmp0 (action, "add") != 0 && g_strcmp0 (action, "remove") != 0) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid 'action' parameter given: '%s' (expected 'add' or 'remove')", action); + return FALSE; + } + + subsystem = mm_kernel_event_properties_get_subsystem (properties); + if (!subsystem) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'subsystem'"); + return FALSE; + } + + name = mm_kernel_event_properties_get_name (properties); + if (!name) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'name'"); + return FALSE; + } + + uid = mm_kernel_event_properties_get_uid (properties); + + mm_dbg ("Kernel event reported:"); + mm_dbg (" action: %s", action); + mm_dbg (" subsystem: %s", subsystem); + mm_dbg (" name: %s", name); + mm_dbg (" uid: %s", uid ? uid : "n/a"); + + kernel_device = mm_kernel_device_udev_new_from_properties (properties, error); + + if (!kernel_device) + return FALSE; + + if (g_strcmp0 (action, "add") == 0) + device_added (self, kernel_device, TRUE, TRUE); + else if (g_strcmp0 (action, "remove") == 0) + device_removed (self, kernel_device); + else + g_assert_not_reached (); + g_object_unref (kernel_device); + + return TRUE; +} + static void handle_uevent (GUdevClient *client, const char *action, @@ -356,57 +430,112 @@ start_device_added (MMBaseManager *self, g_idle_add ((GSourceFunc)start_device_added_idle, ctx); } -void -mm_base_manager_start (MMBaseManager *manager, - gboolean manual_scan) +static void +process_scan (MMBaseManager *self, + gboolean manual_scan) { GList *devices, *iter; - g_return_if_fail (manager != NULL); - g_return_if_fail (MM_IS_BASE_MANAGER (manager)); - - if (!manager->priv->auto_scan && !manual_scan) - return; - - mm_dbg ("Starting %s device scan...", manual_scan ? "manual" : "automatic"); - - devices = g_udev_client_query_by_subsystem (manager->priv->udev, "tty"); + devices = g_udev_client_query_by_subsystem (self->priv->udev, "tty"); for (iter = devices; iter; iter = g_list_next (iter)) { - start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan); + start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan); g_object_unref (G_OBJECT (iter->data)); } g_list_free (devices); - devices = g_udev_client_query_by_subsystem (manager->priv->udev, "net"); + devices = g_udev_client_query_by_subsystem (self->priv->udev, "net"); for (iter = devices; iter; iter = g_list_next (iter)) { - start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan); + start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan); g_object_unref (G_OBJECT (iter->data)); } g_list_free (devices); - devices = g_udev_client_query_by_subsystem (manager->priv->udev, "usb"); + devices = g_udev_client_query_by_subsystem (self->priv->udev, "usb"); for (iter = devices; iter; iter = g_list_next (iter)) { const gchar *name; name = g_udev_device_get_name (G_UDEV_DEVICE (iter->data)); if (name && g_str_has_prefix (name, "cdc-wdm")) - start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan); + start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan); g_object_unref (G_OBJECT (iter->data)); } g_list_free (devices); /* Newer kernels report 'usbmisc' subsystem */ - devices = g_udev_client_query_by_subsystem (manager->priv->udev, "usbmisc"); + devices = g_udev_client_query_by_subsystem (self->priv->udev, "usbmisc"); for (iter = devices; iter; iter = g_list_next (iter)) { const gchar *name; name = g_udev_device_get_name (G_UDEV_DEVICE (iter->data)); if (name && g_str_has_prefix (name, "cdc-wdm")) - start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan); + start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan); g_object_unref (G_OBJECT (iter->data)); } g_list_free (devices); +} + +static void +process_initial_kernel_events (MMBaseManager *self) +{ + gchar *contents = NULL; + gchar *line; + GError *error = NULL; + + if (!self->priv->initial_kernel_events) + return; + + if (!g_file_get_contents (self->priv->initial_kernel_events, &contents, NULL, &error)) { + g_warning ("Couldn't load initial kernel events: %s", error->message); + g_error_free (error); + return; + } + + line = contents; + while (line) { + gchar *next; + + next = strchr (line, '\n'); + if (next) { + *next = '\0'; + next++; + } + + /* ignore empty lines */ + if (line[0] != '\0') { + MMKernelEventProperties *properties; + + properties = mm_kernel_event_properties_new_from_string (line, &error); + if (!properties) { + g_warning ("Couldn't parse line '%s' as initial kernel event %s", line, error->message); + g_clear_error (&error); + } else if (!handle_kernel_event (self, properties, &error)) { + g_warning ("Couldn't process line '%s' as initial kernel event %s", line, error->message); + g_clear_error (&error); + } else + g_debug ("Processed initial kernel event:' %s'", line); + } + + line = next; + } + + g_free (contents); +} + +void +mm_base_manager_start (MMBaseManager *self, + gboolean manual_scan) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_BASE_MANAGER (self)); + + if (!self->priv->auto_scan && !manual_scan) { + /* If we have a list of initial kernel events, process it now */ + process_initial_kernel_events (self); + return; + } + mm_dbg ("Starting %s device scan...", manual_scan ? "manual" : "automatic"); + process_scan (self, manual_scan); mm_dbg ("Finished device scan..."); } @@ -615,6 +744,81 @@ handle_scan_devices (MmGdbusOrgFreedesktopModemManager1 *manager, } /*****************************************************************************/ + +typedef struct { + MMBaseManager *self; + GDBusMethodInvocation *invocation; + GVariant *dictionary; +} ReportKernelEventContext; + +static void +report_kernel_event_context_free (ReportKernelEventContext *ctx) +{ + g_object_unref (ctx->invocation); + g_object_unref (ctx->self); + g_variant_unref (ctx->dictionary); + g_slice_free (ReportKernelEventContext, ctx); +} + +static void +report_kernel_event_auth_ready (MMAuthProvider *authp, + GAsyncResult *res, + ReportKernelEventContext *ctx) +{ + GError *error = NULL; + MMKernelEventProperties *properties = NULL; + + if (!mm_auth_provider_authorize_finish (authp, res, &error)) + goto out; + + if (ctx->self->priv->auto_scan) { + error = g_error_new_literal (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Cannot report kernel event: " + "udev monitoring already in place"); + goto out; + } + + properties = mm_kernel_event_properties_new_from_dictionary (ctx->dictionary, &error); + if (!properties) + goto out; + + handle_kernel_event (ctx->self, properties, &error); + +out: + if (error) + g_dbus_method_invocation_take_error (ctx->invocation, error); + else + mm_gdbus_org_freedesktop_modem_manager1_complete_report_kernel_event ( + MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self), + ctx->invocation); + + if (properties) + g_object_unref (properties); + report_kernel_event_context_free (ctx); +} + +static gboolean +handle_report_kernel_event (MmGdbusOrgFreedesktopModemManager1 *manager, + GDBusMethodInvocation *invocation, + GVariant *dictionary) +{ + ReportKernelEventContext *ctx; + + ctx = g_slice_new0 (ReportKernelEventContext); + ctx->self = g_object_ref (manager); + ctx->invocation = g_object_ref (invocation); + ctx->dictionary = g_variant_ref (dictionary); + + mm_auth_provider_authorize (ctx->self->priv->authp, + invocation, + MM_AUTHORIZATION_MANAGER_CONTROL, + ctx->self->priv->authp_cancellable, + (GAsyncReadyCallback)report_kernel_event_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ /* Test profile setup */ static gboolean @@ -684,6 +888,7 @@ MMBaseManager * mm_base_manager_new (GDBusConnection *connection, const gchar *plugin_dir, gboolean auto_scan, + const gchar *initial_kernel_events, gboolean enable_test, GError **error) { @@ -695,6 +900,7 @@ mm_base_manager_new (GDBusConnection *connection, MM_BASE_MANAGER_CONNECTION, connection, MM_BASE_MANAGER_PLUGIN_DIR, plugin_dir, MM_BASE_MANAGER_AUTO_SCAN, auto_scan, + MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS, initial_kernel_events, MM_BASE_MANAGER_ENABLE_TEST, enable_test, NULL); } @@ -740,6 +946,10 @@ set_property (GObject *object, g_free (priv->plugin_dir); priv->plugin_dir = g_value_dup_string (value); break; + case PROP_INITIAL_KERNEL_EVENTS: + g_free (priv->initial_kernel_events); + priv->initial_kernel_events = g_value_dup_string (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -767,6 +977,9 @@ get_property (GObject *object, case PROP_PLUGIN_DIR: g_value_set_string (value, priv->plugin_dir); break; + case PROP_INITIAL_KERNEL_EVENTS: + g_value_set_string (value, priv->initial_kernel_events); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -812,6 +1025,10 @@ mm_base_manager_init (MMBaseManager *manager) "handle-scan-devices", G_CALLBACK (handle_scan_devices), NULL); + g_signal_connect (manager, + "handle-report-kernel-event", + G_CALLBACK (handle_report_kernel_event), + NULL); } static gboolean @@ -864,6 +1081,7 @@ finalize (GObject *object) { MMBaseManagerPrivate *priv = MM_BASE_MANAGER (object)->priv; + g_free (priv->initial_kernel_events); g_free (priv->plugin_dir); g_hash_table_destroy (priv->devices); @@ -943,4 +1161,12 @@ mm_base_manager_class_init (MMBaseManagerClass *manager_class) "Where to look for plugins", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_INITIAL_KERNEL_EVENTS, + g_param_spec_string (MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS, + "Initial kernel events", + "Path to a file with the list of initial kernel events", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } diff --git a/src/mm-base-manager.h b/src/mm-base-manager.h index 43e7cae0..56b3016e 100644 --- a/src/mm-base-manager.h +++ b/src/mm-base-manager.h @@ -34,6 +34,7 @@ #define MM_BASE_MANAGER_AUTO_SCAN "auto-scan" /* Construct-only */ #define MM_BASE_MANAGER_ENABLE_TEST "enable-test" /* Construct-only */ #define MM_BASE_MANAGER_PLUGIN_DIR "plugin-dir" /* Construct-only */ +#define MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS "initial-kernel-events" /* Construct-only */ typedef struct _MMBaseManagerPrivate MMBaseManagerPrivate; @@ -51,6 +52,7 @@ GType mm_base_manager_get_type (void); MMBaseManager *mm_base_manager_new (GDBusConnection *bus, const gchar *plugin_dir, gboolean auto_scan, + const gchar *initial_kernel_events, gboolean enable_test, GError **error); diff --git a/src/mm-context.c b/src/mm-context.c index df8d07c7..cf8025bc 100644 --- a/src/mm-context.c +++ b/src/mm-context.c @@ -26,6 +26,8 @@ static const gchar *log_level; static const gchar *log_file; static gboolean show_ts; static gboolean rel_ts; +static const gchar *initial_kernel_events; +static gboolean no_auto_scan; static const GOptionEntry entries[] = { { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, "Print version", NULL }, @@ -34,6 +36,8 @@ static const GOptionEntry entries[] = { { "log-file", 0, 0, G_OPTION_ARG_FILENAME, &log_file, "Path to log file", "[PATH]" }, { "timestamps", 0, 0, G_OPTION_ARG_NONE, &show_ts, "Show timestamps in log output", NULL }, { "relative-timestamps", 0, 0, G_OPTION_ARG_NONE, &rel_ts, "Use relative timestamps (from MM start)", NULL }, + { "no-auto-scan", 0, 0, G_OPTION_ARG_NONE, &no_auto_scan, "Don't auto-scan looking for devices", NULL }, + { "initial-kernel-events", 0, 0, G_OPTION_ARG_FILENAME, &initial_kernel_events, "Path to initial kernel events file (requires --no-auto-scan)", "[PATH]" }, { NULL } }; @@ -67,17 +71,27 @@ mm_context_get_relative_timestamps (void) return rel_ts; } +const gchar * +mm_context_get_initial_kernel_events (void) +{ + return initial_kernel_events; +} + +gboolean +mm_context_get_no_auto_scan (void) +{ + return no_auto_scan; +} + /*****************************************************************************/ /* Test context */ static gboolean test_session; -static gboolean test_no_auto_scan; static gboolean test_enable; static gchar *test_plugin_dir; static const GOptionEntry test_entries[] = { { "test-session", 0, 0, G_OPTION_ARG_NONE, &test_session, "Run in session DBus", NULL }, - { "test-no-auto-scan", 0, 0, G_OPTION_ARG_NONE, &test_no_auto_scan, "Don't auto-scan looking for devices", NULL }, { "test-enable", 0, 0, G_OPTION_ARG_NONE, &test_enable, "Enable the Test interface in the daemon", NULL }, { "test-plugin-dir", 0, 0, G_OPTION_ARG_FILENAME, &test_plugin_dir, "Path to look for plugins", "[PATH]" }, { NULL } @@ -104,12 +118,6 @@ mm_context_get_test_session (void) } gboolean -mm_context_get_test_no_auto_scan (void) -{ - return test_no_auto_scan; -} - -gboolean mm_context_get_test_enable (void) { return test_enable; @@ -149,7 +157,7 @@ mm_context_init (gint argc, g_option_context_add_group (ctx, test_get_option_group ()); if (!g_option_context_parse (ctx, &argc, &argv, &error)) { - g_warning ("%s\n", error->message); + g_warning ("error: %s", error->message); g_error_free (error); exit (1); } @@ -166,4 +174,10 @@ mm_context_init (gint argc, /* If just version requested, print and exit */ if (version_flag) print_version (); + + /* Initial kernel events processing may only be used if autoscan is disabled */ + if (!no_auto_scan && initial_kernel_events) { + g_warning ("error: --initial-kernel-events must be used only if --no-auto-scan is also used"); + exit (1); + } } diff --git a/src/mm-context.h b/src/mm-context.h index 6627a601..63a8ec4c 100644 --- a/src/mm-context.h +++ b/src/mm-context.h @@ -26,16 +26,17 @@ void mm_context_init (gint argc, gchar **argv); -gboolean mm_context_get_debug (void); -const gchar *mm_context_get_log_level (void); -const gchar *mm_context_get_log_file (void); -gboolean mm_context_get_timestamps (void); -gboolean mm_context_get_relative_timestamps (void); +gboolean mm_context_get_debug (void); +const gchar *mm_context_get_log_level (void); +const gchar *mm_context_get_log_file (void); +gboolean mm_context_get_timestamps (void); +gboolean mm_context_get_relative_timestamps (void); +const gchar *mm_context_get_initial_kernel_events (void); +gboolean mm_context_get_no_auto_scan (void); /* Testing support */ -gboolean mm_context_get_test_session (void); -gboolean mm_context_get_test_no_auto_scan (void); -gboolean mm_context_get_test_enable (void); -const gchar *mm_context_get_test_plugin_dir (void); +gboolean mm_context_get_test_session (void); +gboolean mm_context_get_test_enable (void); +const gchar *mm_context_get_test_plugin_dir (void); #endif /* MM_CONTEXT_H */ |