Battery reminder POSIX script having issues with DBUS - cron

I wrote a POSIX shell script to remind me about my battery life using notify-send and a cronjob, but I'm having problems with DBUS stuff
This is what the script looks like
#!/bin/sh
percent=`upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep percentage:
notify-send "battery life" "$percent"
It works as intended, and pops up with this notification when called.
I wrote the script because my window manager, i3 lacks a notification system for battery status, so I found myself running out of battery on my laptop even though I was right next to an outlet at home.
Of course; having a script like this is pointlesss unless it's automated to pop up by itself, so after some fiddling, I set up a Cron-job that runs the script every 10 minutes.
This is what the cron-tab looks like:
*/10 * * * * export DISPLAY=:0 ; export DBUS_SESSION_BUS_ADDRESS=a; batterystatus.sh
It works, except that without the little snippet about the DBUS_SESSION_BUS_ADDRESS stuff, for some reason notify-status doesn't work.
So, everything was cool until I rebooted and found that this value used in the cron-tab: unix:abstract=/tmp/dbus-FOSTebXqX5,guid=a7ad198d91d224b8c056efc6615a3610 changes upon boot.
That means that I would have to change the cron-job everytime I boot up my computer so that the script would work. Is there any way around this?

Rather than use crontab, it might be better to use systemd. You can have a systemd user service and timer so that D-Bus has access to the session D-Bus information for session notifications.
I've tested this out using the Python dbus library pydbus as an example bus I'm sure you could use your shell script also.
Python script for getting battery power and posting notifications:
#!/usr/bin/python3
import pydbus
sys_bus = pydbus.SystemBus()
ses_bus = pydbus.SessionBus()
power = sys_bus.get('.UPower', '/org/freedesktop/UPower/devices/battery_BAT0')
notifications = ses_bus.get('.Notifications')
print(power.Percentage)
notifications.Notify('test', 0, 'dialog-information', "Battery Notification!",
f"Percentage: {power.Percentage}%", [], {}, 5000)
Created a user service in /etc/systemd/user/battery.service:
[Unit]
Description=Check for battery percentage
[Service]
Type=dbus
BusName=org.freedesktop.Notifications
ExecStart=/home/thinkabit1/stack_overflow/battery_monitor.py
You can test this works with:
$ systemctl --user start battery.service
To see the status of the service do:
$ systemctl --user status battery.service
To have a timer run this every 10 minutes create /etc/systemd/user/battery.timer
[Unit]
Description=Run battery monitor every 10 minutes
[Timer]
OnBootSec=10min
OnUnitActiveSec=10min
[Install]
WantedBy=timers.target
This can be started with:
$ systemctl --user start battery.timer
To see the status:
$ systemctl --user list-timers
To have it start automatically after reboot use:
systemctl --user enable battery.timer

Related

Is there a way to make crontab run a gnu screen session?

I have a discord bot running on a raspberry pi that i need to restart every day, I'm trying to do this through crontab but with no luck.
It manages to kill the active screen processes but never starts an instance, not that I can see with "screen -ls" and I can tell that it doesn't create one that I can't see as the bot itself does not come online.
Here is the script:
#!/bin/bash
sudo pkill screen
sleep 2
screen -dmS discordBot
sleep 2
screen -S "discordBot" -X stuff "node discordBot/NEWNEWNEWN\n"
Here is also the crontab:
0 0 * * * /bin/bash /home/admin/discordBot/script.sh
Is it possible to have crontab run a screen session? And if so how?
Previously I tried putting the screen command stright into cron but now I have it in a bash script instead.
If I run the script in the terminal it works perfectly, it’s just cron where it fails. Also replacing "screen" with the full path "/usr/bin/screen" does not change anything.
So the best way of doing it, without investigating the underlying problem would be to create a systemd service and putting a restart command into cron.
 
/etc/systemd/system/mydiscordbot.service:
[Unit]
Description=A very simple discord bot
[Service]
Type=simple
ExecStart=node /path/to/my/discordBot.js
[Install]
WantedBy=multi-user.target
Now you can run your bot with systemctl start mydiscordbot and can view your logs with journalctl --follow -u mydiscord bot
Now you only need to put
45 2 * * * systemctl restart discordbot
into root's crontab and you should be good to go.
You probably should also write the logs into a logfile, maybe /var/log/mydiscordbot.log, but how and if you do that is up to you.
OLD/WRONG ANSWER:
cron is run with a minimal environment, and depending on your os, /usr/bin/ is probably not in the $PATH var. screen is mostlikely located at /usr/bin/screen, so cron can't run it because it can't find the screen binary. try replacing screen in your script with /usr/bin/screen
But the other question here is: Why does your discord bot need to be restarted every day. I run multiple bots with 100k+ users, and they don't need to be restarted at all. Maybe you should open a new question with the error and some code snippets.
I don't know what os your rpi is running, but I had a similar issue earlier today trying to get vms on a server to launch a terminal and run a python script with crontab. I found a very easily solution to automatically restart it on crashes, and to run something simply in the background. Don't rely on crontab or an init service. If your rpi as an x server running, or anything that can be launched on session start, there is a simple shell solution. Make a bash script like
while :; do
/my/script/to/keep/running/here -foo -bar -baz >> /var/log/myscriptlog 2>&1
done
and then you would start it on .xprofile or some similar startup operation like
/path/to/launcher.sh &
to have it run the background. It will restart automatically everytime it closes if started in something like .xprofile, .xinitrc, or anything ran at startup.
Then maybe make a cronjob to restart the raspberry pi or whatever system is running the script but this script wil restart the service whenever it's closed anyway. Maybe you can put that cronjob on a script but I don't think it would launch the GUI.
Some other things you can do to launch a GUI terminal in my research with cronjob that you can try, though they didn't work for my situation on these custom linux vms, and that I read was a security risk to do this from a cronjob, is you can specify the display.
* * * * * DISPLAY=:0 /your/gui/stuff/here
but you would would need to make sure the user has the right permissions and it's considered an unsafe hack to even do this as far as I have read.
for my issue, I had to launch a terminal that stayed open, and then changed to the directory of a python script and ran the script, so that the local files in directory would be called in the python script. here is a rough example of the "launcher.sh" I called from the startup method this strange linux distro used lol.
#!/bin/sh
while :; do
/usr/bin/urxvt -e /bin/bash -c "cd /path/to/project && python loader.py"
done
Also check this source for process management
https://mywiki.wooledge.org/ProcessManagement

Linux: Triggering desktop notification pop-up when service is executed with systemd

I want to trigger a desktop notification pop-up when a service is executed with systemd on my Linux desktop. The main reason why I am doing this is that I want to learn how to work with systemd timers and services by creating my own scheduled jobs and I would like to pop-up a desktop notification, when a service/job is executed, just to know that something is happening.
I have created a basic example to do that:
notifysystemd.sh:
#!/bin/bash
# Variable to hold path to systemd job logs
SYSTEMD_LOG_DIR='/home/jay/scheduledJobLogs/systemDJobLogs'
SYSTEMD_JOB_NAME='NotifySystemD'
CURRENT_MONTH=$(date '+%b')
# Send notification to desktop
notify-send 'You can automate and schedule anything with systemd today!'
# Write down in the log
CURRENT_TIME=$(date '+%Y-%m-%d-%H:%M')
LOG_RECORD="${CURRENT_TIME} SystemD notification job executed."
# Create a directory for systemd jobs logging, if it doesn't already exist. And don't error if it does exist
mkdir -p $SYSTEMD_LOG_DIR/$SYSTEMD_JOB_NAME
# Write the log record!
echo $LOG_RECORD >> $SYSTEMD_LOG_DIR/$SYSTEMD_JOB_NAME/$CURRENT_MONTH.txt
with this service file:
notifysystemd.service:
[Unit]
Description=A basic service to send a desktop notification using the systemd scheduler
Wants=notifysystemd.timer
[Service]
Type=forking
ExecStart=/home/jay/systemDJobs/notifysystemd.sh
Environment="DISPLAY=:0" "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus" "XAUTHORITY=/home/jay/.Xauthority"
[Install]
WantedBy=default.target
and this timer file:
notifysystemd.timer:
[Unit]
Description=Send a notification three minutes after PC start
RefuseManualStart=false # Allow manual starts
RefuseManualStop=false # Allow manual stops
[Timer]
#Execute job if it missed a run due to machine being off
Persistent=true
OnBootSec=180
#File describing job to execute
Unit=notifysystemd.service
[Install]
WantedBy=timers.target
The service is executed correctly with the correct delay (I can see that in the log created), but I am getting no desktop notification.
I have looked into several questions already asked on this forum:
systemd service not executing notify-send
notify-send command doesn't launch the notification through systemd service
Which suggest specifying environment variables in either the .service file or in the shell script.
I have tried all of them and none led to a notification appearing.
I have done the same with cronie, where was sufficient to specify the DISPLAY and DBUS_SESSION_BUS_ADDRESS environement variables the same way as I did in the notifysystemd.service file.
Lastly, if there is a better way how to achieve the same result, but which revolves around usage of systemd, I am opened to optimal, or more ergonomic solutions.
In systemd instead of notify-send put the output in a file.
Write a bash script and start it on desktop login (so this script can notify-send).
In the script write an endless loop.
Use inotifywait in the loop to monitor the output file for modification.
(This will suspend your script and won't eat cpu)
After inotify, read the content of the file and use notify-send.

Setting up a cronjob on Google Compute Engine

I am new to setting up cronjobs and I'm trying to do it on a virtual machine in google compute engine. After a bit of research, I found this StackOverflow question: Running Python script at Regular intervals using Cron in Virtual Machine (Google Cloud Platform)
As per the answer, I managed to enter the crontab -e edit mode and set up a test cronjob like 10 8 * * * /usr/bin/python /scripts/kite-data-pull/dataPull.py. I also checked the system time, which was in UTC, and entered the time according to that.
The step I'm supposed to take, as per the answer, is to run sudo systemctl restart cron which is throwing an error for me:
sudo systemctl restart cron
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
Any suggestions on what I can do to set up this cronjob correctly?
Edit a cron jobs with crontab -e and inset a line:
* * * * * echo test123 > /your_homedir_path/file.log
That will write test123 every minute into file.log file.
Then do tail if and wait a couple minutes. You should see test123 lines appearing in the file (and screen).
If it runs try running your python file but first make your .py file executable with "chmod +x script.py"
Here you can find my reply to similar question.

Linux - shutdown-script with SSH

I would like to make a shutdown-script for my raspberry pi to shut down anothe raspberry pi over ssh.
The script works if it is running itself but at the shutdown routine the ssh command is not executed.
So that I have done until now:
Made the script in /etc/init.d:
#!/bin/sh
# the first thing is to test if the shutdown script is working
echo "bla bla bla " | sudo tee -a /test.txt
ssh pi#10.0.0.98 sudo shutdown -h now
Made it executable
sudo chmod +x /etc/init.d/raspi.sh
Made a symlink to the rc0.d
sudo ln -s /etc/init.d/raspi.sh /etc/rc0.d/S01raspi.sh
Now I know so far that the shutdown script is working outside of the shutdown routing by calling itself and the shutdown symlink I made is also working partially because I see the changes in the test.txt file every time I shut down.
Can anyone help me how to solve my problem?
Have you tried with single quotes?
The first link in Google has it
http://malcontentcomics.com/systemsboy/2006/07/send-remote-commands-via-ssh.html
What about the sudo, how do you solve entering the password?
https://superuser.com/questions/117870/ssh-execute-sudo-command
Please check this or other links on the web that have useful information.
I would have send all this in a comment but I cant yet because of reputation.
I have now got the script running by myself. I do not really know why it is now working but I write it down beneath and maybe someone else can clearifiy it.
I don´t think the first two changes at my system makes a difference but I also write it down. In the meanwhile because I do not managed the script to get working I had made a button to shutdown the system manually. Also I made a script which backs the mysql-database up (which is on the Raspberry Pi which I would like to switch off with the script) and copies the backup to the raspberry pi which should switch of the other raspberry automatically via the shutdown-script. This happens with scp and also for the password is a key generated.
I have also changed my script to get a log-message out of the script.
#!/bin/sh
ssh -t -t pi#10.0.0.99 'sudo shutdown -h now' >> /home/osmc/shutdown.log 2>&1
To get it into the shutdown-routine I used:
sudo update-rc.d raspi-b stop 01 0
I hope somebody can say me why my code now worked on the first day but not on the next few days until now.
I structured a command to suspend or shutdown a remote host over ssh. You may find this useful. This may be used to suspend / shutdown a remote computer without an interactive session and yet not keep a terminal busy. You will need to give permissions to the remote user to shutdown / suspend using sudo without a password. Additionally, the local and remote machines should be set up to SSH without an interactive login. The script is more useful for suspending the machine as a suspended machine will not disconnect the terminal.
local_user#hostname:~$ ssh remote_user#remote_host "screen -d -m sudo pm-suspend"
source: कार्यशाला (Kāryaśālā)

How to make sure an application keeps running on Linux

I'm trying to ensure a script remains running on a development server. It collates stats and provides a web service so it's supposed to persist, yet a few times a day, it dies off for unknown reasons. When we notice we just launch it again, but it's a pain in the rear and some users don't have permission (or the knowhow) to launch it up.
The programmer in me wants to spend a few hours getting to the bottom of the problem but the busy person in me thinks there must be an easy way to detect if an app is not running, and launch it again.
I know I could cron-script ps through grep:
ps -A | grep appname
But again, that's another hour of my life wasted on doing something that must already exist... Is there not a pre-made app that I can pass an executable (optionally with arguments) and that will keep a process running indefinitely?
In case it makes any difference, it's Ubuntu.
I have used a simple script with cron to make sure that the program is running. If it is not, then it will start it up. This may not be the perfect solution you are looking for, but it is simple and works rather well.
#!/bin/bash
#make-run.sh
#make sure a process is always running.
export DISPLAY=:0 #needed if you are running a simple gui app.
process=YourProcessName
makerun="/usr/bin/program"
if ps ax | grep -v grep | grep $process > /dev/null
then
exit
else
$makerun &
fi
exit
Then add a cron job every minute, or every 5 minutes.
Monit is perfect for this :)
You can write simple config files which tell monit to watch e.g. a TCP port, a PID file etc
monit will run a command you specify when the process it is monitoring is unavailable/using too much memory/is pegging the CPU for too long/etc. It will also pop out an email alert telling you what happened and whether it could do anything about it.
We use it to keep a load of our websites running while giving us early warning when something's going wrong.
-- Your faithful employee, Monit
Notice: Upstart is in maintenance mode and was abandoned by Ubuntu which uses systemd. One should check the systemd' manual for details how to write service definition.
Since you're using Ubuntu, you may be interested in Upstart, which has replaced the traditional sysV init. One key feature is that it can restart a service if it dies unexpectedly. Fedora has moved to upstart, and Debian is in experimental, so it may be worth looking into.
This may be overkill for this situation though, as a cron script will take 2 minutes to implement.
#!/bin/bash
if [[ ! `pidof -s yourapp` ]]; then
invoke-rc.d yourapp start
fi
If you are using a systemd-based distro such as Fedora and recent Ubuntu releases, you can use systemd's "Restart" capability for services. It can be setup as a system service or as a user service if it needs to be managed by, and run as, a particular user, which is more likely the case in OP's particular situation.
The Restart option takes one of no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, or always.
To run it as a user, simply place a file like the following into ~/.config/systemd/user/something.service:
[Unit]
Description=Something
[Service]
ExecStart=/path/to/something
Restart=on-failure
[Install]
WantedBy=graphical.target
then:
systemctl --user daemon-reload
systemctl --user [status|start|stop|restart] something
No root privilege / modification of system files needed, no cron jobs needed, nothing to install, flexible as hell (see all the related service options in the documentation).
See also https://wiki.archlinux.org/index.php/Systemd/User for more information about using the per-user systemd instance.
I have used from cron "killall -0 programname || /etc/init.d/programname start". kill will error if the process doesn't exist. If it does exist, it'll deliver a null signal to the process (which the kernel will ignore and not bother passing on.)
This idiom is simple to remember (IMHO). Generally I use this while I'm still trying to discover why the service itself is failing. IMHO a program shouldn't just disappear unexpectedly :)
Put your run in a loop- so when it exits, it runs again... while(true){ run my app.. }
I couldn't get Chris Wendt solution to work for some reason, and it was hard to debug. This one is pretty much the same but easier to debug, excludes bash from the pattern matching. To debug just run: bash ./root/makerun-mysql.sh. In the following example with mysql-server just replace the value of the variables for process and makerun for your process.
Create a BASH-script like this (nano /root/makerun-mysql.sh):
#!/bin/bash
process="mysql"
makerun="/etc/init.d/mysql restart"
if ps ax | grep -v grep | grep -v bash | grep --quiet $process
then
printf "Process '%s' is running.\n" "$process"
exit
else
printf "Starting process '%s' with command '%s'.\n" "$process" "$makerun"
$makerun
fi
exit
Make sure it's executable by adding proper file permissions (i.e. chmod 700 /root/makerun-mysql.sh)
Then add this to your crontab (crontab -e):
# Keep processes running every 5 minutes
*/5 * * * * bash /root/makerun-mysql.sh
The supervise tool from daemontools would be my preference - but then everything Dan J Bernstein writes is my preference :)
http://cr.yp.to/daemontools/supervise.html
You have to create a particular directory structure for your application startup script, but it's very simple to use.
first of all, how do you start this app? Does it fork itself to the background? Is it started with nohup .. & etc? If it's the latter, check why it died in nohup.out, if it's the first, build logging.
As for your main question: you could cron it, or run another process on the background (not the best choice) and use pidof in a bashscript, easy enough:
if [ `pidof -s app` -eq 0 ]; then
nohup app &
fi
You could make it a service launched from inittab (although some Linuxes have moved on to something newer in /etc/event.d). These built in systems make sure your service keeps running without writing your own scripts or installing something new.
It's a job for a DMD (daemon monitoring daemon). there are a few around; but I usually just write a script that checks if the daemon is running, and run if not, and put it in cron to run every minute.
Check out 'nanny' referenced in Chapter 9 (p197 or thereabouts) of "Unix Hater's Handbook" (one of several sources for the book in PDF).
A nice, simple way to do this is as follows:
Write your server to die if it can't listen on the port it expects
Set a cronjob to try to launch your server every minute
If it isn't running it'll start, and if it is running it won't. In any case, your server will always be up.
I think a better solution is if you test the function, too. For example, if you had to test an apache, it is not enough only to test, if "apache" processes on the systems exist.
If you want to test if apache OK is, then try to download a simple web page, and test if your unique code is in the output.
If not, kill the apache with -9 and then do a restart. And send a mail to the root (which is a forwarded mail address to the roots of the company/server/project).
It's even simplier:
#!/bin/bash
export DISPLAY=:0
process=processname
makerun="/usr/bin/processname"
if ! pgrep $process > /dev/null
then
$makerun &
fi
You have to remember though to make sure processname is unique.
One can install minutely monitoring cronjob like this:
crontab -l > crontab;echo -e '* * * * * export DISPLAY=":0.0" && for
app in "eiskaltdcpp-qt" "transmission-gtk" "nicotine";do ps aux|grep
-v grep|grep "$app";done||"$app" &' >> crontab;crontab crontab
disadvantage is that the app names you enter have to be found in ps aux|grep "appname" output and at same time being able to be launched using that name: "appname" &
also you can use the pm2 library.
sudo apt-get pm2
And if its a node app can install.
Sudo npm install pm2 -g
them can run the service.
linux service:
sudo pm2 start [service_name]
npm service app:
pm2 start index.js

Resources