Cron scheduler of python script using notify-send - python

I'm having some difficulty using these three technologies together.
Cron entry:
* * * * * /usr/bin/python /path/to/python/email/program.py
Python program:
1 #!/usr/bin/python
2
3 import imaplib
4 import os
5 import sys
6 import pynotify
7
8 if not pynotify.init('Emails'):
9 sys.exit(1)
10
11 with open('/path/to/python/email/count.tmp', 'r') as file:
12 data = int(file.read().strip())
13 client = imaplib.IMAP4_SSL('mail.sever.com', '993')
14 client.login('user#server.com', 'password')
15 client.select()
16
17 unseen = client.search(None, 'UnSeen')[1][0].split()
18
19 if unseen[0] == '':
20 pass
21 else:
22 if len(unseen) != data:
23 n = pynotify.Notification(str(len(unseen) - data) + " New Messages",
24 str(len(unseen)) + " Unread Messages",
25 "file:///path/to/python/email/mail.png")
26 if not n.show():
27 print "Fail"
28 sys.exit(1)
30 with open('/path/to/python/email/count.tmp', 'w') as file:
31 file.write(str(len(unseen)))
The script works correctly when run by itself, but it will not run when scheduled as a cron job. I checked the syslog and it says the script is being run, and I've run as sudo the line from the log to verify.
I've checked
Execute python Script on Crontab
and
Cron with notify-send
but neither, nor further links seem to address this combination.
Any thoughts?
-Update 1-
As pynotify seems not to allow the program to work at all, I've replaced their calls with an os.system call. At least this updates the small tmp file, but still no notify.
os.system('/usr/bin/X11/notify-send "{} New Messages" "{} Unread Messages"'.format(len(unseen) - data, len(unseen))
-Update 2-
/usr/lib/python2.7/dist-packages/gtk-2.0/gtk/__init__.py:57: GtkWarning: could not open display
warnings.warn(str(e), _gtk.Warning)
** (other.py:16509): WARNING **: Command line `dbus-launch --autolaunch=91bdcc4a583bfb56734fbe1200000010 --binary-syntax --close-stderr' exited with non-zero exit status 1: Autolaunch error: X11 initialization failed.\n

When you run your program manually, it works because the variable DISPLAY is being set to your current display (:0 in most cases), however when running the script via cron, there is no such variable being set and notifications doesn't work.
As mentionned in this answer, you should export both the display and the Xauthority, and finally running the script as your user (and not as root).
Try something like this :
* * * * * export DISPLAY=:0.0 && export XAUTHORITY=/home/<username>/.Xauthority && sudo -u <username> /usr/bin/python /path/to/python/email/program.py
(change <username> with your username)

Change your cron entry to
* * * * * /usr/bin/python /path/to/python/email/program.py >> /var/log/program.log 2>&1
and see what you get in log file program.log
You can add print statements to your program to debug better (as you will get them in program.log)

Try this command:
*\1 * * * * sudo -u user DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send 'Hi.'

In an answer to another question I described a generalised notify-desktop github gist written for use in cron and timer jobs. The script deals with identifying the desktop user(s) and offers a solution for Wayland as well as X11.

Under Ubuntu 20.04, the following Python script stored under /home/<user>/notify.py:
import subprocess
subprocess.Popen(['notify-send', 'Running. Finally.'])
sucessfully sends a message from my user's crontab (crontab -e, not root) with the entry:
*/1 * * * * export DISPLAY=:0.0 && export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus && /usr/bin/python3 /home/<user>/notify.py
For me, the DBUS_SESSION_BUS_ADDRESS parameter made the difference. Solutions without it didn't work.

Related

Python: difference for subProcess output between cron and manual

I'm on SLES 12 SP5 with python 3 and I met some difficulties for executing a part of my code through cron.
The function in error has to retrieve a file name in another cron file:
def getBackupFileName():
if os.path.exists(BACKUP_CRON_FILE):
cmd='/usr/bin/more /etc/cron.d/backup | cut -d\" \" -f10'
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = ps.communicate()[0]
sendBackup_logger.info("sortie getBackupNameFile :"+output.decode().strip())
return output.decode().strip()
return null
The /etc/cron.d/backup is:
0 0 * * * root myApp db backup BackupTest.sql.gz
and the cron using the faulty code is:
0 1 * * * root myApp db sendBackup 195.168.1.10
When manually executing myApp db sendBackup 195.168.1.10, this function returns: BackupTest.sql.gz, which is what I was waiting.
During the execution through cron, this function returns:
::::::::::::::
/etc/cron.d/backup
::::::::::::::
BackupTest.sql.gz
Maybe an issue in environment variables? I checked:
myApp: starts with #!/usr/bin/env python3
in crontab environment: PATH=/usr/bin:/usr/sbin:/sbin:/bin:/usr/lib/news/bin
application is launched with root.
Any idea why I got some additional lines in output with cron?

Python : python-crontab does not execute

I want to make crontab execute python script every minute, but It seems like crontab does not execute script at all.
This is python file where I made crontab job.
schedule.py
from crontab import CronTab
def main():
my_cron = CronTab(user='jelly')
cmd = '.venv/bin/python Users/jelly/PycharmProjects/test/writeDate.py'
job = my_cron.new(cmd, comment='test')
job.minute.every(1)
my_cron.write()
print(my_cron.render())
if __name__ == "__main__":
main()
This is message when I run schdule.py
* * * * * .venv/bin/python Users/hyun/PycharmProjects/test/writeDate.py # test
This is script supposed to be executed
import datetime
with open('dateInfo.txt', 'a') as outFile:
outFile.write('\n' + str(datetime.datetime.now()))
When I go into terminal and typed command line
ps aux | grep cron
I could see messages like this
root 47903 0.0 0.0 2460724 1696 ?? Ss 6:28PM 0:00.31 /usr/sbin/cron
jelly 71389 0.0 0.0 2432804 1380 s003 R+ 12:00PM 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn cron
This message sometimes shows R+ and sometimes shoes S+, so I assumed that crontab is running, but I can't find any new txt files that supposed to be created in directory. I wonder which part went wrong and how can I fix it.
Problem lies in path to python. Cron runs with limited environment, so it does not know where is .venv/bin/python and Users/jelly/PycharmProjects/test/writeDate.py
You must use absoulte paths

python-notify module & cron: gio.Error

I'm asking some help to show notifications using python-crontab, because everything I've tried do not work. The display is not initilised when the script is launched by cron. When I start it manually, that's work.
The codes I've tried:
#!/usr/bin/env python
# coding: utf8
import subprocess
import os
#os.environ.setdefault("XAUTHORITY", "/home/guillaume" + "/.Xauthority")
#os.environ.setdefault('DISPLAY', ':0.0') # do not work
#os.environ['DISPLAY'] = ':0.0' # do not work
print = os.environ
cmd2 = 'notify-send test'
subprocess.call(cmd2, shell=True)
# more code, which is working (using VLC)
cmd3 = "cvlc rtp://232.0.2.183:8200 --sout file/mkv:/path/save/file.mkv" # to download TV's flow
with open("/path/debug_cvlc.log", 'w') as out:
proc = subprocess.Popen(cmd3, stderr=out, shell=True, preexec_fn=os.setsid)
pid = proc.pid # to get the pid
with open("/path/pid.log", "w") as f:
f.write(str(pid)) # to write the pid in a file
# I'm using the pid to stop the download with another cron's task, and to display another notify message.
# Download and stop is working very well, and zenity too. But not notify-send
Thanks
Edit: here are the environment variables I have for this cron's script:
{'LANG': 'fr_FR.UTF-8', 'SHELL': '/bin/sh', 'PWD': '/home/guillaume', 'LOGNAME': 'guillaume', 'PATH': '/usr/bin:/bin', 'HOME': '/home/guillaume', 'DISPLAY': ':0.0'}
Edit2: I'm calling my script in cron like this:
45 9 30 6 * export DISPLAY=:0.0 && python /home/path/script.py > /home/path/debug_cron_on.log 2>&1
I precise I have two screens, so I think DISPLAY:0.0 is the way to display this notify..
But I don't see it.
Edit3: It appears that I've a problem with notify-send, because it's working using zenity:
subprocess.call("zenity --warning --timeout 5 --text='this test is working'", shell=True)
I have notify-send version 0.7.3, and I precise that notify-send is working with the terminal.
Edit4: Next try with python-notify.
import pynotify
pynotify.init("Basic")
n = pynotify.Notification("Title", "TEST")
n.show()
The log file show this: (in french)
Traceback (most recent call last):
File "/home/path/script.py", line 22, in <module>
n.show()
gio.Error: Impossible de se connecter : Connexion refusée
#Translating: Unable to connect : Connection refused
So, I have problem with dbus? what is this?
Solution: Get the DBUS_SESSION_BUS_ADDRESS before creating the cron order:
cron = CronTab()
dbus = os.getenv("DBUS_SESSION_BUS_ADDRESS") # get the dbus
# creating cron
cmd_start = "export DBUS_SESSION_BUS_ADDRESS=" + str(dbus) + " && export DISPLAY=:0.0 && cd /path && python /path/script.py > path/debug_cron.log 2>&1"
job = cron.new(cmd_start)
job = job_start.day.on(self.day_on) # and all the lines to set cron, with hours etc..
cron.write() # write the cron's file
Finally, the cron's line is like that:
20 15 1 7 * export DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-M0JCXXbuhC && export DISPLAY=:0.0 && python script.py
Then the notification is displaying. Problem resolved !! :)
You are calling the cron like
45 9 30 6 * DISPLAY=:0.0 python /home/path/script.py > /home/path/debug_cron_on.log 2>&1
which is incorrect, since you are not exporting the DISPLAY variable, and the subsequent command does not run.
Try this instead
45 9 30 6 * export DISPLAY=:0.0 && cd /home/path/ && python script.py >> debug_cron.log 2>&1
Also, you are setting the DISPLAY variable within your cron job as well, so try if the cron job works without exporting it in the job line
45 9 30 6 * cd /home/path/ && python script.py >> debug_cron.log 2>&1
EDIT
While debugging, run the cron job every minute. Following worked for me:
Cron entry
* * * * * cd /home/user/Desktop/test/send-notify && python script.py
script.py
#!/usr/bin/env python
import subprocess
import os
os.environ.setdefault('DISPLAY', ':0.0')
print os.environ
cmd2 = 'notify-send test'
subprocess.call(cmd2, shell=True)
EDIT 2
Using pynotify, script.py becomes
#!/usr/bin/env python
import pynotify
import os
os.environ.setdefault('DISPLAY', ':0.0')
pynotify.init("Basic")
n = pynotify.Notification("Title", "TEST123")
n.show()
and cron entry becomes
* * * * * cd /home/user/Desktop/test/send-notify && python script.py
EDIT 3
One environment variable DBUS_SESSION_BUS_ADDRESS is missing from the cron environment.
It can be set in this and this fashion
crontab is considered an external host -- it doesn't have permission to write to your display.
Workaround: allow anyone to write to your display. Type this in your shell when you're logged in:
xhost +

Django commands being called by Crontab, but they are not executed

I have my 'sudo crontab -e' set up as follows:
0 7,8,9,10,11,20 * * * /home/ubuntu/mydjangoapp/manage.py command >> /home/ubuntu/mydjangoapp/file.log
Today it executed correctly at 7am and 8am, however it suddenly stopped working... I have checked the /var/log/syslog and it says that the cronjob is being run, however nothing happens.
I tried changing the crontab to
0 7,8,9,10,11,20 * * * /home/ubuntu/mydjangoapp/manage.py command
then
0 7,8,9,10,11,20 * * * /home/ubuntu/mydjangoapp/manage.py command
and
0 7,8,9,10,11,20 * * * /usr/bin/python /home/ubuntu/mydjangoapp/manage.py command
However there was no success - /var/log/syslog was still saying that the command was being executed, but nothing happens.
I tried running a simple command such as
10 * * * * cat /absolutepath/file1 >> /absolutepath/file2
and it actually worked, so I thought it might work if I create a script.sh that runs my command:
!/bin/sh
/usr/bin/python /home/ubuntu/myapp/manage.py chronwakeup
but there was no success - then I thought, maybe my python path was not defined, so I added:
!/bin/sh
export PATH="{$PATH}:/usr/local/lib/python2.7/dist-packages/django_evolution-0.6.9-py2.7.egg:/usr/local/lib/python2.7/dist-packages/dnspython-1.11.1-py2.7.egg:/usr/local/lib/python2.7/dist-packages/Django-1.5-py2.7.egg:/usr/lib/python2.7:/usr/lib/python2.7/plat-linux2:/usr/lib/python2.7/lib-tk:/usr/lib/python2.7/lib-old:/usr/lib/python2.7/lib-dynload:/usr/local/lib/python2.7/dist-packages:/usr/lib/python2.7/dist-packages:/usr/lib/python2.7/dist-packages/PIL:/usr/lib/pymodules/python2.7"
/usr/bin/python /home/ubuntu/myapp/manage.py chronwakeup
However again, there was no success. I then tried creating a simple django command called dostuff.py containing only two print statements:
from django.core.management.base import NoArgsCommand, make_option
class Command(NoArgsCommand):
help = "Excecute Chron Wake Up Chron Roulette for the current batch of wake-ups."
option_list = NoArgsCommand.option_list + (
make_option('--verbose', action='store_true'),
)
def handle_noargs(self, **options):
print "doing stuff..."
print "Done doing stuff."
and added the crontab command as 'sudo crontab -e':
43 7,8,9,10,11,20 * * * /home/ubuntu/myapp/manage.py dostuff >> /home/ubuntu/itworked
Once again, the command was executed, and the file was created, but nothing was created inside the file...
The weirdest thing is that it was working this morning, and it suddenly stopped working...
I would be really grateful if you could help me out!

How to run script from django?

I would like to know how to run a script from a django view.
It works from the command line: Eg: $ python sync.py But not via the django view. Thanks in advance
script 1: /home/ubuntu/webapps/sony_mv/sync.py
#!/usr/bin/env python
from subprocess import call
call(["/bin/sh", "/home/ubuntu/webapps/sony_mv/sync.sh"])
script 2: /home/ubuntu/webapps/sony_mv/sync.sh
cd /home/ubuntu/webapps/sony_mv
heroku pgbackups:capture -a staging-db --expire
heroku pgbackups:capture -a prod-db --expire
heroku pgbackups:restore DATABASE -a prod-db `heroku pgbackups:url -a staging-d` --confirm prod-db
views.py
def sync_staging_to_production(request):
try:
token = request.GET['token']
except:
token = False
if token == '382749813256-231952135':
from subprocess import *
import sys
p = Popen([sys.executable, '/home/ubuntu/webapps/sony_mv/sync.py'],stdout=PIPE,stderr=STDOUT)
return render_to_response('hannibal/sync_staging_to_production.html',{'feedback':'Success. Sync in progress.'},context_instance=RequestContext(request))
else:
return render_to_response('hannibal/sync_staging_to_production.html',{'feedback':'Authorization required'},context_instance=RequestContext(request))
ls output
$ ls -l sync.*
-rwxrwxr-x 1 root 108 2013-04-09 16:35 sync.py
-rwxrwxr-x 1 root 326 2013-04-09 16:44 sync.sh
whoami output
$ python
>>> from subprocess import call
>>> call(["/usr/bin/whoami"])
ubuntu
0
>>>
Adding a log for the output of the shell commands helped to debug.
The issue was related with permissions and ssh keys for the corresponding user.
Adding the corresponding user SSH keys fixed the issue.
Thanks everyone

Categories