1
0
Fork 0

Merge remote-tracking branch 'origin/third-party-review'

This commit is contained in:
pivaf 2025-05-16 20:30:56 +00:00
commit 33b1977416
21 changed files with 2325 additions and 0 deletions

View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
make

126
.github/workflows/codeql.yml vendored Normal file
View file

@ -0,0 +1,126 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
# push:
# branches: [ "main", "master" ]
schedule:
- cron: '0 0 * * *'
pull_request:
branches: '*'
jobs:
analyze:
name: Analyze
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners
# Consider using larger runners for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-20.04' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: recursive
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
queries: security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
#- name: Autobuild
# uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
- run: |
./.github/workflows/codeql-buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
upload: false
id: step1
# Filter out rules with low severity or high false positve rate
# Also filter out warnings in third-party code
- name: Filter out unwanted errors and warnings
uses: advanced-security/filter-sarif@v1
with:
patterns: |
-**:cpp/path-injection
-**:cpp/world-writable-file-creation
-**:cpp/poorly-documented-function
-**:cpp/potentially-dangerous-function
-**:cpp/use-of-goto
-**:cpp/integer-multiplication-cast-to-long
-**:cpp/comparison-with-wider-type
-**:cpp/leap-year/*
-**:cpp/ambiguously-signed-bit-field
-**:cpp/suspicious-pointer-scaling
-**:cpp/suspicious-pointer-scaling-void
-**:cpp/unsigned-comparison-zero
-**/cmake*/Modules/**
input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
- name: Upload CodeQL results to code scanning
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: ${{ steps.step1.outputs.sarif-output }}
category: "/language:${{matrix.language}}"
- name: Upload CodeQL results as an artifact
if: success() || failure()
uses: actions/upload-artifact@v3
with:
name: codeql-results
path: ${{ steps.step1.outputs.sarif-output }}
retention-days: 5
- name: Fail if an error is found
run: |
./.github/workflows/fail_on_error.py \
${{ steps.step1.outputs.sarif-output }}/cpp.sarif

34
.github/workflows/fail_on_error.py vendored Executable file
View file

@ -0,0 +1,34 @@
#!/usr/bin/env python3
import json
import sys
# Return whether SARIF file contains error-level results
def codeql_sarif_contain_error(filename):
with open(filename, 'r') as f:
s = json.load(f)
for run in s.get('runs', []):
rules_metadata = run['tool']['driver']['rules']
if not rules_metadata:
rules_metadata = run['tool']['extensions'][0]['rules']
for res in run.get('results', []):
if 'ruleIndex' in res:
rule_index = res['ruleIndex']
elif 'rule' in res and 'index' in res['rule']:
rule_index = res['rule']['index']
else:
continue
try:
rule_level = rules_metadata[rule_index]['defaultConfiguration']['level']
except IndexError as e:
print(e, rule_index, len(rules_metadata))
else:
if rule_level == 'error':
return True
return False
if __name__ == "__main__":
if codeql_sarif_contain_error(sys.argv[1]):
sys.exit(1)

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
*.o
*.a
*.so
*.so.*
*.pc

49
Android.bp Normal file
View file

@ -0,0 +1,49 @@
package {
default_applicable_licenses: ["external_libudev_zero_license"],
}
license {
name: "external_libudev_zero_license",
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-ISC",
],
license_text: [
"LICENSE",
],
}
cc_genrule {
name: "libudev_zero_copy_headers",
srcs: ["udev.h"],
out: ["libudev.h"],
cmd: "cp $(in) $(out)",
}
cc_library_static {
name: "libudev-zero",
visibility: [
"//external/igt-gpu-tools",
],
srcs: [
"udev.c",
"udev_list.c",
"udev_device.c",
"udev_monitor.c",
"udev_enumerate.c",
],
generated_headers: [
"libudev_zero_copy_headers",
],
export_generated_headers: [
"libudev_zero_copy_headers",
],
cflags: [
"-Wall",
"-Wextra",
"-Wpedantic",
"-Wmissing-prototypes",
"-Wstrict-prototypes",
"-Wno-unused-parameter",
],
}

15
LICENSE Normal file
View file

@ -0,0 +1,15 @@
ISC License
Copyright (c) 2020-2021 illiliti <illiliti@protonmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

16
METADATA Normal file
View file

@ -0,0 +1,16 @@
name: "libudev-zero"
description:
"Drop-in replacement for libudev intended to work with any device manager"
third_party {
homepage: "https://github.com/illiliti/libudev-zero"
identifier {
type: "Git"
value: "https://github.com/illiliti/libudev-zero"
primary_source: true
version: "bbeb7ad51c1edb7ab3cf63f30a21e9bb383b7994"
}
version: "bbeb7ad51c1edb7ab3cf63f30a21e9bb383b7994"
last_upgrade_date { year: 2025 month: 4 day: 28 }
license_type: NOTICE
}

0
MODULE_LICENSE_ISC Normal file
View file

79
Makefile Normal file
View file

@ -0,0 +1,79 @@
# SPDX-License-Identifier: ISC
.POSIX:
PREFIX = /usr/local
LIBDIR = ${PREFIX}/lib
INCLUDEDIR = ${PREFIX}/include
PKGCONFIGDIR = ${LIBDIR}/pkgconfig
XCFLAGS = ${CPPFLAGS} ${CFLAGS} -std=c99 -fPIC -D_XOPEN_SOURCE=700 \
-Wall -Wextra -Wpedantic -Wmissing-prototypes -Wstrict-prototypes \
-Wno-unused-parameter
XLDFLAGS = ${LDFLAGS} -shared -Wl,-soname,libudev.so.1
XARFLAGS = -rc
AR = ar
OBJ = \
udev.o \
udev_list.o \
udev_device.o \
udev_monitor.o \
udev_enumerate.o
all: libudev.so.1 libudev.a
.c.o:
${CC} ${XCFLAGS} -c -o $@ $<
libudev.a: ${OBJ}
${AR} ${XARFLAGS} $@ ${OBJ}
libudev.so.1: ${OBJ}
${CC} ${XCFLAGS} -o $@ ${OBJ} ${XLDFLAGS}
libudev.pc: libudev.pc.in
libdir="${LIBDIR}"; \
if [ "$${libdir#${PREFIX}}" != "$$libdir" ]; then \
libdir="\$${exec_prefix}$${libdir#${PREFIX}}"; \
fi; \
includedir="${INCLUDEDIR}"; \
if [ "$${includedir#${PREFIX}}" != "$$includedir" ]; then \
includedir="\$${prefix}$${includedir#${PREFIX}}"; \
fi; \
sed -e 's|@prefix@|${PREFIX}|g' \
-e 's|@exec_prefix@|${PREFIX}|g' \
-e "s|@libdir@|$$libdir|g" \
-e "s|@includedir@|$$includedir|g" \
-e 's|@VERSION@|251|g' \
libudev.pc.in > libudev.pc
install-headers: udev.h
mkdir -p ${DESTDIR}${INCLUDEDIR}
cp -f udev.h ${DESTDIR}${INCLUDEDIR}/libudev.h
install-pkgconfig: libudev.pc
mkdir -p ${DESTDIR}${PKGCONFIGDIR}
cp -f libudev.pc ${DESTDIR}${PKGCONFIGDIR}/libudev.pc
install-shared: libudev.so.1 install-headers install-pkgconfig
mkdir -p ${DESTDIR}${LIBDIR}
cp -f libudev.so.1 ${DESTDIR}${LIBDIR}/libudev.so.1
ln -fs libudev.so.1 ${DESTDIR}${LIBDIR}/libudev.so
install-static: libudev.a install-headers install-pkgconfig
mkdir -p ${DESTDIR}${LIBDIR}
cp -f libudev.a ${DESTDIR}${LIBDIR}/libudev.a
install: install-shared install-static
uninstall:
rm -f ${DESTDIR}${LIBDIR}/libudev.a \
${DESTDIR}${LIBDIR}/libudev.so \
${DESTDIR}${LIBDIR}/libudev.so.1 \
${DESTDIR}${PKGCONFIGDIR}/libudev.pc \
${DESTDIR}${INCLUDEDIR}/libudev.h
clean:
rm -f libudev.so.1 libudev.a libudev.pc ${OBJ}
.PHONY: all clean install-headers install-pkgconfig install-shared \
install-static install uninstall

1
OWNERS Normal file
View file

@ -0,0 +1 @@
include platform/system/core:main:/janitors/OWNERS

100
README.md Normal file
View file

@ -0,0 +1,100 @@
# libudev-zero
Drop-in replacement for `libudev` intended to work with any device manager
## Why?
We all know that systemd is very hostile towards portability. udev inherited
the same problem by exposing a very badly designed library interface. This
dramatically reduces portability, user choice and basically creates vendor
lock-in because the library interface highly tied to the udev daemon.
Another udev problem is the non-portable home-grown language called "udev
rules". The udev authors definitely don't know(or care) why it's better to
avoid reinventing the wheel. Strictly speaking, I think they did that on
purpose to overcomplicate udev as much as possible. Why? So that only the
authors(RedHat/IBM) can rule and dictate the future of udev. The recent eudev
death only proves that it's really hard to support such unmaintainable mess.
The udev hwdb is yet another illustration of a systemd-like approach. What the
hell does "userspace /dev" have to do with parsing hardware database(pci.ids,
usb.ids) and setting/remapping buttons? udev smells like systemd by trying to
implement all possible functionality in the single daemon/code base. Programs
that follow the UNIX philosophy is much better suited for such purposes.
## Pros/Cons
Keep in mind that libudev-zero isn't ideal. Here are some pros/cons:
### Pros
* Very portable. Doesn't depend on GNU features.
* No lock-in. Any device manager can be used, even smdev and CONFIG_UEVENT_HELPER.
* Source code is much cleaner than udev because of less abstractions and clever code.
### Cons
* Udev rules must be converted to shell script in order to work with any device manager.
* Udev hwdb interface isn't implemented. pciutils and usbutils will not display any meaningful info.
* Many functions and interfaces still aren't implemented, which may lead to breakage in some programs.
## What doesn't work
* dosfstools - requires udev_enumerate_add_match_parent()
* PulseAudio - highly depends on udev internal properties. [workaround](https://gist.github.com/capezotte/03ee5548218e819b06459819bb120b4b#pulseaudio)
* udisks2 - highly depends on udev internal properties
* android-tools - requires udev rules for non-root usage
* NetworkManager - needs investigation
* libgudev - needs investigation
* PipeWire - depends on udev internal properties. [patch](https://github.com/illiliti/libudev-zero/issues/26#issuecomment-1848802791)
* ldm - depends on udev internal properties
* lvm2 - uses deprecated `udev_queue` API
* cups - needs investigation
* ???
## Dependencies
* C99 compiler (build time)
* POSIX make (build time)
* POSIX & XSI libc
* Linux >= 2.6.39
## Installation
```sh
make
make PREFIX=/usr install
```
## Hotplugging
Note that hotplugging support is fully optional. You can skip
this step if you don't have a need for the hotplugging capability.
If you're using an mdev-like device manager, refer to [mdev.conf](contrib/mdev.conf)
for a config example.
If you're using another device manager, you need to configure it to rebroadcast
kernel uevents. You can do this by either patching(see below) the device manager
or simply executing [helper.c](contrib/helper.c) for each uevent.
If you're developing your own device manager, you need to rebroadcast kernel
uevents to the `0x4` netlink group of `NETLINK_KOBJECT_UEVENT`. This is required
because libudev-zero can't simply listen to kernel uevents due to potential
race conditions. Refer to (but don't copy blindly) [helper.c](contrib/helper.c)
for an example of how it could be implemented in C.
Don't hesitate to ask me about anything you don't understand. I'm usually hanging
around in #kisslinux at libera.chat, but you can also email me or open an issue here.
## Future directions
1. Write a better cross-platform(*nix, maybe macos and windows) device enumeration library.
2. Convince mainstream apps(libinput, wlroots, ...) to use a new library instead of libudev.
3. Declare libudev as obsolete library and archive this project.
## Donate
You can send a donation to `BTC: 1BwrcsgtWZeLVvNeEQSg4A28a3yrGN3FpK`
Thank you very much!

80
contrib/helper.c Normal file
View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2020-2021 illiliti <illiliti@protonmail.com>
* SPDX-License-Identifier: ISC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*
* Construct uevent message from environment and send it to 0x4 netlink group.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
int main(int argc, char **argv)
{
struct sockaddr_nl sa = {0};
struct msghdr hdr = {0};
struct iovec iov = {0};
extern char **environ;
char buf[8192];
size_t len;
int i, fd;
iov.iov_base = buf;
iov.iov_len = 0;
for (i = 0; environ[i]; i++) {
if (strncmp(environ[i], "PATH=", 5) == 0 ||
strncmp(environ[i], "HOME=", 5) == 0) {
continue;
}
len = strlen(environ[i]) + 1;
if (iov.iov_len + len > sizeof(buf)) {
fprintf(stderr, "%s: uevent exceeds buffer size", argv[0]);
return 1;
}
memcpy(buf + iov.iov_len, environ[i], len);
iov.iov_len += len;
}
sa.nl_family = AF_NETLINK;
sa.nl_groups = 0x4; // XXX
hdr.msg_name = &sa;
hdr.msg_namelen = sizeof(sa);
hdr.msg_iov = &iov;
hdr.msg_iovlen = 1;
fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (fd == -1) {
perror("socket");
return 1;
}
if (sendmsg(fd, &hdr, 0) == -1) {
perror("sendmsg");
close(fd);
return 1;
}
close(fd);
return 0;
}

16
contrib/mdev.conf Normal file
View file

@ -0,0 +1,16 @@
# example rules for mdev.conf
#
# NOTE:
# since https://github.com/skarnet/mdevd/commit/b61ab0373a724d38e059655d561b5fb3dcb2aff6,
# mdevd can rebroadcast uevents natively which means that you don't need to add anything
# to /etc/mdev.conf. In order to enable this functionality, you need to add `-O 4` to
# mdevd arguments.
#
# NOTE: replace /path/to/helper with path to compiled binary of helper.c
# handle all uevents(not recommended)
#-.* root:root 660 */path/to/helper
# handle only drm and input uevents(recommended)
SUBSYSTEM=drm;.* root:video 660 */path/to/helper
SUBSYSTEM=input;.* root:input 660 */path/to/helper

12
libudev.pc.in Normal file
View file

@ -0,0 +1,12 @@
# SPDX-License-Identifier: ISC
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libudev
Description: Daemonless replacement for libudev
Version: @VERSION@
URL: https://github.com/illiliti/libudev-zero
Libs: -L${libdir} -ludev
Cflags: -I${includedir}

97
udev.c Normal file
View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2020-2021 illiliti <illiliti@protonmail.com>
* SPDX-License-Identifier: ISC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include "udev.h"
struct udev {
int refcount;
};
struct udev *udev_new(void)
{
struct udev *udev;
udev = calloc(1, sizeof(*udev));
if (!udev) {
return NULL;
}
udev->refcount = 1;
return udev;
}
struct udev *udev_ref(struct udev *udev)
{
if (!udev) {
return NULL;
}
udev->refcount++;
return udev;
}
struct udev *udev_unref(struct udev *udev)
{
if (!udev) {
return NULL;
}
if (--udev->refcount > 0) {
return udev;
}
free(udev);
return NULL;
}
void udev_set_log_fn(struct udev *udev, void (*log_fn)(struct udev *udev,
int priority, const char *file, int line, const char *fn,
const char *format, va_list args))
{
}
void udev_set_log_priority(struct udev *udev, int priority)
{
}
int udev_get_log_priority(struct udev *udev)
{
return 0;
}
/* XXX NOT IMPLEMENTED */ struct udev_hwdb *udev_hwdb_new(struct udev *udev)
{
return NULL;
}
/* XXX NOT IMPLEMENTED */ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb)
{
return NULL;
}
/* XXX NOT IMPLEMENTED */ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb)
{
return NULL;
}
/* XXX NOT IMPLEMENTED */ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags)
{
return NULL;
}

139
udev.h Normal file
View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2020-2021 illiliti <illiliti@protonmail.com>
* SPDX-License-Identifier: ISC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdarg.h>
#include <sys/types.h>
// we must keep this include here to fix some legacy programs
// https://freenode.logbot.info/kisslinux/20200722#c4467755
#include <sys/sysmacros.h>
#ifndef _LIBUDEV_H_
#define _LIBUDEV_H_
#ifdef __cplusplus
extern "C" {
#endif
#define udev_list_entry_foreach(list_entry, first_entry) \
for (list_entry = first_entry; list_entry; list_entry = udev_list_entry_get_next(list_entry))
struct udev;
struct udev_hwdb;
struct udev_device;
struct udev_monitor;
struct udev_enumerate;
struct udev_list_entry;
struct udev *udev_new(void);
struct udev *udev_ref(struct udev *udev);
struct udev *udev_unref(struct udev *udev);
void udev_set_log_fn(struct udev *udev, void (*log_fn)(struct udev *udev,
int priority, const char *file, int line, const char *fn,
const char *format, va_list args));
void udev_set_log_priority(struct udev *udev, int priority);
int udev_get_log_priority(struct udev *udev);
const char *udev_device_get_syspath(struct udev_device *udev_device);
const char *udev_device_get_sysname(struct udev_device *udev_device);
const char *udev_device_get_sysnum(struct udev_device *udev_device);
const char *udev_device_get_devpath(struct udev_device *udev_device);
const char *udev_device_get_devnode(struct udev_device *udev_device);
dev_t udev_device_get_devnum(struct udev_device *udev_device);
unsigned long long udev_device_get_seqnum(struct udev_device *udev_device);
unsigned long long udev_device_get_usec_since_initialized(struct udev_device *udev_device);
const char *udev_device_get_devtype(struct udev_device *udev_device);
const char *udev_device_get_subsystem(struct udev_device *udev_device);
const char *udev_device_get_driver(struct udev_device *udev_device);
struct udev *udev_device_get_udev(struct udev_device *udev_device);
struct udev_device *udev_device_get_parent(struct udev_device *udev_device);
struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype);
int udev_device_get_is_initialized(struct udev_device *udev_device);
const char *udev_device_get_action(struct udev_device *udev_device);
int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device);
struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device);
struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device);
struct udev_list_entry *udev_device_get_current_tags_list_entry(struct udev_device *udev_device);
struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device);
const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key);
const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr);
int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, const char *value);
struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath);
struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum);
struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname);
struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id);
struct udev_device *udev_device_new_from_environment(struct udev *udev);
struct udev_device *udev_device_ref(struct udev_device *udev_device);
struct udev_device *udev_device_unref(struct udev_device *udev_device);
int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem);
int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem);
int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value);
int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname);
int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag);
int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent);
int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate);
int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate);
int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate);
struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate);
int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath);
struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate);
struct udev_enumerate *udev_enumerate_new(struct udev *udev);
struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate);
struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate);
struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry);
struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name);
const char *udev_list_entry_get_name(struct udev_list_entry *list_entry);
const char *udev_list_entry_get_value(struct udev_list_entry *list_entry);
struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor);
int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor);
int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size);
int udev_monitor_get_fd(struct udev_monitor *udev_monitor);
struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor);
int udev_monitor_filter_update(struct udev_monitor *udev_monitor);
int udev_monitor_filter_remove(struct udev_monitor *udev_monitor);
int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype);
int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag);
struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name);
struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor);
struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor);
struct udev_hwdb *udev_hwdb_new(struct udev *udev);
struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb);
struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb);
struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags);
// this is "libudev-zero" extension. do not use if portability is concern
struct udev_device *udev_device_new_from_uevent(struct udev *udev, char *buf, size_t len);
#ifdef __cplusplus
}
#endif
#endif

757
udev_device.c Normal file
View file

@ -0,0 +1,757 @@
/*
* Copyright (c) 2020-2021 illiliti <illiliti@protonmail.com>
* SPDX-License-Identifier: ISC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/stat.h>
#include <linux/input.h>
#include "udev.h"
#include "udev_list.h"
#ifndef LINE_MAX
#define LINE_MAX 2048
#endif
#ifndef LONG_BIT
#define LONG_BIT (sizeof(unsigned long) * 8)
#endif
// https://kernel.org/doc/html/latest/input/ff.html#querying-device-capabilities
// https://kernel.org/doc/html/latest/input/input-programming.html#bits-to-longs-bit-word-bit-mask
#define BIT_WORD(x) ((x) / LONG_BIT)
#define BIT_MASK(x) (1UL << ((x) % LONG_BIT))
#define BITS_TO_LONGS(x) (((x) + LONG_BIT - 1) / LONG_BIT)
#ifndef INPUT_PROP_POINTING_STICK
#define INPUT_PROP_POINTING_STICK 0x05
#endif
#ifndef INPUT_PROP_ACCELEROMETER
#define INPUT_PROP_ACCELEROMETER 0x06
#endif
#ifndef INPUT_PROP_CNT
#define INPUT_PROP_CNT 0x20
#endif
struct udev_device {
struct udev_list_entry properties;
struct udev_list_entry sysattrs;
struct udev_device *parent;
struct udev *udev;
int refcount;
};
const char *udev_device_get_syspath(struct udev_device *udev_device)
{
return udev_device_get_property_value(udev_device, "SYSPATH");
}
const char *udev_device_get_sysname(struct udev_device *udev_device)
{
return udev_device_get_property_value(udev_device, "SYSNAME");
}
const char *udev_device_get_sysnum(struct udev_device *udev_device)
{
return udev_device_get_property_value(udev_device, "SYSNUM");
}
const char *udev_device_get_devpath(struct udev_device *udev_device)
{
return udev_device_get_property_value(udev_device, "DEVPATH");
}
const char *udev_device_get_devnode(struct udev_device *udev_device)
{
return udev_device_get_property_value(udev_device, "DEVNAME");
}
unsigned long long udev_device_get_seqnum(struct udev_device *udev_device)
{
const char *seqnum;
seqnum = udev_device_get_property_value(udev_device, "SEQNUM");
if (!seqnum) {
return 0;
}
return strtoull(seqnum, NULL, 10);
}
unsigned long long udev_device_get_usec_since_initialized(struct udev_device *udev_device)
{
return 0;
}
dev_t udev_device_get_devnum(struct udev_device *udev_device)
{
const char *major, *minor;
major = udev_device_get_property_value(udev_device, "MAJOR");
minor = udev_device_get_property_value(udev_device, "MINOR");
if (!major || !minor) {
return makedev(0, 0);
}
return makedev(atoi(major), atoi(minor));
}
const char *udev_device_get_devtype(struct udev_device *udev_device)
{
return udev_device_get_property_value(udev_device, "DEVTYPE");
}
const char *udev_device_get_subsystem(struct udev_device *udev_device)
{
return udev_device_get_property_value(udev_device, "SUBSYSTEM");
}
const char *udev_device_get_driver(struct udev_device *udev_device)
{
return udev_device_get_property_value(udev_device, "DRIVER");
}
struct udev *udev_device_get_udev(struct udev_device *udev_device)
{
return udev_device ? udev_device->udev : NULL;
}
struct udev_device *udev_device_get_parent(struct udev_device *udev_device)
{
char *pos, *path, *syspath;
if (!udev_device) {
return NULL;
}
if (udev_device->parent) {
return udev_device->parent;
}
syspath = strdup(udev_device_get_syspath(udev_device));
if (!syspath) {
return NULL;
}
path = syspath + 5;
do {
if ((pos = strrchr(path, '/'))) {
*pos = '\0';
}
else {
break;
}
udev_device->parent = udev_device_new_from_syspath(udev_device->udev, syspath);
}
while (!udev_device->parent);
free(syspath);
return udev_device->parent;
}
struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype)
{
const char *parent_subsystem, *parent_devtype;
struct udev_device *parent;
if (!udev_device || !subsystem) {
return NULL;
}
for (parent = udev_device_get_parent(udev_device); parent; parent = udev_device_get_parent(parent)) {
parent_subsystem = udev_device_get_subsystem(parent);
parent_devtype = udev_device_get_devtype(parent);
if (!parent_subsystem || strcmp(parent_subsystem, subsystem) != 0) {
continue;
}
if (!devtype || (parent_devtype && strcmp(parent_devtype, devtype) == 0)) {
return parent;
}
}
return NULL;
}
/* XXX NOT IMPLEMENTED */ int udev_device_get_is_initialized(struct udev_device *udev_device)
{
return 1;
}
const char *udev_device_get_action(struct udev_device *udev_device)
{
return udev_device_get_property_value(udev_device, "ACTION");
}
/* XXX NOT IMPLEMENTED */ int udev_device_has_tag(struct udev_device *udev_device, const char *tag)
{
return 0;
}
/* XXX NOT IMPLEMENTED */ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device)
{
return NULL;
}
struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device)
{
return udev_device ? udev_list_entry_get_next(&udev_device->properties) : NULL;
}
/* XXX NOT IMPLEMENTED */ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device)
{
return NULL;
}
/* XXX NOT IMPLEMENTED */ struct udev_list_entry *udev_device_get_current_tags_list_entry(struct udev_device *udev_device)
{
return NULL;
}
struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device)
{
return udev_device ? udev_list_entry_get_next(&udev_device->sysattrs) : NULL;
}
const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key)
{
if (!udev_device || !key) {
return NULL;
}
return udev_list_entry_get_value(udev_list_entry_get_by_name(&udev_device->properties, key));
}
const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr)
{
struct udev_list_entry *list_entry;
char data[4096], path[PATH_MAX];
struct stat st;
size_t len;
FILE *file;
if (!udev_device || !sysattr) {
return NULL;
}
list_entry = udev_list_entry_get_by_name(&udev_device->sysattrs, sysattr);
if (list_entry) {
return udev_list_entry_get_value(list_entry);
}
snprintf(path, sizeof(path), "%s/%s", udev_device_get_syspath(udev_device), sysattr);
file = fopen(path, "r");
if (!file) {
return NULL;
}
if (fstat(fileno(file), &st) != 0 || !S_ISREG(st.st_mode)) {
fclose(file);
return NULL;
}
// TODO dynamic allocation of data
len = fread(data, 1, sizeof(data) - 1, file);
if (len != sizeof(data) - 1 && ferror(file)) {
fclose(file);
return NULL;
}
fclose(file);
data[len] = '\0';
while (len-- > 0 && data[len] == '\n') {
data[len] = '\0';
}
list_entry = udev_list_entry_add(&udev_device->sysattrs, sysattr, data, 0);
return udev_list_entry_get_value(list_entry);
}
int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, const char *value)
{
char path[PATH_MAX];
struct stat st;
size_t len;
FILE *file;
if (!udev_device || !sysattr || !value) {
return -1;
}
snprintf(path, sizeof(path), "%s/%s", udev_device_get_syspath(udev_device), sysattr);
file = fopen(path, "w");
if (!file) {
return -1;
}
if (fstat(fileno(file), &st) != 0 || !S_ISREG(st.st_mode)) {
fclose(file);
return -1;
}
len = strlen(value);
if (fwrite(value, 1, len, file) != len) {
fclose(file);
return -1;
}
fclose(file);
udev_list_entry_add(&udev_device->sysattrs, sysattr, value, 1);
return 0;
}
static char *read_symlink(const char *syspath, const char *name)
{
char link[PATH_MAX], path[PATH_MAX];
ssize_t len;
snprintf(path, sizeof(path), "%s/%s", syspath, name);
len = readlink(path, link, sizeof(link));
if (len == -1) {
return NULL;
}
link[len] = '\0';
return strdup(strrchr(link, '/') + 1);
}
static int set_properties_from_uevent(struct udev_device *udev_device, const char *syspath)
{
char line[LINE_MAX], path[PATH_MAX + sizeof("/uevent")], devnode[PATH_MAX];
FILE *file;
char *pos;
snprintf(path, sizeof(path), "%s/uevent", syspath);
file = fopen(path, "r");
if (!file) {
return -1;
}
while (fgets(line, sizeof(line), file)) {
line[strlen(line) - 1] = '\0';
if (strncmp(line, "DEVNAME=", 8) == 0) {
snprintf(devnode, sizeof(devnode), "/dev/%s", line + 8);
udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 0);
}
else if ((pos = strchr(line, '='))) {
*pos = '\0';
udev_list_entry_add(&udev_device->properties, line, pos + 1, 0);
}
}
fclose(file);
return 0;
}
static void make_bit(unsigned long *arr, int cnt, const char *str)
{
size_t len;
int i;
if (!str) {
return;
}
for (i = 0, len = strlen(str); len > 0; len--) {
if (str[len] == ' ') {
arr[i++] = strtoul(str + len + 1, NULL, 16);
if (i == cnt) {
return;
}
}
}
arr[i] = strtoul(str, NULL, 16);
}
static int test_bit(unsigned long *arr, unsigned long bit)
{
return !!(arr[BIT_WORD(bit)] & BIT_MASK(bit));
}
static void set_properties_from_evdev(struct udev_device *udev_device)
{
// https://kernel.org/doc/html/latest/driver-api/input.html#c.input_dev
unsigned long prop_bits[BITS_TO_LONGS(INPUT_PROP_CNT)] = {0};
unsigned long abs_bits[BITS_TO_LONGS(ABS_CNT)] = {0};
unsigned long rel_bits[BITS_TO_LONGS(REL_CNT)] = {0};
unsigned long key_bits[BITS_TO_LONGS(KEY_CNT)] = {0};
unsigned long ev_bits[BITS_TO_LONGS(EV_CNT)] = {0};
struct udev_device *parent;
const char *subsystem;
unsigned long bit;
subsystem = udev_device_get_subsystem(udev_device);
if (!subsystem || strcmp(subsystem, "input") != 0) {
return;
}
parent = udev_device;
while (1) {
if (!parent) {
return;
}
if (udev_device_get_property_value(parent, "EV")) {
break;
}
parent = udev_device_get_parent_with_subsystem_devtype(parent, "input", NULL);
}
make_bit(ev_bits, sizeof(ev_bits) / sizeof(ev_bits[0]), udev_device_get_property_value(parent, "EV"));
make_bit(abs_bits, sizeof(abs_bits) / sizeof(abs_bits[0]), udev_device_get_property_value(parent, "ABS"));
make_bit(rel_bits, sizeof(rel_bits) / sizeof(rel_bits[0]), udev_device_get_property_value(parent, "REL"));
make_bit(key_bits, sizeof(key_bits) / sizeof(key_bits[0]), udev_device_get_property_value(parent, "KEY"));
make_bit(prop_bits, sizeof(prop_bits) / sizeof(prop_bits[0]), udev_device_get_property_value(parent, "PROP"));
udev_list_entry_add(&udev_device->properties, "ID_INPUT", "1", 0);
if (test_bit(prop_bits, INPUT_PROP_POINTING_STICK)) {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_POINTINGSTICK", "1", 0);
}
if (test_bit(prop_bits, INPUT_PROP_ACCELEROMETER)) {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_ACCELEROMETER", "1", 0);
}
if (test_bit(ev_bits, EV_SW)) {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_SWITCH", "1", 0);
}
if (test_bit(ev_bits, EV_ABS)) {
if (test_bit(key_bits, BTN_SELECT) || test_bit(key_bits, BTN_TR) ||
test_bit(key_bits, BTN_START) || test_bit(key_bits, BTN_TL)) {
if (test_bit(key_bits, BTN_TOUCH)) {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_TOUCHSCREEN", "1", 0);
}
else {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_JOYSTICK", "1", 0);
}
}
else if (test_bit(abs_bits, ABS_Y) && test_bit(abs_bits, ABS_X)) {
if (test_bit(abs_bits, ABS_Z) && !test_bit(ev_bits, EV_KEY)) {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_ACCELEROMETER", "1", 0);
}
else if (test_bit(key_bits, BTN_STYLUS) || test_bit(key_bits, BTN_TOOL_PEN)) {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_TABLET", "1", 0);
}
else if (test_bit(key_bits, BTN_TOUCH)) {
if (test_bit(key_bits, BTN_TOOL_FINGER)) {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_TOUCHPAD", "1", 0);
}
else {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_TOUCHSCREEN", "1", 0);
}
}
else if (test_bit(key_bits, BTN_MOUSE)) {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_MOUSE", "1", 0);
}
}
}
else if (test_bit(ev_bits, EV_REL)) {
if (test_bit(rel_bits, REL_Y) && test_bit(rel_bits, REL_X) &&
test_bit(key_bits, BTN_MOUSE)) {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_MOUSE", "1", 0);
}
}
if (!test_bit(ev_bits, EV_KEY)) {
return;
}
// iterate over key bits until BTN_* block is hit and test if bitmask has KEY_*.
// this way, we can check if device has keys or buttons.
// https://github.com/torvalds/linux/blob/f5b6eb1e018203913dfefcf6fa988649ad11ad6e/include/uapi/linux/input-event-codes.h#L76-L338
for (bit = KEY_ESC; bit < BTN_MISC; bit++) {
if (!test_bit(key_bits, bit)) {
continue;
}
udev_list_entry_add(&udev_device->properties, "ID_INPUT_KEY", "1", 0);
if (test_bit(key_bits, KEY_ENTER)) {
udev_list_entry_add(&udev_device->properties, "ID_INPUT_KEYBOARD", "1", 0);
}
return;
}
}
static void set_properties_from_props(struct udev_device *udev_device)
{
const char *sysname, *subsystem;
struct udev_device *parent;
char id[256];
subsystem = udev_device_get_subsystem(udev_device);
if (!subsystem || strcmp(subsystem, "drm") != 0) {
return;
}
parent = udev_device_get_parent_with_subsystem_devtype(udev_device, "pci", NULL);
sysname = udev_device_get_sysname(parent);
if (!sysname) {
return;
}
snprintf(id, sizeof(id), "pci-%s", sysname);
udev_list_entry_add(&udev_device->properties, "ID_PATH", id, 0);
}
struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath)
{
char *subsystem, *driver, *sysname;
struct udev_device *udev_device;
char path[PATH_MAX];
int i;
if (!udev || !syspath) {
return NULL;
}
if (!realpath(syspath, path)) {
return NULL;
}
udev_device = calloc(1, sizeof(*udev_device));
if (!udev_device) {
return NULL;
}
udev_device->udev = udev;
udev_device->refcount = 1;
udev_device->parent = NULL;
udev_list_entry_init(&udev_device->properties);
udev_list_entry_init(&udev_device->sysattrs);
if (set_properties_from_uevent(udev_device, path) == -1) {
free(udev_device);
return NULL;
}
udev_list_entry_add(&udev_device->properties, "SYSPATH", path, 0);
udev_list_entry_add(&udev_device->properties, "DEVPATH", path + 4, 0);
sysname = strrchr(path, '/') + 1;
driver = read_symlink(path, "driver");
subsystem = read_symlink(path, "subsystem");
udev_list_entry_add(&udev_device->properties, "SUBSYSTEM", subsystem, 0);
udev_list_entry_add(&udev_device->properties, "SYSNAME", sysname, 0);
udev_list_entry_add(&udev_device->properties, "DRIVER", driver, 0);
for (i = 0; sysname[i] != '\0'; i++) {
if (sysname[i] >= '0' && sysname[i] <= '9') {
udev_list_entry_add(&udev_device->properties, "SYSNUM", sysname + i, 0);
break;
}
}
set_properties_from_evdev(udev_device);
set_properties_from_props(udev_device);
free(driver);
free(subsystem);
return udev_device;
}
struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum)
{
char path[PATH_MAX];
if (!udev) {
return NULL;
}
switch (type) {
case 'c':
snprintf(path, sizeof(path), "/sys/dev/char/%u:%u", major(devnum), minor(devnum));
break;
case 'b':
snprintf(path, sizeof(path), "/sys/dev/block/%u:%u", major(devnum), minor(devnum));
break;
default:
return NULL;
}
return udev_device_new_from_syspath(udev, path);
}
struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname)
{
const char *fmt[] = { "/sys/bus/%s/devices/%s", "/sys/class/%s/%s", NULL };
char path[PATH_MAX];
int i;
if (!udev || !subsystem || !sysname) {
return NULL;
}
for (i = 0; fmt[i]; i++) {
snprintf(path, sizeof(path), fmt[i], subsystem, sysname);
if (access(path, R_OK) == 0) {
return udev_device_new_from_syspath(udev, path);
}
}
return NULL;
}
struct udev_device *udev_device_new_from_uevent(struct udev *udev, char *buf, size_t len)
{
char syspath[PATH_MAX], devnode[PATH_MAX];
struct udev_device *udev_device;
const char *sysname;
char *end, *pos;
int i, cnt = 0;
udev_device = calloc(1, sizeof(*udev_device));
if (!udev_device) {
return NULL;
}
udev_device->udev = udev;
udev_device->refcount = 1;
udev_device->parent = NULL;
udev_list_entry_init(&udev_device->properties);
udev_list_entry_init(&udev_device->sysattrs);
for (end = buf + len; buf < end; buf += strlen(buf) + 1) {
if (strncmp(buf, "DEVPATH=", 8) == 0) {
snprintf(syspath, sizeof(syspath), "/sys%s", buf + 8);
udev_list_entry_add(&udev_device->properties, "SYSPATH", syspath, 0);
udev_list_entry_add(&udev_device->properties, "DEVPATH", buf + 8, 0);
sysname = strrchr(syspath, '/') + 1;
udev_list_entry_add(&udev_device->properties, "SYSNAME", sysname, 0);
for (i = 0; sysname[i] != '\0'; i++) {
if (sysname[i] >= '0' && sysname[i] <= '9') {
udev_list_entry_add(&udev_device->properties, "SYSNUM", sysname + i, 0);
break;
}
}
cnt++;
}
else if (strncmp(buf, "DEVNAME=", 8) == 0) {
snprintf(devnode, sizeof(devnode), "/dev/%s", buf + 8);
udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 0);
}
else {
pos = strchr(buf, '=');
if (!pos) {
continue;
}
*pos = '\0';
if (strcmp(buf, "SUBSYSTEM") == 0 ||
strcmp(buf, "ACTION") == 0 ||
strcmp(buf, "SEQNUM") == 0) {
cnt++;
}
udev_list_entry_add(&udev_device->properties, buf, pos + 1, 0);
*pos = '=';
}
}
if (cnt != 4) {
udev_device_unref(udev_device);
return NULL;
}
set_properties_from_props(udev_device);
set_properties_from_evdev(udev_device);
return udev_device;
}
/* XXX NOT IMPLEMENTED */ struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id)
{
return NULL;
}
/* XXX NOT IMPLEMENTED */ struct udev_device *udev_device_new_from_environment(struct udev *udev)
{
return NULL;
}
struct udev_device *udev_device_ref(struct udev_device *udev_device)
{
if (!udev_device) {
return NULL;
}
udev_device->refcount++;
return udev_device;
}
struct udev_device *udev_device_unref(struct udev_device *udev_device)
{
if (!udev_device) {
return NULL;
}
if (--udev_device->refcount > 0) {
return NULL;
}
if (udev_device->parent) {
udev_device_unref(udev_device->parent);
}
udev_list_entry_free_all(&udev_device->properties);
udev_list_entry_free_all(&udev_device->sysattrs);
free(udev_device);
return NULL;
}

375
udev_enumerate.c Normal file
View file

@ -0,0 +1,375 @@
/*
* Copyright (c) 2020-2021 illiliti <illiliti@protonmail.com>
* SPDX-License-Identifier: ISC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include "udev.h"
#include "udev_list.h"
struct udev_enumerate {
struct udev_list_entry subsystem_nomatch;
struct udev_list_entry subsystem_match;
struct udev_list_entry sysattr_nomatch;
struct udev_list_entry property_match;
struct udev_list_entry sysattr_match;
struct udev_list_entry sysname_match;
struct udev_list_entry devices;
struct udev *udev;
int refcount;
};
int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
{
return udev_enumerate ? !!udev_list_entry_add(&udev_enumerate->subsystem_match, subsystem, NULL, 0) - 1 : -1;
}
int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
{
return udev_enumerate ? !!udev_list_entry_add(&udev_enumerate->subsystem_nomatch, subsystem, NULL, 0) - 1 : -1;
}
int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value)
{
return udev_enumerate ? !!udev_list_entry_add(&udev_enumerate->sysattr_match, sysattr, value, 0) - 1 : -1;
}
int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value)
{
return udev_enumerate ? !!udev_list_entry_add(&udev_enumerate->sysattr_nomatch, sysattr, value, 0) - 1 : -1;
}
int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value)
{
return udev_enumerate ? !!udev_list_entry_add(&udev_enumerate->property_match, property, value, 0) - 1 : -1;
}
int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname)
{
return udev_enumerate ? !!udev_list_entry_add(&udev_enumerate->sysname_match, sysname, NULL, 0) - 1 : -1;
}
/* XXX NOT IMPLEMENTED */ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag)
{
return 0;
}
/* XXX NOT IMPLEMENTED */ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent)
{
return 0;
}
/* XXX NOT IMPLEMENTED */ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate)
{
return 0;
}
static int filter_subsystem(struct udev_enumerate *udev_enumerate, struct udev_device *udev_device)
{
struct udev_list_entry *list_entry;
const char *subsystem;
subsystem = udev_device_get_subsystem(udev_device);
list_entry = udev_list_entry_get_next(&udev_enumerate->subsystem_nomatch);
if (!subsystem) {
return 0;
}
while (list_entry) {
if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0) {
return 0;
}
list_entry = udev_list_entry_get_next(list_entry);
}
list_entry = udev_list_entry_get_next(&udev_enumerate->subsystem_match);
if (list_entry) {
while (list_entry) {
if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0) {
return 1;
}
list_entry = udev_list_entry_get_next(list_entry);
}
return 0;
}
return 1;
}
static int filter_sysname(struct udev_enumerate *udev_enumerate, struct udev_device *udev_device)
{
struct udev_list_entry *list_entry;
const char *sysname;
sysname = udev_device_get_sysname(udev_device);
list_entry = udev_list_entry_get_next(&udev_enumerate->sysname_match);
if (!list_entry) {
return 1;
}
while (list_entry) {
if (fnmatch(udev_list_entry_get_name(list_entry), sysname, 0) == 0) {
return 1;
}
list_entry = udev_list_entry_get_next(list_entry);
}
return 0;
}
static int filter_property(struct udev_enumerate *udev_enumerate, struct udev_device *udev_device)
{
const char *property, *property2, *value, *value2;
struct udev_list_entry *list_entry, *list_entry2;
list_entry = udev_list_entry_get_next(&udev_enumerate->property_match);
if (!list_entry) {
return 1;
}
while (list_entry) {
property = udev_list_entry_get_name(list_entry);
value = udev_list_entry_get_value(list_entry);
list_entry2 = udev_device_get_properties_list_entry(udev_device);
if (!list_entry2) {
return 0;
}
while (list_entry2) {
property2 = udev_list_entry_get_name(list_entry2);
value2 = udev_list_entry_get_value(list_entry2);
if (value && value2) {
if (fnmatch(property, property2, 0) == 0 &&
fnmatch(value, value2, 0) == 0) {
return 1;
}
}
list_entry2 = udev_list_entry_get_next(list_entry2);
}
list_entry = udev_list_entry_get_next(list_entry);
}
return 0;
}
static int filter_sysattr(struct udev_enumerate *udev_enumerate, struct udev_device *udev_device)
{
const char *sysattr, *value, *value2;
struct udev_list_entry *list_entry;
list_entry = udev_list_entry_get_next(&udev_enumerate->sysattr_nomatch);
while (list_entry) {
sysattr = udev_list_entry_get_name(list_entry);
value2 = udev_list_entry_get_value(list_entry);
value = udev_device_get_sysattr_value(udev_device, sysattr);
if (value && value2 && fnmatch(value2, value, 0) == 0) {
return 0;
}
list_entry = udev_list_entry_get_next(list_entry);
}
list_entry = udev_list_entry_get_next(&udev_enumerate->sysattr_match);
if (list_entry) {
while (list_entry) {
sysattr = udev_list_entry_get_name(list_entry);
value2 = udev_list_entry_get_value(list_entry);
value = udev_device_get_sysattr_value(udev_device, sysattr);
if (value && value2 && fnmatch(value2, value, 0) == 0) {
return 1;
}
list_entry = udev_list_entry_get_next(list_entry);
}
return 0;
}
return 1;
}
static void add_device(struct udev_enumerate *udev_enumerate, const char *path)
{
struct udev_device *udev_device;
udev_device = udev_device_new_from_syspath(udev_enumerate->udev, path);
if (!udev_device) {
return;
}
if (!filter_subsystem(udev_enumerate, udev_device) ||
!filter_sysname(udev_enumerate, udev_device) ||
!filter_property(udev_enumerate, udev_device) ||
!filter_sysattr(udev_enumerate, udev_device)) {
udev_device_unref(udev_device);
return;
}
udev_list_entry_add(&udev_enumerate->devices, udev_device_get_syspath(udev_device), NULL, 0);
udev_device_unref(udev_device);
}
static int filter_dot(const struct dirent *de)
{
return de->d_name[0] != '.';
}
static int scan_devices(struct udev_enumerate *udev_enumerate, const char *path)
{
int i, cnt;
struct dirent **de;
cnt = scandir(path, &de, filter_dot, NULL);
if (cnt == -1) {
return 0;
}
for (i = 0; i < cnt; i++) {
char device_path[PATH_MAX];
snprintf(device_path, sizeof(device_path), "%s/%s", path, de[i]->d_name);
add_device(udev_enumerate, device_path);
}
for (i = 0; i < cnt; i++) {
free(de[i]);
}
free(de);
return 1;
}
int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
{
const char *path[] = { "/sys/dev/block", "/sys/dev/char", NULL };
int i;
if (!udev_enumerate) {
return -1;
}
for (i = 0; path[i]; i++) {
if (!scan_devices(udev_enumerate, path[i])) {
return -1;
}
}
return 0;
}
/* XXX NOT IMPLEMENTED */ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate)
{
return 0;
}
struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate)
{
return udev_enumerate ? udev_list_entry_get_next(&udev_enumerate->devices) : NULL;
}
/* XXX NOT IMPLEMENTED */ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath)
{
return 0;
}
struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate)
{
return udev_enumerate ? udev_enumerate->udev : NULL;
}
struct udev_enumerate *udev_enumerate_new(struct udev *udev)
{
struct udev_enumerate *udev_enumerate;
if (!udev) {
return NULL;
}
udev_enumerate = calloc(1, sizeof(*udev_enumerate));
if (!udev_enumerate) {
return NULL;
}
udev_enumerate->refcount = 1;
udev_enumerate->udev = udev;
udev_list_entry_init(&udev_enumerate->subsystem_nomatch);
udev_list_entry_init(&udev_enumerate->subsystem_match);
udev_list_entry_init(&udev_enumerate->sysattr_nomatch);
udev_list_entry_init(&udev_enumerate->property_match);
udev_list_entry_init(&udev_enumerate->sysattr_match);
udev_list_entry_init(&udev_enumerate->sysname_match);
udev_list_entry_init(&udev_enumerate->devices);
return udev_enumerate;
}
struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate)
{
if (!udev_enumerate) {
return NULL;
}
udev_enumerate->refcount++;
return udev_enumerate;
}
struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate)
{
if (!udev_enumerate) {
return NULL;
}
if (--udev_enumerate->refcount > 0) {
return NULL;
}
udev_list_entry_free_all(&udev_enumerate->subsystem_nomatch);
udev_list_entry_free_all(&udev_enumerate->subsystem_match);
udev_list_entry_free_all(&udev_enumerate->sysattr_nomatch);
udev_list_entry_free_all(&udev_enumerate->property_match);
udev_list_entry_free_all(&udev_enumerate->sysattr_match);
udev_list_entry_free_all(&udev_enumerate->sysname_match);
udev_list_entry_free_all(&udev_enumerate->devices);
free(udev_enumerate);
return NULL;
}

129
udev_list.c Normal file
View file

@ -0,0 +1,129 @@
/*
* Copyright (c) 2020-2021 illiliti <illiliti@protonmail.com>
* SPDX-License-Identifier: ISC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <string.h>
#include <stdlib.h>
#include "udev.h"
#include "udev_list.h"
void udev_list_entry_init(struct udev_list_entry *list_entry)
{
list_entry->value = NULL;
list_entry->name = NULL;
list_entry->next = NULL;
}
void udev_list_entry_free(struct udev_list_entry *list_entry)
{
free(list_entry->value);
free(list_entry->name);
free(list_entry);
}
void udev_list_entry_free_all(struct udev_list_entry *list_entry)
{
struct udev_list_entry *list_entry2 = list_entry->next;
while (list_entry2) {
list_entry = list_entry2;
list_entry2 = list_entry->next;
udev_list_entry_free(list_entry);
}
}
struct udev_list_entry *udev_list_entry_add(struct udev_list_entry *list_entry, const char *name, const char *value, int uniq)
{
struct udev_list_entry *list_entry2;
if (uniq) {
list_entry2 = udev_list_entry_get_by_name(list_entry, name);
if (list_entry2 && value) {
if (list_entry2->value && strcmp(list_entry2->value, value) == 0) {
return list_entry2;
}
free(list_entry2->value);
list_entry2->value = strdup(value);
if (!list_entry2->value) {
return NULL;
}
return list_entry2;
}
}
list_entry2 = calloc(1, sizeof(*list_entry2));
if (!list_entry2) {
return NULL;
}
list_entry2->name = strdup(name);
if (!list_entry2->name) {
free(list_entry2);
return NULL;
}
if (value) {
list_entry2->value = strdup(value);
if (!list_entry2->value) {
free(list_entry2->name);
free(list_entry2);
return NULL;
}
}
list_entry2->next = list_entry->next;
list_entry->next = list_entry2;
return list_entry2;
}
struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry)
{
return list_entry ? list_entry->next : NULL;
}
struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name)
{
if (!list_entry || !name) {
return NULL;
}
do {
if (list_entry->name && strcmp(list_entry->name, name) == 0) {
return list_entry;
}
}
while ((list_entry = list_entry->next));
return NULL;
}
const char *udev_list_entry_get_name(struct udev_list_entry *list_entry)
{
return list_entry ? list_entry->name : NULL;
}
const char *udev_list_entry_get_value(struct udev_list_entry *list_entry)
{
return list_entry ? list_entry->value : NULL;
}

27
udev_list.h Normal file
View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2020-2021 illiliti <illiliti@protonmail.com>
* SPDX-License-Identifier: ISC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct udev_list_entry {
struct udev_list_entry *next;
char *value;
char *name;
};
void udev_list_entry_init(struct udev_list_entry *list_entry);
void udev_list_entry_free(struct udev_list_entry *list_entry);
void udev_list_entry_free_all(struct udev_list_entry *list_entry);
struct udev_list_entry *udev_list_entry_add(struct udev_list_entry *list_entry, const char *name, const char *value, int uniq);

265
udev_monitor.c Normal file
View file

@ -0,0 +1,265 @@
/*
* Copyright (c) 2020-2021 illiliti <illiliti@protonmail.com>
* SPDX-License-Identifier: ISC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include "udev.h"
#include "udev_list.h"
#ifndef UDEV_MONITOR_NLGRP
#define UDEV_MONITOR_NLGRP 0x4
#endif
struct udev_monitor {
struct udev_list_entry subsystem_match;
struct udev_list_entry devtype_match;
struct udev *udev;
unsigned nlgrp;
int refcount;
int fd;
};
static int filter_devtype(struct udev_monitor *udev_monitor, struct udev_device *udev_device)
{
struct udev_list_entry *list_entry;
const char *devtype;
devtype = udev_device_get_devtype(udev_device);
list_entry = udev_list_entry_get_next(&udev_monitor->devtype_match);
if (!list_entry) {
return 1;
}
if (!devtype) {
return 0;
}
while (list_entry) {
if (strcmp(devtype, udev_list_entry_get_name(list_entry)) == 0) {
return 1;
}
list_entry = udev_list_entry_get_next(list_entry);
}
return 0;
}
static int filter_subsystem(struct udev_monitor *udev_monitor, struct udev_device *udev_device)
{
struct udev_list_entry *list_entry;
const char *subsystem;
subsystem = udev_device_get_subsystem(udev_device);
list_entry = udev_list_entry_get_next(&udev_monitor->subsystem_match);
if (!list_entry) {
return 1;
}
if (!subsystem) {
return 0;
}
while (list_entry) {
if (strcmp(subsystem, udev_list_entry_get_name(list_entry)) == 0) {
return 1;
}
list_entry = udev_list_entry_get_next(list_entry);
}
return 0;
}
struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor)
{
struct udev_device *udev_device;
struct sockaddr_nl sa = {0};
struct msghdr hdr = {0};
struct iovec iov = {0};
char buf[8192];
ssize_t len;
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
hdr.msg_name = &sa;
hdr.msg_namelen = sizeof(sa);
hdr.msg_iov = &iov;
hdr.msg_iovlen = 1;
while (1) {
len = recvmsg(udev_monitor->fd, &hdr, 0);
if (len <= 0) {
break;
}
if (hdr.msg_flags & MSG_TRUNC) {
continue;
}
if (sa.nl_groups == 0x0 || (sa.nl_groups == 0x1 && sa.nl_pid)) {
continue;
}
udev_device = udev_device_new_from_uevent(udev_monitor->udev, buf, len);
if (!udev_device) {
continue;
}
if (!filter_subsystem(udev_monitor, udev_device) ||
!filter_devtype(udev_monitor, udev_device)) {
udev_device_unref(udev_device);
continue;
}
return udev_device;
}
return NULL;
}
int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
{
struct sockaddr_nl sa = {0};
if (!udev_monitor) {
return -1;
}
sa.nl_family = AF_NETLINK;
sa.nl_groups = udev_monitor->nlgrp;
return bind(udev_monitor->fd, (struct sockaddr *)&sa, sizeof(sa));
}
int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
{
return udev_monitor ? setsockopt(udev_monitor->fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) : -1;
}
int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
{
return udev_monitor ? udev_monitor->fd : -1;
}
struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
{
return udev_monitor ? udev_monitor->udev : NULL;
}
/* XXX NOT IMPLEMENTED */ int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
{
return 0;
}
/* XXX NOT IMPLEMENTED */ int udev_monitor_filter_remove(struct udev_monitor *udev_monitor)
{
return 0;
}
int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype)
{
if (!udev_monitor || !subsystem) {
return -1;
}
udev_list_entry_add(&udev_monitor->subsystem_match, subsystem, NULL, 0);
if (devtype) {
udev_list_entry_add(&udev_monitor->devtype_match, devtype, NULL, 0);
}
return 0;
}
/* XXX NOT IMPLEMENTED */ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag)
{
return 0;
}
struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name)
{
struct udev_monitor *udev_monitor;
if (!udev || !name) {
return NULL;
}
udev_monitor = calloc(1, sizeof(*udev_monitor));
if (!udev_monitor) {
return NULL;
}
if (strcmp(name, "udev") == 0) {
udev_monitor->nlgrp = UDEV_MONITOR_NLGRP;
}
else if (strcmp(name, "kernel") == 0) {
udev_monitor->nlgrp = 0x1;
}
else {
free(udev_monitor);
return NULL;
}
udev_monitor->fd = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
if (udev_monitor->fd == -1) {
free(udev_monitor);
return NULL;
}
udev_monitor->refcount = 1;
udev_monitor->udev = udev;
return udev_monitor;
}
struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
{
if (!udev_monitor) {
return NULL;
}
udev_monitor->refcount++;
return udev_monitor;
}
struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor)
{
if (!udev_monitor) {
return NULL;
}
if (--udev_monitor->refcount > 0) {
return NULL;
}
udev_list_entry_free_all(&udev_monitor->devtype_match);
udev_list_entry_free_all(&udev_monitor->subsystem_match);
close(udev_monitor->fd);
free(udev_monitor);
return NULL;
}