Created
February 13, 2025 17:53
-
-
Save CynicRus/c526c25129a40163fb9fda81b76fb596 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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