Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keyboard does not work when runing keyd as a runit service on Void Linux #263

Closed
ghost opened this issue Jul 21, 2022 · 16 comments
Closed
Labels

Comments

@ghost
Copy link

ghost commented Jul 21, 2022

I'm using Void Linux and autostarting keyd on boot. When i enter sway or river (wayland window managers), my keyboard does not work.

If i start keyd AFTER starting sway or river (with sudo keyd on river init file), it works fine.

The service file on Void Linux (it uses runit instead of systemd) has just 'exec keyd'.

It's keyd v2.4.1

@rvaiya
Copy link
Owner

rvaiya commented Jul 22, 2022

Does this problem occur if you manually restart keyd in a tty before starting your window manager? By 'not work' do you mean your keyboard is entirely unresponsive, or simply that it doesn't have your modified bindings?

It is possible that the service is being initialized before your file system has fully loaded (meaning it won't have access to /etc/keyd), in which case it is not a keyd bug (and you should probably report it to the package maintainer).

@ghost
Copy link
Author

ghost commented Jul 22, 2022

They keyboard is not responsive and i have to shutdown using the power button.

It probably is a problem of timing. I disabled the service and now i start when i enter the graphical environment and everything is working flawlessly

@rvaiya
Copy link
Owner

rvaiya commented Jul 24, 2022

This sounds like a distro specific init issue. Can you post the keyd log output from the problematic initialization? I assume there is some way to recover it from runit, though I am unfamiliar with it.

@ghost
Copy link
Author

ghost commented Jul 26, 2022

Weird. I reverted back to using the runit service and the problem is gone. I'll use a few more days and report back if anything happens again. If not, i'll close the issue.

@rpanicker
Copy link

I have a similar problem on Void Linux when i start it using runit. It seems to capture all keyboard input when the X server starts up. When I use the escape sequence, keyd restarts and then it's all good. i can login and the mapping works.

@herrsimon
Copy link
Contributor

In case you would like to investigate further: Are you using eudev (the default) or did you replace it with libudev-zero This sometimes confuses libinput and causes issues with device hotplugging, so it could be that keyd's virtual keyboard is not properly picked up by wayland/x11.

To avoid having to install a syslog, you could temporarily redirect keyd's STDOUT and STDERR to some file by adapting exec keyd in the service file.

In general, runit unfortunately lacks proper service supervision capabilities as touched upon for example here. In particular, it has no builtin dependency management. However, there is an ongoing and long awaited project which will fill this gap.

@ghost
Copy link
Author

ghost commented Jul 28, 2022

The problem came back and looking at log files, it does not seem to show anything.

Here is what i found in svlogtail:
keyd_log.txt

@rvaiya
Copy link
Owner

rvaiya commented Jul 29, 2022

I spent some time digging into this, and it looks like it is indeed related to initialization order.

From what I understand, void initializes udev twice (for reasons I don't fully understand), once as a core service and then again as a system service.

The system service kills the original instance and then starts a new one, creating a gap in which udev is not actively monitoring kernel events. It seems keyd is started between the first and second udev instances which prevents eudev from responding to the kernel event and creating a database entry. This subsequently causes libinput/X not to identify it as an input device.

If my understanding is correct, this should probably be regarded as a void bug, but it is possible I have misunderstood something.

In the meantime the solution is to add a timeout to the keyd runit service

E.G:

/etc/sv/keyd/run:

#!/bin/sh
sleep .1
exec keyd > /var/log/keyd.log 2>&1

or else to simply disable the system udev service (rm /var/service/udevd) and rely on the core-service, though this might have other implications.

If someone with a better grasp of void internals could corroborate this or offer corrections, it would be greatly appreciated.

@rvaiya rvaiya added the init label Aug 3, 2022
@herrsimon
Copy link
Contributor

From what I understand, void initializes udev twice (for reasons I don't fully understand), once as a core service and then again as a system service.

As I understand it, their core service mechanism is primarily there to start one shot services, which are not properly supported by runit (termination of a service is interpreted as service failure if I'm not mistaken, so runit would try to start the service again, resulting in an infinite loop in the case of one shots or forking services). They however need udev at that stage for some core service scripts called later.

The second call as a system service is then needed so that udev is monitored by runit (and can for example be cleanly terminated on system shut down).

The system service kills the original instance and then starts a new one, creating a gap in which udev is not actively monitoring kernel events. It seems keyd is started between the first and second udev instances which prevents eudev from responding to the kernel event and creating a database entry. This subsequently causes libinput/X not to identify it as an input device.

This should exactly be the issue here.

If my understanding is correct, this should probably be regarded as a void bug, but it is possible I have misunderstood something.

In the meantime the solution is to add a timeout to the keyd runit service

E.G:

/etc/sv/keyd/run:

#!/bin/sh
sleep .1
exec keyd > /var/log/keyd.log 2>&1

While this should very likely fix the problem most of the time, it technically still does not guarantee proper startup in all cases. The safest route here is to check whether the “right” (i.e. system service) udev is running, using a combination of runit's sv status and udevadm control --ping instead of the sleep. However, both of these solutions are rather hacky in my opinion and just needed because void's default init system lacks a proper service supervisor.

For the same reason, this issue can be closed here as keyd itself can't do anything about it.

@rvaiya
Copy link
Owner

rvaiya commented Aug 3, 2022

The second call as a system service is then needed so that udev is monitored by runit (and can for example be cleanly terminated on system shut down).

This makes sense, though it seems like a bit of a kluge.

While this should very likely fix the problem most of the time, it technically still does not guarantee proper startup in all cases.

Indeed, it was intended as stopgap.

The safest route here is to check whether the “right” (i.e. system service) udev is running, using a combination of runit's sv status and udevadm control --ping instead of the sleep.

I considered this, but neither will work. The second will produce a false positive before the first instance of udev has terminated and the first will produce a false positive after the init script has been run but before the second instance has been started.

For the same reason, this issue can be closed here as keyd itself can't do anything about it.

+1.

I'm closing this for now.

@rvaiya rvaiya closed this as completed Aug 3, 2022
@rvaiya
Copy link
Owner

rvaiya commented Aug 3, 2022

^pinging @Barbaross93 who appears to be the void package maintainer.

@Barbaross93
Copy link

Hey @rvaiya, thanks for the ping.

I've been having this problem as well and was just using the Backspace+Esc+Enter key combo and letting the service restart keyd.

Regarding your earlier comment:

The safest route here is to check whether the “right” (i.e. system service) udev is running, using a combination of runit's sv status and udevadm control --ping instead of the sleep.

I considered this, but neither will work. The second will produce a false positive before the first instance of udev has terminated and the first will produce a false positive after the init script has been run but before the second instance has been started.

Por que no los dos?

We could do something like:

#!/bin/sh

sv status udevd || exit 1
udevadm control --ping || exit 1

exec keyd

In this way, we first check to make sure that the udevd service started before checking to see if its actually operational. The problem currently is that eudev for whatever reason doesn't have the --ping flag for the control subcommand. I've been looking over the man page and haven't really found a suitable replacement. Any ideas @herrsimon ?

@rvaiya
Copy link
Owner

rvaiya commented Aug 4, 2022

The problem with that is there is still a window in which the service has started but the old udevd is still running. This may or may not be an issue in practice, but it is still technically a race condition.

The problem currently is that eudev for whatever reason doesn't have the --ping flag for the control

You could check if the process exists using something like pgrep, but the problem above remains.

@Barbaross93
Copy link

Barbaross93 commented Aug 4, 2022

Hmm, I suppose that's true. It's about a one half of a split second wide window with those checks, but it is a window nonetheless.

Well, the good news is that runsv keeps track of the pid of the program that is being supervised. All we need to do instead is confirm that the currently running udevd has the same pid as the one that is being supervised.

So something like this should work:

#!/bin/sh

sv_pid=$(sv check udevd | grep -o '[0-9]*' | head -1)

[ "$sv_pid" = "$(pgrep -x udevd)" ] || exit 1

exec keyd

Obviously it could be cleaner; this is just a quick proof of concept.

@rvaiya
Copy link
Owner

rvaiya commented Aug 4, 2022

That looks like the ticket. You can use the supervise subdirectory in the service directory to get the pid directly instead of parsing sv check. You should probably also redirect the output to a log file to avoid polluting stdout and make soliciting logs easier.

Something like this:

#!/bin/sh

[ "$(cat /var/service/udevd/supervise/pid)" = "$(pgrep -x udevd)" ] || exit 1
exec keyd >> /var/log/keyd.log 2>&1

@tororutsu
Copy link

tororutsu commented Mar 11, 2023

I want to post this here being the only Void related matter I could find, is setup the same, meaning create a keyd folder in /etc and putting the config in there? Asking just because I am new to Void

Edit
Well I tried and it didn't work. So I'm definitely missing something. I tried a reboot with the recommended configuration placed in /etc/keyd/default.conf

Edit 2
Welp I figured it out. I forgot to manually link the service to run 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants