You are not logged in.

#1 2020-12-20 23:01:31

gearhead
Member
Registered: 2018-03-24
Posts: 60

udev and bluetooth - 'btmgmt' will not run in a script, Why?

I have a udev rule which triggers a php script to interrogate newly added bluetooth devices.

/etc/udev/rules.d/60-bluetooth.rules
ACTION=="add", SUBSYSTEM=="bluetooth", RUN+="/srv/http/command/bt-info.php start"
ACTION=="remove", SUBSYSTEM=="bluetooth", RUN+="/srv/http/command/bt-info.php stop"

Basically, when a device is added or removed, I want to add or remove it from a list of 'currently connected' devices in a redis hash.

I have verified that the php script runs and does what I want from the command line. It leverages the command 'btmgmt con' which lists the currently connected bluetooth devices. What does not work is when this same script is triggered by udev. When udev runs this script, it never gets output from the 'btmgmt con' command and the script fails. from the command line it works.

I investigated the possibility of permissions being the issue and noted that btmgmt will only run as a user if steuid bit is set for btmgmt. I get the same response regardless of this setuid bit.
What am I missing here?

#!/usr/bin/php
<?php
// bt-info.php script
// script which runs when a BT device is 'connected' to the systemctl
// bluetoothctl connect MAC will cause this to run as it is triggerd by
// udev rule 60-bluetooth.rules
// Gearhead 12/2020

// common include
ini_set('display_errors', '1');
// ini_set('error_reporting', -1);
ini_set('error_log', '/var/log/runeaudio/refresh_bt.log');
// Connect to Redis backend
$redis = new Redis();
$redis->connect('/run/redis/socket');
require_once('/srv/http/app/libs/runeaudio.php');

sleep (5);

// log file
$myfile = fopen("/srv/http/command/bt-diag.txt", "w") or die("Unable to open file!");

// these are devices we know that have been connected
$knownBT = array_keys($redis->Hgetall("bt-known"));

fwrite($myfile, "Known BTs\n");
    foreach ($knownBT as &$value){
    fwrite($myfile, $value."\n");
}
// this generates an array of currently attached BT devices
$attachedBT = sysCmd("btmgmt con | cut -d ' ' -f 1");
fwrite($myfile, "Attached BTs\n");
    foreach ($attachedBT as &$value){
    fwrite($myfile, $value."\n");
}
fwrite($myfile, $argv[1]."\n");
switch ($argv[1]) {// passed from udev rule as parameter
case "start":
foreach($attachedBT as &$value){
      //is it already connected?
      if (!preg_grep("/$value/",$knownBT)) {
        fwrite($myfile, "\n found new BT MAC \n");
            $found_source = 0;
            $bt_capability=sysCmd('bluetoothctl info '.$value);
            if (preg_grep("/0000110a/s",$bt_capability)) {
            // we have an Audio Source
            fwrite($myfile," New Audio Source\n");
            sysCmd ('mpc stop');
            sysCmd ('systemctl start bluealsa-aplay');
            // add to the list of attached BTs
            $redis->hset("bt-known", $value, 1);
            $found_source = 1;
             }
            if (preg_grep("/0000110b/s",$bt_capability) && !$found_source) {..
            fwrite($myfile," New Audio Sink\n");
            // set up as an alsa device for MPD
            //Need to send this line by line to mpd.conf
            //"audio_output {"
            //    "type            alsa"
            //    "name           ".$name
            //    "device          bluealsa:DEV=".$value.",PROFILE=a2dp"
            //    "auto_resample    no"
            //    "auto_format      no"
            //    "enabled          yes"
            //"}"
            // need to set shairport-sync and spotify to use this 'device' as well
            $redis->hset("bt-known", $value, 0);
            }
            elseif (!$found_source){
            // nothing
            fwrite($myfile, " Keyboard\n");
            $redis->hset("bt-known", $value, 2);
            }
        }
        else {
        fwrite($myfile," Already Known\n");
        }
    }
    fclose($myfile);
break;;
case "stop":
// disconnect
fwrite($myfile, " Checking newly removed BT MAC\n");
foreach($knownBT as &$value){
      //is it just removed?
      if (!preg_grep("/$value/",$attachedBT)) {
          // this is the removed device
          $type = $redis->Hmget("bt-known", [$value]);
          switch ($type[$value]) {
          case 0:
            // sink
            print_r(" sink removed\n");
            fwrite($myfile, " sink removed\n");
            // remove the lines from mpd.conf
            // remove from mpd.conf and change AO to something else
          break;;
          case 1:
            // source
            print_r(" source removed\n");
            fwrite($myfile, " source removed\n");
            sysCmd ('systemctl stop bluealsa-aplay');
          break;;
          case 2:
             fwrite($myfile, " other removed\n");
            // not audio
          break;;
          }
        $redis->Hdel("bt-known", $value);
        }
    }
    fclose($myfile);
break;;
}
?>

Last edited by gearhead (2020-12-25 16:48:47)

Offline

#2 2020-12-20 23:41:15

loqs
Member
Registered: 2014-03-06
Posts: 18,861

Re: udev and bluetooth - 'btmgmt' will not run in a script, Why?

Have you examined the restrictions in /usr/lib/systemd/system/systemd-udevd.service

Offline

#3 2020-12-21 01:08:03

gearhead
Member
Registered: 2018-03-24
Posts: 60

Re: udev and bluetooth - 'btmgmt' will not run in a script, Why?

I had not, but just perused them I see nothing in the basic list that jumps out at me.

#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Rule-based Manager for Device Events and Files
Documentation=man:systemd-udevd.service(8) man:udev(7)
DefaultDependencies=no
After=systemd-sysusers.service systemd-hwdb-update.service
Before=sysinit.target
ConditionPathIsReadWrite=/sys

[Service]
DeviceAllow=block-* rwm
DeviceAllow=char-* rwm
Type=notify
# Note that udev also adjusts the OOM score internally and will reset the value internally for its workers
OOMScoreAdjust=-1000
Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket
Restart=always
RestartSec=0
ExecStart=/usr/lib/systemd/systemd-udevd
ExecReload=udevadm control --reload --timeout 0
KillMode=mixed
TasksMax=infinity
PrivateMounts=yes
ProtectClock=yes
ProtectHostname=yes
MemoryDenyWriteExecute=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallFilter=@system-service @module @raw-io
SystemCallErrorNumber=EPERM
SystemCallArchitectures=native
LockPersonality=yes
IPAddressDeny=any
WatchdogSec=3min

I do not understand all of these settings, but this php script should be running as root, right? It appears so as the log file that is created is created as root. The command works on the cli:

# btmgmt con | cut -d ' ' -f 1
08:B7:38:11:A6:C2
# ls -al bt*
-rw-r--r-- 1 root root   29 Dec 20 18:06 bt-diag.txt

I want to put this value (MAC addresses) in an array so I can continue to process this info. It does not seem to have a problem executing 'systemctl stop *'or start * from the php. As far as permissions, udev can actually mount file systems from udev rules, so piping the output from a single command should be possible, no? What am I missing?

If I add a BT keyboard and allow udev to trigger it, I get this in my log file:

# cat bt-diag.txt
Known BTs
Attached BTs
start

if I trigger it from the cli, I get this:

# cat bt-diag.txt
Known BTs
Attached BTs
08:B7:38:11:A6:C2
start

 found new BT MAC
 Keyboard

Confused...

Offline

#4 2020-12-21 01:40:53

loqs
Member
Registered: 2014-03-06
Posts: 18,861

Re: udev and bluetooth - 'btmgmt' will not run in a script, Why?

What if you override the unit disabling SystemcCallFilter and RestrictAddressFamilies?

Using mount in udev see Udev#Mounting_drives_in_rules.

Offline

#5 2020-12-21 21:11:12

gearhead
Member
Registered: 2018-03-24
Posts: 60

Re: udev and bluetooth - 'btmgmt' will not run in a script, Why?

No change. the journal continually says:

Dec 21 10:59:54 rune64 systemd-udevd[388062]: hci0:12: Process '/srv/http/command/bt-trig.sh stop' failed with exit code 1.

did a search on this and think I will try making this a service file and have udev trigger that and see how that works... This seems really strange that it doesn't just work.

Offline

#6 2020-12-21 21:38:18

loqs
Member
Registered: 2014-03-06
Posts: 18,861

Re: udev and bluetooth - 'btmgmt' will not run in a script, Why?

Minimal systemd-udevd.service if that still fails can you determine which call in the script is causing it to exit with code 1?

[Unit]
Description=Rule-based Manager for Device Events and Files
Documentation=man:systemd-udevd.service(8) man:udev(7)
DefaultDependencies=no
After=systemd-sysusers.service systemd-hwdb-update.service
Before=sysinit.target
ConditionPathIsReadWrite=/sys

[Service]
Type=notify
# Note that udev also adjusts the OOM score internally and will reset the value internally for its workers
OOMScoreAdjust=-1000
Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket
Restart=always
RestartSec=0
ExecStart=/usr/lib/systemd/systemd-udevd
ExecReload=udevadm control --reload --timeout 0
KillMode=mixed
TasksMax=infinity
WatchdogSec=3min

Offline

#7 2020-12-21 22:40:24

gearhead
Member
Registered: 2018-03-24
Posts: 60

Re: udev and bluetooth - 'btmgmt' will not run in a script, Why?

Just read your response. Will try it tonight and see what is going on.

On a different tack, I thought I'd try to get it to run as a systemd service. The btuetooth.rules is:

ACTION=="add", SUBSYSTEM=="bluetooth", ENV{SYSTEMD_WANTS}="bt-device@.service start"
ACTION=="remove", SUBSYSTEM=="bluetooth", ENV{SYSTEMD_WANTS}="bt-device@.service stop"

and the service file is called bt-device@.service:

[Unit]
Description= Bluetooth device add/remove
After=multi-user.target network.target redis.target

[Service]
Environment="SCRIPT_ARGS=%I"
ExecStart=/srv/http/command/bt-info.php $SCRIPT_ARG

[Install]
WantedBy=multi-user.target

now I get even stranger response. in the 'log' it shows:

Known BTs
Attached BTs
sys/devices/platform/soc/3f201000.serial/serial0/serial0-0/bluetooth/hci0/hci0:12

and the journal shows this:

Dec 21 16:21:04 rune64 systemd[1]: Started Bluetooth device add/remove.
Dec 21 16:21:04 rune64 kernel: hid-generic 0005:04CA:0079.0014: unknown main item tag 0x0
Dec 21 16:21:04 rune64 kernel: input: HP Bluetooth Keyboard K4000 Keyboard as /devices/platform/soc/3f201000.serial/serial0/serial0-0/bluetooth/hci0/hci0:12/0005:04CA:0079.0014/input/input60
Dec 21 16:21:04 rune64 kernel: input: HP Bluetooth Keyboard K4000 Consumer Control as /devices/platform/soc/3f201000.serial/serial0/serial0-0/bluetooth/hci0/hci0:12/0005:04CA:0079.0014/input/input61
Dec 21 16:21:04 rune64 kernel: input: HP Bluetooth Keyboard K4000 System Control as /devices/platform/soc/3f201000.serial/serial0/serial0-0/bluetooth/hci0/hci0:12/0005:04CA:0079.0014/input/input62
Dec 21 16:21:04 rune64 kernel: hid-generic 0005:04CA:0079.0014: input,hidraw1: BLUETOOTH HID v5.01 Keyboard [HP Bluetooth Keyboard K4000] on 43:45:c0:00:1f:ac
Dec 21 16:21:04 rune64 systemd-logind[3821]: Watching system buttons on /dev/input/event7 (HP Bluetooth Keyboard K4000 System Control)
Dec 21 16:21:04 rune64 systemd-logind[3821]: Watching system buttons on /dev/input/event5 (HP Bluetooth Keyboard K4000 Keyboard)
Dec 21 16:21:09 rune64 bt-info.php[439815]: Known BT
Dec 21 16:21:09 rune64 bt-info.php[439815]: Attached BT
Dec 21 16:21:09 rune64 bt-info.php[439815]: sys/devices/platform/soc/3f201000.serial/serial0/serial0-0/bluetooth/hci0/hci0:12
Dec 21 16:21:09 rune64 systemd[1]: bt-device@sys-devices-platform-soc-3f201000.serial-serial0-serial0\x2d0-bluetooth-hci0-hci0:12.service: Succeeded.

Hmmm, I want to pass 'start' or 'stop' not 'sys/devices/platform/soc/3f201000.serial/serial0/serial0-0/bluetooth/hci0/hci0:12'...
how do I get udev to pass the 'start' or 'stop' to the service? Regardless of this, It appears that the 'btmgmt' command is also not passing the results of the 'btmgmt con' to the script when called this way, either, as it does not actually get the MAC address of the device. When run from cli, it is supposed to do this:

Known BT
Attached BT
08:B7:38:11:A6:C2
start

and the 'log' is supposed to show this

Known BTs
Attached BTs
08:B7:38:11:A6:C2
start

 found new BT MAC
 Keyboard

it seems this btmgmt command is the problem... Is there another way to get a readout of the MAC addresses attached to the machine?

Even when I try to not be so clever and do not try to pass any arguments, I get the same thing. udev rule just triggers the service to tun and the service is

ExecStart=/srv/http/command/bt-info.php start

The result in the log is:

Dec 22 12:18:23 rune64 systemd[1]: Started Bluetooth device add/remove.
Dec 22 12:18:28 rune64 bt-info.php[422710]: Known BT
Dec 22 12:18:28 rune64 bt-info.php[422710]: Attached BT
Dec 22 12:18:28 rune64 bt-info.php[422710]: start
Dec 22 12:18:28 rune64 systemd[1]: bt-device.service: Succeeded.

The log shows the same thing. It never gets the result of the 'btmgmt con' command. I can get it to read from redis but never take the result of the btmgmt command. I can probably re-write the script so that it does not need a 'start' or 'stop' command passed, but it needs to be able to get the result of the 'btmgmt con' command unless there is another way to know the MAC of the bt device connected.

Last edited by gearhead (2020-12-22 18:32:27)

Offline

#8 2020-12-22 18:08:29

gearhead
Member
Registered: 2018-03-24
Posts: 60

Re: udev and bluetooth - 'btmgmt' will not run in a script, Why?

@logs The suggested udev.service file causes a core dump:

Dec 22 11:31:23 rune64 systemd[1]: systemd-udevd.service: Scheduled restart job, restart counter is at 5.
Dec 22 11:31:23 rune64 systemd[1]: Stopped Rule-based Manager for Device Events and Files.
Dec 22 11:31:23 rune64 systemd[1]: systemd-udevd.service: Start request repeated too quickly.
Dec 22 11:31:23 rune64 systemd[1]: systemd-udevd.service: Failed with result 'core-dump'.
Dec 22 11:31:23 rune64 systemd[1]: Failed to start Rule-based Manager for Device Events and Files.
Dec 22 11:31:23 rune64 systemd[1]: systemd-udevd-kernel.socket: Failed with result 'service-start-limit-hit'.
Dec 22 11:31:23 rune64 systemd[1]: systemd-udevd-control.socket: Failed with result 'service-start-limit-hit'.
Dec 22 11:31:23 rune64 systemd-coredump[393911]: [?] Process 393906 (systemd-udevd) of user 0 dumped core.

                                                 Stack trace of thread 393906:
                                                 #0  0x0000007fa9f6891c __brk (/usr/lib/ld-2.32.so + 0x1791c)
                                                 #1  0x0000007fa9f674d8 _dl_sysdep_start (/usr/lib/ld-2.32.so + 0x164d8)
                                                 #2  0x0000007fa9f52bf8 _dl_start_final (/usr/lib/ld-2.32.so + 0x1bf8)
                                                 #3  0x0000007fa9f52e38 _dl_start (/usr/lib/ld-2.32.so + 0x1e38)
                                                 #4  0x0000007fa9f5210c _start (/usr/lib/ld-2.32.so + 0x110c)
                                                 #5  0x0000007fa9f5210c _start (/usr/lib/ld-2.32.so + 0x110c)
Dec 22 11:31:23 rune64 systemd[1]: systemd-coredump@14-393907-0.service: Succeeded.

I tried comparing the snippet you gave to the default one and through trial and error I was able to get it to run but had to add a line:

SystemCallFilter=@system-service @module @raw-io

When I add this line to the service file, though, I get the same result. I no longer get the 'failed with exit code 1' error in the log, but the script never gets the result of the command "btmgmt con | cut -d ' ' -f 1". This is critical as I want to know what the MAC address of the devices which are connected, according to the btmgmt command. When I run the script as root from the cli it gets this result every time but never when run as triggered by udev.

Offline

#9 2020-12-22 22:57:14

gearhead
Member
Registered: 2018-03-24
Posts: 60

Re: udev and bluetooth - 'btmgmt' will not run in a script, Why?

Ok, so I am simplifying the script to bash instead of php and to only touch the part I am interested in (the btmgmt command). It is almost as basic as this example:
https://opensource.com/article/18/11/udev
the udev rule:

ACTION=="add", SUBSYSTEM=="bluetooth", RUN+="/srv/http/command/trigger.sh"
ACTION=="remove", SUBSYSTEM=="bluetooth", RUN+="/srv/http/command/trigger.sh"

the trigger.sh function:

#!/usr/bin/bash
/usr/bin/date >> /srv/http/command/udev.log
/usr/bin/btmgmt con >> /srv/http/command/udev.log

I am running the simplified udev service file posted above. If I run the script from cli, it works. I start from nothing connected and connect my keyboard:

root@rune64(rw):/srv/http/command# cat udev.log
Tue Dec 22 16:32:09 CST 2020
root@rune64(rw):/srv/http/command# btmgmt con
08:B7:38:11:A6:C2 type BR/EDR
root@rune64(rw):/srv/http/command# ./trigger.sh
root@rune64(rw):/srv/http/command# cat udev.log
Tue Dec 22 16:32:09 CST 2020
Tue Dec 22 04:32:37 PM CST 2020
08:B7:38:11:A6:C2 type BR/EDR

The first line shows that the script ran as it got the date and time but nothing from the btmgmt command. The second line is me running the 'btmgmt con' command to show that there is actually a BT device connected that was not 'detected' when udev triggered the script. Then I ran the script from cli. Sure enough, the results of that command get recorded in the log file. Why is this command off limits to udev? If there is another way for me to know the MAC of an attached device I am all ears.

When I reset the systemd-udevd.service file to the original one, I get the 'failed with exit code 1.' (expected) in the log but the *same exact* result in the log file. Nothing. No 'Permissions error' no 'illegal function' nothing helpful to diagnose anything. This is extremely frustrating. I get that BT is a security issue, but telling a regular user the MAC address of the attached device is too much? That and udev, which is supposed to run as root, cannot see it either? This makes no sense to me.

Last edited by gearhead (2020-12-23 00:01:18)

Offline

#10 2020-12-23 18:20:19

gearhead
Member
Registered: 2018-03-24
Posts: 60

Re: udev and bluetooth - 'btmgmt' will not run in a script, Why?

So, digging through this, it seems that systemd is what is blocking this command. How do I unblock this command from systemd?

Offline

#11 2020-12-25 16:58:01

gearhead
Member
Registered: 2018-03-24
Posts: 60

Re: udev and bluetooth - 'btmgmt' will not run in a script, Why?

I renamed the thread.

I did a lot more trials and it seems that btmgmt will not run when run in a script. Period.

The Bluez group wants me to adapt to dbus which is a lot of learning for me but, regardless, I still want to know why 'btmgmt' will not run (or maybe it is just that it does not report anything?) except as root from cli.
It is not the directory it is in (path concerns) as it and bluetoothctl are both in /usr/bin and bluetoothctl works fine in a script or from cli as long as the permissions of the script running it are correct. Both are owned by root and have the same permissions. I am using' btmtmg con', but 'btmgmt info' will also not run and it gives no errors. It just will not report anything except to cli. No messages in the journal, I think it runs, but it just does not report anything back to the script.

Why?

Offline

Board footer

Powered by FluxBB