Skip to content

Instantly share code, notes, and snippets.

@CynicRus
Created February 13, 2025 17:53
Show Gist options
  • Select an option

  • Save CynicRus/c526c25129a40163fb9fda81b76fb596 to your computer and use it in GitHub Desktop.

Select an option

Save CynicRus/c526c25129a40163fb9fda81b76fb596 to your computer and use it in GitHub Desktop.
program UsbMonitor;
{$mode objfpc}{$H+}
uses
dl, BaseUnix, Unix, SysUtils;
const
LIBUDEV_SO = 'libudev.so.1';
type
Pudev = Pointer;
Pudev_monitor = Pointer;
Pudev_device = Pointer;
Pudev_enumerate = Pointer;
Pudev_list_entry = Pointer;
TUdevNew = function: Pudev; cdecl;
TUdevUnref = procedure(udev: Pudev); cdecl;
TUdevMonitorNew = function(udev: Pudev; const name: PChar): Pudev_monitor; cdecl;
TUdevMonitorFilterAddMatchSubsystemDevtype = function(monitor: Pudev_monitor; const subsystem: PChar; const devtype: PChar): Integer; cdecl;
TUdevMonitorEnableReceiving = function(monitor: Pudev_monitor): Integer; cdecl;
TUdevMonitorGetFd = function(monitor: Pudev_monitor): Integer; cdecl;
TUdevMonitorReceiveDevice = function(monitor: Pudev_monitor): Pudev_device; cdecl;
TUdevDeviceGetAction = function(dev: Pudev_device): PChar; cdecl;
TUdevDeviceGetDevnode = function(dev: Pudev_device): PChar; cdecl;
TUdevDeviceUnref = procedure(dev: Pudev_device); cdecl;
TUdevMonitorUnref = procedure(monitor: Pudev_monitor); cdecl;
EUdevError = class(Exception);
var
LibHandle: TLibHandle;
udev_new: TUdevNew;
udev_unref: TUdevUnref;
udev_monitor_new_from_netlink: TUdevMonitorNew;
udev_monitor_filter_add_match_subsystem_devtype: TUdevMonitorFilterAddMatchSubsystemDevtype;
udev_monitor_enable_receiving: TUdevMonitorEnableReceiving;
udev_monitor_get_fd: TUdevMonitorGetFd;
udev_monitor_receive_device: TUdevMonitorReceiveDevice;
udev_device_get_action: TUdevDeviceGetAction;
udev_device_get_devnode: TUdevDeviceGetDevnode;
udev_device_unref: TUdevDeviceUnref;
udev_monitor_unref: TUdevMonitorUnref;
function LoadLibrary: Boolean;
var
ErrorMsg: string;
begin
Result := False;
try
LibHandle := dlopen(LIBUDEV_SO, RTLD_LAZY);
if LibHandle = NilHandle then
begin
ErrorMsg := dlerror;
raise EUdevError.CreateFmt('Error loading library %s: %s', [LIBUDEV_SO, ErrorMsg]);
end;
Pointer(udev_new) := dlsym(LibHandle, 'udev_new');
Pointer(udev_unref) := dlsym(LibHandle, 'udev_unref');
Pointer(udev_monitor_new_from_netlink) := dlsym(LibHandle, 'udev_monitor_new_from_netlink');
Pointer(udev_monitor_filter_add_match_subsystem_devtype) := dlsym(LibHandle, 'udev_monitor_filter_add_match_subsystem_devtype');
Pointer(udev_monitor_enable_receiving) := dlsym(LibHandle, 'udev_monitor_enable_receiving');
Pointer(udev_monitor_get_fd) := dlsym(LibHandle, 'udev_monitor_get_fd');
Pointer(udev_monitor_receive_device) := dlsym(LibHandle, 'udev_monitor_receive_device');
Pointer(udev_device_get_action) := dlsym(LibHandle, 'udev_device_get_action');
Pointer(udev_device_get_devnode) := dlsym(LibHandle, 'udev_device_get_devnode');
Pointer(udev_device_unref) := dlsym(LibHandle, 'udev_device_unref');
Pointer(udev_monitor_unref) := dlsym(LibHandle, 'udev_monitor_unref');
Result := True;
except
on E: Exception do
begin
if LibHandle <> NilHandle then
dlclose(LibHandle);
raise;
end;
end;
end;
procedure MonitorUSBDevices;
var
udev: Pudev;
monitor: Pudev_monitor;
fd: Integer;
fds: TFDSet;
dev: Pudev_device;
action, node: PChar;
tv: TimeVal;
selectResult: Integer;
begin
udev := nil;
monitor := nil;
try
udev := udev_new();
if udev = nil then
raise EUdevError.Create('Failed to create udev context');
monitor := udev_monitor_new_from_netlink(udev, 'udev');
if monitor = nil then
raise EUdevError.Create('Failed to create udev monitor');
if udev_monitor_filter_add_match_subsystem_devtype(monitor, 'usb', nil) < 0 then
raise EUdevError.Create('Error setting filter');
if udev_monitor_enable_receiving(monitor) < 0 then
raise EUdevError.Create('Error enabling monitoring');
fd := udev_monitor_get_fd(monitor);
if fd < 0 then
raise EUdevError.Create('Error getting file descriptor');
WriteLn('Starting USB device monitoring...');
WriteLn('Press Ctrl+C to exit');
while True do
begin
FD_ZERO(fds);
FD_SET(fd, fds);
tv.tv_sec := 1;
tv.tv_usec := 0;
try
selectResult := fpSelect(fd + 1, @fds, nil, nil, @tv);
if selectResult < 0 then
raise EUdevError.Create('Error in fpSelect');
if selectResult > 0 then
begin
dev := udev_monitor_receive_device(monitor);
if dev <> nil then
try
action := udev_device_get_action(dev);
node := udev_device_get_devnode(dev);
Write('Event: ');
if action <> nil then
Write(action);
Write(' ');
if node <> nil then
WriteLn(node)
else
WriteLn;
finally
udev_device_unref(dev);
end;
end;
except
on E: Exception do
WriteLn('Error processing event: ', E.Message);
end;
end;
finally
if monitor <> nil then
udev_monitor_unref(monitor);
if udev <> nil then
udev_unref(udev);
end;
end;
begin
try
if not LoadLibrary then
Exit;
try
MonitorUSBDevices;
finally
dlclose(LibHandle);
end;
except
on E: Exception do
begin
WriteLn('Critical error: ', E.Message);
ExitCode := 1;
end;
end;
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment