NetworkManager/shared/nm-udev-aux/nm-udev-utils.c
Thomas Haller 97ed143e04
udev/trivial: rename nm_udev_client_unref() to nm_udev_client_destory()
NMUdevClient does not actually implement ref-counting, because it's not
used. Still, the destroy function was named nm_udev_client_unref(),
because theoretically then we could later, as the need arises, make
the type ref-counted. Then unref function already had the right name.

However, NMUdevClient also has a callback function that emits monitor
events. Again for simplicity, this callback function cannot be reset, it
can only be set once (in the constructor) and can also not be unset nor
disabled.

When the user of NMUdevClient is done with the instance and calls
"unref", then it must be sure that the callback is no longer invoked
afterwards. In practice that is already the case, but "unref" makes it
sound as if somebody else could also still hold a reference -- in which
case the user would have to first unset/disable the callback.

Rename the function to "destroy()", so that it's clear that the instance
is gone afterwards and that the callback will not be invoked anymore.
2021-02-03 14:58:00 +01:00

265 lines
6.8 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2017 Red Hat, Inc.
*/
#include "nm-default.h"
#include "nm-udev-utils.h"
#include <libudev.h>
struct _NMPUdevClient {
char ** subsystems;
GSource * watch_source;
struct udev * udev;
struct udev_monitor *monitor;
NMUdevClientEvent event_handler;
gpointer event_user_data;
};
/*****************************************************************************/
gboolean
nm_udev_utils_property_as_boolean(const char *uproperty)
{
/* taken from g_udev_device_get_property_as_boolean() */
if (uproperty) {
if (strcmp(uproperty, "1") == 0 || g_ascii_strcasecmp(uproperty, "true") == 0)
return TRUE;
}
return FALSE;
}
const char *
nm_udev_utils_property_decode(const char *uproperty, char **to_free)
{
const char *p;
char * unescaped = NULL;
char * n = NULL;
if (!uproperty) {
*to_free = NULL;
return NULL;
}
p = uproperty;
while (*p) {
int a, b;
if (p[0] == '\\' && p[1] == 'x' && (a = g_ascii_xdigit_value(p[2])) >= 0
&& (b = g_ascii_xdigit_value(p[3])) >= 0 && (a || b)) {
if (!n) {
gssize l = p - uproperty;
unescaped = g_malloc(l + strlen(p) + 1 - 3);
memcpy(unescaped, uproperty, l);
n = &unescaped[l];
}
*n++ = (a << 4) | b;
p += 4;
} else {
if (n)
*n++ = *p;
p++;
}
}
if (!n) {
*to_free = NULL;
return uproperty;
}
*n++ = '\0';
return (*to_free = unescaped);
}
char *
nm_udev_utils_property_decode_cp(const char *uproperty)
{
char *cpy;
uproperty = nm_udev_utils_property_decode(uproperty, &cpy);
return cpy ?: g_strdup(uproperty);
}
/*****************************************************************************/
static void
_subsystem_split(const char * subsystem_full,
const char **out_subsystem,
const char **out_devtype,
char ** to_free)
{
char *tmp, *s;
nm_assert(subsystem_full);
nm_assert(out_subsystem);
nm_assert(out_devtype);
nm_assert(to_free);
s = strstr(subsystem_full, "/");
if (s) {
tmp = g_strdup(subsystem_full);
s = &tmp[s - subsystem_full];
*s = '\0';
*out_subsystem = tmp;
*out_devtype = &s[1];
*to_free = tmp;
} else {
*out_subsystem = subsystem_full;
*out_devtype = NULL;
*to_free = NULL;
}
}
static struct udev_enumerate *
nm_udev_utils_enumerate(struct udev *uclient, const char *const *subsystems)
{
struct udev_enumerate *enumerate;
guint n;
enumerate = udev_enumerate_new(uclient);
if (subsystems) {
for (n = 0; subsystems[n]; n++) {
const char * subsystem;
const char * devtype;
gs_free char *to_free = NULL;
_subsystem_split(subsystems[n], &subsystem, &devtype, &to_free);
udev_enumerate_add_match_subsystem(enumerate, subsystem);
if (devtype != NULL)
udev_enumerate_add_match_property(enumerate, "DEVTYPE", devtype);
}
}
return enumerate;
}
struct udev *
nm_udev_client_get_udev(NMUdevClient *self)
{
g_return_val_if_fail(self, NULL);
return self->udev;
}
struct udev_enumerate *
nm_udev_client_enumerate_new(NMUdevClient *self)
{
g_return_val_if_fail(self, NULL);
return nm_udev_utils_enumerate(self->udev, (const char *const *) self->subsystems);
}
/*****************************************************************************/
static gboolean
monitor_event(int fd, GIOCondition condition, gpointer user_data)
{
NMUdevClient * self = user_data;
struct udev_device *udevice;
if (!self->monitor)
goto out;
udevice = udev_monitor_receive_device(self->monitor);
if (udevice == NULL)
goto out;
self->event_handler(self, udevice, self->event_user_data);
udev_device_unref(udevice);
out:
return TRUE;
}
/**
* nm_udev_client_new:
* @subsystems: the subsystems
* @event_handler: callback for events
* @event_user_data: user-data for @event_handler
*
* Basically, it is g_udev_client_new(), and most notably
* g_udev_client_constructed().
*
* Returns: a new NMUdevClient instance.
*/
NMUdevClient *
nm_udev_client_new(const char *const *subsystems,
NMUdevClientEvent event_handler,
gpointer event_user_data)
{
NMUdevClient *self;
guint n;
self = g_slice_new0(NMUdevClient);
self->event_handler = event_handler;
self->event_user_data = event_user_data;
self->subsystems = subsystems && subsystems[0] ? g_strdupv((char **) subsystems) : NULL;
self->udev = udev_new();
if (!self->udev)
goto fail;
/* connect to event source */
if (self->event_handler) {
self->monitor = udev_monitor_new_from_netlink(self->udev, "udev");
if (!self->monitor)
goto fail;
if (self->subsystems) {
/* install subsystem filters to only wake up for certain events */
for (n = 0; self->subsystems[n]; n++) {
gs_free char *to_free = NULL;
const char * subsystem;
const char * devtype;
_subsystem_split(self->subsystems[n], &subsystem, &devtype, &to_free);
udev_monitor_filter_add_match_subsystem_devtype(self->monitor, subsystem, devtype);
}
/* listen to events, and buffer them */
udev_monitor_set_receive_buffer_size(self->monitor, 4 * 1024 * 1024);
udev_monitor_enable_receiving(self->monitor);
self->watch_source = nm_g_unix_fd_source_new(udev_monitor_get_fd(self->monitor),
G_IO_IN,
G_PRIORITY_DEFAULT,
monitor_event,
self,
NULL);
g_source_attach(self->watch_source, g_main_context_get_thread_default());
}
}
return self;
fail:
return nm_udev_client_destroy(self);
}
NMUdevClient *
nm_udev_client_destroy(NMUdevClient *self)
{
if (!self)
return NULL;
nm_clear_g_source_inst(&self->watch_source);
udev_monitor_unref(self->monitor);
self->monitor = NULL;
udev_unref(self->udev);
self->udev = NULL;
g_strfreev(self->subsystems);
g_slice_free(NMUdevClient, self);
return NULL;
}