Run a script on monitor connection/disconnection event - linux

I read many threads about how to do this and yet it doesn't work for me.
What I want to achieve is:
When my laptop has 2 additional displays connected when I close its lid, I don't want it to go to sleep. However, if I disconnect the displays and the lid is closed, I want to put the laptop to sleep (this way, not forgetting it on).
Therefore, I created a BASH script that should be executed on VGA / HDMI connection events. The BASH script counts how many displays are connected and if there's only 1, it will put the laptop to sleep when the lid is closed.
I have Ubuntu 14.04 LTS. This is what I've done so far:
Created 2 files: displays_count_sleep.sh and on_hdmi_connected.rules
https://gist.github.com/nbtk123/9ffbf7541e47b9c0015f5c3e9f44b7c9
Put this on_hdmi_connected.rules into /etc/udev/rules.d to catch the event:
SUBSYSTEM=="drm", RUN+="/bin/bash /home/nir/dev/scripts/displays_count_sleep.sh"
Put the bash script I want to run, displays_count_sleep.sh, into /home/nir/dev/scripts:
#!/bin/bash
DISPLAYS_NUM=2
`touch test`
display_count=`xrandr -d :0 -q | grep ' connected' | wc -l`
echo "display count="$display_count
echo "display_num="$DISPLAYS_NUM
if [ "$display_count" -ge "$DISPLAYS_NUM" ]; then
echo "nothing"
`gsettings set org.gnome.settings-daemon.plugins.power lid-close-ac-action nothing`
`gsettings set org.gnome.settings-daemon.plugins.power lid-close-battery-action nothing`
else
echo "sleep"
`gsettings set org.gnome.settings-daemon.plugins.power lid-close-ac-action suspend`
`gsettings set org.gnome.settings-daemon.plugins.power lid-close-battery-action suspend`
fi
Both files have been created as root. When running "udevadm test", the output is as in the file 'udevadm_test_output' on the link.
I don't know if the event is catched, but on the real monitor (dis)connection, the script doesn't run. It works okay if I run it manually.

Related

How can we prevent CTRL-C from screen terminating?

I'm currently writing a bash script that would create multiple shell instances (with screen command) and execute a subprogram.
The problem is when I try to interrupt the subprogram, it interrupts the screen instance too. I already searched for trap command on internet with SIGINT but I don't really know how to use it in this case.
Here is my code to show you how do I create the screen:
#!/bin/bash
#ALL PATHS ARE DECLARED HERE.
declare -A PATHS; declare -a orders;
PATHS["proxy"]=/home/luna/proxy/HydraProxy; orders+=( "proxy" )
PATHS["bot"]=/home/luna/bot; orders+=( "bot" )
#LAUNCH SERVERS
SERVERS=/home/luna/servers
cd "$SERVERS"
for dir in */; do
d=$(basename "$dir")
PATHS["$d"]="$(realpath $dir)"; orders+=( "$d" )
done
for name in "${orders[#]}"; do
if ! screen -list | grep -q "$name"; then
path="${PATHS[$name]}"
cd "$path"
screen -dmS "$name" ./start.sh
echo "$name CREATED AT $path"
sleep 2
else
echo "SCREEN $name IS ALREADY CREATED"
fi
done
Could you help me more to find a solution please ? Thank you very much for your time.
Each of your screen instances is created to run a single command, start.sh. When this command terminates, for instance when you interrupt it, the screen will have done its job and terminate. The reason for this is that screen runs shell scripts directly in a non-interactive shell, rather than spawning a new interactive shell and running it there.
If you wanted to run start.sh inside an interactive shell in each screen, you'd do something like this:
screen -dmS "$name" /bin/bash -i
screen -S "$name" -X stuff "./start.sh^M"
The ^M is needed as it simulates pressing enter in your shell within screen.
If you use this, then when you interrupt a script within screen, you will still be left with an interactive prompt afterward to deal with as you see fit.

Is it possible to auto reboot for 5 loops through mint?

I am currently using the following command to run reboot
sudo shutdown -r now
however, I would need to run it for 5 loops before and after executing some other programs. Was wondering if it is possible to do it in MINT environment?
First a disclaimer: I haven't tried this because I don't want to reboot my machine right now...
Anyway, the idea is to make a script that can track it's iteration progress to a file as #david-c-rankin suggested. This bash script could look like this (I did test this):
#!/bin/sh
ITERATIONS="5"
TRACKING_FILE="/path/to/bootloop.txt"
touch "$TRACKING_FILE"
N=$(cat "$TRACKING_FILE" | wc -c)
if [ "$N" -lt "$ITERATIONS" ]; then
printf "." >> "$TRACKING_FILE"
echo "rebooting (iteration $N)"
# TODO: this is where you put the reboot command
# and anything you want to run before rebooting each time
else
rm "$TRACKING_FILE"
# TODO: other commands to resume anything required
fi
Then add a call to this script somewhere where it will be run on boot. eg. cron (#reboot) or systemd. Don't forget to remove it from a startup/boot command when you're finished or next time you reboot, it will reboot N times.
Not sure exactly how you are planning on using it, but the general workflow would look like:
save script to /path/to/reboot_five_times.sh
add script to run on boot (cron, etc.)
do stuff (manually or in a script)
call the script
computer reboots 5 times
anything in the second TODO section of the script is then run
go back to step 3, or if finished remove from cron/systemd so it won't reboot when you don't want it to.
First create a text document wherever you want,I created one on Desktop,
Then use this file as a physical counter and write a daemon file to run things at startup
For example:
#!/bin/sh
var=$(cat a.txt)
echo "$var"
if [ "$var" != 5 ]
then
var=$((var+1))
echo "$var" > a.txt
echo "restart here"
sudo shutdown -r now
else
echo "stop restart"
echo 0 > a.txt
fi
Hope this helps
I found a way to create a file at startup for my reboot script. I incorporated it with the answers provided by swalladge and also shubh. Thank you so much!
#!/bin/bash
#testing making a startup application
echo "
[Desktop Entry]
Type=Application
Exec=notify-send success
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name[en_CA]=This is a Test
Name=This is a Test
Comment[en_CA]=
Comment=" > ~/.config/autostart/test.desktop
I create a /etc/rc.local file to execute user3089519's script, and this works for my case. (And for bootloop.txt, I put it here: /usr/local/share/bootloop.txt )
First: sudo nano /etc/rc.local
Then edit this:
#!/bin/bash
#TODO: things you want to execute when system boot.
/path/to/reboot_five_times.sh
exit 0
Then it works.
Don't forget edit /etc/rc.local and remove /path/to/reboot_five_times.sh when you done reboot cycling.

Wifi disconnected before init.d script is run

I've set up a simple init.d script "S3logrotate" to run on shutdown. The "S3logrotate" script works fine when run manually from command line but the script does not function correctly on shut down.
The script uploads logs from my PC to an Amazon S3 bucket and requires wifi to run correctly.
Debugging proved that the script is actually run but the upload process fails.
I found that the problem seems to be that the script seems to run after wifi is terminated.
These are the blocks I used to test my internet connection in the script.
if ping -q -c 1 -W 1 8.8.8.8 >/dev/null; then
echo "IPv4 is up" >> *x.txt*
else
echo "IPv4 is down" >> *x.txt*
fi
if ping -q -c 1 -W 1 google.com >/dev/null; then
echo "The network is up" >> *x.txt*
else
echo "The network is down" >> *x.txt*
fi
The output for this block is:
IPv4 is down
The network is down
Is there any way to set the priority of an init.d script? As in, can I make my script run before the network connection is terminated? If not, is there any alternative to init.d?
I use Ubuntu 16.04 and have dual booted with Windows 10 if that's significant.
Thanks,
sganesan7
You should place you scrip in:
/etc/NetworkManager/dispatcher.d/pre-down.d
change group and owner to root
chown root:root S3logrotate
and it should work. If you need to do this for separate interface place script in
create a script inside
/etc/NetworkManager/dispatcher.d/
and name it (for example):
wlan0-down
and should work too.

Starting a custom screensaver on boot behaves differently than manually starting the screensaver

I have a touchscreen kiosk on which I'm running a webserver. I want to show a slideshow if the screen hasn't been touched for a certain amount of time. For that purpose I have the script below.
#!/bin/sh
# Wanted trigger timeout in milliseconds.
IDLE_TIME=$((15*1000))
# Sequence to execute when timeout triggers.
trigger_cmd() {
DISPLAY=:0 feh -ZXYrzFD 10 /home/pi/screensaver/img --zoom fill &
echo '"pkill -n feh; pkill -n xbindkeys"'>/home/pi/screensaver/xbindkeys.temp
echo "b:1">>/home/pi/screensaver/xbindkeys.temp
DISPLAY=:0 xbindkeys -n -f /home/pi/screensaver/xbindkeys.temp
sudo rm /home/pi/screensaver/xbindkeys.temp
}
sleep_time=$IDLE_TIME
triggered=false
# ceil() instead of floor()
while sleep $(((sleep_time+999)/1000)); do
idle=$(DISPLAY=:0 xprintidle)
if [ $idle -gt $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
I use xprintidle to check how long the screen has been idle.
The xbindkeys part is for killing feh when the screen is touched. When I start the script manually I can close the slideshow by touching the screen once and it will reopen after the given idle time. When I start the script via a script in init.d I have to touch the screen twice before it will open the slideshow again, and will never reopen the slideshow if you only touched the screen once.
The script in init.d simply starts the script above as the user pi.
Can someone help me figure out why starting the script on boot apparently causes the script to require two clicks instead of one to start the idle timer?
The touchscreen script is most likely being run by init.d before your DISPLAY environment variable is set (i.e. user pi is not logged in).
Try running this from .bash_profile. That way all your user environment variables will be set especially $DISPLAY and the script will run once upon login.

Redirecting Output of Bash Child Scripts

I have a basic script that outputs various status messages. e.g.
~$ ./myscript.sh
0 of 100
1 of 100
2 of 100
...
I wanted to wrap this in a parent script, in order to run a sequence of child-scripts and send an email upon overall completion, e.g. topscript.sh
#!/bin/bash
START=$(date +%s)
/usr/local/bin/myscript.sh
/usr/local/bin/otherscript.sh
/usr/local/bin/anotherscript.sh
RET=$?
END=$(date +%s)
echo -e "Subject:Task Complete\nBegan on $START and finished at $END and exited with status $RET.\n" | sendmail -v group#mydomain.com
I'm running this like:
~$ topscript.sh >/var/log/topscript.log 2>&1
However, when I run tail -f /var/log/topscript.log to inspect the log I see nothing, even though running top shows myscript.sh is currently being executed, and therefore, presumably outputting status messages.
Why isn't the stdout/stderr from the child scripts being captured in the parent's log? How do I fix this?
EDIT: I'm also running these on a remote machine, connected via ssh using pseudo-tty allocation, e.g. ssh -t user#host. Could the pseudo-tty be interfering?
I just tried your the following: I have three files t1.sh, t2.sh, and t3.sh all with the following content:
#!/bin/bash
for((i=0;i<10;i++)) ; do
echo $i of 9
sleep 1
done
And a script called myscript.sh with the following content:
#!/bin/bash
./t1.sh
./t2.sh
./t3.sh
echo "All Done"
When I run ./myscript.sh > topscript.log 2>&1 and then in another terminal run tail -f topscript.log I see the lines being output just fine in the log file.
Perhaps the things being run in your subscripts use a large output buffer? I know when I've run python scripts before, it has a pretty big output buffer so you don't see any output for a while. Do you actually see the entire output in the email that gets sent out at the end of topscript.sh? Is it just that while the processes run you're not seeing the output?
try
unbuffer topscript.sh >/var/log/topscript.log 2>&1
Note that unbuffer is not always available as a std binary in old-style Unix platforms and may require a search and installation for a package to support it.
I hope this helps.

Resources