Merge remote-tracking branch 'origin/third-party-review'
This commit is contained in:
commit
33b1977416
21 changed files with 2325 additions and 0 deletions
3
.github/workflows/codeql-buildscript.sh
vendored
Normal file
3
.github/workflows/codeql-buildscript.sh
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
make
|
||||
126
.github/workflows/codeql.yml
vendored
Normal file
126
.github/workflows/codeql.yml
vendored
Normal 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
34
.github/workflows/fail_on_error.py
vendored
Executable 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
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.so.*
|
||||
*.pc
|
||||
49
Android.bp
Normal file
49
Android.bp
Normal 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
15
LICENSE
Normal 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
16
METADATA
Normal 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
0
MODULE_LICENSE_ISC
Normal file
79
Makefile
Normal file
79
Makefile
Normal 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
1
OWNERS
Normal file
|
|
@ -0,0 +1 @@
|
|||
include platform/system/core:main:/janitors/OWNERS
|
||||
100
README.md
Normal file
100
README.md
Normal 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
80
contrib/helper.c
Normal 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
16
contrib/mdev.conf
Normal 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
12
libudev.pc.in
Normal 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
97
udev.c
Normal 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
139
udev.h
Normal 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
757
udev_device.c
Normal 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
375
udev_enumerate.c
Normal 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
129
udev_list.c
Normal 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
27
udev_list.h
Normal 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
265
udev_monitor.c
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue