Command to run - Best practices
Best practices for "command to run
Many configuration actions in qbee allow you to invoke a "command to run" after a file is distributed or a process is restarted. This document shows you some best practices how to use this.
When files are being written on the system by the qbee agent, there are no execution bit set. This means that files are not executable by default. So if the files you have distributed are scripts, then best practice is to call the interpreter of the script (eg. bash, python etc.).
Executing scripts with command to run
The best practice is to call the interpreter of the script (eg. bash, python etc.)
As an example, if you distribute a bash script called /usr/local/bin/myscript.sh, then this would be executed with:
bash /usr/local/bin/myscript.sh
Some scripts and executables do not properly close stdin/stderr/stdout. So if you experience that execution of scripts hang then it might be necessary to explicitly close stdin/stderr/stdout. This is done as follows:
bash /usr/local/bin/myscript.sh > /dev/null < /dev/null 2>&1
Understanding stdin and stdout
Linux uses stdin/stderr/stdout to handle input, output and error information. Some scripts and executables do not properly close stdin/stderr/stdout. In order to prevent these from hanging they can be routed to /dev/null. This prevents the system from hanging, but then the qbee-agent will not be able to capture any output from the command execution. This command chain invokes the following parts:
> /dev/null
close stdout
< /dev/null
close stdin
2>&1
send stderr to stdout
Make sure your binaries are executable
Note that if you are calling a system binary or have indeed distributed your own binaries, you will need to make them executable by setting the executable bits. Eg. a binary built using golang called /usr/local/bin/my-golang-bin:
chmod 755 /usr/local/bin/my-golang-bin && /usr/local/bin/my-golang-bin
System binaries already installed on the system can be called directly as they are already set to be executable. Eg. restarting a the ssh service
systemctl restart sshd
Running commands as user
All commands are run as the administrative user (root). If you need to run your command as a different user, you need to use either sudo or su:
sudo -u myuser bash /usr/local/bin/myscript.sh
or
su -c 'bash /usr/local/bin/myscript.sh' -l myuser
Running commands in the background
If you need to run processes in the background using an & , the best practice is to do this from a script. Also, it is often required to close the stdin/stderr/stdout file descriptors:
Eg. /usr/local/bin/myscript.sh
#!/usr/bin/env bash
/usr/bin/my-background-process &
And then call it in qbee configuration as follows:
bash /usr/local/bin/myscript.sh > /dev/null < /dev/null 2>&1
Please be careful with chmod on templated files
Setting permissions with chmod on templated files currently doesn’t work as the permissions will be set to the same as the template on disk (which is -rw-r--r--). You should therefore avoid setting permissions on files produced by templates as this will be constantly be reverted to be consistent with the template, potentially causing an execution loop. Best practice is to read them into the main script. See an example here:
node-red-service.sh
#!/usr/bin/env bash
source /home/pi/node-red-service.conf
sudo systemctl $STATE nodered.service
This is including the templated config file with the source command. The node-red-service.conf.tmpl could look like this:
This script is uploaded to the qbee file manager with the name node-red-service.sh
.
Then the template file is created and uploaded as node-red-service.tmpl
.
node-red-service.tmpl
# node-red service template for node-red-service script
# run once trigger {{epoch}}
# use start,stop,restart, disable, enable
export STATE="{{service-state}}"