This article (https://news.ycombinator.com/item?id=9793466) makes a case for to sparsely use forking in systemd. Following this advice I try the following python service script:
import time
def run():
with open("/tmp/pysystemd/svc.out","w") as f:
while True:
print("***")
f.write("+++\n")
time.sleep(0.5)
run()
with the following systemd.service script:
[Unit]
Description=Simple zebra service
After=multi-user.target
[Service]
Type=Simple
#ExecStart = /usr/bin/python /tmp/pysystemd/svc.py > /tmp/pysystemd/std.out
ExecStart = /bin/bash -c '/usr/bin/python /tmp/pysystemd/svc.py > /tmp/pysystemd/std.out'
WorkingDirectory = /tmp/pysystemd
[Install]
WantedBy=multi-user.target
Though file /tmp/pysystemd/std.out is created it doesn't contain the expected output... Help appreciated.
Your output problem is probably buffered output. I don't see any output buffer flushes in your program.
See How to flush output of Python print?
Also, redirecting your output to your own log file isn't really the systemd way to do it. If you let your standard output and error go to defaults they will be in the journal and you can get it with journalctl -u my-service
Related
My Dockerfile looks like this:
FROM ubuntu:latest
# ... there is more
ENTRYPOINT ["/root/startup.sh"]
CMD ["choose_random"]
So startup.sh is called with a default paramater if none is provided by the user.
My startup.sh is this one:
#!/bin/bash
# init database etc.
python3 /root/boot.py $1
# snmpd
/usr/sbin/snmpd -f -Lo &
# logger
tail -f /var/log/mylog.log
Now I want to simply call cleanup.py whenever a SIGTERM is received. I saw a few examples but my knowledge about the bash syntax is too little to actually understand and transfer onto my situation. I understand that I need a SIGTERM trap, but how do I actually call the cleanup script a wait until it's finished?
Thanks in advance!
Markus
Have you tried this method?
If it works, you can just change what's inside the function to call your cleanup script.
I have a python bot that I launch in command line and askes me a login to start working.
To skip this step, I add a pipe to directly insert the login in the command as the following and it works :
printf "login" | python_module-py
Now, I want to schedule the bot's launch to don't have to launch it by myself and avoid the bot needing my computer always on.
So I bought a Debian VPS and tried to create a systemd service. I put the command in a shell. Here is my service : (assuming my script is in /home/user and I have all the rights rwx)
[Unit]
Description=Description
After=network.target
[Service]
Type=simple
User=user
Group=group
WorkingDirectory=/home/user
ExecStart=./script.sh
[Install]
WantedBy=multi-user.target
I tried to start it but it failed because of this error :
TypeError: unsupported operand type(s) for /: 'NoneType' and 'int'
I'm almost sure that's because the login is not passed through the pipe and I'm wondering why.
Please, pardon me in advance for my english.
[ALSO TESTED]
Keep the command in the service using /bin/sh -c also giving the same error :
ExecStart=/bin/sh -c '/usr/bin/printf "login" | /usr/bin/python3.7 -m python_module'
The pipe won't work directly in an e.g. ExecStart statement, instead you should use something like ExecStart=/bin/sh -c 'printf "login" | python_module-py' to let /bin/sh handle the pipe.
You should also be able to pass a file as standard input to your service, by setting StandardInput.
I solved the problem.
In my case, I had to use the xargs command to ensure that the text pasted in the pipe is really pasted.
printf "login" | xargs python_module-py
Short Description:
I want to auto-start an executable (opencv binary file, generate via c++) via a systemd service-script after booting, but I am unsuccessful.
I narrowed down the error to the code statement "cv::imshow(....)" which opens a window and displays an image. At this point, the code throws the error: "QXcbConnection: Could not connect to display"
However, if I manually execute the sh-script or the binary, both work fine. I searched around stackoverflow for the most common errors, and I tried to fix all I could found. I am quite sure, that:
My service file actually runs at start (until the error occurred)
Manually execution of the binary file works fine
Manually execution of the .sh-script works fine
I do not have runtime-linking errors (see .sh-script)
I would appreciate any help. Please help me fix the error, and please explain to me, why this error even occurs in the first place. Thanks a lot :)
.
My system:
Machine: Raspberry Pi 3 Model B
Architecture: arm32 / ARMv7
OS: NOOBS
.
My script in /etc/systemd/system/ (test.service):
[Unit]
Description=lalala
[Service]
Type=oneshot
ExecStart=/bin/bash "/home/pi/Desktop/test.sh" start
ExecStop=/bin/bash "/home/pi/Desktop/test.sh" stop
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Moreover, I did execute the following commands:
sudo chmod u+rwx /etc/systemd/system/test.service
sudo systemctl enable test
And if I start the service manually, it runs with the same error output as while autostarting during the boot process:
sudo systemctl enable test
.
My shell script (test.sh):
#!/bin/sh -e
exec 2> /tmp/test.sh.log # send stderr to a log file
exec 1>&2 # send stdout to the same log file
set -x # tell sh to display commands before execution
echo "in script"
start()
{
echo "in start"
sleep 30
LD_LIBRARY_PATH=/usr/local/OpenCV/lib:/usr/local/SFML/lib:/usr/local/curl/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
/home/pi/Desktop/test/main -e &
}
# THE OTHER CASES, NOT PUT IN HERE (stop, status)
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
stop
start
;;
*)
echo "Usage: {start|stop|status|restart}"
exit 1
;;
esac
exit 0
.
Minimal example of my source code: (executable)
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
int main()
{
cv::Mat frame;
cv::namedWindow("result", cv::WINDOW_NORMAL);
## CRASH
return 0;
}
.
P.S:
I am aware that there is a similar thread like this (Run OpenCV script on start with imshow). But as there is no solution for this question, and as I have more information to share, I thought it would be more appropriate to start a new thread.
Luckily, I solved the problem:
The problem was in the configuration of my serviced-script. I did know that I need a DISPLAY variable to the location of the X Display, but I was not aware of the fact that it needs authorization as well. This thread helped me figure it out:
https://unix.stackexchange.com/questions/85244/setting-display-in-systemd-service-file
In short:
Add these to lines to test.service in /etc/serviced/service:
Environment=XAUTHORITY=/home/pi/.Xauthority
Environment=DISPLAY=:0.0
[Unit]
Description=lalala
[Service]
Type=oneshot
ExecStart=/bin/bash "/home/pi/Desktop/test.sh" start
ExecStop=/bin/bash "/home/pi/Desktop/test.sh" stop
RemainAfterExit=yes
Environment=XAUTHORITY=/home/pi/.Xauthority
Environment=DISPLAY=:0.0
[Install]
WantedBy=multi-user.target
Full code
The short version is:
I have a systemd unit that I want to check the return code of a script when I call:
systemctl status service.service
Long version: I had a lsb init script that did exactly that, when status was passed as parameter it called a script that checked the state of several processes and based on the return code the init system returned the state correctly of the software.
Now when adapting the script to systemd I can't find out how to configure this behaviour.
Short answer
This is impossible in systemd. The systemctl status verb always does the same thing, it cannot be overrided per-unit to a custom action.
Long answer
You can write a foo-status.service unit file with Type=oneshot and ExecStart= pointing to your custom status script, and then run systemctl start foo-status. However, this will only provide a zero/nonzero information (any nonzero exit code will be converted to 1).
To get the real exit code of your status script, run systemctl show -pExecMainStatus foo-status, however, if you go this far, then it is simpler to run your script directly.
You can use:
systemctl show -p ExecMainStatus service.service | sed 's/ExecMainStatus=//g'
This will return the exit code of the service.
If you are in control of the code of the service you start / stop that way, then you can easily edit it and save the result in a file.
Otherwise, you can always add a wrapper that does that for you.
#!/bin/sh
/path/to/service and args here
echo $? >/run/service.result
Then your status can be accessed using the contents of that file:
STATUS=`cat /run/service.result`
if test $STATUS = 1
then
echo "An error occurred..."
fi
(Side note: /run/ is only writable by root, use /tmp/ if you are not root.)
I have this service script:
[Unit]
Description=Description
[Service]
Type=simple
EnvironmentFile=/usr/local/etc/env.conf
ExecStart=/usr/local/bin/script.sh
User=my_user
Group=my_user
StandardOutput=journal
StandardError=journal
Restart=on-failure
RestartPreventExitStatus=13
RestartSec=10s
[Install]
WantedBy=multi-user.target
File script.sh looks as follow:
#!/usr/bin/env bash
echo "BOOM #1";
# /home/my_user/script.py
echo "BOOM #2";
File script.py looks as follow:
#!/usr/bin/env python
print "BOOM #P"
The problem is I do not see anything in journalctl if python script is commented. If I uncomment python script I will see in journalctl following lines:
BOOM #1
BOOM #P
I am suspecting that it might be some kind of buffering issue, but this is my guess. Any hint why to do to actually see echoes?