Graceful shutdown on Archlinux - linux

Bringing this question straight from here.
I am running archlinux and I have a VM running often on it along with the system. Most of the time actually.
My goal is to produce the following behaviour:
A shutdown / poweroff / reboot / halt signal is sent to the system
No action other then trying to shut down the virtual machines gracefully
If the VMs are shut down gracefully after X seconds, proceeds with shutting down the host system too.
If not, execute a different command
Just give me a good idea on what to work on, because I don't even know from where to begin. I guess there is a call to the kernel that can be looked at.
Let me know.
My current code
At the moment I am using these scripts to gracefully shutdown my kvm virtual machines, and it works! But only as long as my user launches a shutdown or a reboot using his shell. Any other case wouldn't work.
These alias:
alias sudocheck="/bin/bash /home/damiano/.script/sudocheck"
alias sudo="sudocheck "
Are triggering this function:
#!/bin/bash
# This script checks for what is being passed to sudo.
# If the command passed is poweroff or reboot, it
# launches a custom script instead, that also looks
# fur currently running virtual machines and shuts them.
sudocheck() {
if [ $1 == "poweroff" ] || [ $1 == "reboot" ]; then
eval "sudo /home/damiano/.script/graceful $#"
else
eval "sudo $#"
fi
}
sudocheck $#
That launches this script if needed:
#!/bin/bash
i=0
e=0
## if virsh finds VMs running
virsh -c qemu:///system list | awk '{ print $3}' | \
if grep running > /dev/null ; then
virsh -c qemu:///system list --all | grep running | awk '{print "-c qemu:///system shutdown "$2}' | \
## shuts them dow gracefully
xargs -L1 virsh
## wait 30 seconds for them to go down
until (( i >= 30 || e == 1 )) ; do
## check every second for their status
virsh -c qemu:///system list --all | awk '{ print $3}' | \
if grep -E '(running|shutdown)' > /dev/null ; then
## keep waiting if still running
if (( i <= 30 )) ; then
sleep 1 && let i++ && echo $i
else
e=1 && notify-send 'Shutdown has been canceled' 'Please check the status of your virtual machines: seems like even though a stop signal has been sent, some are still running.' --urgency=critical
fi
else
## if no machine is running anymore, original power command can be executed
e=1 && eval $#
fi
done
fi
Systemd Unit
I also made the following draft, to manage the execution of my VM:
bootvm#.service
[Unit]
Description=This service manages the execution of the %i virtual machine
Documentation=https://libvirt.org/manpages/virsh.html
[Service]
ExecStartPre=virsh -c qemu:///system
ExecStart=virsh start %i
ExecStop=virsh -c qemu:///system
ExecStop=virsh shutdown %i
TimeoutStopSec=30
KillMode=none
[Install]
WantedBy=multi-user.target
But how can I tell the system to don't shut down the desktop environment, to stay as it is UNTIL the VM has been successfully shut down?
Because if the system can't shut down the vm, I want to do it while still in my DE. I don't want the computer to begin stopping all the services and remain hung until it just forces the shut down.

Related

switch desktops after 5 minutes idle (xprintidle): crontab or daemon?

on my raspberry pi (raspbian running) I would like to have the current desktop switched to desktop n#0 after 5 minutes of idle system (no mouse or keyboard action), through wmctrl -s 0 and xprintidle for idle time checking.
Please keep in mind I'm no expert...
I tried 2 different ways, none of them working and I was wondering which one of them is the best way to do have the job done:
bash script and crontab
I wrote a simple script which checks if xprintidle is greater than a previously set $IDLE_TIME, than it switches desktops (saved in /usr/local/bin/switchDesktop0OnIdle):
#!/bin/bash
# 5 minutes in ms
IDLE_TIME=$((5*60*1000))
# Sequence to execute when timeout triggers.
trigger_cmd() {
wmctrl -s 0
}
sleep_time=$IDLE_TIME
triggered=false
while sleep $(((sleep_time+999)/1000)); do
idle=$(xprintidle)
if [ $idle -ge $IDLE_TIME ]; then
if ! $triggered; then
trigger_cmd
triggered=true
sleep_time=$IDLE_TIME
fi
else
triggered=false
# Give 100 ms buffer to avoid frantic loops shortly before triggers.
sleep_time=$((IDLE_TIME-idle+100))
fi
done
script itself works.
Then I added it to crontab (crontab -e) for have it run every 6 minutes
*/6 * * * * * sudo /usr/local/bin/switchDesktop0OnIdle
not sure sudo is necessary or not.
Anyway It doesn't work: googling around I understood that crontab runs in its own environment with its own variables. Even though I don't remember how to access this environment (oops) I do remember that I get these 2 errors running the script in it (which correctly works in "normal" shell)
could not open display (is it important ?)
bla bla -ge error, unary operator expected or similar: basically xprintidle doesn't work in this environment a gives back an empty value
What am I missing ?
infinite-while bash script running as daemon
second method I tried to set up a script with an internal infinite-while checking if xprintidle is greater then 5 minutes. In this case desktop is switched (less elegant?). Saved also in /usr/local/bin/switchDesktop0OnIdle
#!/bin/bash
triggered=false
while :
do
if [ `xprintidle` -ge 300000 ]; then
if [ triggered == false ]
wmctrl -s 0
triggered = true
fi
else
triggered = false
fi
fi
done
again the script itself works.
I tried to create a daemon in /etc/init.d/switchDesktop0OnIdle (really not an expert here, modified an existing one)
#! /bin/sh
# /etc/init.d/switchDesktop0OnIdle
### BEGIN INIT INFO
# Provides: switchDesktop0OnIdle
# Required-Start: $all
# Required-Stop: $all
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description:
# Description:
### END INIT INFO
DAEMON=/usr/local/bin/switchDesktop0OnIdle
NAME=switchDesktop0OnIdle
test -x $DAEMON || exit 0
case "$1" in
start)
echo -n "Starting daemon: "
start-stop-daemon --start --exec $DAEMON
echo "switchDesktop0OnIdle."
;;
stop)
echo -n "Shutting down daemon:"
start-stop-daemon --stop --oknodo --retry 30 --exec $DAEMON
echo "switchDesktop0OnIdle."
;;
restart)
echo -n "Restarting daemon: "
start-stop-daemon --stop --oknodo --retry 30 --exec $DAEMON
start-stop-daemon --start --exec $DAEMON
echo "switchDesktop0OnIdle."
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0
I set it up
sudo update-rc.d switchDesktop0OnIdle defaults
and
sudo service switchDesktop0OnIdle start
(necessary?)
...and nothing happens...
also I don't find the process with ps -ef | grep switchDesktop0OnIdle but it seems running with sudo service switchDesktop0OnIdle status
can anyone please help?
thank you
Giuseppe
As you suspected, the issue is that when you run your scripts from init or from cron, they are not running within the GUI environment you want them to control. In principle, a Linux system can have multiple X environments running. When you are using one, there are environment variables that direct the executables you are using to the environment you are in.
There are two parts to the solution: your scripts have to know which environment they are acting on, and they have to have authorization to interact with that environment.
You almost certainly are using a DISPLAY value of ":0", so export DISPLAY=:0 at the beginning of your script will handle the first part of the problem. (It might be ":0.0", which is effectively equivalent).
Authorization is a bit more complex. X can be set up to do authorization in different ways, but the most common is to have a file .Xauthority in your home directory which contains a token that is checked by the X server. If you install a script in your own crontab, it will run under your own user id (you probabl shouldn't use sudo), so it will read the right .Xauthority file. If you run from the root crontab, or from an init script, it will run as the root user, so it will have access to everything but will still need to know where to take the token from. I think that adding export XAUTHORITY=/home/joe/.Xauthority to the script will work. (Assuming your user id is joe.)

Shutting Down Windows Network Using Bash

Here's my current code, which works but is slow
for i in {1..255..1}; do
for j in {1..255..1}; do
ip="10.8.$i.$j"
sudo net rpc shutdown -I $ip -U Username%Password -t 1 -f
echo $ip
done
done
I would like to be able to go through these IPs and attempt to shut them down. But if there is not a PC at that IP it has to wait for it to timeout before attempting the next one. So how can I find and shutdown all windows PCs on a network? (they all have the same credentials)
A trivial solution is to just run a pile of them in parallel:
for i in {1..255..1}; do
for j in {1..255..1}; do
ip="10.8.$i.$j"
sudo net rpc shutdown -I $ip -U Username%Password -t 1 -f &
echo $ip
done
wait
done
This runs 255 at a time and waits for them all to finish. Smarter and more flexible parallelization can be had through xargs, sem or parallel if Windows supports that.

BASH - why my NodeJS server script crashing?

Running CentOS (Linux signal-server 3.10.0-123.8.1.el7.x86_64). When i login to the server using SSH and then execute this following line manually it works, but crash when i exit my SSH connection to the server.
$ node /var/www/html/signal-server/avi.js &
This robot also fails to let the server run when i am not manually logged in via SSH:
#!/bin/bash
while :
do
# 1
avi=$(pgrep -f "avi.js")
if [[ "$avi" ]]; then
log1="1 - running avi.js $1 $2"
else
log1="1 - re-launch avi.js $1 $2"
ps aux | grep "avi.js" | awk '{print $2}' | xargs kill
sleep 1
# same if nohup also used
node /var/www/html/signal-server/avi.js &
fi
tt=$(date +"%Y-%m-%d %T")
echo "$tt" "$log1"
sleep 2
done
How can i make it run for-ever please? (i have also other services they are working fine only this one always crashing when i logout from the SSH session)
nohup will do what you want
nohup node /var/www/html/signal-server/avi.js &
You should really write a systemd script for you service, then if your server restarts your service will startup again.
This is a rough idea what such a script may look like
[Service]
ExecStart=node /var/www/html/signal-server/avi.js
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=node-avi
User=some-user
Group=some-group
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
Spend a bit of time reading the systemd documentation

how to create a script for checking if ssh is running and if not restart it

I'm running a headless system with a raspberry pi and after a while of not connecting via ssh the system will stop responding to ssh, it is not the Wi-Fi dongle falling asleep, I have checked, seeing I have a piglow running piglow-sysmon, and the part of the pi glow that monitors network activity does show activity when the pi stops responding to ssh. I found a nice script for checking if Wi-Fi is up and if not restart it, although im not that great with bash scripting, and cannot figure out how or if i can mod it to work with ssh instead of Wi-Fi, if anyone can help me mod it, or provide a small quick one, I'm using cron to run it (once I can get it modded) every few minutes
here the script I'm trying to mod
#!/bin/bash
LOGFILE=/home/pi/network-monitor.log
if ifconfig wlan0 | grep -q "inet addr:" ;
then
echo "$(date "+%m %d %Y %T") : Wifi OK" >> $LOGFILE
else
echo "$(date "+%m %d %Y %T") : Wifi connection down! Attempting reconnection." >> $LOGFILE
ifup --force wlan0
OUT=$? #save exit status of last command to decide what to do next
if [ $OUT -eq 0 ] ; then
STATE=$(ifconfig wlan0 | grep "inet addr:")
echo "$(date "+%m %d %Y %T") : Network connection reset. Current state is" $STATE >> $LOGFILE
else
echo "$(date "+%m %d %Y %T") : Failed to reset wifi connection" >> $LOGFILE
fi
fi
Try the following script. It makes a few assumptions:
1) Your account has its own ssh key in the authorized_keys file, so that "ssh localhost" essentially just gives you another shell, without prompting for a password
2) If the ssh command does not complete in three seconds, it would be safe to assume that the ssh daemon is up, but stuck for some reason:
#! /bin/bash
ssh localhost /bin/true &
sleep 3; kill -9 $!
if wait $!
then
echo Up
else
echo Down
fi
A bit crude, but should be effective. It's up to you to figure out how to restart the ssh service in an optimum way, here. Fill in the blanks.
You may also want to discard all standard error here, as it'll likely to have some unimportant noise...
If, on the other hand, this script reports that the ssh service is running, but you still can't connect from the outside, the problem is not the ssh service, but it lies elsewhere, so it's back to the drawing board for you.

How to kill port forwarding once you have have finished using it

In order to sync my home and work file systems I need to go via an intermediary computer and use port forwarding. Let us call the home computer A, the intermediate one B and the work computer C. From the command line I do this
ssh -N -f -L 2025:C:22 me_B#B && unison foo ssh://me_C#localhost:2025/foo
I would like to put this one-liner into a bash script. How can I make it quit gracefully at the end and not leave any port forwarding still set up?
ssh -N -f -L 2025:C:22 me_B#B &
pid=$! # ssh PID
rc=$? # ssh return code
# set up to kill ssh when this script finishes
function finish {
kill $pid
}
trap finish EXIT
[ $rc -eq 0 ] && unison foo ssh://me_C#localhost:2025/foo

Resources