core: allow building and running without udev
Instead of relying on the udev daemon and GUDev to manage the devices reported by the kernel, we can now run ModemManager relying solely on the kernel events reported via the new ReportKernelEvent() API. Therefore, the '--no-auto-scan' option is implicit for the ModemManager daemon when udev is disabled in the build. Additionally, a new custom implementation of the kernel device object is provided, which uses sysfs to load the properties and attributes required in each kernel device, instead of using a GUdevDevice. The udev rule files are kept in place, and a simple custom parser is provided which preloads all rules in memory once and then applies them to the different kernel objects reported via ReportKernelEvent(), e.g. to set port type hints. A simple unit test setup is prepared to validate the udev rules during the `check' Makefile target.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -56,6 +56,7 @@ Makefile.in
|
|||||||
/src/tests/test-at-serial-port
|
/src/tests/test-at-serial-port
|
||||||
/src/tests/test-sms-part-3gpp
|
/src/tests/test-sms-part-3gpp
|
||||||
/src/tests/test-sms-part-cdma
|
/src/tests/test-sms-part-cdma
|
||||||
|
/src/tests/test-udev-rules
|
||||||
|
|
||||||
/cli/mmcli
|
/cli/mmcli
|
||||||
|
|
||||||
@@ -158,6 +159,7 @@ Makefile.in
|
|||||||
/uml290/uml290mode
|
/uml290/uml290mode
|
||||||
|
|
||||||
/plugins/test-suite.log
|
/plugins/test-suite.log
|
||||||
|
/plugins/test-udev-rules
|
||||||
/plugins/test-modem-helpers-huawei*
|
/plugins/test-modem-helpers-huawei*
|
||||||
/plugins/test-modem-helpers-altair*
|
/plugins/test-modem-helpers-altair*
|
||||||
/plugins/test-modem-helpers-cinterion*
|
/plugins/test-modem-helpers-cinterion*
|
||||||
@@ -169,6 +171,7 @@ Makefile.in
|
|||||||
|
|
||||||
/test/lsudev
|
/test/lsudev
|
||||||
/test/mmtty
|
/test/mmtty
|
||||||
|
/test/mmrules
|
||||||
|
|
||||||
/ModemManager-*-coverage.info
|
/ModemManager-*-coverage.info
|
||||||
/ModemManager-*-coverage/
|
/ModemManager-*-coverage/
|
||||||
|
@@ -2,7 +2,6 @@ bin_PROGRAMS = mmcli
|
|||||||
|
|
||||||
mmcli_CPPFLAGS = \
|
mmcli_CPPFLAGS = \
|
||||||
$(MMCLI_CFLAGS) \
|
$(MMCLI_CFLAGS) \
|
||||||
$(GUDEV_CFLAGS) \
|
|
||||||
-I$(top_srcdir) \
|
-I$(top_srcdir) \
|
||||||
-I$(top_srcdir)/include \
|
-I$(top_srcdir)/include \
|
||||||
-I$(top_builddir)/include \
|
-I$(top_builddir)/include \
|
||||||
@@ -35,11 +34,15 @@ mmcli_SOURCES = \
|
|||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
mmcli_LDADD = \
|
mmcli_LDADD = \
|
||||||
$(GUDEV_LIBS) \
|
|
||||||
$(MMCLI_LIBS) \
|
$(MMCLI_LIBS) \
|
||||||
$(top_builddir)/libmm-glib/libmm-glib.la \
|
$(top_builddir)/libmm-glib/libmm-glib.la \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
if WITH_UDEV
|
||||||
|
mmcli_CPPFLAGS += $(GUDEV_CFLAGS)
|
||||||
|
mmcli_LDADD += $(GUDEV_LIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
completiondir = $(datadir)/bash-completion/completions
|
completiondir = $(datadir)/bash-completion/completions
|
||||||
|
|
||||||
install-data-hook:
|
install-data-hook:
|
||||||
|
@@ -29,7 +29,9 @@
|
|||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
|
||||||
#include <gudev/gudev.h>
|
#if WITH_UDEV
|
||||||
|
# include <gudev/gudev.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define _LIBMM_INSIDE_MMCLI
|
#define _LIBMM_INSIDE_MMCLI
|
||||||
#include "libmm-glib.h"
|
#include "libmm-glib.h"
|
||||||
@@ -41,7 +43,9 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
MMManager *manager;
|
MMManager *manager;
|
||||||
GCancellable *cancellable;
|
GCancellable *cancellable;
|
||||||
|
#if WITH_UDEV
|
||||||
GUdevClient *udev;
|
GUdevClient *udev;
|
||||||
|
#endif
|
||||||
} Context;
|
} Context;
|
||||||
static Context *ctx;
|
static Context *ctx;
|
||||||
|
|
||||||
@@ -51,7 +55,10 @@ static gboolean monitor_modems_flag;
|
|||||||
static gboolean scan_modems_flag;
|
static gboolean scan_modems_flag;
|
||||||
static gchar *set_logging_str;
|
static gchar *set_logging_str;
|
||||||
static gchar *report_kernel_event_str;
|
static gchar *report_kernel_event_str;
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
static gboolean report_kernel_event_auto_scan;
|
static gboolean report_kernel_event_auto_scan;
|
||||||
|
#endif
|
||||||
|
|
||||||
static GOptionEntry entries[] = {
|
static GOptionEntry entries[] = {
|
||||||
{ "set-logging", 'G', 0, G_OPTION_ARG_STRING, &set_logging_str,
|
{ "set-logging", 'G', 0, G_OPTION_ARG_STRING, &set_logging_str,
|
||||||
@@ -74,10 +81,12 @@ static GOptionEntry entries[] = {
|
|||||||
"Report kernel event",
|
"Report kernel event",
|
||||||
"[\"key=value,...\"]"
|
"[\"key=value,...\"]"
|
||||||
},
|
},
|
||||||
|
#if WITH_UDEV
|
||||||
{ "report-kernel-event-auto-scan", 0, 0, G_OPTION_ARG_NONE, &report_kernel_event_auto_scan,
|
{ "report-kernel-event-auto-scan", 0, 0, G_OPTION_ARG_NONE, &report_kernel_event_auto_scan,
|
||||||
"Automatically report kernel events based on udev notifications",
|
"Automatically report kernel events based on udev notifications",
|
||||||
NULL
|
NULL
|
||||||
},
|
},
|
||||||
|
#endif
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -110,8 +119,11 @@ mmcli_manager_options_enabled (void)
|
|||||||
monitor_modems_flag +
|
monitor_modems_flag +
|
||||||
scan_modems_flag +
|
scan_modems_flag +
|
||||||
!!set_logging_str +
|
!!set_logging_str +
|
||||||
!!report_kernel_event_str +
|
!!report_kernel_event_str);
|
||||||
report_kernel_event_auto_scan);
|
|
||||||
|
#if WITH_UDEV
|
||||||
|
n_actions += report_kernel_event_auto_scan;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (n_actions > 1) {
|
if (n_actions > 1) {
|
||||||
g_printerr ("error: too many manager actions requested\n");
|
g_printerr ("error: too many manager actions requested\n");
|
||||||
@@ -121,8 +133,10 @@ mmcli_manager_options_enabled (void)
|
|||||||
if (monitor_modems_flag)
|
if (monitor_modems_flag)
|
||||||
mmcli_force_async_operation ();
|
mmcli_force_async_operation ();
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
if (report_kernel_event_auto_scan)
|
if (report_kernel_event_auto_scan)
|
||||||
mmcli_force_async_operation ();
|
mmcli_force_async_operation ();
|
||||||
|
#endif
|
||||||
|
|
||||||
checked = TRUE;
|
checked = TRUE;
|
||||||
return !!n_actions;
|
return !!n_actions;
|
||||||
@@ -134,8 +148,11 @@ context_free (Context *ctx)
|
|||||||
if (!ctx)
|
if (!ctx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
if (ctx->udev)
|
if (ctx->udev)
|
||||||
g_object_unref (ctx->udev);
|
g_object_unref (ctx->udev);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (ctx->manager)
|
if (ctx->manager)
|
||||||
g_object_unref (ctx->manager);
|
g_object_unref (ctx->manager);
|
||||||
if (ctx->cancellable)
|
if (ctx->cancellable)
|
||||||
@@ -308,6 +325,8 @@ cancelled (GCancellable *cancellable)
|
|||||||
mmcli_async_operation_done ();
|
mmcli_async_operation_done ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_uevent (GUdevClient *client,
|
handle_uevent (GUdevClient *client,
|
||||||
const char *action,
|
const char *action,
|
||||||
@@ -324,6 +343,8 @@ handle_uevent (GUdevClient *client,
|
|||||||
g_object_unref (properties);
|
g_object_unref (properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
get_manager_ready (GObject *source,
|
get_manager_ready (GObject *source,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
@@ -367,6 +388,7 @@ get_manager_ready (GObject *source,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
if (report_kernel_event_auto_scan) {
|
if (report_kernel_event_auto_scan) {
|
||||||
const gchar *subsys[] = { "tty", "usbmisc", "net", NULL };
|
const gchar *subsys[] = { "tty", "usbmisc", "net", NULL };
|
||||||
guint i;
|
guint i;
|
||||||
@@ -400,6 +422,7 @@ get_manager_ready (GObject *source,
|
|||||||
NULL);
|
NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Request to monitor modems? */
|
/* Request to monitor modems? */
|
||||||
if (monitor_modems_flag) {
|
if (monitor_modems_flag) {
|
||||||
@@ -457,10 +480,12 @@ mmcli_manager_run_synchronous (GDBusConnection *connection)
|
|||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
if (report_kernel_event_auto_scan) {
|
if (report_kernel_event_auto_scan) {
|
||||||
g_printerr ("error: monitoring udev events cannot be done synchronously\n");
|
g_printerr ("error: monitoring udev events cannot be done synchronously\n");
|
||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Initialize context */
|
/* Initialize context */
|
||||||
ctx = g_new0 (Context, 1);
|
ctx = g_new0 (Context, 1);
|
||||||
|
30
configure.ac
30
configure.ac
@@ -95,7 +95,6 @@ dnl Build dependencies
|
|||||||
dnl
|
dnl
|
||||||
|
|
||||||
GLIB_MIN_VERSION=2.36.0
|
GLIB_MIN_VERSION=2.36.0
|
||||||
GUDEV_MIN_VERSION=147
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(MM,
|
PKG_CHECK_MODULES(MM,
|
||||||
glib-2.0 >= $GLIB_MIN_VERSION
|
glib-2.0 >= $GLIB_MIN_VERSION
|
||||||
@@ -121,10 +120,6 @@ PKG_CHECK_MODULES(MMCLI,
|
|||||||
AC_SUBST(MMCLI_CFLAGS)
|
AC_SUBST(MMCLI_CFLAGS)
|
||||||
AC_SUBST(MMCLI_LIBS)
|
AC_SUBST(MMCLI_LIBS)
|
||||||
|
|
||||||
PKG_CHECK_MODULES(GUDEV, gudev-1.0 >= $GUDEV_MIN_VERSION)
|
|
||||||
AC_SUBST(GUDEV_CFLAGS)
|
|
||||||
AC_SUBST(GUDEV_LIBS)
|
|
||||||
|
|
||||||
dnl Some required utilities
|
dnl Some required utilities
|
||||||
GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
|
GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
|
||||||
AC_SUBST(GLIB_MKENUMS)
|
AC_SUBST(GLIB_MKENUMS)
|
||||||
@@ -182,6 +177,30 @@ if test "x$with_systemdsystemunitdir" != xno; then
|
|||||||
fi
|
fi
|
||||||
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$SYSTEMD_UNIT_DIR" -a "$SYSTEMD_UNIT_DIR" != xno ])
|
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$SYSTEMD_UNIT_DIR" -a "$SYSTEMD_UNIT_DIR" != xno ])
|
||||||
|
|
||||||
|
dnl-----------------------------------------------------------------------------
|
||||||
|
dnl udev support (enabled by default)
|
||||||
|
dnl
|
||||||
|
|
||||||
|
GUDEV_VERSION=147
|
||||||
|
|
||||||
|
AC_ARG_WITH(udev, AS_HELP_STRING([--without-udev], [Build without udev support]), [], [with_udev=yes])
|
||||||
|
AM_CONDITIONAL(WITH_UDEV, test "x$with_udev" = "xyes")
|
||||||
|
case $with_udev in
|
||||||
|
yes)
|
||||||
|
PKG_CHECK_MODULES(GUDEV, [gudev-1.0 >= $GUDEV_VERSION], [have_gudev=yes],[have_gudev=no])
|
||||||
|
if test "x$have_gudev" = "xno"; then
|
||||||
|
AC_MSG_ERROR([Couldn't find gudev >= $GUDEV_VERSION. Install it, or otherwise configure using --without-udev to disable udev support.])
|
||||||
|
else
|
||||||
|
AC_DEFINE(WITH_UDEV, 1, [Define if you want udev support])
|
||||||
|
AC_SUBST(GUDEV_CFLAGS)
|
||||||
|
AC_SUBST(GUDEV_LIBS)
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
with_udev=no
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
dnl-----------------------------------------------------------------------------
|
dnl-----------------------------------------------------------------------------
|
||||||
dnl Suspend/resume support
|
dnl Suspend/resume support
|
||||||
dnl
|
dnl
|
||||||
@@ -396,6 +415,7 @@ echo "
|
|||||||
systemd unit directory: ${with_systemdsystemunitdir}
|
systemd unit directory: ${with_systemdsystemunitdir}
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
udev support: ${with_udev}
|
||||||
policykit support: ${with_polkit}
|
policykit support: ${with_polkit}
|
||||||
mbim support: ${with_mbim}
|
mbim support: ${with_mbim}
|
||||||
qmi support: ${with_qmi}
|
qmi support: ${with_qmi}
|
||||||
|
@@ -1386,6 +1386,49 @@ mm_get_uint_from_str (const gchar *str,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mm_get_uint_from_hex_str:
|
||||||
|
* @str: the hex string to convert to an unsigned int
|
||||||
|
* @out: on success, the number
|
||||||
|
*
|
||||||
|
* Converts a string to an unsigned number. All characters in the string
|
||||||
|
* MUST be valid hexadecimal digits (0-9, A-F, a-f), otherwise FALSE is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* An optional "0x" prefix may be given in @str.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the string was converted, %FALSE if it was not or if it
|
||||||
|
* did not contain only digits.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
mm_get_uint_from_hex_str (const gchar *str,
|
||||||
|
guint *out)
|
||||||
|
{
|
||||||
|
gulong num;
|
||||||
|
|
||||||
|
if (!str)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (g_str_has_prefix (str, "0x"))
|
||||||
|
str = &str[2];
|
||||||
|
|
||||||
|
if (!str[0])
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (num = 0; str[num]; num++) {
|
||||||
|
if (!g_ascii_isxdigit (str[num]))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
num = strtoul (str, NULL, 16);
|
||||||
|
if (!errno && num <= G_MAXUINT) {
|
||||||
|
*out = (guint)num;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
mm_get_uint_from_match_info (GMatchInfo *match_info,
|
mm_get_uint_from_match_info (GMatchInfo *match_info,
|
||||||
guint32 match_index,
|
guint32 match_index,
|
||||||
|
@@ -143,6 +143,8 @@ gboolean mm_get_int_from_match_info (GMatchInfo *match_info,
|
|||||||
gint *out);
|
gint *out);
|
||||||
gboolean mm_get_uint_from_str (const gchar *str,
|
gboolean mm_get_uint_from_str (const gchar *str,
|
||||||
guint *out);
|
guint *out);
|
||||||
|
gboolean mm_get_uint_from_hex_str (const gchar *str,
|
||||||
|
guint *out);
|
||||||
gboolean mm_get_uint_from_match_info (GMatchInfo *match_info,
|
gboolean mm_get_uint_from_match_info (GMatchInfo *match_info,
|
||||||
guint32 match_index,
|
guint32 match_index,
|
||||||
guint *out);
|
guint *out);
|
||||||
|
@@ -306,6 +306,8 @@ libmm_plugin_huawei_la_LIBADD = $(builddir)/libhelpers-huawei.la
|
|||||||
|
|
||||||
dist_udevrules_DATA += huawei/77-mm-huawei-net-port-types.rules
|
dist_udevrules_DATA += huawei/77-mm-huawei-net-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_HUAWEI=\"${srcdir}/huawei\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: ericsson mbm
|
# plugin: ericsson mbm
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -321,6 +323,8 @@ libmm_plugin_ericsson_mbm_la_LIBADD = $(MBM_COMMON_LIBADD_FLAGS)
|
|||||||
|
|
||||||
dist_udevrules_DATA += mbm/77-mm-ericsson-mbm.rules
|
dist_udevrules_DATA += mbm/77-mm-ericsson-mbm.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_MBM=\"${srcdir}/mbm\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: option
|
# plugin: option
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -423,6 +427,8 @@ libmm_plugin_nokia_icera_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS)
|
|||||||
|
|
||||||
dist_udevrules_DATA += nokia/77-mm-nokia-port-types.rules
|
dist_udevrules_DATA += nokia/77-mm-nokia-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_NOKIA=\"${srcdir}/nokia\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: zte
|
# plugin: zte
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -444,6 +450,8 @@ libmm_plugin_zte_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS)
|
|||||||
|
|
||||||
dist_udevrules_DATA += zte/77-mm-zte-port-types.rules
|
dist_udevrules_DATA += zte/77-mm-zte-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_ZTE=\"${srcdir}/zte\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: longcheer (and rebranded dongles)
|
# plugin: longcheer (and rebranded dongles)
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -460,6 +468,8 @@ libmm_plugin_longcheer_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
|
|||||||
|
|
||||||
dist_udevrules_DATA += longcheer/77-mm-longcheer-port-types.rules
|
dist_udevrules_DATA += longcheer/77-mm-longcheer-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_LONGCHEER=\"${srcdir}/longcheer\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: anydata cdma
|
# plugin: anydata cdma
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -504,6 +514,8 @@ libmm_plugin_simtech_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
|
|||||||
|
|
||||||
dist_udevrules_DATA += simtech/77-mm-simtech-port-types.rules
|
dist_udevrules_DATA += simtech/77-mm-simtech-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_SIMTECH=\"${srcdir}/simtech\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: alcatel/TCT/JRD x220D and possibly others
|
# plugin: alcatel/TCT/JRD x220D and possibly others
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -520,6 +532,8 @@ libmm_plugin_x22x_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
|
|||||||
|
|
||||||
dist_udevrules_DATA += x22x/77-mm-x22x-port-types.rules
|
dist_udevrules_DATA += x22x/77-mm-x22x-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_X22X=\"${srcdir}/x22x\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: pantech
|
# plugin: pantech
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -595,6 +609,8 @@ libmm_plugin_cinterion_la_LIBADD = $(builddir)/libhelpers-cinterion.la
|
|||||||
|
|
||||||
dist_udevrules_DATA += cinterion/77-mm-cinterion-port-types.rules
|
dist_udevrules_DATA += cinterion/77-mm-cinterion-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_CINTERION=\"${srcdir}/cinterion\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: iridium
|
# plugin: iridium
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -694,6 +710,8 @@ libmm_plugin_dell_la_LIBADD = $(NOVATEL_COMMON_LIBADD_FLAGS) $(SIERRA_COMMON_L
|
|||||||
|
|
||||||
dist_udevrules_DATA += dell/77-mm-dell-port-types.rules
|
dist_udevrules_DATA += dell/77-mm-dell-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_DELL=\"${srcdir}/dell\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: altair lte
|
# plugin: altair lte
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -778,6 +796,8 @@ libmm_plugin_telit_la_LIBADD = $(builddir)/libhelpers-telit.la $(TELIT_COMMON_
|
|||||||
|
|
||||||
dist_udevrules_DATA += telit/77-mm-telit-port-types.rules
|
dist_udevrules_DATA += telit/77-mm-telit-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_TELIT=\"${srcdir}/telit\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: mtk
|
# plugin: mtk
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -794,6 +814,8 @@ libmm_plugin_mtk_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
|
|||||||
|
|
||||||
dist_udevrules_DATA += mtk/77-mm-mtk-port-types.rules
|
dist_udevrules_DATA += mtk/77-mm-mtk-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_MTK=\"${srcdir}/mtk\"
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# plugin: haier
|
# plugin: haier
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -808,6 +830,22 @@ libmm_plugin_haier_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
|
|||||||
|
|
||||||
dist_udevrules_DATA += haier/77-mm-haier-port-types.rules
|
dist_udevrules_DATA += haier/77-mm-haier-port-types.rules
|
||||||
|
|
||||||
|
AM_CFLAGS += -DTESTUDEVRULESDIR_HAIER=\"${srcdir}/haier\"
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# udev rules tester
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
noinst_PROGRAMS += test-udev-rules
|
||||||
|
test_udev_rules_SOURCES = \
|
||||||
|
tests/test-udev-rules.c \
|
||||||
|
$(NULL)
|
||||||
|
test_udev_rules_LDADD = \
|
||||||
|
$(top_builddir)/src/libkerneldevice.la \
|
||||||
|
$(top_builddir)/libmm-glib/libmm-glib.la \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
TEST_PROGS += $(noinst_PROGRAMS)
|
TEST_PROGS += $(noinst_PROGRAMS)
|
||||||
|
164
plugins/tests/test-udev-rules.c
Normal file
164
plugins/tests/test-udev-rules.c
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/* -*- 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 Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
#define _LIBMM_INSIDE_MM
|
||||||
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
|
/* Define symbol to enable test message traces */
|
||||||
|
#undef ENABLE_TEST_MESSAGE_TRACES
|
||||||
|
|
||||||
|
#include "mm-kernel-device-generic-rules.h"
|
||||||
|
#include "mm-log.h"
|
||||||
|
|
||||||
|
/************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
common_test (const gchar *plugindir)
|
||||||
|
{
|
||||||
|
GArray *rules;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rules = mm_kernel_device_generic_rules_load (plugindir, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert (rules);
|
||||||
|
g_assert (rules->len > 0);
|
||||||
|
|
||||||
|
g_array_unref (rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_huawei (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_HUAWEI);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_mbm (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_MBM);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_nokia (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_NOKIA);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_zte (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_ZTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_longcheer (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_LONGCHEER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_simtech (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_SIMTECH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_x22x (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_X22X);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_cinterion (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_CINTERION);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_dell (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_DELL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_telit (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_TELIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_mtk (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_MTK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_haier (void)
|
||||||
|
{
|
||||||
|
common_test (TESTUDEVRULESDIR_HAIER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************/
|
||||||
|
|
||||||
|
void
|
||||||
|
_mm_log (const char *loc,
|
||||||
|
const char *func,
|
||||||
|
guint32 level,
|
||||||
|
const char *fmt,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
#if defined ENABLE_TEST_MESSAGE_TRACES
|
||||||
|
/* Dummy log function */
|
||||||
|
va_list args;
|
||||||
|
gchar *msg;
|
||||||
|
|
||||||
|
va_start (args, fmt);
|
||||||
|
msg = g_strdup_vprintf (fmt, args);
|
||||||
|
va_end (args);
|
||||||
|
g_print ("%s\n", msg);
|
||||||
|
g_free (msg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
setlocale (LC_ALL, "");
|
||||||
|
|
||||||
|
g_type_init ();
|
||||||
|
g_test_init (&argc, &argv, NULL);
|
||||||
|
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/huawei", test_huawei);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/mbm", test_mbm);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/nokia", test_nokia);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/zte", test_zte);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/longcheer", test_longcheer);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/simtech", test_simtech);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/x22x", test_x22x);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/cinterion", test_cinterion);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/dell", test_dell);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/telit", test_telit);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/mtk", test_mtk);
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/haier", test_haier);
|
||||||
|
|
||||||
|
return g_test_run ();
|
||||||
|
}
|
@@ -102,14 +102,29 @@ endif
|
|||||||
|
|
||||||
noinst_LTLIBRARIES += libkerneldevice.la
|
noinst_LTLIBRARIES += libkerneldevice.la
|
||||||
|
|
||||||
|
libkerneldevice_la_CPPFLAGS = \
|
||||||
|
-DUDEVRULESDIR=\"$(udevrulesdir)\" \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
libkerneldevice_la_SOURCES = \
|
libkerneldevice_la_SOURCES = \
|
||||||
kerneldevice/mm-kernel-device.h \
|
kerneldevice/mm-kernel-device.h \
|
||||||
kerneldevice/mm-kernel-device.c \
|
kerneldevice/mm-kernel-device.c \
|
||||||
kerneldevice/mm-kernel-device-generic.h \
|
kerneldevice/mm-kernel-device-generic.h \
|
||||||
kerneldevice/mm-kernel-device-generic.c \
|
kerneldevice/mm-kernel-device-generic.c \
|
||||||
|
kerneldevice/mm-kernel-device-generic-rules.h \
|
||||||
|
kerneldevice/mm-kernel-device-generic-rules.c \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
if WITH_UDEV
|
||||||
|
libkerneldevice_la_SOURCES += \
|
||||||
kerneldevice/mm-kernel-device-udev.h \
|
kerneldevice/mm-kernel-device-udev.h \
|
||||||
kerneldevice/mm-kernel-device-udev.c \
|
kerneldevice/mm-kernel-device-udev.c \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
endif
|
||||||
|
|
||||||
|
libkerneldevice_la_LIBADD = \
|
||||||
|
$(top_builddir)/libmm-glib/libmm-glib.la \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# ports library
|
# ports library
|
||||||
|
446
src/kerneldevice/mm-kernel-device-generic-rules.c
Normal file
446
src/kerneldevice/mm-kernel-device-generic-rules.c
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
/* -*- 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <ModemManager.h>
|
||||||
|
#define _LIBMM_INSIDE_MM
|
||||||
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
|
#include "mm-log.h"
|
||||||
|
#include "mm-kernel-device-generic-rules.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
udev_rule_match_clear (MMUdevRuleMatch *rule_match)
|
||||||
|
{
|
||||||
|
g_free (rule_match->parameter);
|
||||||
|
g_free (rule_match->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
udev_rule_clear (MMUdevRule *rule)
|
||||||
|
{
|
||||||
|
switch (rule->result.type) {
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_PROPERTY:
|
||||||
|
g_free (rule->result.content.property.name);
|
||||||
|
g_free (rule->result.content.property.value);
|
||||||
|
break;
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG:
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_LABEL:
|
||||||
|
g_free (rule->result.content.tag);
|
||||||
|
break;
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX:
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_UNKNOWN:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule->conditions)
|
||||||
|
g_array_unref (rule->conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
split_item (const gchar *item,
|
||||||
|
gchar **out_left,
|
||||||
|
gchar **out_operator,
|
||||||
|
gchar **out_right,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
const gchar *aux;
|
||||||
|
gchar *left = NULL;
|
||||||
|
gchar *operator = NULL;
|
||||||
|
gchar *right = NULL;
|
||||||
|
GError *inner_error = NULL;
|
||||||
|
|
||||||
|
g_assert (item && out_left && out_operator && out_right);
|
||||||
|
|
||||||
|
/* Get left/operator/right */
|
||||||
|
if (((aux = strstr (item, "==")) != NULL) || ((aux = strstr (item, "!=")) != NULL)) {
|
||||||
|
operator = g_strndup (aux, 2);
|
||||||
|
right = g_strdup (aux + 2);
|
||||||
|
} else if ((aux = strstr (item, "=")) != NULL) {
|
||||||
|
operator = g_strndup (aux, 1);
|
||||||
|
right = g_strdup (aux + 1);
|
||||||
|
} else {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
"Invalid rule item, missing operator: '%s'", item);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
left = g_strndup (item, (aux - item));
|
||||||
|
g_strstrip (left);
|
||||||
|
if (!left[0]) {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
"Invalid rule item, missing left field: '%s'", item);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strdelimit (right, "\"", ' ');
|
||||||
|
g_strstrip (right);
|
||||||
|
if (!right[0]) {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
"Invalid rule item, missing right field: '%s'", item);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (inner_error) {
|
||||||
|
g_free (left);
|
||||||
|
g_free (operator);
|
||||||
|
g_free (right);
|
||||||
|
g_propagate_error (error, inner_error);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_left = left;
|
||||||
|
*out_operator = operator;
|
||||||
|
*out_right = right;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
load_rule_result (MMUdevRuleResult *rule_result,
|
||||||
|
const gchar *item,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gchar *left;
|
||||||
|
gchar *operator;
|
||||||
|
gchar *right;
|
||||||
|
GError *inner_error = NULL;
|
||||||
|
gsize left_len;
|
||||||
|
|
||||||
|
if (!split_item (item, &left, &operator, &right, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!g_str_equal (operator, "=")) {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
"Invalid rule result operator: '%s'", item);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_str_equal (left, "LABEL")) {
|
||||||
|
rule_result->type = MM_UDEV_RULE_RESULT_TYPE_LABEL;
|
||||||
|
rule_result->content.tag = right;
|
||||||
|
right = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_str_equal (left, "GOTO")) {
|
||||||
|
rule_result->type = MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG;
|
||||||
|
rule_result->content.tag = right;
|
||||||
|
right = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
left_len = strlen (left);
|
||||||
|
if (g_str_has_prefix (left, "ENV{") && left[left_len - 1] == '}') {
|
||||||
|
rule_result->type = MM_UDEV_RULE_RESULT_TYPE_PROPERTY;
|
||||||
|
rule_result->content.property.name = g_strndup (left + 4, left_len - 5);
|
||||||
|
rule_result->content.property.value = right;
|
||||||
|
right = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
"Invalid rule result parameter: '%s'", item);
|
||||||
|
|
||||||
|
out:
|
||||||
|
g_free (left);
|
||||||
|
g_free (operator);
|
||||||
|
g_free (right);
|
||||||
|
|
||||||
|
if (inner_error) {
|
||||||
|
g_propagate_error (error, inner_error);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
load_rule_match (MMUdevRuleMatch *rule_match,
|
||||||
|
const gchar *item,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gchar *left;
|
||||||
|
gchar *operator;
|
||||||
|
gchar *right;
|
||||||
|
|
||||||
|
if (!split_item (item, &left, &operator, &right, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (g_str_equal (operator, "=="))
|
||||||
|
rule_match->type = MM_UDEV_RULE_MATCH_TYPE_EQUAL;
|
||||||
|
else if (g_str_equal (operator, "!="))
|
||||||
|
rule_match->type = MM_UDEV_RULE_MATCH_TYPE_NOT_EQUAL;
|
||||||
|
else {
|
||||||
|
g_free (left);
|
||||||
|
g_free (operator);
|
||||||
|
g_free (right);
|
||||||
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
"Invalid rule match, wrong match type: '%s'", item);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (operator);
|
||||||
|
rule_match->parameter = left;
|
||||||
|
rule_match->value = right;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
load_rule_from_line (MMUdevRule *rule,
|
||||||
|
const gchar *line,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gchar **split;
|
||||||
|
guint n_items;
|
||||||
|
GError *inner_error = NULL;
|
||||||
|
|
||||||
|
split = g_strsplit (line, ",", -1);
|
||||||
|
n_items = g_strv_length (split);
|
||||||
|
|
||||||
|
/* Conditions */
|
||||||
|
if (n_items > 1) {
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
rule->conditions = g_array_sized_new (FALSE, FALSE, sizeof (MMUdevRuleMatch), n_items - 1);
|
||||||
|
g_array_set_clear_func (rule->conditions, (GDestroyNotify) udev_rule_match_clear);
|
||||||
|
|
||||||
|
/* All items except for the last one are conditions */
|
||||||
|
for (i = 0; !inner_error && i < (n_items - 1); i++) {
|
||||||
|
MMUdevRuleMatch rule_match = { 0 };
|
||||||
|
|
||||||
|
/* If condition correctly preloaded, add it to the rule */
|
||||||
|
if (!load_rule_match (&rule_match, split[i], &inner_error))
|
||||||
|
goto out;
|
||||||
|
g_assert (rule_match.type != MM_UDEV_RULE_MATCH_TYPE_UNKNOWN);
|
||||||
|
g_assert (rule_match.parameter);
|
||||||
|
g_assert (rule_match.value);
|
||||||
|
g_array_append_val (rule->conditions, rule_match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Last item, the result */
|
||||||
|
if (!load_rule_result (&rule->result, split[n_items - 1], &inner_error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
g_assert ((rule->result.type == MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG && rule->result.content.tag) ||
|
||||||
|
(rule->result.type == MM_UDEV_RULE_RESULT_TYPE_LABEL && rule->result.content.tag) ||
|
||||||
|
(rule->result.type == MM_UDEV_RULE_RESULT_TYPE_PROPERTY && rule->result.content.property.name && rule->result.content.property.value));
|
||||||
|
|
||||||
|
out:
|
||||||
|
g_strfreev (split);
|
||||||
|
|
||||||
|
if (inner_error) {
|
||||||
|
g_propagate_error (error, inner_error);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
process_goto_tags (GArray *rules,
|
||||||
|
guint first_rule_index,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = first_rule_index; i < rules->len; i++) {
|
||||||
|
MMUdevRule *rule;
|
||||||
|
|
||||||
|
rule = &g_array_index (rules, MMUdevRule, i);
|
||||||
|
if (rule->result.type == MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG) {
|
||||||
|
guint j;
|
||||||
|
guint label_index = 0;
|
||||||
|
|
||||||
|
for (j = i + 1; j < rules->len; j++) {
|
||||||
|
MMUdevRule *walker;
|
||||||
|
|
||||||
|
walker = &g_array_index (rules, MMUdevRule, j);
|
||||||
|
if (walker->result.type == MM_UDEV_RULE_RESULT_TYPE_LABEL &&
|
||||||
|
g_str_equal (rule->result.content.tag, walker->result.content.tag)) {
|
||||||
|
|
||||||
|
if (label_index) {
|
||||||
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
"More than one label '%s' found", rule->result.content.tag);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
label_index = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!label_index) {
|
||||||
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
"Couldn't find label '%s'", rule->result.content.tag);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule->result.type = MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX;
|
||||||
|
g_free (rule->result.content.tag);
|
||||||
|
rule->result.content.index = label_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
load_rules_from_file (GArray *rules,
|
||||||
|
const gchar *path,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GFile *file;
|
||||||
|
GFileInputStream *fistream;
|
||||||
|
GDataInputStream *distream = NULL;
|
||||||
|
GError *inner_error = NULL;
|
||||||
|
gchar *line;
|
||||||
|
guint first_rule_index;
|
||||||
|
|
||||||
|
mm_dbg ("[rules] loading rules from: %s", path);
|
||||||
|
first_rule_index = rules->len;
|
||||||
|
|
||||||
|
file = g_file_new_for_path (path);
|
||||||
|
fistream = g_file_read (file, NULL, &inner_error);
|
||||||
|
if (!fistream)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
distream = g_data_input_stream_new (G_INPUT_STREAM (fistream));
|
||||||
|
|
||||||
|
while (((line = g_data_input_stream_read_line_utf8 (distream, NULL, NULL, &inner_error)) != NULL) && !inner_error) {
|
||||||
|
const gchar *aux;
|
||||||
|
|
||||||
|
aux = line;
|
||||||
|
while (*aux == ' ')
|
||||||
|
aux++;
|
||||||
|
if (*aux != '#' && *aux != '\0') {
|
||||||
|
MMUdevRule rule = { 0 };
|
||||||
|
|
||||||
|
if (load_rule_from_line (&rule, aux, &inner_error))
|
||||||
|
g_array_append_val (rules, rule);
|
||||||
|
else
|
||||||
|
udev_rule_clear (&rule);
|
||||||
|
}
|
||||||
|
g_free (line);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
|
||||||
|
if (distream)
|
||||||
|
g_object_unref (distream);
|
||||||
|
if (fistream)
|
||||||
|
g_object_unref (fistream);
|
||||||
|
g_object_unref (file);
|
||||||
|
|
||||||
|
if (inner_error) {
|
||||||
|
g_propagate_error (error, inner_error);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first_rule_index < rules->len && !process_goto_tags (rules, first_rule_index, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GList *
|
||||||
|
list_rule_files (const gchar *rules_dir_path)
|
||||||
|
{
|
||||||
|
static const gchar *expected_rules_prefix[] = { "77-mm-", "78-mm-", "79-mm-", "80-mm-" };
|
||||||
|
GFile *udevrulesdir;
|
||||||
|
GFileEnumerator *enumerator;
|
||||||
|
GList *children = NULL;
|
||||||
|
|
||||||
|
udevrulesdir = g_file_new_for_path (rules_dir_path);
|
||||||
|
enumerator = g_file_enumerate_children (udevrulesdir,
|
||||||
|
G_FILE_ATTRIBUTE_STANDARD_NAME,
|
||||||
|
G_FILE_QUERY_INFO_NONE,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
if (enumerator) {
|
||||||
|
GFileInfo *info;
|
||||||
|
|
||||||
|
/* If we get any kind of error, assume we need to stop enumerating */
|
||||||
|
while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL) {
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (expected_rules_prefix); i++) {
|
||||||
|
if (g_str_has_prefix (g_file_info_get_name (info), expected_rules_prefix[i])) {
|
||||||
|
children = g_list_prepend (children, g_build_path (G_DIR_SEPARATOR_S, rules_dir_path, g_file_info_get_name (info), NULL));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_object_unref (info);
|
||||||
|
}
|
||||||
|
g_object_unref (enumerator);
|
||||||
|
}
|
||||||
|
g_object_unref (udevrulesdir);
|
||||||
|
|
||||||
|
return g_list_sort (children, (GCompareFunc) g_strcmp0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GArray *
|
||||||
|
mm_kernel_device_generic_rules_load (const gchar *rules_dir,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GList *rule_files, *l;
|
||||||
|
GArray *rules;
|
||||||
|
GError *inner_error = NULL;
|
||||||
|
|
||||||
|
mm_dbg ("[rules] rules directory set to '%s'...", rules_dir);
|
||||||
|
|
||||||
|
rules = g_array_new (FALSE, FALSE, sizeof (MMUdevRule));
|
||||||
|
g_array_set_clear_func (rules, (GDestroyNotify) udev_rule_clear);
|
||||||
|
|
||||||
|
/* List rule files in rules dir */
|
||||||
|
rule_files = list_rule_files (rules_dir);
|
||||||
|
if (!rule_files) {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
"No rule files found in '%s'", rules_dir);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate over rule files */
|
||||||
|
for (l = rule_files; l; l = g_list_next (l)) {
|
||||||
|
if (!load_rules_from_file (rules, (const gchar *)(l->data), &inner_error))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fail if no rules were loaded */
|
||||||
|
if (rules->len == 0) {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No rules loaded");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
mm_dbg ("[rules] %u loaded", rules->len);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (rule_files)
|
||||||
|
g_list_free_full (rule_files, (GDestroyNotify) g_free);
|
||||||
|
|
||||||
|
if (inner_error) {
|
||||||
|
g_propagate_error (error, inner_error);
|
||||||
|
g_array_unref (rules);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
}
|
62
src/kerneldevice/mm-kernel-device-generic-rules.h
Normal file
62
src/kerneldevice/mm-kernel-device-generic-rules.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/* -*- 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 Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MM_UDEV_RULE_MATCH_TYPE_UNKNOWN,
|
||||||
|
MM_UDEV_RULE_MATCH_TYPE_EQUAL,
|
||||||
|
MM_UDEV_RULE_MATCH_TYPE_NOT_EQUAL,
|
||||||
|
} MMUdevRuleMatchType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
MMUdevRuleMatchType type;
|
||||||
|
gchar *parameter;
|
||||||
|
gchar *value;
|
||||||
|
} MMUdevRuleMatch;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MM_UDEV_RULE_RESULT_TYPE_UNKNOWN,
|
||||||
|
MM_UDEV_RULE_RESULT_TYPE_PROPERTY,
|
||||||
|
MM_UDEV_RULE_RESULT_TYPE_LABEL,
|
||||||
|
MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX,
|
||||||
|
MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG, /* internal use only */
|
||||||
|
} MMUdevRuleResultType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gchar *name;
|
||||||
|
gchar *value;
|
||||||
|
} MMUdevRuleResultProperty;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
MMUdevRuleResultType type;
|
||||||
|
union {
|
||||||
|
MMUdevRuleResultProperty property;
|
||||||
|
gchar *tag;
|
||||||
|
guint index;
|
||||||
|
} content;
|
||||||
|
} MMUdevRuleResult;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GArray *conditions;
|
||||||
|
MMUdevRuleResult result;
|
||||||
|
} MMUdevRule;
|
||||||
|
|
||||||
|
GArray *mm_kernel_device_generic_rules_load (const gchar *rules_dir,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
G_END_DECLS
|
@@ -22,8 +22,13 @@
|
|||||||
#include <libmm-glib.h>
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
#include "mm-kernel-device-generic.h"
|
#include "mm-kernel-device-generic.h"
|
||||||
|
#include "mm-kernel-device-generic-rules.h"
|
||||||
#include "mm-log.h"
|
#include "mm-log.h"
|
||||||
|
|
||||||
|
#if !defined UDEVRULESDIR
|
||||||
|
# error UDEVRULESDIR is not defined
|
||||||
|
#endif
|
||||||
|
|
||||||
static void initable_iface_init (GInitableIface *iface);
|
static void initable_iface_init (GInitableIface *iface);
|
||||||
|
|
||||||
G_DEFINE_TYPE_EXTENDED (MMKernelDeviceGeneric, mm_kernel_device_generic, MM_TYPE_KERNEL_DEVICE, 0,
|
G_DEFINE_TYPE_EXTENDED (MMKernelDeviceGeneric, mm_kernel_device_generic, MM_TYPE_KERNEL_DEVICE, 0,
|
||||||
@@ -32,6 +37,7 @@ G_DEFINE_TYPE_EXTENDED (MMKernelDeviceGeneric, mm_kernel_device_generic, MM_TYP
|
|||||||
enum {
|
enum {
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_PROPERTIES,
|
PROP_PROPERTIES,
|
||||||
|
PROP_RULES,
|
||||||
PROP_LAST
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,8 +46,330 @@ static GParamSpec *properties[PROP_LAST];
|
|||||||
struct _MMKernelDeviceGenericPrivate {
|
struct _MMKernelDeviceGenericPrivate {
|
||||||
/* Input properties */
|
/* Input properties */
|
||||||
MMKernelEventProperties *properties;
|
MMKernelEventProperties *properties;
|
||||||
|
/* Rules to apply */
|
||||||
|
GArray *rules;
|
||||||
|
|
||||||
|
/* Contents from sysfs */
|
||||||
|
gchar *driver;
|
||||||
|
gchar *sysfs_path;
|
||||||
|
gchar *interface_sysfs_path;
|
||||||
|
guint8 interface_class;
|
||||||
|
guint8 interface_subclass;
|
||||||
|
guint8 interface_protocol;
|
||||||
|
guint8 interface_number;
|
||||||
|
gchar *physdev_sysfs_path;
|
||||||
|
guint16 physdev_vid;
|
||||||
|
guint16 physdev_pid;
|
||||||
|
gchar *physdev_manufacturer;
|
||||||
|
gchar *physdev_product;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static guint
|
||||||
|
read_sysfs_property_as_hex (const gchar *path,
|
||||||
|
const gchar *property)
|
||||||
|
{
|
||||||
|
gchar *aux;
|
||||||
|
gchar *contents = NULL;
|
||||||
|
guint val = 0;
|
||||||
|
|
||||||
|
aux = g_strdup_printf ("%s/%s", path, property);
|
||||||
|
if (g_file_get_contents (aux, &contents, NULL, NULL)) {
|
||||||
|
g_strdelimit (contents, "\r\n", ' ');
|
||||||
|
g_strstrip (contents);
|
||||||
|
mm_get_uint_from_hex_str (contents, &val);
|
||||||
|
}
|
||||||
|
g_free (contents);
|
||||||
|
g_free (aux);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
read_sysfs_property_as_string (const gchar *path,
|
||||||
|
const gchar *property)
|
||||||
|
{
|
||||||
|
gchar *aux;
|
||||||
|
gchar *contents = NULL;
|
||||||
|
|
||||||
|
aux = g_strdup_printf ("%s/%s", path, property);
|
||||||
|
if (g_file_get_contents (aux, &contents, NULL, NULL)) {
|
||||||
|
g_strdelimit (contents, "\r\n", ' ');
|
||||||
|
g_strstrip (contents);
|
||||||
|
}
|
||||||
|
g_free (aux);
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Load contents */
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_sysfs_path (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
gchar *tmp;
|
||||||
|
|
||||||
|
if (self->priv->sysfs_path)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* sysfs can be built directly using subsystem and name; e.g. for subsystem
|
||||||
|
* usbmisc and name cdc-wdm0:
|
||||||
|
* $ realpath /sys/class/usbmisc/cdc-wdm0
|
||||||
|
* /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.3/4-1.3:1.8/usbmisc/cdc-wdm0
|
||||||
|
*/
|
||||||
|
tmp = g_strdup_printf ("/sys/class/%s/%s",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
|
|
||||||
|
self->priv->sysfs_path = canonicalize_file_name (tmp);
|
||||||
|
if (!self->priv->sysfs_path || !g_file_test (self->priv->sysfs_path, G_FILE_TEST_EXISTS)) {
|
||||||
|
mm_warn ("Invalid sysfs path read for %s/%s",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
|
g_clear_pointer (&self->priv->sysfs_path, g_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->priv->sysfs_path)
|
||||||
|
mm_dbg ("(%s/%s) sysfs path: %s",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->sysfs_path);
|
||||||
|
g_free (tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_interface_sysfs_path (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
gchar *dirpath;
|
||||||
|
gchar *aux;
|
||||||
|
|
||||||
|
if (self->priv->interface_sysfs_path || !self->priv->sysfs_path)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* parent sysfs can be built directly using subsystem and name; e.g. for
|
||||||
|
* subsystem usbmisc and name cdc-wdm0:
|
||||||
|
* $ realpath /sys/class/usbmisc/cdc-wdm0/device
|
||||||
|
* /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.3/4-1.3:1.8
|
||||||
|
*
|
||||||
|
* This sysfs path will be equal for all devices exported from within the
|
||||||
|
* same interface (e.g. a pair of cdc-wdm/wwan devices).
|
||||||
|
*
|
||||||
|
* The correct parent dir we want to have is the first one with "usb" subsystem.
|
||||||
|
*/
|
||||||
|
aux = g_strdup_printf ("%s/device", self->priv->sysfs_path);
|
||||||
|
dirpath = canonicalize_file_name (aux);
|
||||||
|
g_free (aux);
|
||||||
|
|
||||||
|
while (dirpath) {
|
||||||
|
gchar *subsystem_filepath;
|
||||||
|
|
||||||
|
/* Directory must exist */
|
||||||
|
if (!g_file_test (dirpath, G_FILE_TEST_EXISTS))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* If subsystem file not found, keep looping */
|
||||||
|
subsystem_filepath = g_strdup_printf ("%s/subsystem", dirpath);
|
||||||
|
if (g_file_test (subsystem_filepath, G_FILE_TEST_EXISTS)) {
|
||||||
|
gchar *canonicalized_subsystem;
|
||||||
|
gchar *subsystem_name;
|
||||||
|
|
||||||
|
canonicalized_subsystem = canonicalize_file_name (subsystem_filepath);
|
||||||
|
g_free (subsystem_filepath);
|
||||||
|
|
||||||
|
subsystem_name = g_path_get_basename (canonicalized_subsystem);
|
||||||
|
g_free (canonicalized_subsystem);
|
||||||
|
|
||||||
|
if (subsystem_name && g_str_equal (subsystem_name, "usb")) {
|
||||||
|
self->priv->interface_sysfs_path = dirpath;
|
||||||
|
g_free (subsystem_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
g_free (subsystem_filepath);
|
||||||
|
|
||||||
|
/* Just in case */
|
||||||
|
if (g_str_equal (dirpath, "/")) {
|
||||||
|
g_free (dirpath);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
aux = g_path_get_dirname (dirpath);
|
||||||
|
g_free (dirpath);
|
||||||
|
dirpath = aux;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->priv->interface_sysfs_path)
|
||||||
|
mm_dbg ("(%s/%s) interface sysfs path: %s",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->interface_sysfs_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_physdev_sysfs_path (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
/* physdev sysfs is the dirname of the parent sysfs path, e.g.:
|
||||||
|
* /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.3
|
||||||
|
*
|
||||||
|
* This sysfs path will be equal for all devices exported from the same
|
||||||
|
* physical device.
|
||||||
|
*/
|
||||||
|
if (!self->priv->physdev_sysfs_path && self->priv->interface_sysfs_path)
|
||||||
|
self->priv->physdev_sysfs_path = g_path_get_dirname (self->priv->interface_sysfs_path);
|
||||||
|
|
||||||
|
if (self->priv->physdev_sysfs_path)
|
||||||
|
mm_dbg ("(%s/%s) physdev sysfs path: %s",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->physdev_sysfs_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_driver (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
if (!self->priv->driver && self->priv->interface_sysfs_path) {
|
||||||
|
gchar *tmp;
|
||||||
|
gchar *tmp2;
|
||||||
|
|
||||||
|
tmp = g_strdup_printf ("%s/driver", self->priv->interface_sysfs_path);
|
||||||
|
tmp2 = canonicalize_file_name (tmp);
|
||||||
|
if (tmp2 && g_file_test (tmp2, G_FILE_TEST_EXISTS))
|
||||||
|
self->priv->driver = g_path_get_basename (tmp2);
|
||||||
|
g_free (tmp2);
|
||||||
|
g_free (tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->priv->driver)
|
||||||
|
mm_dbg ("(%s/%s) driver: %s",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_physdev_vid (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
if (!self->priv->physdev_vid && self->priv->physdev_sysfs_path) {
|
||||||
|
guint val;
|
||||||
|
|
||||||
|
val = read_sysfs_property_as_hex (self->priv->physdev_sysfs_path, "idVendor");
|
||||||
|
if (val && val <= G_MAXUINT16)
|
||||||
|
self->priv->physdev_vid = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->priv->physdev_vid)
|
||||||
|
mm_dbg ("(%s/%s) vid: 0x%04x",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->physdev_vid);
|
||||||
|
else
|
||||||
|
mm_dbg ("(%s/%s) vid: unknown",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_physdev_pid (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
if (!self->priv->physdev_pid && self->priv->physdev_sysfs_path) {
|
||||||
|
guint val;
|
||||||
|
|
||||||
|
val = read_sysfs_property_as_hex (self->priv->physdev_sysfs_path, "idProduct");
|
||||||
|
if (val && val <= G_MAXUINT16)
|
||||||
|
self->priv->physdev_pid = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->priv->physdev_pid)
|
||||||
|
mm_dbg ("(%s/%s) pid: 0x%04x",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->physdev_pid);
|
||||||
|
else
|
||||||
|
mm_dbg ("(%s/%s) pid: unknown",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_manufacturer (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
if (!self->priv->physdev_manufacturer)
|
||||||
|
self->priv->physdev_manufacturer = (self->priv->physdev_sysfs_path ? read_sysfs_property_as_string (self->priv->physdev_sysfs_path, "manufacturer") : NULL);
|
||||||
|
|
||||||
|
mm_dbg ("(%s/%s) manufacturer: %s",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->physdev_manufacturer ? self->priv->physdev_manufacturer : "unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_product (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
if (!self->priv->physdev_product)
|
||||||
|
self->priv->physdev_product = (self->priv->physdev_sysfs_path ? read_sysfs_property_as_string (self->priv->physdev_sysfs_path, "product") : NULL);
|
||||||
|
|
||||||
|
mm_dbg ("(%s/%s) product: %s",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->physdev_product ? self->priv->physdev_product : "unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_interface_class (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
self->priv->interface_class = (self->priv->interface_sysfs_path ? read_sysfs_property_as_hex (self->priv->interface_sysfs_path, "bInterfaceClass") : 0x00);
|
||||||
|
mm_dbg ("(%s/%s) interface class: 0x%02x",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->interface_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_interface_subclass (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
self->priv->interface_subclass = (self->priv->interface_sysfs_path ? read_sysfs_property_as_hex (self->priv->interface_sysfs_path, "bInterfaceSubClass") : 0x00);
|
||||||
|
mm_dbg ("(%s/%s) interface subclass: 0x%02x",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->interface_subclass);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_interface_protocol (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
self->priv->interface_protocol = (self->priv->interface_sysfs_path ? read_sysfs_property_as_hex (self->priv->interface_sysfs_path, "bInterfaceProtocol") : 0x00);
|
||||||
|
mm_dbg ("(%s/%s) interface protocol: 0x%02x",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->interface_protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_interface_number (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
self->priv->interface_number = (self->priv->interface_sysfs_path ? read_sysfs_property_as_hex (self->priv->interface_sysfs_path, "bInterfaceNumber") : 0x00);
|
||||||
|
mm_dbg ("(%s/%s) interface number: 0x%02x",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
self->priv->interface_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_contents (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
preload_sysfs_path (self);
|
||||||
|
preload_interface_sysfs_path (self);
|
||||||
|
preload_interface_class (self);
|
||||||
|
preload_interface_subclass (self);
|
||||||
|
preload_interface_protocol (self);
|
||||||
|
preload_interface_number (self);
|
||||||
|
preload_physdev_sysfs_path (self);
|
||||||
|
preload_manufacturer (self);
|
||||||
|
preload_product (self);
|
||||||
|
preload_driver (self);
|
||||||
|
preload_physdev_vid (self);
|
||||||
|
preload_physdev_pid (self);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static const gchar *
|
static const gchar *
|
||||||
@@ -65,7 +393,7 @@ kernel_device_get_sysfs_path (MMKernelDevice *self)
|
|||||||
{
|
{
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
|
||||||
|
|
||||||
return NULL;
|
return MM_KERNEL_DEVICE_GENERIC (self)->priv->sysfs_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const gchar *
|
static const gchar *
|
||||||
@@ -73,16 +401,30 @@ kernel_device_get_parent_sysfs_path (MMKernelDevice *self)
|
|||||||
{
|
{
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
|
||||||
|
|
||||||
return NULL;
|
return MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_sysfs_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const gchar *
|
static const gchar *
|
||||||
kernel_device_get_physdev_uid (MMKernelDevice *self)
|
kernel_device_get_physdev_uid (MMKernelDevice *self)
|
||||||
{
|
{
|
||||||
|
const gchar *uid;
|
||||||
|
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
|
||||||
|
|
||||||
/* Prefer the one coming in the properties, if any */
|
/* Prefer the one coming in the properties, if any */
|
||||||
return mm_kernel_event_properties_get_uid (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties);
|
if ((uid = mm_kernel_event_properties_get_uid (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties)) != NULL)
|
||||||
|
return uid;
|
||||||
|
|
||||||
|
/* Try to load from properties set */
|
||||||
|
if ((uid = mm_kernel_device_get_property (self, "ID_MM_PHYSDEV_UID")) != NULL)
|
||||||
|
return uid;
|
||||||
|
|
||||||
|
/* Use physical device path, if any */
|
||||||
|
if (MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_sysfs_path)
|
||||||
|
return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_sysfs_path;
|
||||||
|
|
||||||
|
/* If there is no physdev sysfs path, e.g. for platform ports, use the device sysfs itself */
|
||||||
|
return MM_KERNEL_DEVICE_GENERIC (self)->priv->sysfs_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const gchar *
|
static const gchar *
|
||||||
@@ -90,7 +432,7 @@ kernel_device_get_driver (MMKernelDevice *self)
|
|||||||
{
|
{
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
|
||||||
|
|
||||||
return NULL;
|
return MM_KERNEL_DEVICE_GENERIC (self)->priv->driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
static guint16
|
static guint16
|
||||||
@@ -98,7 +440,7 @@ kernel_device_get_physdev_vid (MMKernelDevice *self)
|
|||||||
{
|
{
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), 0);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), 0);
|
||||||
|
|
||||||
return 0;
|
return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_vid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static guint16
|
static guint16
|
||||||
@@ -106,15 +448,59 @@ kernel_device_get_physdev_pid (MMKernelDevice *self)
|
|||||||
{
|
{
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), 0);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), 0);
|
||||||
|
|
||||||
return 0;
|
return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
kernel_device_is_candidate (MMKernelDevice *_self,
|
kernel_device_is_candidate (MMKernelDevice *_self,
|
||||||
gboolean manual_scan)
|
gboolean manual_scan)
|
||||||
{
|
{
|
||||||
|
MMKernelDeviceGeneric *self;
|
||||||
|
const gchar *name;
|
||||||
|
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (_self), FALSE);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (_self), FALSE);
|
||||||
|
|
||||||
|
self = MM_KERNEL_DEVICE_GENERIC (_self);
|
||||||
|
|
||||||
|
name = mm_kernel_event_properties_get_name (self->priv->properties);
|
||||||
|
|
||||||
|
/* ignore VTs */
|
||||||
|
if (strncmp (name, "tty", 3) == 0 && g_ascii_isdigit (name[3])) {
|
||||||
|
mm_dbg ("(%s/%s) VT ignored",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* only ports tagged as candidate */
|
||||||
|
if (!mm_kernel_device_get_property_as_boolean (_self, "ID_MM_CANDIDATE")) {
|
||||||
|
mm_dbg ("(%s/%s) device not flagged with ID_MM_CANDIDATE",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no devices without physical device */
|
||||||
|
if (!self->priv->physdev_sysfs_path) {
|
||||||
|
mm_dbg ("(%s/%s) device without physdev sysfs path",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ignore ports explicitly ignored; note that in this case the property
|
||||||
|
* is set in this kernel device itself, unlike in the udev backend, that
|
||||||
|
* goes in the parent udev device */
|
||||||
|
if (mm_kernel_device_get_property_as_boolean (_self, "ID_MM_DEVICE_IGNORE")) {
|
||||||
|
mm_dbg ("(%s/%s) device flagged with ID_MM_DEVICE_IGNORE",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mm_dbg ("(%s/%s) device is candidate",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,13 +515,305 @@ kernel_device_cmp (MMKernelDevice *a,
|
|||||||
!g_strcmp0 (mm_kernel_device_get_name (a), mm_kernel_device_get_name (b)));
|
!g_strcmp0 (mm_kernel_device_get_name (a), mm_kernel_device_get_name (b)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
string_match (const gchar *str,
|
||||||
|
const gchar *original_pattern)
|
||||||
|
{
|
||||||
|
gchar *pattern;
|
||||||
|
gchar *start;
|
||||||
|
gboolean open_prefix = FALSE;
|
||||||
|
gboolean open_suffix = FALSE;
|
||||||
|
gboolean match;
|
||||||
|
|
||||||
|
pattern = g_strdup (original_pattern);
|
||||||
|
start = pattern;
|
||||||
|
|
||||||
|
if (start[0] == '*') {
|
||||||
|
open_prefix = TRUE;
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start[strlen (start) - 1] == '*') {
|
||||||
|
open_suffix = TRUE;
|
||||||
|
start[strlen (start) - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (open_suffix && !open_prefix)
|
||||||
|
match = g_str_has_prefix (str, start);
|
||||||
|
else if (!open_suffix && open_prefix)
|
||||||
|
match = g_str_has_suffix (str, start);
|
||||||
|
else if (open_suffix && open_prefix)
|
||||||
|
match = !!strstr (str, start);
|
||||||
|
else
|
||||||
|
match = g_str_equal (str, start);
|
||||||
|
|
||||||
|
g_free (pattern);
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_condition (MMKernelDeviceGeneric *self,
|
||||||
|
MMUdevRuleMatch *match)
|
||||||
|
{
|
||||||
|
gboolean condition_equal;
|
||||||
|
|
||||||
|
condition_equal = (match->type == MM_UDEV_RULE_MATCH_TYPE_EQUAL);
|
||||||
|
|
||||||
|
/* We only apply 'add' rules */
|
||||||
|
if (g_str_equal (match->parameter, "ACTION"))
|
||||||
|
return ((!!strstr (match->value, "add")) == condition_equal);
|
||||||
|
|
||||||
|
/* We look for the subsystem string in the whole sysfs path.
|
||||||
|
*
|
||||||
|
* Note that we're not really making a difference between "SUBSYSTEMS"
|
||||||
|
* (where the whole device tree is checked) and "SUBSYSTEM" (where just one
|
||||||
|
* single device is checked), because a lot of the MM udev rules are meant
|
||||||
|
* to just tag the physical device (e.g. with ID_MM_DEVICE_IGNORE) instead
|
||||||
|
* of the single ports. In our case with the custom parsing, we do tag all
|
||||||
|
* independent ports.
|
||||||
|
*/
|
||||||
|
if (g_str_equal (match->parameter, "SUBSYSTEMS") || g_str_equal (match->parameter, "SUBSYSTEM"))
|
||||||
|
return ((self->priv->sysfs_path && !!strstr (self->priv->sysfs_path, match->value)) == condition_equal);
|
||||||
|
|
||||||
|
/* Exact DRIVER match? We also include the check for DRIVERS, even if we
|
||||||
|
* only apply it to this port driver. */
|
||||||
|
if (g_str_equal (match->parameter, "DRIVER") || g_str_equal (match->parameter, "DRIVERS"))
|
||||||
|
return ((!g_strcmp0 (match->value, mm_kernel_device_get_driver (MM_KERNEL_DEVICE (self)))) == condition_equal);
|
||||||
|
|
||||||
|
/* Device name checks */
|
||||||
|
if (g_str_equal (match->parameter, "KERNEL"))
|
||||||
|
return (string_match (mm_kernel_device_get_name (MM_KERNEL_DEVICE (self)), match->value) == condition_equal);
|
||||||
|
|
||||||
|
/* Device sysfs path checks; we allow both a direct match and a prefix patch */
|
||||||
|
if (g_str_equal (match->parameter, "DEVPATH")) {
|
||||||
|
const gchar *sysfs_path;
|
||||||
|
gchar *prefix_match = NULL;
|
||||||
|
gboolean result = FALSE;
|
||||||
|
|
||||||
|
sysfs_path = mm_kernel_device_get_sysfs_path (MM_KERNEL_DEVICE (self));
|
||||||
|
|
||||||
|
/* If not already doing a prefix match, do an implicit one. This is so that
|
||||||
|
* we can add properties to the usb_device owning all ports, and then apply
|
||||||
|
* the property to all ports individually processed here. */
|
||||||
|
if (match->value[0] && match->value[strlen (match->value) - 1] != '*')
|
||||||
|
prefix_match = g_strdup_printf ("%s/*", match->value);
|
||||||
|
|
||||||
|
if (string_match (sysfs_path, match->value) == condition_equal) {
|
||||||
|
result = TRUE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefix_match && string_match (sysfs_path, prefix_match) == condition_equal) {
|
||||||
|
result = TRUE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_str_has_prefix (sysfs_path, "/sys")) {
|
||||||
|
if (string_match (&sysfs_path[4], match->value) == condition_equal) {
|
||||||
|
result = TRUE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (prefix_match && string_match (&sysfs_path[4], prefix_match) == condition_equal) {
|
||||||
|
result = TRUE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
g_free (prefix_match);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attributes checks */
|
||||||
|
if (g_str_has_prefix (match->parameter, "ATTRS")) {
|
||||||
|
gchar *attribute;
|
||||||
|
gchar *contents = NULL;
|
||||||
|
gboolean result = FALSE;
|
||||||
|
guint val;
|
||||||
|
|
||||||
|
attribute = g_strdup (&match->parameter[5]);
|
||||||
|
g_strdelimit (attribute, "{}", ' ');
|
||||||
|
g_strstrip (attribute);
|
||||||
|
|
||||||
|
/* VID/PID directly from our API */
|
||||||
|
if (g_str_equal (attribute, "idVendor"))
|
||||||
|
result = ((mm_get_uint_from_hex_str (match->value, &val)) &&
|
||||||
|
((mm_kernel_device_get_physdev_vid (MM_KERNEL_DEVICE (self)) == val) == condition_equal));
|
||||||
|
else if (g_str_equal (attribute, "idProduct"))
|
||||||
|
result = ((mm_get_uint_from_hex_str (match->value, &val)) &&
|
||||||
|
((mm_kernel_device_get_physdev_pid (MM_KERNEL_DEVICE (self)) == val) == condition_equal));
|
||||||
|
/* manufacturer in the physdev */
|
||||||
|
else if (g_str_equal (attribute, "manufacturer"))
|
||||||
|
result = ((self->priv->physdev_manufacturer && g_str_equal (self->priv->physdev_manufacturer, match->value)) == condition_equal);
|
||||||
|
/* product in the physdev */
|
||||||
|
else if (g_str_equal (attribute, "product"))
|
||||||
|
result = ((self->priv->physdev_product && g_str_equal (self->priv->physdev_product, match->value)) == condition_equal);
|
||||||
|
/* interface class/subclass/protocol/number in the interface */
|
||||||
|
else if (g_str_equal (attribute, "bInterfaceClass"))
|
||||||
|
result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) &&
|
||||||
|
((self->priv->interface_class == val) == condition_equal)));
|
||||||
|
else if (g_str_equal (attribute, "bInterfaceSubClass"))
|
||||||
|
result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) &&
|
||||||
|
((self->priv->interface_subclass == val) == condition_equal)));
|
||||||
|
else if (g_str_equal (attribute, "bInterfaceProtocol"))
|
||||||
|
result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) &&
|
||||||
|
((self->priv->interface_protocol == val) == condition_equal)));
|
||||||
|
else if (g_str_equal (attribute, "bInterfaceNumber"))
|
||||||
|
result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) &&
|
||||||
|
((self->priv->interface_number == val) == condition_equal)));
|
||||||
|
else
|
||||||
|
mm_warn ("Unknown attribute: %s", attribute);
|
||||||
|
|
||||||
|
g_free (contents);
|
||||||
|
g_free (attribute);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Previously set property checks */
|
||||||
|
if (g_str_has_prefix (match->parameter, "ENV")) {
|
||||||
|
gchar *property;
|
||||||
|
gboolean result = FALSE;
|
||||||
|
|
||||||
|
property = g_strdup (&match->parameter[3]);
|
||||||
|
g_strdelimit (property, "{}", ' ');
|
||||||
|
g_strstrip (property);
|
||||||
|
|
||||||
|
result = ((!g_strcmp0 ((const gchar *) g_object_get_data (G_OBJECT (self), property), match->value)) == condition_equal);
|
||||||
|
|
||||||
|
g_free (property);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
mm_warn ("Unknown match condition parameter: %s", match->parameter);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
check_rule (MMKernelDeviceGeneric *self,
|
||||||
|
guint rule_i)
|
||||||
|
{
|
||||||
|
MMUdevRule *rule;
|
||||||
|
gboolean apply = TRUE;
|
||||||
|
|
||||||
|
g_assert (rule_i < self->priv->rules->len);
|
||||||
|
|
||||||
|
rule = &g_array_index (self->priv->rules, MMUdevRule, rule_i);
|
||||||
|
if (rule->conditions) {
|
||||||
|
guint condition_i;
|
||||||
|
|
||||||
|
for (condition_i = 0; condition_i < rule->conditions->len; condition_i++) {
|
||||||
|
MMUdevRuleMatch *match;
|
||||||
|
|
||||||
|
match = &g_array_index (rule->conditions, MMUdevRuleMatch, condition_i);
|
||||||
|
if (!check_condition (self, match)) {
|
||||||
|
apply = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apply) {
|
||||||
|
switch (rule->result.type) {
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_PROPERTY: {
|
||||||
|
gchar *property_value_read = NULL;
|
||||||
|
|
||||||
|
if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceClass}"))
|
||||||
|
property_value_read = g_strdup_printf ("%02x", self->priv->interface_class);
|
||||||
|
else if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceSubClass}"))
|
||||||
|
property_value_read = g_strdup_printf ("%02x", self->priv->interface_subclass);
|
||||||
|
else if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceProtocol}"))
|
||||||
|
property_value_read = g_strdup_printf ("%02x", self->priv->interface_protocol);
|
||||||
|
else if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceNumber}"))
|
||||||
|
property_value_read = g_strdup_printf ("%02x", self->priv->interface_number);
|
||||||
|
|
||||||
|
/* add new property */
|
||||||
|
mm_dbg ("(%s/%s) property added: %s=%s",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties),
|
||||||
|
rule->result.content.property.name,
|
||||||
|
property_value_read ? property_value_read : rule->result.content.property.value);
|
||||||
|
|
||||||
|
if (!property_value_read)
|
||||||
|
/* NOTE: we keep a reference to the list of rules ourselves, so it isn't
|
||||||
|
* an issue if we re-use the same string (i.e. without g_strdup-ing it)
|
||||||
|
* as a property value. */
|
||||||
|
g_object_set_data (G_OBJECT (self),
|
||||||
|
rule->result.content.property.name,
|
||||||
|
rule->result.content.property.value);
|
||||||
|
else
|
||||||
|
g_object_set_data_full (G_OBJECT (self),
|
||||||
|
rule->result.content.property.name,
|
||||||
|
property_value_read,
|
||||||
|
g_free);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_LABEL:
|
||||||
|
/* noop */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX:
|
||||||
|
/* Jump to a new index */
|
||||||
|
return rule->result.content.index;
|
||||||
|
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG:
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_UNKNOWN:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Go to the next rule */
|
||||||
|
return rule_i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
preload_properties (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
g_assert (self->priv->rules);
|
||||||
|
g_assert (self->priv->rules->len > 0);
|
||||||
|
|
||||||
|
/* Start to process rules */
|
||||||
|
i = 0;
|
||||||
|
while (i < self->priv->rules->len) {
|
||||||
|
guint next_rule;
|
||||||
|
|
||||||
|
next_rule = check_rule (self, i);
|
||||||
|
i = next_rule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_preload (MMKernelDeviceGeneric *self)
|
||||||
|
{
|
||||||
|
/* Only preload when properties and rules are set */
|
||||||
|
if (!self->priv->properties || !self->priv->rules)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Don't preload on "remove" actions, where we don't have the device any more */
|
||||||
|
if (g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove") == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Don't preload for devices in the 'virtual' subsystem */
|
||||||
|
if (g_strcmp0 (mm_kernel_event_properties_get_subsystem (self->priv->properties), "virtual") == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mm_dbg ("(%s/%s) preloading contents and properties...",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
|
preload_contents (self);
|
||||||
|
preload_properties (self);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
kernel_device_has_property (MMKernelDevice *self,
|
kernel_device_has_property (MMKernelDevice *self,
|
||||||
const gchar *property)
|
const gchar *property)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), FALSE);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), FALSE);
|
||||||
|
|
||||||
return FALSE;
|
return !!g_object_get_data (G_OBJECT (self), property);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const gchar *
|
static const gchar *
|
||||||
@@ -144,42 +822,70 @@ kernel_device_get_property (MMKernelDevice *self,
|
|||||||
{
|
{
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
|
||||||
|
|
||||||
return NULL;
|
return g_object_get_data (G_OBJECT (self), property);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
kernel_device_get_property_as_boolean (MMKernelDevice *self,
|
kernel_device_get_property_as_boolean (MMKernelDevice *self,
|
||||||
const gchar *property)
|
const gchar *property)
|
||||||
{
|
{
|
||||||
|
const gchar *value;
|
||||||
|
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), FALSE);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), FALSE);
|
||||||
|
|
||||||
return FALSE;
|
value = g_object_get_data (G_OBJECT (self), property);
|
||||||
|
return (value && mm_common_get_boolean_from_string (value, NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
kernel_device_get_property_as_int (MMKernelDevice *self,
|
kernel_device_get_property_as_int (MMKernelDevice *self,
|
||||||
const gchar *property)
|
const gchar *property)
|
||||||
{
|
{
|
||||||
|
const gchar *value;
|
||||||
|
gint aux = 0;
|
||||||
|
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), -1);
|
g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), -1);
|
||||||
|
|
||||||
return 0;
|
value = g_object_get_data (G_OBJECT (self), property);
|
||||||
|
return ((value && mm_get_int_from_str (value, &aux)) ? aux : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
MMKernelDevice *
|
MMKernelDevice *
|
||||||
mm_kernel_device_generic_new (MMKernelEventProperties *properties,
|
mm_kernel_device_generic_new_with_rules (MMKernelEventProperties *properties,
|
||||||
GError **error)
|
GArray *rules,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (properties), NULL);
|
g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (properties), NULL);
|
||||||
|
g_return_val_if_fail (rules != NULL, NULL);
|
||||||
|
|
||||||
return MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_GENERIC,
|
return MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_GENERIC,
|
||||||
NULL,
|
NULL,
|
||||||
error,
|
error,
|
||||||
"properties", properties,
|
"properties", properties,
|
||||||
|
"rules", rules,
|
||||||
NULL));
|
NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MMKernelDevice *
|
||||||
|
mm_kernel_device_generic_new (MMKernelEventProperties *properties,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
static GArray *rules = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (properties), NULL);
|
||||||
|
|
||||||
|
/* We only try to load the default list of rules once */
|
||||||
|
if (G_UNLIKELY (!rules)) {
|
||||||
|
rules = mm_kernel_device_generic_rules_load (UDEVRULESDIR, error);
|
||||||
|
if (!rules)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mm_kernel_device_generic_new_with_rules (properties, rules, error);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -201,6 +907,12 @@ set_property (GObject *object,
|
|||||||
case PROP_PROPERTIES:
|
case PROP_PROPERTIES:
|
||||||
g_assert (!self->priv->properties);
|
g_assert (!self->priv->properties);
|
||||||
self->priv->properties = g_value_dup_object (value);
|
self->priv->properties = g_value_dup_object (value);
|
||||||
|
check_preload (self);
|
||||||
|
break;
|
||||||
|
case PROP_RULES:
|
||||||
|
g_assert (!self->priv->rules);
|
||||||
|
self->priv->rules = g_value_dup_boxed (value);
|
||||||
|
check_preload (self);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
@@ -220,6 +932,9 @@ get_property (GObject *object,
|
|||||||
case PROP_PROPERTIES:
|
case PROP_PROPERTIES:
|
||||||
g_value_set_object (value, self->priv->properties);
|
g_value_set_object (value, self->priv->properties);
|
||||||
break;
|
break;
|
||||||
|
case PROP_RULES:
|
||||||
|
g_value_set_boxed (value, self->priv->rules);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -241,18 +956,27 @@ initable_init (GInitable *initable,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_str_equal (subsystem, "virtual")) {
|
|
||||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
|
||||||
"only virtual subsystem supported");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mm_kernel_device_get_name (MM_KERNEL_DEVICE (self))) {
|
if (!mm_kernel_device_get_name (MM_KERNEL_DEVICE (self))) {
|
||||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
||||||
"name is mandatory in kernel device");
|
"name is mandatory in kernel device");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* sysfs path is mandatory as output, and will only be given if the
|
||||||
|
* specified device exists; but only if this wasn't a 'remove' event
|
||||||
|
* and not a virtual device.
|
||||||
|
*/
|
||||||
|
if (self->priv->properties &&
|
||||||
|
g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove") &&
|
||||||
|
g_strcmp0 (mm_kernel_event_properties_get_subsystem (self->priv->properties), "virtual") &&
|
||||||
|
!self->priv->sysfs_path) {
|
||||||
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
||||||
|
"device %s/%s not found",
|
||||||
|
mm_kernel_event_properties_get_subsystem (self->priv->properties),
|
||||||
|
mm_kernel_event_properties_get_name (self->priv->properties));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +985,13 @@ dispose (GObject *object)
|
|||||||
{
|
{
|
||||||
MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object);
|
MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object);
|
||||||
|
|
||||||
g_clear_object (&self->priv->properties);
|
g_clear_pointer (&self->priv->physdev_product, g_free);
|
||||||
|
g_clear_pointer (&self->priv->physdev_manufacturer, g_free);
|
||||||
|
g_clear_pointer (&self->priv->physdev_sysfs_path, g_free);
|
||||||
|
g_clear_pointer (&self->priv->interface_sysfs_path, g_free);
|
||||||
|
g_clear_pointer (&self->priv->sysfs_path, g_free);
|
||||||
|
g_clear_pointer (&self->priv->rules, g_array_unref);
|
||||||
|
g_clear_object (&self->priv->properties);
|
||||||
|
|
||||||
G_OBJECT_CLASS (mm_kernel_device_generic_parent_class)->dispose (object);
|
G_OBJECT_CLASS (mm_kernel_device_generic_parent_class)->dispose (object);
|
||||||
}
|
}
|
||||||
@@ -306,4 +1036,12 @@ mm_kernel_device_generic_class_init (MMKernelDeviceGenericClass *klass)
|
|||||||
MM_TYPE_KERNEL_EVENT_PROPERTIES,
|
MM_TYPE_KERNEL_EVENT_PROPERTIES,
|
||||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||||
g_object_class_install_property (object_class, PROP_PROPERTIES, properties[PROP_PROPERTIES]);
|
g_object_class_install_property (object_class, PROP_PROPERTIES, properties[PROP_PROPERTIES]);
|
||||||
|
|
||||||
|
properties[PROP_RULES] =
|
||||||
|
g_param_spec_boxed ("rules",
|
||||||
|
"Rules",
|
||||||
|
"List of rules to apply",
|
||||||
|
G_TYPE_ARRAY,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||||
|
g_object_class_install_property (object_class, PROP_RULES, properties[PROP_RULES]);
|
||||||
}
|
}
|
||||||
|
@@ -44,8 +44,11 @@ struct _MMKernelDeviceGenericClass {
|
|||||||
MMKernelDeviceClass parent;
|
MMKernelDeviceClass parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType mm_kernel_device_generic_get_type (void);
|
GType mm_kernel_device_generic_get_type (void);
|
||||||
MMKernelDevice *mm_kernel_device_generic_new (MMKernelEventProperties *properties,
|
MMKernelDevice *mm_kernel_device_generic_new (MMKernelEventProperties *properties,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
MMKernelDevice *mm_kernel_device_generic_new_with_rules (MMKernelEventProperties *properties,
|
||||||
|
GArray *rules,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
#endif /* MM_KERNEL_DEVICE_GENERIC_H */
|
#endif /* MM_KERNEL_DEVICE_GENERIC_H */
|
||||||
|
@@ -17,11 +17,17 @@
|
|||||||
* Copyright (C) 2011 - 2016 Aleksander Morgado <aleksander@aleksander.es>
|
* Copyright (C) 2011 - 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#include <gmodule.h>
|
#include <gmodule.h>
|
||||||
#include "mm-kernel-device-udev.h"
|
|
||||||
|
#if WITH_UDEV
|
||||||
|
# include "mm-kernel-device-udev.h"
|
||||||
|
#endif
|
||||||
|
#include "mm-kernel-device-generic.h"
|
||||||
|
|
||||||
#include <ModemManager.h>
|
#include <ModemManager.h>
|
||||||
#include <mm-errors-types.h>
|
#include <mm-errors-types.h>
|
||||||
@@ -62,8 +68,6 @@ struct _MMBaseManagerPrivate {
|
|||||||
gchar *plugin_dir;
|
gchar *plugin_dir;
|
||||||
/* Path to the list of initial kernel events */
|
/* Path to the list of initial kernel events */
|
||||||
gchar *initial_kernel_events;
|
gchar *initial_kernel_events;
|
||||||
/* The UDev client */
|
|
||||||
GUdevClient *udev;
|
|
||||||
/* The authorization provider */
|
/* The authorization provider */
|
||||||
MMAuthProvider *authp;
|
MMAuthProvider *authp;
|
||||||
GCancellable *authp_cancellable;
|
GCancellable *authp_cancellable;
|
||||||
@@ -76,6 +80,11 @@ struct _MMBaseManagerPrivate {
|
|||||||
|
|
||||||
/* The Test interface support */
|
/* The Test interface support */
|
||||||
MmGdbusTest *test_skeleton;
|
MmGdbusTest *test_skeleton;
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
|
/* The UDev client */
|
||||||
|
GUdevClient *udev;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -346,7 +355,11 @@ handle_kernel_event (MMBaseManager *self,
|
|||||||
mm_dbg (" name: %s", name);
|
mm_dbg (" name: %s", name);
|
||||||
mm_dbg (" uid: %s", uid ? uid : "n/a");
|
mm_dbg (" uid: %s", uid ? uid : "n/a");
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
kernel_device = mm_kernel_device_udev_new_from_properties (properties, error);
|
kernel_device = mm_kernel_device_udev_new_from_properties (properties, error);
|
||||||
|
#else
|
||||||
|
kernel_device = mm_kernel_device_generic_new (properties, error);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!kernel_device)
|
if (!kernel_device)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -362,6 +375,8 @@ handle_kernel_event (MMBaseManager *self,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_uevent (GUdevClient *client,
|
handle_uevent (GUdevClient *client,
|
||||||
const char *action,
|
const char *action,
|
||||||
@@ -474,6 +489,8 @@ process_scan (MMBaseManager *self,
|
|||||||
g_list_free (devices);
|
g_list_free (devices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
process_initial_kernel_events (MMBaseManager *self)
|
process_initial_kernel_events (MMBaseManager *self)
|
||||||
{
|
{
|
||||||
@@ -534,9 +551,13 @@ mm_base_manager_start (MMBaseManager *self,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
mm_dbg ("Starting %s device scan...", manual_scan ? "manual" : "automatic");
|
mm_dbg ("Starting %s device scan...", manual_scan ? "manual" : "automatic");
|
||||||
process_scan (self, manual_scan);
|
process_scan (self, manual_scan);
|
||||||
mm_dbg ("Finished device scan...");
|
mm_dbg ("Finished device scan...");
|
||||||
|
#else
|
||||||
|
mm_dbg ("Unsupported %s device scan...", manual_scan ? "manual" : "automatic");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -714,11 +735,17 @@ scan_devices_auth_ready (MMAuthProvider *authp,
|
|||||||
if (!mm_auth_provider_authorize_finish (authp, res, &error))
|
if (!mm_auth_provider_authorize_finish (authp, res, &error))
|
||||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||||
else {
|
else {
|
||||||
|
#if WITH_UDEV
|
||||||
/* Otherwise relaunch device scan */
|
/* Otherwise relaunch device scan */
|
||||||
mm_base_manager_start (MM_BASE_MANAGER (ctx->self), TRUE);
|
mm_base_manager_start (MM_BASE_MANAGER (ctx->self), TRUE);
|
||||||
mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices (
|
mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices (
|
||||||
MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
|
MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
|
||||||
ctx->invocation);
|
ctx->invocation);
|
||||||
|
#else
|
||||||
|
g_dbus_method_invocation_return_error_literal (
|
||||||
|
ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||||
|
"Cannot request manual scan of devices: unsupported");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
scan_devices_context_free (ctx);
|
scan_devices_context_free (ctx);
|
||||||
@@ -771,12 +798,14 @@ report_kernel_event_auth_ready (MMAuthProvider *authp,
|
|||||||
if (!mm_auth_provider_authorize_finish (authp, res, &error))
|
if (!mm_auth_provider_authorize_finish (authp, res, &error))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
if (ctx->self->priv->auto_scan) {
|
if (ctx->self->priv->auto_scan) {
|
||||||
error = g_error_new_literal (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
error = g_error_new_literal (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||||
"Cannot report kernel event: "
|
"Cannot report kernel event: "
|
||||||
"udev monitoring already in place");
|
"udev monitoring already in place");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
properties = mm_kernel_event_properties_new_from_dictionary (ctx->dictionary, &error);
|
properties = mm_kernel_event_properties_new_from_dictionary (ctx->dictionary, &error);
|
||||||
if (!properties)
|
if (!properties)
|
||||||
@@ -990,7 +1019,6 @@ static void
|
|||||||
mm_base_manager_init (MMBaseManager *manager)
|
mm_base_manager_init (MMBaseManager *manager)
|
||||||
{
|
{
|
||||||
MMBaseManagerPrivate *priv;
|
MMBaseManagerPrivate *priv;
|
||||||
const gchar *subsys[5] = { "tty", "net", "usb", "usbmisc", NULL };
|
|
||||||
|
|
||||||
/* Setup private data */
|
/* Setup private data */
|
||||||
manager->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
|
manager->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
|
||||||
@@ -1004,8 +1032,14 @@ mm_base_manager_init (MMBaseManager *manager)
|
|||||||
/* Setup internal lists of device objects */
|
/* Setup internal lists of device objects */
|
||||||
priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
|
priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
|
||||||
|
|
||||||
/* Setup UDev client */
|
#if WITH_UDEV
|
||||||
priv->udev = g_udev_client_new (subsys);
|
{
|
||||||
|
const gchar *subsys[5] = { "tty", "net", "usb", "usbmisc", NULL };
|
||||||
|
|
||||||
|
/* Setup UDev client */
|
||||||
|
priv->udev = g_udev_client_new (subsys);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* By default, enable autoscan */
|
/* By default, enable autoscan */
|
||||||
priv->auto_scan = TRUE;
|
priv->auto_scan = TRUE;
|
||||||
@@ -1038,9 +1072,11 @@ initable_init (GInitable *initable,
|
|||||||
{
|
{
|
||||||
MMBaseManagerPrivate *priv = MM_BASE_MANAGER (initable)->priv;
|
MMBaseManagerPrivate *priv = MM_BASE_MANAGER (initable)->priv;
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
/* If autoscan enabled, list for udev events */
|
/* If autoscan enabled, list for udev events */
|
||||||
if (priv->auto_scan)
|
if (priv->auto_scan)
|
||||||
g_signal_connect (priv->udev, "uevent", G_CALLBACK (handle_uevent), initable);
|
g_signal_connect (priv->udev, "uevent", G_CALLBACK (handle_uevent), initable);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Create plugin manager */
|
/* Create plugin manager */
|
||||||
priv->plugin_manager = mm_plugin_manager_new (priv->plugin_dir, error);
|
priv->plugin_manager = mm_plugin_manager_new (priv->plugin_dir, error);
|
||||||
@@ -1086,8 +1122,10 @@ finalize (GObject *object)
|
|||||||
|
|
||||||
g_hash_table_destroy (priv->devices);
|
g_hash_table_destroy (priv->devices);
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
if (priv->udev)
|
if (priv->udev)
|
||||||
g_object_unref (priv->udev);
|
g_object_unref (priv->udev);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (priv->plugin_manager)
|
if (priv->plugin_manager)
|
||||||
g_object_unref (priv->plugin_manager);
|
g_object_unref (priv->plugin_manager);
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
|
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "mm-context.h"
|
#include "mm-context.h"
|
||||||
@@ -20,14 +21,20 @@
|
|||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Application context */
|
/* Application context */
|
||||||
|
|
||||||
static gboolean version_flag;
|
static gboolean version_flag;
|
||||||
static gboolean debug;
|
static gboolean debug;
|
||||||
static const gchar *log_level;
|
static const gchar *log_level;
|
||||||
static const gchar *log_file;
|
static const gchar *log_file;
|
||||||
static gboolean show_ts;
|
static gboolean show_ts;
|
||||||
static gboolean rel_ts;
|
static gboolean rel_ts;
|
||||||
|
|
||||||
|
#if WITH_UDEV
|
||||||
|
static gboolean no_auto_scan = FALSE;
|
||||||
|
#else
|
||||||
|
static gboolean no_auto_scan = TRUE;
|
||||||
|
#endif
|
||||||
|
|
||||||
static const gchar *initial_kernel_events;
|
static const gchar *initial_kernel_events;
|
||||||
static gboolean no_auto_scan;
|
|
||||||
|
|
||||||
static const GOptionEntry entries[] = {
|
static const GOptionEntry entries[] = {
|
||||||
{ "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, "Print version", NULL },
|
{ "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, "Print version", NULL },
|
||||||
@@ -36,8 +43,14 @@ static const GOptionEntry entries[] = {
|
|||||||
{ "log-file", 0, 0, G_OPTION_ARG_FILENAME, &log_file, "Path to log file", "[PATH]" },
|
{ "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 },
|
{ "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 },
|
{ "relative-timestamps", 0, 0, G_OPTION_ARG_NONE, &rel_ts, "Use relative timestamps (from MM start)", NULL },
|
||||||
|
#if WITH_UDEV
|
||||||
{ "no-auto-scan", 0, 0, G_OPTION_ARG_NONE, &no_auto_scan, "Don't auto-scan looking for devices", 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]" },
|
#else
|
||||||
|
/* Keep the option when udev disabled, just so that the unit test setup can
|
||||||
|
* unconditionally use --no-auto-scan */
|
||||||
|
{ "no-auto-scan", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &no_auto_scan, NULL, NULL },
|
||||||
|
#endif
|
||||||
|
{ "initial-kernel-events", 0, 0, G_OPTION_ARG_FILENAME, &initial_kernel_events, "Path to initial kernel events file", "[PATH]" },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -176,8 +189,10 @@ mm_context_init (gint argc,
|
|||||||
print_version ();
|
print_version ();
|
||||||
|
|
||||||
/* Initial kernel events processing may only be used if autoscan is disabled */
|
/* Initial kernel events processing may only be used if autoscan is disabled */
|
||||||
|
#if WITH_UDEV
|
||||||
if (!no_auto_scan && initial_kernel_events) {
|
if (!no_auto_scan && initial_kernel_events) {
|
||||||
g_warning ("error: --initial-kernel-events must be used only if --no-auto-scan is also used");
|
g_warning ("error: --initial-kernel-events must be used only if --no-auto-scan is also used");
|
||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,7 @@ AM_CFLAGS = \
|
|||||||
-I${top_srcdir}/src/ \
|
-I${top_srcdir}/src/ \
|
||||||
-I${top_builddir}/src/ \
|
-I${top_builddir}/src/ \
|
||||||
-I${top_srcdir}/src/kerneldevice \
|
-I${top_srcdir}/src/kerneldevice \
|
||||||
|
-DTESTUDEVRULESDIR=\"${top_srcdir}/src/\" \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
AM_LDFLAGS = \
|
AM_LDFLAGS = \
|
||||||
@@ -24,6 +25,7 @@ AM_LDFLAGS = \
|
|||||||
$(CODE_COVERAGE_LDFLAGS) \
|
$(CODE_COVERAGE_LDFLAGS) \
|
||||||
$(top_builddir)/src/libhelpers.la \
|
$(top_builddir)/src/libhelpers.la \
|
||||||
$(top_builddir)/src/libport.la \
|
$(top_builddir)/src/libport.la \
|
||||||
|
$(top_builddir)/src/libkerneldevice.la \
|
||||||
-lutil \
|
-lutil \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ endif
|
|||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# tests
|
# tests
|
||||||
# note: we abuse AM_LDFLAGS to include libhelpers and libport.
|
# note: we abuse AM_LDFLAGS to include the libraries being tested
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
noinst_PROGRAMS = \
|
noinst_PROGRAMS = \
|
||||||
@@ -49,6 +51,7 @@ noinst_PROGRAMS = \
|
|||||||
test-at-serial-port \
|
test-at-serial-port \
|
||||||
test-sms-part-3gpp \
|
test-sms-part-3gpp \
|
||||||
test-sms-part-cdma \
|
test-sms-part-cdma \
|
||||||
|
test-udev-rules \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
if WITH_QMI
|
if WITH_QMI
|
||||||
|
79
src/tests/test-udev-rules.c
Normal file
79
src/tests/test-udev-rules.c
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/* -*- 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 Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
#define _LIBMM_INSIDE_MM
|
||||||
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
|
/* Define symbol to enable test message traces */
|
||||||
|
#undef ENABLE_TEST_MESSAGE_TRACES
|
||||||
|
|
||||||
|
#include "mm-kernel-device-generic-rules.h"
|
||||||
|
#include "mm-log.h"
|
||||||
|
|
||||||
|
/************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_load_cleanup_core (void)
|
||||||
|
{
|
||||||
|
GArray *rules;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rules = mm_kernel_device_generic_rules_load (TESTUDEVRULESDIR, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert (rules);
|
||||||
|
g_assert (rules->len > 0);
|
||||||
|
|
||||||
|
g_array_unref (rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************/
|
||||||
|
|
||||||
|
void
|
||||||
|
_mm_log (const char *loc,
|
||||||
|
const char *func,
|
||||||
|
guint32 level,
|
||||||
|
const char *fmt,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
#if defined ENABLE_TEST_MESSAGE_TRACES
|
||||||
|
/* Dummy log function */
|
||||||
|
va_list args;
|
||||||
|
gchar *msg;
|
||||||
|
|
||||||
|
va_start (args, fmt);
|
||||||
|
msg = g_strdup_vprintf (fmt, args);
|
||||||
|
va_end (args);
|
||||||
|
g_print ("%s\n", msg);
|
||||||
|
g_free (msg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
setlocale (LC_ALL, "");
|
||||||
|
|
||||||
|
g_type_init ();
|
||||||
|
g_test_init (&argc, &argv, NULL);
|
||||||
|
|
||||||
|
g_test_add_func ("/MM/test-udev-rules/load-cleanup-core", test_load_cleanup_core);
|
||||||
|
|
||||||
|
return g_test_run ();
|
||||||
|
}
|
@@ -6,12 +6,16 @@ EXTRA_DIST =
|
|||||||
# lsudev
|
# lsudev
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
if WITH_UDEV
|
||||||
|
|
||||||
noinst_PROGRAMS += lsudev
|
noinst_PROGRAMS += lsudev
|
||||||
|
|
||||||
lsudev_SOURCES = lsudev.c
|
lsudev_SOURCES = lsudev.c
|
||||||
lsudev_CPPFLAGS = $(GUDEV_CFLAGS)
|
lsudev_CPPFLAGS = $(GUDEV_CFLAGS)
|
||||||
lsudev_LDADD = $(GUDEV_LIBS)
|
lsudev_LDADD = $(GUDEV_LIBS)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# mmtty
|
# mmtty
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -37,6 +41,31 @@ mmtty_LDADD = \
|
|||||||
$(top_builddir)/src/libport.la \
|
$(top_builddir)/src/libport.la \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# mmrules
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
noinst_PROGRAMS += mmrules
|
||||||
|
|
||||||
|
mmrules_SOURCES = mmrules.c
|
||||||
|
|
||||||
|
mmrules_CPPFLAGS = \
|
||||||
|
$(MM_CFLAGS) \
|
||||||
|
-I$(top_srcdir) \
|
||||||
|
-I$(top_srcdir)/src \
|
||||||
|
-I$(top_srcdir)/src/kerneldevice \
|
||||||
|
-I$(top_srcdir)/include \
|
||||||
|
-I$(top_builddir)/include \
|
||||||
|
-I$(top_srcdir)/libmm-glib \
|
||||||
|
-I$(top_srcdir)/libmm-glib/generated \
|
||||||
|
-I$(top_builddir)/libmm-glib/generated
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
mmrules_LDADD = \
|
||||||
|
$(MM_LIBS) \
|
||||||
|
$(top_builddir)/src/libkerneldevice.la \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# mmcli-test-sms
|
# mmcli-test-sms
|
||||||
################################################################################
|
################################################################################
|
||||||
|
173
test/mmrules.c
Normal file
173
test/mmrules.c
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include <mm-log.h>
|
||||||
|
#include <mm-kernel-device-generic-rules.h>
|
||||||
|
|
||||||
|
#define PROGRAM_NAME "mmrules"
|
||||||
|
#define PROGRAM_VERSION PACKAGE_VERSION
|
||||||
|
|
||||||
|
/* Context */
|
||||||
|
static gchar *path;
|
||||||
|
static gboolean verbose_flag;
|
||||||
|
static gboolean version_flag;
|
||||||
|
|
||||||
|
static GOptionEntry main_entries[] = {
|
||||||
|
{ "path", 'p', 0, G_OPTION_ARG_FILENAME, &path,
|
||||||
|
"Specify path to udev rules directory",
|
||||||
|
"[PATH]"
|
||||||
|
},
|
||||||
|
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag,
|
||||||
|
"Run action with verbose logs",
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
{ "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag,
|
||||||
|
"Print version",
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
_mm_log (const char *loc,
|
||||||
|
const char *func,
|
||||||
|
guint32 level,
|
||||||
|
const char *fmt,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
gchar *msg;
|
||||||
|
|
||||||
|
if (!verbose_flag)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start (args, fmt);
|
||||||
|
msg = g_strdup_vprintf (fmt, args);
|
||||||
|
va_end (args);
|
||||||
|
g_print ("%s\n", msg);
|
||||||
|
g_free (msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_version_and_exit (void)
|
||||||
|
{
|
||||||
|
g_print ("\n"
|
||||||
|
PROGRAM_NAME " " PROGRAM_VERSION "\n"
|
||||||
|
"Copyright (2015) Aleksander Morgado\n"
|
||||||
|
"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\n"
|
||||||
|
"This is free software: you are free to change and redistribute it.\n"
|
||||||
|
"There is NO WARRANTY, to the extent permitted by law.\n"
|
||||||
|
"\n");
|
||||||
|
exit (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_rule (MMUdevRule *rule)
|
||||||
|
{
|
||||||
|
/* Process conditions */
|
||||||
|
if (rule->conditions) {
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0; i < rule->conditions->len; i++) {
|
||||||
|
MMUdevRuleMatch *rule_match;
|
||||||
|
|
||||||
|
rule_match = &g_array_index (rule->conditions, MMUdevRuleMatch, i);
|
||||||
|
switch (rule_match->type) {
|
||||||
|
case MM_UDEV_RULE_MATCH_TYPE_EQUAL:
|
||||||
|
g_print (" [condition %u] %s == %s\n",
|
||||||
|
i, rule_match->parameter, rule_match->value);
|
||||||
|
break;
|
||||||
|
case MM_UDEV_RULE_MATCH_TYPE_NOT_EQUAL:
|
||||||
|
g_print (" [condition %u] %s != %s\n",
|
||||||
|
i, rule_match->parameter, rule_match->value);
|
||||||
|
break;
|
||||||
|
case MM_UDEV_RULE_MATCH_TYPE_UNKNOWN:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process result */
|
||||||
|
switch (rule->result.type) {
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_LABEL:
|
||||||
|
g_print (" [result] label %s\n", rule->result.content.tag);
|
||||||
|
break;
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX:
|
||||||
|
g_print (" [result] jump to rule %u\n", rule->result.content.index);
|
||||||
|
break;
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_PROPERTY:
|
||||||
|
g_print (" [result] set property %s = %s\n",
|
||||||
|
rule->result.content.property.name, rule->result.content.property.value);
|
||||||
|
break;
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG:
|
||||||
|
case MM_UDEV_RULE_RESULT_TYPE_UNKNOWN:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
GOptionContext *context;
|
||||||
|
GArray *rules;
|
||||||
|
guint i;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
setlocale (LC_ALL, "");
|
||||||
|
|
||||||
|
g_type_init ();
|
||||||
|
|
||||||
|
/* Setup option context, process it and destroy it */
|
||||||
|
context = g_option_context_new ("- ModemManager udev rules testing");
|
||||||
|
g_option_context_add_main_entries (context, main_entries, NULL);
|
||||||
|
g_option_context_parse (context, &argc, &argv, NULL);
|
||||||
|
g_option_context_free (context);
|
||||||
|
|
||||||
|
if (version_flag)
|
||||||
|
print_version_and_exit ();
|
||||||
|
|
||||||
|
/* No device path given? */
|
||||||
|
if (!path) {
|
||||||
|
g_printerr ("error: no path specified\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load rules from directory */
|
||||||
|
rules = mm_kernel_device_generic_rules_load (path, &error);
|
||||||
|
if (!rules) {
|
||||||
|
g_printerr ("error: couldn't load rules: %s", error->message);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print loaded rules */
|
||||||
|
for (i = 0; i < rules->len; i++) {
|
||||||
|
g_print ("-----------------------------------------\n");
|
||||||
|
g_print ("rule [%u]:\n", i);
|
||||||
|
print_rule (&g_array_index (rules, MMUdevRule, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_unref (rules);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
Reference in New Issue
Block a user