mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-27 02:20:12 +01:00
>>>
H0=a3e75f329446a93a61ca4c458a7657bd919f4fe6
commit_has_file() {
git ls-tree -r "$1" | grep -q "\\s$2"\$
}
print_commit_authors() {
git --no-pager log --full-history --follow --no-merges --pretty='format:<%ae>' $H0 -- "$1" | sort | uniq
}
print_blame_authors() {
local LAST_H
if commit_has_file $H0 "$1"; then
LAST_H=$H0
else
LAST_H="$(git log --full-history --no-merges -n1 --pretty='format:%H' $H0 -- "$1")"^1
fi
git blame --no-progress -C -C -C20 -M -M10 -e "$LAST_H" -- "$1" | sed 's/.*\(<[^>]\+@[^>]\+>\).*/\1/' | sort | uniq
}
print_grep() {
git --no-pager log -p --full-history --follow $H0 -- "$1" | grep -i '[a-z0-9]@\|author\|copyright' | sort | uniq
}
prefix() {
sed "s/^/>>>$1 /"
}
collect_all() {
for F; do
print_commit_authors "$F" | prefix 1
echo
print_blame_authors "$F" | prefix 2
echo
print_grep "$F" | prefix 3
done |
sort |
uniq |
sed 's/@/(at)/'
}
collect_all \
shared/nm-udev-aux/nm-udev-utils.c \
shared/nm-udev-aux/nm-udev-utils.h \
shared/nm-utils/nm-udev-utils.c \
shared/nm-utils/nm-udev-utils.h \
;
<<<
gives:
>>>1 <bgalvani(at)redhat.com>
>>>1 <lkundrak(at)v3.sk>
>>>1 <thaller(at)redhat.com>
>>>2 <bgalvani(at)redhat.com>
>>>2 <dcbw(at)redhat.com>
>>>2 <rstrode(at)redhat.com>
>>>2 <thaller(at)redhat.com>
>>>3 [...] Red Hat, Inc.
Everybody in this list agreed to relicensing according to RELICENSE.md.
The code was initially added by commit e32839838e ('udev: drop
libgudev in favor of libudev'). It did expand on top of existing GPL
code, which is problematic. But it was introduced as an original
contribution.
265 lines
6.8 KiB
C
265 lines
6.8 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
/*
|
|
* 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_unref(self);
|
|
}
|
|
|
|
NMUdevClient *
|
|
nm_udev_client_unref(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;
|
|
}
|