core: allow disabling auto-scan and notifying ports one by one via API

This commit enables a new core ModemManager daemon option, so that automatic
detection of available modems is totally disabled: '--no-auto-scan'. Note that
this option also replaces the previously used '--test-no-auto-scan' option,
which was only used during tests.

Along with the new ModemManager option, a new ReportKernelEvent() method in
the API is defined, which allows notifying the daemon of which interfaces it
should be accessing, as well as the main details of each interface. The only
mandatory parameters in the new method are 'action' (add/remove), 'name' (the
name of the interface) and 'subsystem' (the subsystem of the interface).

The mmcli tool has support for using the new api method via several new options:

 * The '--report-kernel-event' option allows specifying device ports one by
   one, and is a direct mapping of the ReportKernelEvent() method:
     $ sudo mmcli --report-kernel-event="action=add,name=wwan0,subsystem=net"
     $ sudo mmcli --report-kernel-event="action=add,name=cdc-wdm0,subsystem=usbmisc"

 * The '--report-kernel-event-auto-scan' option uses udev monitoring to notify
   events automatically to the daemon. This allows to operate in a way
   equivalent to the default daemon operation (with implicit auto-scan).

Worth noting that the ReportKernelEvent() method is only usable when
'--no-auto-scan' is explicitly used in the daemon. An error will be reported if
the method is tried while standard udev monitoring is enabled (implicit if
auto scan isn't explicitly disabled in the daemon).

If mmcli is going to be used only to report 'real time' events, an optional
'--initial-kernel-events=[PATH]' may be given in the ModemManager call to
automatically process a set of port kernel events one by one on boot. The file
may e.g. contain:
  action=add,name=wwan0,subsystem=net
  action=add,name=cdc-wdm0,subsystem=usbmisc
This commit is contained in:
Aleksander Morgado
2016-09-28 18:49:08 +02:00
parent aa4577dfb9
commit c4a584416a
19 changed files with 1437 additions and 73 deletions

View File

@@ -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)

View File

@@ -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)
@@ -129,6 +149,47 @@ mmcli_manager_shutdown (void)
context_free (ctx);
}
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)
@@ -247,6 +308,22 @@ cancelled (GCancellable *cancellable)
mmcli_async_operation_done ();
}
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,
@@ -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);

View File

@@ -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

View File

@@ -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>

View File

@@ -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
@@ -32,6 +35,37 @@ MM_TYPE_MANAGER
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>
@@ -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

View File

@@ -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>

View File

@@ -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 =

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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)
{

View File

@@ -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_ */

View File

@@ -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;
return g_udev_device_get_subsystem (MM_KERNEL_DEVICE_UDEV (self)->priv->device);
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);
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;
return g_udev_device_get_name (MM_KERNEL_DEVICE_UDEV (self)->priv->device);
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_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,10 +730,17 @@ 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)
{
@@ -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]);
}

View File

@@ -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 */

View File

@@ -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) {

View File

@@ -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...");
}
@@ -614,6 +743,81 @@ handle_scan_devices (MmGdbusOrgFreedesktopModemManager1 *manager,
return TRUE;
}
/*****************************************************************************/
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 */
@@ -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));
}

View File

@@ -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);

View File

@@ -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 }
@@ -103,12 +117,6 @@ mm_context_get_test_session (void)
return test_session;
}
gboolean
mm_context_get_test_no_auto_scan (void)
{
return test_no_auto_scan;
}
gboolean
mm_context_get_test_enable (void)
{
@@ -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);
}
}

View File

@@ -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 */