firecat53 / networkmanager-dmenu

Control NetworkManager via dmenu
MIT License
783 stars 74 forks source link

Add toggle for Bluetooth #90

Closed The-Compiler closed 3 years ago

The-Compiler commented 3 years ago

I use NetworkManager for Bluetooth tethering with Android - for some reason, NetworkManager itself doesn't provide any way to disable/enable Bluetooth radios, even though it can connect to them just fine, if they are enabled.

Thus, I always need to first use rfkill or blueman to enable Bluetooth before I can connect with networkmanager_dmenu. With this change, I can do it all right from rofi.


Admittedly, this is a bit of a hack. I first thought I'd use the commandline output of the rfkill tool first, but its output seems to have changed a bit between different util-linux versions. It does offer JSON output since utils-linux v2.31.1 (December 2017) which does seem to be part of Debian Buster (the current stable) and Ubuntu 18.04 LTS, but not e.g. Ubuntu 16.04 LTS which is still supported until 2021.

Thus, I decided to use the kernel interfaces directly, which is a bit more stable and avoids having to spawn a subprocess which essentially does the same in C anyways.

I'm not aware of a way to do the same thing in a more high-level way (e.g. via Networkmanager/DBus), so that's what I ended up with looking at the Blueman code. Blueman does actually use DBus if ConnMan is available, but since that'd probably conflict with NetworkManager I doubt anyone using networkmanager_dmenu will have it running.

firecat53 commented 3 years ago

Ah, that's unfortunate. I'm in the wheel group, which apparently allows me to run Networkmanager commands as $USER.

$ ./networkmanager_dmenu 
Traceback (most recent call last):
  File "./networkmanager_dmenu", line 859, in <module>
    run()
  File "./networkmanager_dmenu", line 855, in run
    sel()
  File "./networkmanager_dmenu", line 284, in __call__
    self.func(*self.args)
  File "./networkmanager_dmenu", line 593, in toggle_bluetooth
    with open('/dev/rfkill', 'r+b', buffering=0) as f:
PermissionError: [Errno 13] Permission denied: '/dev/rfkill'

Any idea how best to fix and document this? Maybe only show the option when a user has permission?

The-Compiler commented 3 years ago

I'm in the rfkill group, which allows me to write to /dev/rfkill as $USER :wink:

$ ls -l /dev/rfkill
crw-rw-r-- root rfkill 0 B Sun Sep 13 11:17:38 2020 /dev/rfkill

But yeah, I suppose that should be documented at least - only showing the option when the user has permission seems like a little bit much magic, but I'm not sure.

firecat53 commented 3 years ago

This article shows a polkit snippet for rfkill permissions through dbus. I don't have conman installed yet I can still disable bluetooth through blueman. Would it be possible to somehow use that to enable the rfkill permissions as an additional alternative? Do all distros have an rfkill group? I don't have an Ubuntu desktop available, but my Ubuntu server doesn't have that group.

As I'm looking, I actually don't know what is giving me permission on my Arch desktop to turn Bluetooth on and off through the blueman applet!

The-Compiler commented 3 years ago

Hmm, good point, blueman did work before I added myself to the rfkill group!

It looks like the Archlinux blueman package comes with a /usr/share/polkit-1/rules.d/blueman.rules:

/* Allow users in wheel group to use blueman feature requiring root without authentication */
polkit.addRule(function(action, subject) {
    if ((action.id == "org.blueman.network.setup" ||
         action.id == "org.blueman.dhcp.client" ||
         action.id == "org.blueman.rfkill.setstate" ||
         action.id == "org.blueman.pppd.pppconnect") &&
        subject.isInGroup("wheel")) {
        return polkit.Result.YES;
    }
});

and the blueman rfkill code does:

self.confirm_authorization(caller, "org.blueman.rfkill.setstate")

with confirm_authorization being defined here: https://github.com/blueman-project/blueman/blob/b4f0738d5a9a360e6f8fef0e2a0bc9d4f9b38383/blueman/main/MechanismApplication.py#L86-L101

It looks like that's not some kind of permission elevation though, just an additional "voluntary" permissions check - what actually makes it work is that /usr/lib/blueman/blueman-mechanism is running as root... Probably via /usr/lib/systemd/system/blueman-mechanism.service somehow? Then again I never started it by hand, this is a bit concerning if I'm honest!

(edit: Ah, I guess via /usr/share/dbus-1/system-services/org.blueman.Mechanism.service which starts that systemd service... Still, lots of crazy stuff going on there!)

However, I hope we agree we don't want this kind of complexity in networkmanager-dmenu :sweat_smile:

The-Compiler commented 3 years ago

I now added a notification when this happens:

notification

I've had to change notify a bit so I can pass a detailed text as well as the urgency - IMHO it makes sense to use "critical" for error messages (the Desktop Notifications Specification says "Critical notifications should not automatically expire, as they are things that the user will most likely want to know about. They should only be closed when the user dismisses them, for example, by clicking on the notification.").

I've also pushed a commit to change all error messages to critical urgency - if you'd prefer this in a separate PR or not at all, please let me know and I'll revert that commit.

firecat53 commented 3 years ago

Looks good! Thanks for the input. I like the critical notifications for errors.