I am really puzzled on this issue. I am using python's SimpleXMLRPC to provide services to a web application.
The problem is that when I start my xmlrpc server from the command line everything runs smoothly but when it is started through crontab it doesn't.
I have tried to hold the start-up via sleep and checking /sys/class/net/eth0/device/net/eth0/operstate but got no luck.
Please find attached the source for the script:
#!/usr/local/bin/python2.5
# -*- coding: utf-8 -*-
# License: GNU
# startxmlrpc.py: startup script for xmlrpc server to deal with processing
## {{{ http://code.activestate.com/recipes/439094/ (r1)
import socket
import fcntl
import struct
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
## end of http://code.activestate.com/recipes/439094/ }}}
import xmlrpclib
import urllib2
import os
from SimpleXMLRPCServer import SimpleXMLRPCServer
from time import sleep
def send(img1,img2,lib,filters):
global HOST_IP
path = '/var/www/%s/' % MD5Cypher(HOST_IP)
makedirs(path)
print "Path: %s" % path
if lib=='devel':
os.system("""python ~/devel_funcs.py %s %s "%s" &""" % (img1_path,img2_path, filters))
if lib=='milena':
import milena_funcs
milena_funcs.mln_process(img1_path, filters)
return HOST_IP + '/' + path.split('/var/www/')[1] + 'out.pgm'
while open('/sys/class/net/eth0/operstate').read().strip() != 'up':
sleep(5)
HOST_IP = get_ip_address('eth0')
server = SimpleXMLRPCServer((HOST_IP, 7070))
server.register_function(send)
server.serve_forever()
This is the error I get if I try to launch my process just after a clean boot:
<class 'xmlrpclib.Fault'>: <Fault 1: "<class 'xmlrpclib.ProtocolError'>:<ProtocolError for 192.168.0.5:7070/RPC2: -1 >">
args = ()
faultCode = 1
faultString = "<class 'xmlrpclib.ProtocolError'>:<ProtocolError for 192.168.0.5:7070/RPC2: -1 >"
message = ''
If I kill it and run it again, it works.
This is the crontab:
usrmln#Slave1:~$ crontab -l
# m h dom mon dow command
* * * * * python ~/master_register.py > /dev/null 2>&1
* * * * * python ~/startxmlrpc.py > /dev/null 2>&1
0 5 * * * find /var/www/ -type d -mtime +3 -exec rm -rf {} \; > /dev/null 2>&1
You don't show what the error is, but, it is possibly that PYTHONPATH is not being set when run from cron. You could set it before running the script.
Or, of course, you are running it as a different user and file permissions are not correctly set. Also ~/devel_funcs.py will not refer to your home directory if cron runs your script as a different user.
I finally got it, I was using python 2.5 locally and I had to add to the execution like this:
/usr/local/bin/python2.5 /home/username/startxmlrpc.py > /dev/null 2>&1
Related
I'm currently creating a Raspberry Pi (Model B) based temperature sensor with a display. I am trying to run a shell start up script in the LX Terminal but keep getting a "Permission Denied" error next to a Python sub-script as follows:
pi#raspberrypi ~ $ sudo /home/pi/tempsense/etc/init.d/envmon start
Starting envmon
pi#raspberrypi ~ $ /home/pi/tempsense/etc/init.d/envmon: 15:/home/pi/tempsense/etc/init.d/envmon: home/pi/tempsense/opt/envmon/displayenvmon.py: Permission denied
The shell script is:
#!/bin/sh
### BEGIN INIT INFO
# Provides: envmon
# Required-Start: $local_fs
# Required-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start/stop envmon
### END INIT INFO
case "$1" in
start)
/home/pi/tempsense/opt/envmon/dht11 &
echo "Starting envmon"
/home/pi/tempsense/opt/envmon/displayenvmon.py &
;;
stop)
pkill dht11
pkill displayenvmon
echo "envmon stopped"
;;
*)
echo "Usage: /home/pi/tempsense/etc/init.d/envmon {start|stop}"
exit 1
;;
esac
exit 0
The Python script causing the Permission denied errors is:
#!/usr/local/lib/python2.7/dist-packages
from RPLCD import CharLCD
import RPi.GPIO as GPIO
import subprocess, re
import time
# Data file - current reading
datfile = "/var/envmon.data"
# Rate at which the LCD is updated
UPDATE_RATE = 5
lcd = CharLCD(cols=16, rows=2,
pin_rw=None,
pin_rs=7,
pin_e=8,
pins_data=[25,24,23,18],
numbering_mode=GPIO.BCM)
lcd.cursor_pos = (0, 0)
lcd.write_string('Visit #UWS_Pi')
lcd.cursor_pos = (1, 0)
lcd.write_string('TEMP & HUMID')
time.sleep(5);
# Get IP address - looks for first IP address which is not 127.0.0.1
address_string = subprocess.getoutput("ip addr")
ipaddr = "Unknown"
# Reg exp to extract IP addresss
search_inet = re.compile('inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
for line in address_string.split('\n') :
match = search_inet.search(line)
if (match != None) :
if (match.group(1) != '127.0.0.1') :
ipaddr = match.group(1)
break
# Print the IP address to the LCD
lcd.cursor_pos = (1,0)
lcd.write_string (ipaddr)
# Sleep to give chance to read IP address
time.sleep(5)
# Loop get most recent reading and display on screen
while (True):
fh = open (datfile, "r")
entry = fh.read()
# split into separate entries
[currtime, currtemp, currhumid] = entry.split()
# Change currtime to a formatted time ready for displaying
formattime = time.strftime ("%d/%m/%Y %H:%M", time.localtime(int(currtime)))
# print time to display
lcd.cursor_pos = (0,0)
lcd.write_string (formattime)
# Print temp and humidity values
lcd.cursor_pos = (1,0)
lcd.write_string ("T " + currtemp + "C RH " + currhumid + "% ")
time.sleep(UPDATE_RATE)
I am new to this and any helpful advice is appreciated.
To use the GPIO pins, the Python script needs to be run with administrator privileges. Either execute the shell script as root or call your Python script using sudo from within the shell script.
Either log in as root, or run the program with sudo, because usually permission denied means you need root permissions, sudo runs that specific program with root permissions if you don't want to log in as root.
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 +
I want use fabric in python, to execute command on remote server.
I wrote these:
from fabric.api import *
from fabric.tasks import execute
def do_some_thing():
run("ls -lh")
if __name__ == '__main__':
execute(do_some_thing,hosts=['root#10.18.103.102'])
but, it doesn't work and make me login..
It's the output:
➜ ~ python test.py
[root#10.18.103.102] Executing task 'do_some_thing'
[root#10.18.103.102] run: ls -lh
[root#10.18.103.102] out: root#svn:~#
[root#10.18.103.102] out: root#svn:~#
Make use of the env variable -
from fabric.api import *
from fabric.contrib.files import *
def myserver():
env.hosts = ['10.18.103.102']
env.user = 'root'
# if you have key based authentication, uncomment and point to private key
# env.key_filename = '~/.ssh/id_rsa'
# if you have password based authentication
env.password = 'ThEpAsAwOrd'
def ls():
run('ls -al')
Now save these in a file call fabfile.py and execute (on the same directory) -
$ fab myserver ls
Fabric will execute both the functions one after another. So when it executes ls() it'll have the server details in env.
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.
I have a problem with a backup script which is supposed to call a bash starting/stopping script, in which a "daemon" (via GNU screen) is managed. For the moment my python backup script is called via cron. Within the launch.sh script there is a determination of the given parameter. If "stop" is given the script echos "Stopping..." and runs the GNU screen command to shut down the session. The same goes for "start". If the script is called via subprocess.call(...,Shell=True) in Python the string is shown but the screen session remains untouched. If it gets called directly in bash everything works fine.
#!/usr/bin/env python
'''
Created on 27.07.2013
BackUp Script v0.2
#author: Nerade
'''
import time
import os
from datetime import date
from subprocess import check_output
import subprocess
script_dir = '/home/minecraft/automated_backup'
#folders = ['/home/minecraft/staff']
folders = ['/home/minecraft/bspack2','/home/minecraft/staff']
# log = 0
backup_date = date.today()
backup_dir = '/home/minecraft/automated_backup/' + backup_date.isoformat()
def main():
global log
init_log()
init_dirs()
for folder in folders:
token = folder.split("/")
stopCmd = folder + '/launch.sh stop'
log.write("Stopping server %s...\n" % (token[3]))
subprocess.call(stopCmd,shell=True)
#print stopCmd
while screen_present(token[3]):
time.sleep(0.5)
log.write("Server %s successfully stopped!\n" % (token[3]))
specificPath = backup_dir + '/' + token[3]
os.makedirs(specificPath)
os.system("cp /home/minecraft/%s/server.log %s/server.log" % (token[3],specificPath))
backup(folder,specificPath + '/' + backup_date.isoformat() + '.tar.gz')
dumpDatabase(backup_dir)
for folder in folders:
token = folder.split("/")
startCmd = folder + '/launch.sh start'
log.write("Starting server %s...\n" % (token[3]))
subprocess.call(startCmd,shell=True)
time.sleep(1)
log.write(screen_present(token[3]))
#print startCmd
def dumpDatabase(target):
global log
log.write("Dumping Database...\n")
cmd = "mysqldump -uroot -p<password> -A --quick --result-file=%s/%s.sql" % (backup_dir,backup_date.isoformat())
os.system(cmd)
#print cmd
def backup(source,target):
global log
log.write("Starting backup of folder %s to %s\n" % (source,target))
cmd = 'tar cfvz %s --exclude-from=%s/backup.conf %s' % (target,source,source)
os.system(cmd)
#print cmd
def screen_present(name):
var = check_output(["screen -ls; true"],shell=True)
if "."+name+"\t(" in var:
return True
else:
return False
def init_log():
global log
log = open("%s/backup.log" % script_dir,'a')
log.write(
"Starting script at %s\n" % time.strftime("%m/%d/%Y %H:%M:%S")
)
def init_dirs():
global backup_dir,log
log.write("Checking and creating directories...\n")
if not os.path.isdir(backup_dir):
os.makedirs(backup_dir)
if __name__ == '__main__':
main()
And the launch.sh:
#!/bin/sh
if [ $# -eq 0 ] || [ "$1" = "start" ]; then
echo "Starting Server bspack2"
screen -S bspack2 -m -d java -Xmx5G -Xms4G -jar mcpc-plus-legacy-1.4.7-R1.1.jar nogui
fi
if [ "$1" = "stop" ]; then
screen -S bspack2 -X stuff 'stop\015'
echo "Stopping Server bspack2"
fi
What's my problem here?
I'm sure by now you've solved this problem, but looking through your question I'd bet the answer is remarkably simple -- mcpc-plus-legacy-1.4.7-R1.1.jar isn't found by java, which fails, and subsequently screen terminates.
In launch.sh, screen will execute in the same directory as the calling script. In this case, your python script, when run by cron, will have an active directory of the running user's home directory (so root crontabs will run in /root/, for instance, and a user crontab in /home/username/).
Simple solution is just to the following:
cd /home/minecraft/bspack2
as the second line in your launch.sh script, just after #!/bash/sh.
In the future, I'd recommend when interacting with screen to leverage the -L parameter. This turns on autologging. By default, in the current directory a file "screenlog.0" will be generated when screen terminates, showing you a log history of activity during the screen session. This will allow you to debug screen problems with ease, and help encourage keeping track of "current directory" while working with shell scripts, to make finding the screen log output simple.