JSubelj / g910-gkey-macro-support

GKey support for Logitech G910 Keyboard on Linux
GNU General Public License v3.0
99 stars 29 forks source link

[Feature] Replace pyUSB with pyhidapi #73

Open suabo opened 1 year ago

suabo commented 1 year ago

I tried adding a .rules file for the supported devices to run the program on user level and could get information from keyboard. But as soon as you want to detach the kernel module you get a permission denied. Without detaching it is not possible to read or write to the USB.

The only way to allow this would be to replace pyusb with pyhidapi.

This would be the next big upgrade to the driver and would fix #53, since it is related to detaching the kernel driver via pyusb.

I will update this post if I am gonna work on it.

suabo commented 1 year ago

I tested a bit more on running on user level, since there is not only the usb we need to access. There is also the uinput device we need to create in order to send keyboard signals. Because of that I added the following to the rules file:
KERNEL=="uinput", TAG+="uaccess"

This will allow for logged in user to access uinput. I needed to run following commands to reload the udev rules after adding them to /etc/udev/rules.d/60-g910-gkeys.rules to make it work:
udevadm control --reload-rules && udevadm trigger

After this I could run python3 cli_entry_point.py -l create with user privileges(see #68). This is because we only need access to uinput here to create the mapping.

Just wanted to document this knowledge somewhere. No updates on implementing pyhidapi yet.

braoult commented 1 year ago

This will allow for logged in user to access uinput.

I am unsure what logged-in user means here (vs currently active). For example, we can have two users (let say Alice and Bob) both having their own X server and session, both active (we can just switch user with Control-alt-F7 and control-alt-F8, all Alice processes continue to work even if we are on Bob session).

How will uinput work in this case ? I mean, I am Alice on vt7, all keyboard and mouse events come to my session. I switch to Bob on vt8, all keyboard and mouse events come to my session (Alice and Bob share the same seat, in X terminology, seat meaning . For example, in my situation :

Alice@lorien:~$ pstree -cal
-lightdm
   |-Xorg -core :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
   |   `-{Xorg}
   |-Xorg -core :1 -seat seat0 -auth /var/run/lightdm/root/:1 -nolisten tcp vt8 -novtswitch
   |   `-{Xorg}
   |-lightdm --session-child 14 21
   |   |-xfce4-session
   |   |   |- [ Alice session programs ]
   |   |   |- ...
   |   |-{lightdm}
   |   `-{lightdm}
   |-lightdm --session-child 16 30
   |   |-xfce4-session
   |   |   |- [ Bob session programs ]
   |   |   |- ...
   |   |-{lightdm}
   |   `-{lightdm}
   |-{lightdm}
   `-{lightdm}

The thing is that we should not mess with permissions, and have the correct keyboard input sent to the correct X server.

I made the test: With my configuration, xev shows the correct mapping (e.g. pressing G3 shows F15 KeyPress and KeyRelease events) on both Alice/vt7 and Bob/vt8. It means the two X servers are able to grab/release the devices when they become active/inactive.

I wonder what KERNEL=="uinput", TAG+="uaccess" means exactly. I believe it could be fine, or the whole uinput would be totally wrong on an Unix-like (the multi-user part of it) system like GNU/Linux.

See This link to understand what is a seat (display, input devices, audio, camera, etc...), session, and multi-session (multiple sessions on the same seat).

suabo commented 1 year ago

Maybe the topic is a not clear enough what the issue is really about. In the first place I tried to make it runable without root permissions, which can't be a bad thing.

I also made some progress on testing what's the gkey mode all about. With the current rules applied to udev you can start the program with user privileges without permission problems (even detaching an re-attaching the kernel driver). I think this is a big step into the right direction.

This is my current rules file:

SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c335", TAG+="uaccess"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c33f", TAG+="uaccess"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c33e", TAG+="uaccess"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c32b", TAG+="uaccess"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c24d", TAG+="uaccess"

KERNEL=="uinput", TAG+="uaccess"

I testing on some other keyboards, too. So they are also included here.

I won't merge any of this into master if I not got a full working concept, so don't worry things getting messed up. I read the libusb documentation as well as the one from uinput and python-uinput. That's the recommended way of accessing it. Since we run on root privileges right now we didn't need to take action to access it.

There are mainly two ways to access: uaccess meas user access there is another option gaccess, which will allow group access This will set the owner of the stream to root:users where users seams to be a virtual group or something.

The other option is to gain access with the file permissions on the stream.You can set MODE to set them. This method is not recommended since you need to set 666 to make it universal and this is a big security problem.

All I am doing now is under the assumption the driver can be installed on a user level and is handled by systemd in that way, so we can fix the problems triggering commands not beeing logged in or not even on a tty. That's the reason why It's in the title.

My idea right now is to allow a install on user level, because there is maybe someone not allowed for sudo wanting to use the driver and can't right now. Best would be to detect root privileges on install and just install user level service then. I have to read some more on this, but I think systemd will handle start/stop of the service if we install it with the right target.

braoult commented 1 year ago

My idea right now is to allow a install on user level

This is exactly the point I raised. I am unsure on how it will be handled when 2 users are logged-in on same seat (same keyboard etc.) at the same time, one only being active, and the user switch happening when using a control-alt-F? combination.

Note: During a switch, there is no login (the user is already there, the active window is still the one which was active during last Bob' switch-out to Alice).

My question was simply : What will be the owner and access right of your device after we switch from Alice to Bob ? And what will happen to Alice and Bob systemd user services (there are two of them, right ?) during a user switch ?

I believe the system is designed to make proper switch (for example X is grabbing/releasing devices, if I understand, but I am not sure of that).

In other words, I wonder if our "driver" will also have to do something (when we switch in/to or out/from a user), if the two services will be notified of some event, or if everything will be auto-magically handled.

suabo commented 1 year ago

In other words, I wonder if our "driver" will also have to do something (when we switch in/to or out/from a user), if the two services will be notified of some event, or if everything will be auto-magically handled.

If there is no option in the service unit then there will be probably a systemwide signal we can listen to on a user switch. Just assuming... I will read into on demand.

I feel like the discussion is more related to "Install User-Level-Service" and maybe #63, then to the implementation of pyhidapi, which wasn't intended. Thats why I'll remove the "Install User-Level-Service" part from the headline now.
If you interested in "installing without root" as a feature feel free to open a feature request and refer this issue.

braoult commented 1 year ago

I feel like the discussion is more related to "Install User-Level-Service"

It is in indeed. The current root service works as expected with this solution, as there is no driver ownership by users (applications will simply get an input generated by root user).

I think that, if some device get user ownership and permission, this discussion is very important (in particular when two users want to run the same service).

But if you separate the root ownership from the pyhidapi migration, I agree it (this discussion) could be done somewhere else, when the multi-user permissions will be a subject.