Redirect systemd script output to console - linux

How can I redirect output of script which is run by systemd ExecStart script to boot console?
I need to debug what is wrong with script until boot but I can't use journalctl because it's embedded linux with ROM rootfs.
Now my .service file looks like:
[Unit]
Description=Init script
After=network.target
Before=getty#tty1.service
[Service]
Type=oneshot
ExecStart=/usr/lib/systemd/test_init_script
ExecStartPre=/usr/bin/echo -e \033%G
ExecReload=/bin/kill -HUP $MAINPID
WorkingDirectory=/
Enviroment=TERM=xterm
[Install]
WantedBy=multi-user.target
test_init_script:
#!/bin/sh -
echo "Test!"
And it didn't work, after boot I receive message:
#systemctl status test_init_script.service
test_init_script.service - Init script
Loaded: loaded (/usr/lib/systemd/test_init_script.service)
Active: failed (Result: exit-code) Since Thu 1970-01-01 08:26:03 CST; 19s ago
Process: 170 ExecStartOre=/usr/bin/echo -e -G (code=exited, status=203/EXEC)
Did anyone know how to redirect script output to terminal?

Ok, so I resolve this problem by building image with changed file:
/etc/journald.conf
Where I change storage option to volatile:
Storage=volatile
This option means that all journald data will be store in RAM so this is a workaround for read only file system.
Please refer to journald manual page to see more options.

Related

Raspberry pi: Associate "screen" and Systemd to auto-start a minecraft server at pi's start

I'm new in raspberry pi programming, and i want to be able to launch a minecraft server at the start of the pi.
For that, I've already loocked at Systemd files and screen command.
I manage to make them work separately, but not together, it's why I'm looking for help there.
Firstly, I'm using a Raspberry pi 4 4Go with raspbian v10, and forge 1.12.2 with java 8.
I did a .sh file to launch easier the server:
#!/bin/bash
screen -S mcserver -dm java -Xms1024M -Xmx2048M -jar /home/pi/MinecraftServer/server/forge-1.12.2-14.23.5.2854.jar nogui
When I run the file, the server start perfectly in a socket as I want.
Secondly, I have a systemd file (auto-run-server.service):
[Unit]
Description=Auto run mc server
[Service]
ExecStart=/home/pi/MinecraftServer/server/minecraft.sh
[Install]
WantedBy=multi-user.target
But when I execute the service, nothing is happening, the status of the service shows a sucess, but there is nothing in screens (screen -list)
And when i replace the ExecStart value by
ExecStart=java -Xms1024M -Xmx2048M -jar /home/pi/MinecraftServer/server/forge-1.12.2-14.23.5.2854.jar nogui
The server starts, but the problem is that I want to access to a terminal to run commands in minecraft server, and i didn't find solution to access from there.
( It's why I want to create a "screen" )
I'm fully open to your answers, even if they don't use "screen", as long as I can access to a server terminal.
Thanks in advance.
I'm using the follow systemd unit for testing:
[Service]
ExecStart=/tmp/screentest.sh
And this screentest.sh shell script:
#!/bin/sh
screen -S mcserver -dm sh -c 'while :; do date; sleep 5; done'
If I start the service (systemctl start screentest) and then run systemctl status screentest, I see:
● screentest.service
Loaded: loaded (/etc/systemd/system/screentest.service; static; vendor preset: enabled)
Active: inactive (dead)
The problem here is that the screen command exits immediately when running with -d, so systemd believes the command has completed and cleans everything up by removing any additional processes spawned by the service.
We can tell systemd that the service spawns a child and exits by setting the service type to forking:
[Service]
Type=forking
ExecStart=/tmp/screentest.sh
With this change in place, after starting the service we see:
● screentest.service
Loaded: loaded (/etc/systemd/system/screentest.service; static; vendor preset: enabled)
Active: active (running) since Sun 2021-01-10 09:58:11 EST; 4s ago
Process: 14461 ExecStart=/tmp/screentest.sh (code=exited, status=0/SUCCESS)
Main PID: 14463 (screen)
Tasks: 3 (limit: 4915)
CGroup: /system.slice/screentest.service
├─14463 SCREEN -S mcserver -dm sh -c while :; do date; sleep 5; done
├─14464 sh -c while :; do date; sleep 5; done
└─14466 sleep 5
And screen -list shows:
root#raspberrypi:/etc/systemd/system# screen -list
There is a screen on:
14612.mcserver (01/10/2021 10:01:55 AM) (Detached)
1 Socket in /run/screen/S-root.

systemctl status shows inactive dead

I am trying to write my own (simple) systemd service which does something simple.( Like writing numbers 1 to 10 to a file, using the shell script).
My service file looks like below.
[Unit]
Description=NandaGopal
Documentation=https://google.com
After=multi-user.target
[Service]
Type=forking
RemainAfterExit=yes
ExecStart=/usr/bin/hello.sh &
[Install]
RequiredBy = multi-user.target
This is my shell script.
#!/usr/bin/env bash
source /etc/profile
a=0
while [ $a -lt 10 ]
do
echo $a >> /var/log//t.txt
a=`expr $a + 1`
done
For some reason, the service doesn't come up and systemctl is showing the below output.
root#TARGET:~ >systemctl status -l hello
* hello.service - NandaGopal
Loaded: loaded (/usr/lib/systemd/system/hello.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: https://google.com
Been trying to figure out what went wrong for the last 2 days.
You have set Type=Forking, but your service doesn't work. Try
Type=oneshot
You have a "&" your ExecStart line, which is not necessary.
The service is disabled, which means it was not enabled to start at boot. You should run systemctl enable hello to set it to start at boot.
You can check man systemd.directives to find an index of all the directives that you can use in your unit files.
Few points:
If you use Type=forking, it is recommended to specify PidFile.
In your case, Type=simple, and ExecStart without & will work.
use systemctl start service-name to start a service
Then use systemctl status service-name to check its status.
status will be inactive/dead if service is not started.

Register daemon controllable by start and stop command in Linux

Many system daemon can be started using start/stop command. I was just curious how start/stop works on Linux system. Say I wrote a daemon executable, how should I configure it so that it can be controlled by start/stop in Linux.
I make a daemon in linux (ArchLinux) few years ago, and it works every day perfectly.
There are 2 ways to do this. Short way and long way:
Short Way:
Create a file in /etc/systemd/system/ called for example mydaemon.service :
/etc/systemd/system/mydaemon.service
[Unit]
Description=This is my first daemon! - Fernando Pucci
After=network.target
[Service]
User=root
WorkingDirectory=/root
Type=oneshotmc
RemainAfterExit=yes
ExecStart=/bin/echo -e "Daemon started"
ExecStop=/bin/echo -e "Daemon Stopped"
[Install]
WantedBy=multi-user.target
This service does nothing but show Daemon Started or Stopped. You can change echoes by the sentences you need.
If you need to run some script, try the Long way:
Long way
Create a file in some directory, like root folder or /usr/lib/systemd/scripts called for example
/root/mydaemon.sh
start() {
<your start sentences here
and here>
}
stop() {
<your stop sentences here
and here>
}
case $1 in
start|stop) "$1" ;;
esac
You must to make it runnable (chmod x)
(And you can execute it with start or stop parameter to test it.)
And as second step, create another file in
/usr/lib/systemd/system/mydaemon.service
[Unit]
Description=Second daemon of Fernando Pucci
After=network.target
[Service]
User=root
WorkingDirectory=/root
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c '/root/mydaemon.sh start'
ExecStart=/bin/echo -e "MyDaemon Started"
ExecStop=/bin/bash -c '/root/mydaemon.sh stop'
ExecStop=/bin/echo -e "MyDaemon Stopped"
[Install]
WantedBy=multi-user.target
Starting and Stopping
systemctl start mydaemon
systemctl stop mydaemon
systemctl status mydaemon
systemctl enable mydaemon
systemctl disable mydaemon
You (and someone) can send me a private msg for help about that.

Why does systemd keep killing my service

I would like to start an interactive script from systemd after the getty.target has been reached. This works so far, however, systemd kills the script after a couple of seconds. The systemd unit looks like the following:
[Unit]
Description = Some interactive script
Requires = getty#tty1.service
After = getty#tty1.service
[Service]
Type = oneshot
ExecStart = /usr/local/bin/my-script
StandardInput = tty
StandardOutput = tty
TTYPath = /dev/tty1
TTYReset = yes
TTYVHangup = yes
[Install]
WantedBy = multi-user.target
Within the script there are calls to dialog, mount etc. Nothing very special but it is an interactive script. Systemd keeps killing the script and I don't understand why. The output of systemctl status interactive-script.service looks like:
● interactive-script.service - Some interactive script
Loaded: loaded (/etc/systemd/system/interactive-script.service; enabled)
Active: inactive (dead) since Tue 2016-06-28 10:18:07 UTC; 14min ago
Main PID: 364 (code=killed, signal=HUP)
And the log output gotten with journalctl -b -u interactive-script.service is empty:
-- Logs begin at Mon 2015-11-09 11:49:52 UTC, end at Tue 2016-06-28 10:30:28 UTC. --
I already tried to add KillMode=none, no luck. Then I tried TimeoutStartSec=infinity - systemd complains that it doesn't understand it therefore I tried to set it to 10000 but the script gets killed after just some seconds. I tried to run it as Type=simple and Type=forking, all to no avail.
The point is that starting the script seems to work fine (the dialogs appear) but systemd keeps killing the script. How can I achieve that systemd does not kill this interactive script?

How to redirect output of systemd service to a file

I am trying to redirect output of a systemd service to a file but it doesn't seem to work:
[Unit]
Description=customprocess
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/bin/binary1 agent -config-dir /etc/sample.d/server
StandardOutput=/var/log1.log
StandardError=/var/log2.log
Restart=always
[Install]
WantedBy=multi-user.target
Please correct my approach.
I think there's a more elegant way to solve the problem: send the stdout/stderr to syslog with an identifier and instruct your syslog manager to split its output by program name.
Use the following properties in your systemd service unit file:
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=<your program identifier> # without any quote
Then, assuming your distribution is using rsyslog to manage syslogs, create a file in /etc/rsyslog.d/<new_file>.conf with the following content:
if $programname == '<your program identifier>' then /path/to/log/file.log
& stop
Now make the log file writable by syslog:
# ls -alth /var/log/syslog
-rw-r----- 1 syslog adm 439K Mar 5 19:35 /var/log/syslog
# chown syslog:adm /path/to/log/file.log
Restart rsyslog (sudo systemctl restart rsyslog) and enjoy! Your program stdout/stderr will still be available through journalctl (sudo journalctl -u <your program identifier>) but they will also be available in your file of choice.
Source via archive.org
If you have a newer distro with a newer systemd (systemd version 236 or newer), you can set the values of StandardOutput or StandardError to file:YOUR_ABSPATH_FILENAME.
Long story:
In newer versions of systemd there is a relatively new option (the github request is from 2016 ish and the enhancement is merged/closed 2017 ish) where you can set the values of StandardOutput or StandardError to file:YOUR_ABSPATH_FILENAME. The file:path option is documented in the most recent systemd.exec man page.
This new feature is relatively new and so is not available for older distros like centos-7 (or any centos before that).
I would suggest adding stdout and stderr file in systemd service file itself.
Referring : https://www.freedesktop.org/software/systemd/man/systemd.exec.html#StandardOutput=
As you have configured it should not like:
StandardOutput=/home/user/log1.log
StandardError=/home/user/log2.log
It should be:
StandardOutput=file:/home/user/log1.log
StandardError=file:/home/user/log2.log
This works when you don't want to restart the service again and again.
This will create a new file and does not append to the existing file.
Use Instead:
StandardOutput=append:/home/user/log1.log
StandardError=append:/home/user/log2.log
NOTE: Make sure you create the directory already. I guess it does not support to create a directory.
You possibly get this error:
Failed to parse output specifier, ignoring: /var/log1.log
From the systemd.exec(5) man page:
StandardOutput=
Controls where file descriptor 1 (STDOUT) of the executed processes is connected to. Takes one of inherit, null, tty, journal, syslog, kmsg, journal+console, syslog+console, kmsg+console or socket.
The systemd.exec(5) man page explains other options related to logging. See also the systemd.service(5) and systemd.unit(5) man pages.
Or maybe you can try things like this (all on one line):
ExecStart=/bin/sh -c '/usr/local/bin/binary1 agent -config-dir /etc/sample.d/server 2>&1 > /var/log.log'
If for a some reason can't use rsyslog, this will do:
ExecStart=/bin/bash -ce "exec /usr/local/bin/binary1 agent -config-dir /etc/sample.d/server >> /var/log/agent.log 2>&1"
Short answer:
StandardOutput=file:/var/log1.log
StandardError=file:/var/log2.log
If you don't want the files to be cleared every time the service is run, use append instead:
StandardOutput=append:/var/log1.log
StandardError=append:/var/log2.log
We are using Centos7, spring boot application with systemd. I was running java as below. and setting StandardOutput to file was not working for me.
ExecStart=/bin/java -jar xxx.jar -Xmx512-Xms32M
Below workaround solution working without setting StandardOutput. running java through sh as below.
ExecStart=/bin/sh -c 'exec /bin/java -jar xxx.jar -Xmx512M -Xms32M >> /data/logs/xxx.log 2>&1'
Assume logs are already put to stdout/stderr, and have systemd unit's log in /var/log/syslog
journalctl -u unitxxx.service
Jun 30 13:51:46 host unitxxx[1437]: time="2018-06-30T11:51:46Z" level=info msg="127.0.0.1
Jun 30 15:02:15 host unitxxx[1437]: time="2018-06-30T13:02:15Z" level=info msg="127.0.0.1
Jun 30 15:33:02 host unitxxx[1437]: time="2018-06-30T13:33:02Z" level=info msg="127.0.0.1
Jun 30 15:56:31 host unitxxx[1437]: time="2018-06-30T13:56:31Z" level=info msg="127.0.0.1
Config rsyslog (System Logging Service)
# Create directory for log file
mkdir /var/log/unitxxx
# Then add config file /etc/rsyslog.d/unitxxx.conf
if $programname == 'unitxxx' then /var/log/unitxxx/unitxxx.log
& stop
Restart rsyslog
systemctl restart rsyslog.service
In my case 2>&1(stdout and stderr file descriptor symbol) had to be placed correctly,then log redirection worked as I expected
[Unit]
Description=events-server
[Service]
User=manjunath
Type=simple
ExecStart=/bin/bash -c '/opt/events-server/bin/start.sh my-conf 2>&1 >> /var/log/events-server/events.log'
[Install]
WantedBy=multi-user.target
Make your service file call a shell script instead of running the app directly. This way you have extra control. For example, you can make output files like those in /var/log/
Make a shell script like /opt/myapp/myapp.sh
#!/bin/sh
/usr/sbin/logrotate --force /opt/myapp/myapp.conf --state /opt/myapp/state.tmp
logger "[myapp] Run" # send a marker to syslog
myapp > /opt/myapp/myapp.log 2>&1 &
And your service file myapp.service contains:
...
[Service]
Type=forking
ExecStart=/bin/sh -c /opt/myapp/myapp.sh
...
A sample of log config file /opt/myapp/myapp.conf
/opt/myapp/myapp.log {
daily
rotate 20
missingok
compress
}
Then you will get myapp.log, and zipped myapp.log.1.gz ... for each time the service was started, and previous zipped.

Resources