Cant terminate QProcess running hcitool on linux? - linux

I am trying to use the Linux/Bluez tool: hcitool, to make a BLE scan from QT.
I use the following to open the process:
QString program = "sudo stdbuf -oL hcitool -i hci0 lescan";
hcitool = new QProcess();
connect(hcitool, SIGNAL(started()), this, SLOT(hcitool_started()));
connect(hcitool, SIGNAL(finished(int)), this, SLOT(hcitool_finished(int)));
connect(this, SIGNAL(kill_hcitool()), hcitool, SLOT(kill()));
connect(this, SIGNAL(terminate_hcitool()), hcitool, SLOT(terminate()));
hcitool->start(program, QProcess::Unbuffered | QProcess::ReadWrite);
Then afterwards I continously read from the process to store all the results:
QString result = hcitool->readLine();
After a couple of seconds, I want to stop the hcitool process, and this is where things goes wrong. I've tried the terminate signal, the kill signal, writing ^C to the process, but nothing works.
Actually the kill signal finishes the process. But not in a good way because afterwards my hci0 is hold up, and I can't start hcitool again before the hci0 has been reset with:
hciconfig hci0 down
hciconfig hci0 up
So any ideas to how I can terminate this process right?

(moving from the comment)
A trick that usually work is to just close() the stream, that should give a SIGHUP or SIGPIPE to the child process, which is often handled more gracefully.

Related

Reading from a USB device using bash and raspberry pi 3?

I'm trying to catch the response from a device that is connected to the USB.
With this code:
sudo stty -F /dev/ttyUSB0 speed 115200 cs8 -cstopb -parenb -echo
sudo stty raw; cat > /home/received.log < /dev/ttyUSB0
echo "Monitor started"
On port ready, send the command request to the device
echo -en '\x5A\x00\x00\x0D\x0A\x71' > /dev/ttyUSB0
then read the log and paste the response in another file converting it properly
xxd -plain /home/received.log > /home/output.txt
so I can show the data,
cat -v < /home/output.txt
But I'm needing something more stable, that code is showing me this error:
stty: 'standard input': Inappropriate ioctl for device
which is weird, because it was working...
I would like to catch the response and store it in a variable.
i suggest getting rid of the stty raw.
Unless you are sending/receiving special characters that the tty subsystem will process, which is unlikely - because you said it is working - you almost certainly won't need it.
If it did work, you might find some undesirable side effects such as:
backspace does not erase a character that you miss-typed
control-c won't terminate your process
and other things that you might rely on in your terminal session.
FWIW, i am doing a similar thing with Arduino to Mac, Windows (cygwin) & Linux (read from the usb/serial port) and i have not been tempted to stty raw in any of those environments.

Running an RFCOMM server in the background using subprocess

I have been trying various ways of getting an RFCOMM server up and running using python's subprocess package for some time now but have hit a wall. What I want to do is start a process in the background with this RFCOMM server running and getting the usual return from the command which is something like "Connected /dev/rfcomm0 to xx:xx:xx:xx:xx:xx on channel n" and "Press CTRL-C for hangup". The thing is, the process starts with two different ways I have tried it but getting this return and letting it sit in the background is an issue for me.
Alternative 1:
ref = Popen("sudo rfcomm connect 0 xx:xx:xx:xx:xx:xx 1 -i hci0 &", stdout=PIPE, stderr=PIPE, shell=True)
The above starts the RFCOMM server but either if I use communicate() or try to read from ref.stdout/stderr the program freezes.
Alternative 2:
res = run("sudo rfcomm connect 0 xx:xx:xx:xx:xx:xx 1 -i hci0 &", stdout=PIPE, stderr=PIPE, shell=True)
Same thing here, if I try to access the CompletedProcess objects stdout/stderr fields, the program freezes and refuses to continue. The RFCOMM server starts but the script does not terminate and I cannot read the stdout/stderr fields.
Alternative 3:
res = run(["sudo", "rfcomm", "connect", "0", "xx:xx:xx:xx:xx:xx", "1", "-i", "hci0"], stdout=PIPE, stderr=PIPE)
Same thing here, even though shell is left at its default and the command is not to be run in the background, the run function does not return and the script does not terminate. The RFCOMM server starts but the script does not terminate and I cannot read the stdout/stderr fields.
So, what I want is to start this RFCOMM server in the background, read the two lines it should show, and continue with my program so that I can poll its availability through other commands.
Quite strange answer in my opinion for this one, but the answer lied in that I assigned stdout and stderr to the PIPE constant. This for some reason "binds" the script to the execution and will not let it finish. When removing these two assignments the script finishes happily and I have to find another way to get the output that I in my question listed I wanted, the "Connected ..." message that is. Seems like there is no way for me to retrieve it for now.
Script that works and starts the server in the background:
mac_addr = "xx:xx:xx:xx:xx:xx"
run("sudo rfcomm connect 0 " + mac_addr + " 1 -i hci0 &", shell=True)
So, as you can see, I just removed stdout and stderr... If someone in turn can answer why that would essentially "lock" the script I would be grateful.

Scan bluetooth low energy using hcitool?

When I run this command which makes the ble device scanning for just 5 seconds only:
$ sudo timeout 5s hcitool -i hci0 lescan
the output is shown in the terminal screen.
But when I redirect the output to a file to save the addresses of the advertising devices, every time I run the command I find the file is empty and the output isn't visible in the terminal nor in the file.
The command I used:
$ sudo timeout 5s hcitool -i hci0 lescan > file.txt
What do I have to do in order to make hcitool correctly redirect its ouput to the file?
timeout by default sends a SIGTERM to the program. Looks like hcitool doesn't handle that gracefully. Instead use SIGINT (equivalent to ctrl-c).
sudo timeout -s SIGINT 5s hcitool -i hci0 lescan > file.txt

How to initialize Bluetooth in a startup script with Yocto Poky Linux

I have a script that initializes my bluetooth setup on an Intel Edison. It allows pairing and connecting to this headless machine running Yocto Poky Linux. It was suggested that I put the startup script in /etc/init.d and run update-rc.d myscript.sh defaults. The script ran but it didn't work (generated boot errors saying bluetooth device not found) because Bluetooth had not started yet. I did some reasearch and after removing my links I did update-rc.d myscript.sh defaults 99 which was claimed to run the script last but it did't make any differrence -- it still ran in the same place in the boot sequence. I verified that the links had S99 on them so it seemed like they were set up correctly. There is another post on SO asking a similar question but that was a Ubuntu system where mine is Poky Linux. That solution suggested putting the startup script in a directory that does not exist on my system. There were other suggestions, putting it in rc.local, which I did and got the same result, it runs before Bluetooth is initialized.
Here is my script. My program is called nmea_thread and is run last. Everything else is initializing Bluetooth.
#!/bin/sh
/usr/sbin/rfkill unblock bluetooth
/usr/bin/hciconfig hci0 up
/usr/bin/hciconfig hci0 piscan
/usr/bin/hciconfig hic0 sspmode 0
/home/root/simpleAgent/simple-agent &
/home/root/nmea_thread
Often bluetooth is initialized asynchronously, so you can't be sure that your script will be run after hci0 is added. Good solution is to wait for BT initialization in background:
#!/bin/bash
if [ "$1" != "background" ]; then
$0 background &
else
#Wait until BT is initialized
for ((i = 0; i <= 100; i++)) do
hciconfig hci0 && break
usleep 100000
done
/usr/sbin/rfkill unblock bluetooth
/usr/bin/hciconfig hci0 up
/usr/bin/hciconfig hci0 piscan
/usr/bin/hciconfig hic0 sspmode 0
/home/root/simpleAgent/simple-agent &
/home/root/nmea_thread
fi
hciattach is the correct way.
syntax
hciattach /dev/ttyAMA0 bcm43xx 3000000
you need to flash the driver first before initializing it. Currently i don't remember how, but thats how i made it with raspberry pi and yocto.
Note if you use systemV, you can do call it from a script and it will work
Using SystemD, you need to make it in a service and wait. Falshing should be done in the two cases.

How to stop ffmpeg remotely?

I'm running ffmpeg on another machine for screen capture. I'd like to be able to stop it recording remotely. FFMPEG requires that q is pressed to stop encoding as it has to do some finalization to finish the file cleanly. I know I could kill it with kill/killall however this can lead to corrupt videos.
Press [q] to stop encoding
I can't find anything on google specifically for this, but some there is suggestion that echoing into /proc//fd/0 will work.
I've tried this but it does not stop ffmpeg. The q is however shown in the terminal in which ffmpeg is running.
echo -n q > /proc/16837/fd/0
So how can I send a character to another existing process in such a way it is as if it were typed locally? Or is there another way of remotely stopping ffmpeg cleanly.
Here's a neat trick I discovered when I was faced with this problem: Make an empty file (it doesn't have to be a named pipe or anything), then write 'q' to it when it's time to stop recording.
$ touch stop
$ <./stop ffmpeg -i ... output.ext >/dev/null 2>>Capture.log &
$ wait for stopping time
$ echo 'q' > stop
FFmpeg stops as though it got 'q' from the terminal STDIN.
Newer versions of ffmpeg don't use 'q' anymore, at least on Ubuntu Oneiric, instead they say to press Ctrl+C to stop them. So with a newer version you can simply use 'killall -INT' to send them SIGINT instead of SIGTERM, and they should exit cleanly.
Elaborating on the answer from sashoalm, i have tested both scenarios, and here are the results:
My experiments shows that doing
killall --user $USER --ignore-case --signal INT ffmpeg
Produces the following on the console where ffmpeg was running
Exiting normally, received signal 2.
While doing
killall --user $USER --ignore-case --signal SIGTERM ffmpeg
Produces
Exiting normally, received signal 15.
So it looks that ffmpeg is fine with both signals.
System: Debian GNU/Linux 9 (stretch), 2020-02-28
You can also try to use "expect" to automate the execution and stop of the program. You would have to start it using some virtual shell like screen, tmux or byobu and then start the ffmpeg inside of it. This way you would be able to get again the virtual shell screen and give the "q" option.
Locally or remotely start a virtual shell session, lets say with "screen". Name the session with -S option, like screen -S recvideo Then you can start the ffmpeg as you like. You can, optionally, detach from this session with a Ctrl+a + d.
Connect to the machine where the ffmpeg is running inside the screen (or tmux or whatever) and reconnect to it: screen -d -RR recvideo and then send the "q"
To do that from inside a script you can then use expect, like:
prompt="> "
expect << EOF
set timeout 20
spawn screen -S recvideo
expect "$prompt"
send -- "ffmpeg xxxxx\r"
set timeout 1
expect eof
EOF
Then, in another moment or script point or in another script you recover it:
expect << EOF
set timeout 30
spawn screen -d -RR recvideo
expect "$prompt"
send -- "q"
expect "$prompt"
send -- "exit\r"
expect eof
EOF
You can also automate the whole ssh session with expect, passing a sequence of commands and "expects" to do what you want.
The question has already been answered for Linux, but it came up when I was looking for the windows equivalent, so I'm gonna add that to the answers:
On powershell, you start the process like this:
$((Start-Process ffmpeg -passthru -argument "FFMPEG_ARGS").ID)
This sends back the PID of the FFMPEG process that you can store in a variable, or echo, and then you send the windows equivalent of sigint (Ctrl + C) using taskkill
taskkill /pid FFMPEG_PID
I tried with Stop-Process (which is what comes up when looking how to do this on Google) but it actually kills the process. (And yes, taskkill doesn't kill it, it gently asks the process to stop... good naming :D)

Resources