udev-cli 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- udev_cli-0.1.0/PKG-INFO +55 -0
- udev_cli-0.1.0/README.md +39 -0
- udev_cli-0.1.0/pyproject.toml +30 -0
- udev_cli-0.1.0/setup.cfg +4 -0
- udev_cli-0.1.0/udev_cli/__init__.py +0 -0
- udev_cli-0.1.0/udev_cli/main.py +134 -0
- udev_cli-0.1.0/udev_cli.egg-info/PKG-INFO +55 -0
- udev_cli-0.1.0/udev_cli.egg-info/SOURCES.txt +10 -0
- udev_cli-0.1.0/udev_cli.egg-info/dependency_links.txt +1 -0
- udev_cli-0.1.0/udev_cli.egg-info/entry_points.txt +2 -0
- udev_cli-0.1.0/udev_cli.egg-info/requires.txt +1 -0
- udev_cli-0.1.0/udev_cli.egg-info/top_level.txt +1 -0
udev_cli-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: udev-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Print, filter, and react to udev events as JSON
|
|
5
|
+
Author: readwithai
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/readwithai/udev-cli
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: System :: Hardware
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: pyudev>=0.22
|
|
16
|
+
|
|
17
|
+
# udev-cli
|
|
18
|
+
Print udev events, filter udev events, and run commands on udev events. udev is a system of events when hardware devices are plugged into a computer on linux.
|
|
19
|
+
|
|
20
|
+
This is unreviewed AI-generated code. But will become more reviewed if people use it.
|
|
21
|
+
|
|
22
|
+
## Motivation
|
|
23
|
+
I have a tried to use udev a few times. But I always find it difficult to actually get my events to fire and debug what is going on. I've decied to just listen to events myself and trigger events rather than mess ith rules.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Alternatives and prior work
|
|
27
|
+
`udev-monitor` is a pypi project which is very similar. However, it could not filter by UUID, so I decided to vibe code something up.
|
|
28
|
+
|
|
29
|
+
`udev` itself has a rule system and
|
|
30
|
+
|
|
31
|
+
`udevadm monitor` can output events from udev put does not output JSON. I am also concerned about buffering.
|
|
32
|
+
|
|
33
|
+
This uses pyudev internally. You could achieve this functionality with a little python script using `pyudev` and do other things - but it would involve a little more debugging to understand what is going on.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
pipx install udev-cli
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
Print all udev events: `udev-cli`. You can use `jq` to help you work out what you want to match.
|
|
41
|
+
|
|
42
|
+
block device events only: `udev-cli -s block`
|
|
43
|
+
|
|
44
|
+
block devices added: `udev-cli -s block -a add`
|
|
45
|
+
|
|
46
|
+
match specific UUID: `udev-cli -f ID_FS_UUID b9335c96-...`
|
|
47
|
+
|
|
48
|
+
match any UUID in file: `udev-cli -f ID_FS_UUID @/etc/backup-uuids.txt`
|
|
49
|
+
|
|
50
|
+
run a command: `udev-cli -s block -a add -f ID_FS_UUID @uuids --run ./backup.sh`
|
|
51
|
+
|
|
52
|
+
## About
|
|
53
|
+
I am @readwithai. I am making tools for reading and agency with and without AI.
|
|
54
|
+
I also make a lot of small tools related to productivity. If this tool is useful you might like [follow me on github](https://github.com/talwrii) or [X](https://x.com/readwithai).
|
|
55
|
+
|
udev_cli-0.1.0/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# udev-cli
|
|
2
|
+
Print udev events, filter udev events, and run commands on udev events. udev is a system of events when hardware devices are plugged into a computer on linux.
|
|
3
|
+
|
|
4
|
+
This is unreviewed AI-generated code. But will become more reviewed if people use it.
|
|
5
|
+
|
|
6
|
+
## Motivation
|
|
7
|
+
I have a tried to use udev a few times. But I always find it difficult to actually get my events to fire and debug what is going on. I've decied to just listen to events myself and trigger events rather than mess ith rules.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Alternatives and prior work
|
|
11
|
+
`udev-monitor` is a pypi project which is very similar. However, it could not filter by UUID, so I decided to vibe code something up.
|
|
12
|
+
|
|
13
|
+
`udev` itself has a rule system and
|
|
14
|
+
|
|
15
|
+
`udevadm monitor` can output events from udev put does not output JSON. I am also concerned about buffering.
|
|
16
|
+
|
|
17
|
+
This uses pyudev internally. You could achieve this functionality with a little python script using `pyudev` and do other things - but it would involve a little more debugging to understand what is going on.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
pipx install udev-cli
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
Print all udev events: `udev-cli`. You can use `jq` to help you work out what you want to match.
|
|
25
|
+
|
|
26
|
+
block device events only: `udev-cli -s block`
|
|
27
|
+
|
|
28
|
+
block devices added: `udev-cli -s block -a add`
|
|
29
|
+
|
|
30
|
+
match specific UUID: `udev-cli -f ID_FS_UUID b9335c96-...`
|
|
31
|
+
|
|
32
|
+
match any UUID in file: `udev-cli -f ID_FS_UUID @/etc/backup-uuids.txt`
|
|
33
|
+
|
|
34
|
+
run a command: `udev-cli -s block -a add -f ID_FS_UUID @uuids --run ./backup.sh`
|
|
35
|
+
|
|
36
|
+
## About
|
|
37
|
+
I am @readwithai. I am making tools for reading and agency with and without AI.
|
|
38
|
+
I also make a lot of small tools related to productivity. If this tool is useful you might like [follow me on github](https://github.com/talwrii) or [X](https://x.com/readwithai).
|
|
39
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "udev-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Print, filter, and react to udev events as JSON"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "readwithai" },
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"pyudev>=0.22",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 3 - Alpha",
|
|
20
|
+
"Environment :: Console",
|
|
21
|
+
"Operating System :: POSIX :: Linux",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Topic :: System :: Hardware",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
udev-cli = "udev_cli.main:main"
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/readwithai/udev-cli"
|
udev_cli-0.1.0/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""udev-cli: Print, filter, and react to udev events."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def device_to_dict(device, action: Optional[str] = None) -> dict:
|
|
13
|
+
"""Convert a pyudev Device to a JSON-serialisable dict."""
|
|
14
|
+
d = {
|
|
15
|
+
"action": action or device.action,
|
|
16
|
+
"sys_path": device.sys_path,
|
|
17
|
+
"sys_name": device.sys_name,
|
|
18
|
+
"device_node": device.device_node,
|
|
19
|
+
"device_type": device.device_type,
|
|
20
|
+
"subsystem": device.subsystem,
|
|
21
|
+
"driver": device.driver,
|
|
22
|
+
"device_path": device.device_path,
|
|
23
|
+
"tags": list(device.tags),
|
|
24
|
+
"properties": dict(device.properties),
|
|
25
|
+
}
|
|
26
|
+
return d
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def load_values_file(path: str) -> list[str]:
|
|
30
|
+
"""Load values from a file, one per line."""
|
|
31
|
+
values = []
|
|
32
|
+
with open(path) as f:
|
|
33
|
+
for line in f:
|
|
34
|
+
line = line.strip()
|
|
35
|
+
if not line or line.startswith("#"):
|
|
36
|
+
continue
|
|
37
|
+
values.append(line)
|
|
38
|
+
return values
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def resolve_filters(raw_filters: list[list[str]]) -> list[tuple[str, list[str]]]:
|
|
42
|
+
"""Resolve -f args into (key, [values]) pairs.
|
|
43
|
+
|
|
44
|
+
-f KEY VALUE -> (KEY, [VALUE])
|
|
45
|
+
-f KEY @file -> (KEY, [val1, val2, ...])
|
|
46
|
+
|
|
47
|
+
All pairs must match (AND). Multiple values = any can match (OR).
|
|
48
|
+
"""
|
|
49
|
+
resolved = []
|
|
50
|
+
for parts in raw_filters:
|
|
51
|
+
if len(parts) != 2:
|
|
52
|
+
print(f"Error: -f expects KEY VALUE, got: {' '.join(parts)}", file=sys.stderr)
|
|
53
|
+
sys.exit(1)
|
|
54
|
+
key, value = parts
|
|
55
|
+
if value.startswith("@"):
|
|
56
|
+
path = value[1:]
|
|
57
|
+
values = load_values_file(path)
|
|
58
|
+
if not values:
|
|
59
|
+
print(f"Warning: no values in {path}", file=sys.stderr)
|
|
60
|
+
resolved.append((key, values))
|
|
61
|
+
else:
|
|
62
|
+
resolved.append((key, [value]))
|
|
63
|
+
return resolved
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def matches_filters(device, action: Optional[str], filters: list[tuple[str, list[str]]], actions: list[str]) -> bool:
|
|
67
|
+
"""Check if a device event matches filters.
|
|
68
|
+
|
|
69
|
+
All filter keys must match (AND).
|
|
70
|
+
For each key, device value must be in the values list (OR).
|
|
71
|
+
"""
|
|
72
|
+
act = action or device.action
|
|
73
|
+
if actions and act not in actions:
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
for key, values in filters:
|
|
77
|
+
dev_value = device.get(key)
|
|
78
|
+
if dev_value is None or dev_value not in values:
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def cmd_monitor(args):
|
|
85
|
+
"""Monitor udev events and print as JSONL."""
|
|
86
|
+
import pyudev
|
|
87
|
+
|
|
88
|
+
context = pyudev.Context()
|
|
89
|
+
monitor = pyudev.Monitor.from_netlink(context)
|
|
90
|
+
|
|
91
|
+
if args.subsystem:
|
|
92
|
+
for sub in args.subsystem:
|
|
93
|
+
monitor.filter_by(sub)
|
|
94
|
+
|
|
95
|
+
filters = resolve_filters(args.filter or [])
|
|
96
|
+
actions = [a.strip() for a in args.action.split(",")] if args.action else []
|
|
97
|
+
|
|
98
|
+
for device in iter(monitor.poll, None):
|
|
99
|
+
action = device.action
|
|
100
|
+
|
|
101
|
+
if not matches_filters(device, action, filters, actions):
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
event = device_to_dict(device, action)
|
|
105
|
+
|
|
106
|
+
line = json.dumps(event)
|
|
107
|
+
print(line, flush=True)
|
|
108
|
+
|
|
109
|
+
if args.run:
|
|
110
|
+
try:
|
|
111
|
+
subprocess.Popen(
|
|
112
|
+
[args.run, line],
|
|
113
|
+
stdout=None, stderr=None,
|
|
114
|
+
)
|
|
115
|
+
except Exception as e:
|
|
116
|
+
print(json.dumps({"error": str(e)}), file=sys.stderr, flush=True)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def main():
|
|
120
|
+
parser = argparse.ArgumentParser(
|
|
121
|
+
description="Print, filter, and react to udev events as JSONL",
|
|
122
|
+
prog="udev-cli",
|
|
123
|
+
)
|
|
124
|
+
parser.add_argument("-s", "--subsystem", action="append", help="Filter by subsystem (e.g. block, usb, input)")
|
|
125
|
+
parser.add_argument("-a", "--action", help="Filter by action (comma-separated: add,remove,change)")
|
|
126
|
+
parser.add_argument("-f", "--filter", nargs=2, action="append", metavar=("KEY", "VALUE"), help="Filter by property: KEY VALUE or KEY @filepath")
|
|
127
|
+
parser.add_argument("--run", help="Run command on match (event JSON passed as first arg)")
|
|
128
|
+
|
|
129
|
+
args = parser.parse_args()
|
|
130
|
+
cmd_monitor(args)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
if __name__ == "__main__":
|
|
134
|
+
main()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: udev-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Print, filter, and react to udev events as JSON
|
|
5
|
+
Author: readwithai
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/readwithai/udev-cli
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: System :: Hardware
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: pyudev>=0.22
|
|
16
|
+
|
|
17
|
+
# udev-cli
|
|
18
|
+
Print udev events, filter udev events, and run commands on udev events. udev is a system of events when hardware devices are plugged into a computer on linux.
|
|
19
|
+
|
|
20
|
+
This is unreviewed AI-generated code. But will become more reviewed if people use it.
|
|
21
|
+
|
|
22
|
+
## Motivation
|
|
23
|
+
I have a tried to use udev a few times. But I always find it difficult to actually get my events to fire and debug what is going on. I've decied to just listen to events myself and trigger events rather than mess ith rules.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Alternatives and prior work
|
|
27
|
+
`udev-monitor` is a pypi project which is very similar. However, it could not filter by UUID, so I decided to vibe code something up.
|
|
28
|
+
|
|
29
|
+
`udev` itself has a rule system and
|
|
30
|
+
|
|
31
|
+
`udevadm monitor` can output events from udev put does not output JSON. I am also concerned about buffering.
|
|
32
|
+
|
|
33
|
+
This uses pyudev internally. You could achieve this functionality with a little python script using `pyudev` and do other things - but it would involve a little more debugging to understand what is going on.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
pipx install udev-cli
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
Print all udev events: `udev-cli`. You can use `jq` to help you work out what you want to match.
|
|
41
|
+
|
|
42
|
+
block device events only: `udev-cli -s block`
|
|
43
|
+
|
|
44
|
+
block devices added: `udev-cli -s block -a add`
|
|
45
|
+
|
|
46
|
+
match specific UUID: `udev-cli -f ID_FS_UUID b9335c96-...`
|
|
47
|
+
|
|
48
|
+
match any UUID in file: `udev-cli -f ID_FS_UUID @/etc/backup-uuids.txt`
|
|
49
|
+
|
|
50
|
+
run a command: `udev-cli -s block -a add -f ID_FS_UUID @uuids --run ./backup.sh`
|
|
51
|
+
|
|
52
|
+
## About
|
|
53
|
+
I am @readwithai. I am making tools for reading and agency with and without AI.
|
|
54
|
+
I also make a lot of small tools related to productivity. If this tool is useful you might like [follow me on github](https://github.com/talwrii) or [X](https://x.com/readwithai).
|
|
55
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
udev_cli/__init__.py
|
|
4
|
+
udev_cli/main.py
|
|
5
|
+
udev_cli.egg-info/PKG-INFO
|
|
6
|
+
udev_cli.egg-info/SOURCES.txt
|
|
7
|
+
udev_cli.egg-info/dependency_links.txt
|
|
8
|
+
udev_cli.egg-info/entry_points.txt
|
|
9
|
+
udev_cli.egg-info/requires.txt
|
|
10
|
+
udev_cli.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyudev>=0.22
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
udev_cli
|