I am building a simple pyhon daemon based on Sander Marechal's code. Daemon's whole purpose is to run a php file every second (php file loops through database checking values and updating database). Problem arises on the section
subprocess.call(['php','test.php'])
I can run "php test.php" on shell and it does what it is suppose to do but when it is called periodically from the daemon it doesn't seem to be executed. I also know daemon works on the background via checking running process ps aux | grep "daemon-example" also i included a do_something function which records every time function executed and appends time to a text file.
#!/usr/bin/env python
import sys, time,subprocess
from daemon import Daemon
def runphp():
#subprocess.call(['php ~/pydaemon/test.php'], shell=True)
subprocess.call(['python', 'test.py'])
def do_something():
with open("/tmp/current_time.txt",'a') as f:
f.write("The time is now\n" + time.ctime())
class MyDaemon(Daemon):
def run(self):
while True:
time.sleep(1)
do_something()
subprocess.call(['php','test.php'])
#runphp()
if __name__ == "__main__":
daemon = MyDaemon('/tmp/daemon-example.pid')
if len(sys.argv) == 2:
if 'start' == sys.argv[1]:
daemon.start()
elif 'stop' == sys.argv[1]:
daemon.stop()
elif 'restart' == sys.argv[1]:
daemon.restart()
else:
print "Unknown command"
sys.exit(2)
sys.exit(0)
else:
print "usage: %s start|stop|restart" % sys.argv[0]
sys.exit(2)
The script you are trying to run is not executed because the working directory is the root directory ('/') and that's because of this piece of code:
# decouple from parent environment
os.chdir("/")
So actually your code tries to execute: python /test.py(which does not exist) and not 'your_current_directory/test.py'.
To fix it either remove os.chdir("/"), or provide the full path to the file like so:
subprocess.call(['python','my_full_path_to_working_directory/test.py'])
Related
I'm relatively new to python so please forgive early level understanding!
I am working to create a kind of flag file. Its job is to monitor a Python executable, the flag file is constantly running and prints "Start" when the executable started, "Running" while it runs and "Stop" when its stopped or crashed, if a crash occurs i want it to be able to restart the script. so far i have this down for the Restart:
from subprocess import run
from time import sleep
# Path and name to the script you are trying to start
file_path = "py"
restart_timer = 2
def start_script():
try:
# Make sure 'python' command is available
run("python "+file_path, check=True)
except:
# Script crashed, lets restart it!
handle_crash()
def handle_crash():
sleep(restart_timer) # Restarts the script after 2 seconds
start_script()
start_script()
how can i implement this along with a flag file?
Not sure what you mean with "flag", but this minimally achieves what you want.
Main file main.py:
import subprocess
import sys
from time import sleep
restart_timer = 2
file_path = 'sub.py' # file name of the other process
def start():
try:
# sys.executable -> same python executable
subprocess.run([sys.executable, file_path], check=True)
except subprocess.CalledProcessError:
sleep(restart_timer)
return True
else:
return False
def main():
print("starting...")
monitor = True
while monitor:
monitor = start()
if __name__ == '__main__':
main()
Then the process that gets spawned, called sub.py:
from time import sleep
sleep(1)
print("doing stuff...")
# comment out to see change
raise ValueError("sub.py is throwing error...")
Put those files into the same directory and run it with python main.py
You can comment out the throwing of the random error to see the main script terminate normally.
On a larger note, this example is not saying it is a good way to achieve the quality you need...
I'm trying to create a "runner" using QProcess.startDetached`, but I'm having issues using Linux's aliases.
So far I've been able to run an alias using this code (based on this answer):
QtCore.QProcess.startDetached('/bin/bash', ['-i', '-c', 'somealias'])
The issue is that, since the actual "program" that is going to run is bash, it always returns True, even if the alias doesn't exists, meaning that the process "believes" that it has started, even if it actually hasn't, because bash was successfully run even if its alias hasn't been "found".
Obviously, this is not going to be a cross-platform program, and I know I could try and parse the current environment from the output of aliases (but, since aliases might include bash commands, that could be a problem).
Anyway I wander if there's another way to run a detached QProcess using Linux aliases, and eventually get the False returned value if the alias doesn't exists.
As the docs points out:
bool QProcess::startDetached(const QString &program, const QStringList
&arguments, const QString &workingDirectory = QString(), qint64 *pid =
nullptr)
This function overloads startDetached().
Starts the program program with the arguments arguments in a new
process, and detaches from it. Returns true on success; otherwise
returns false. If the calling process exits, the detached process will
continue to run unaffected.
Argument handling is identical to the respective start() overload.
The process will be started in the directory workingDirectory. If
workingDirectory is empty, the working directory is inherited from the
calling process.
If the function is successful then *pid is set to the process
identifier of the started process.
(emphasis mine)
In other words, returns a boolean that indicates if the command was started, it does not analyze if in the execution of the program your command was broken so it is not optimal for your requirement.
In this case, the solution is to use the finished signal that returns the execution status:
from PyQt5 import QtCore
if __name__ == "__main__":
import sys
command = "foo"
app = QtCore.QCoreApplication(sys.argv)
process = QtCore.QProcess()
def on_finished(exitCode, exitStatus):
print(exitCode == 0)
QtCore.QCoreApplication.quit()
process.finished.connect(on_finished)
process.start("/bin/bash", ["-i", "-c", command])
sys.exit(app.exec_())
Output
False
from PyQt5 import QtCore
def check_alias(command):
val = False
process = QtCore.QProcess()
loop = QtCore.QEventLoop()
def on_finished(exitCode, exitStatus):
nonlocal val
val = exitCode == 0
loop.quit()
process.finished.connect(on_finished)
process.start("/bin/bash", ["-i", "-c", command])
loop.exec_()
return val
if __name__ == "__main__":
import sys
app = QtCore.QCoreApplication(sys.argv)
for command in ("foo", "ls"):
print(f"{command}: {check_alias(command)}")
Output:
foo: False
ls: True
from PyQt5 import QtCore
def check_alias(command):
val = False
process = QtCore.QProcess()
process.start("/bin/bash", ["-i", "-c", command])
process.waitForFinished(-1)
return process.exitCode() == 0
if __name__ == "__main__":
import sys
app = QtCore.QCoreApplication(sys.argv)
for command in ("foo", "ls"):
print(f"{command}: {check_alias(command)}")
Output:
foo: False
ls: True
this is the daemon class i am using
it is acting as a base class which i want to spawn 2 seperate daemons from another controller file
class Daemon:
"""A generic daemon class.
Usage: subclass the daemon class and override the run() method."""
def __init__(self, pidfile,outfile='/tmp/daemon_out',errfile='/tmp/daemon_log'):
self.pidfile = pidfile
self.outfile = outfile
self.errfile = errfile
def daemonize(self):
"""Deamonize class. UNIX double fork mechanism."""
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as err:
sys.stderr.write('fork #1 failed: {0}\n'.format(err))
sys.exit(1)
# decouple from parent environment
os.chdir('/')
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError as err:
sys.stderr.write('fork #2 failed: {0}\n'.format(err))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = open(os.devnull, 'r')
so = open(self.outfile, 'a+')
se = open(self.errfile, 'a+')
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# write pidfile
atexit.register(self.delpid)
pid = str(os.getpid())
with open(self.pidfile,'w+') as f:
f.write(pid + '\n')
#method for removing the pidfile before stopping the program
#remove the commented part if you want to delete the output & error file before stopping the program
def delpid(self):
os.remove(self.pidfile)
#os.remove(self.outfile)
#os.remove(self.errfile)
def start(self):
"""Start the daemon."""
# Check for a pidfile to see if the daemon already runs
try:
with open(self.pidfile,'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
if pid:
message = "pidfile {0} already exist. " + \
"Daemon already running?\n"
sys.stderr.write(message.format(self.pidfile))
sys.exit(1)
# Start the daemon
self.daemonize()
self.run()
def stop(self):
#Stop the daemon.
# Get the pid from the pidfile
try:
with open(self.pidfile,'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
if not pid:
message = "pidfile {0} does not exist. " + \
"Daemon not running?\n"
sys.stderr.write(message.format(self.pidfile))
return # not an error in a restart
# Try killing the daemon process
try:
while 1:
os.kill(pid, signal.SIGTERM)
time.sleep(0.1)
except OSError as err:
e = str(err.args)
if e.find("No such process") > 0:
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
print (str(err.args))
sys.exit(1)
def restart(self):
"""Restart the daemon."""
self.stop()
self.start()
def run(self):
"""override this method when you subclass Daemon.
It will be called after the process has been daemonized by
start() or restart()."""
here is the code i am using in a different file
in this file i am extending the daemon class from seperate classes & overriding the run() method.
#! /usr/bin/python3.6
import sys, time, os, psutil, datetime
from daemon import Daemon
class net(Daemon):
def run(self):
while(True):
print("net daemon : ",os.getpid())
time.sleep(200)
class file(Daemon):
def run(self):
while(True):
print("file daemon : ",os.getpid())
time.sleep(200)
if __name__ == "__main__":
net_daemon = net(pidfile='/tmp/net_pidFile',outfile='/tmp/network_out.log',errfile='/tmp/net_error.log')
file_daemon = file(pidfile='/tmp/file_pidFile',outfile='/tmp/filesys_out.log',errfile='/tmp/file_error.log')
if len(sys.argv) == 2:
if 'start' == sys.argv[1]:
net_daemon.start()
file_daemon.start()
elif 'stop' == sys.argv[1]:
file_daemon.stop()
net_daemon.stop()
elif 'restart' == sys.argv[1]:
file_daemon.restart()
net_daemon.restart()
else:
print("Unknown command")
sys.exit(2)
sys.exit(0)
else:
print("usage: %s start|stop|restart" % sys.argv[0])
sys.exit(2)
the first class to run the start() method is running currently &
only the net Daemon works now how do i make the 2 classes spawn 2 seperate daemons ??
The real problem here is that you've chosen the wrong code for the task you want. You're asking "How do I use this power saw to hammer in this nail?" And in this case, it's not even a professionally-produced saw with an instruction manual, it's a home-made saw you found in someone's garage, built by a guy who probably knew what he was doing but you can't actually be sure because you don't know what he was doing.
The proximate problem that you're complaining about is in daemonize:
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
The first time you call this, the parent process exits. Which means the parent process never gets to launch the second daemon, or do anything else.
For a self-daemonizing program that can be managed by a separate program, this is exactly what you want. (Whether it gets all the details right, I don't know, but the basic idea is definitely right.)
For a managing program that spawns daemons, this is exactly what you don't want. And that's what you're trying to write. So this is the wrong tool for the job.
But the tasks aren't that much different. If you understand what you're doing (and crack open your copy of Unix Network Programming—nobody understands this stuff well enough to get it right off the top of their head), you can convert one into the other. Which might be a useful exercise, even if for any real application I'd just use one of the well-tested, well-documented, nicely-maintained libraries on PyPI.
What happens if you just replace the sys.exit(0) calls that happen in the parent process (but not the ones that happen in the intermediate child!) with return True? (Well, you probably want to also replace the sys.exit(1) in the parent with a return False or raise some kind of exception.) Then daemonize no longer daemonizes you, but instead spawns a daemon and reports back on whether it succeeded. Which is what you wanted, right?
No guarantees that it does everything else right (and I'd bet it doesn't), but it does solve the specific problem you were asking about.
If nothing obvious is going wrong after that, the next step would probably be to read through PEP 3143 (which does a pretty nice job translating all the details in Stevens' book into Python terms and making sure they're up to date for 21st century linux and BSD) and come up with a checklist of tests to run, and then run them to see what less obvious things you're still getting wrong.
I am trying to call a script from python-daemon but its not working. this is what i am tying to do, is it correct?
I also want to pass a random argument to that script, currently i have hard coded it
import daemon
import time
import subprocess
import os
def interval_monitoring():
print "Inside interval monitoring"
while True:
print "its working"
# os.system("XYZ.py 5416ce0eac3d94693cf7dbd8") Tried this too but not working
subprocess.Popen("XYZ.py 5416ce0eac3d94693cf7dbd8", shell=False)
time.sleep(60)
print "condition true"
def run():
print daemon.__file__
with daemon.DaemonContext():
interval_monitoring()
if __name__ == "__main__":
run()
If you didn't make XYZ.py executable and added #!/usr/bin/env python in the top line, you need to call it via python, rather than directly. So your line would be something like this:
subprocess.check_output(["python", "XYZ.py", "5416ce0eac3d94693cf7dbd8"])
I have a simple web.py program to load data. In the server I don't want to install apache or any webserver.
I try to put it as a background service with http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
And subclassing:
(from http://www.jejik.com/files/examples/daemon.py)
class Daemon:
def start(self):
"""
Start the daemon
"""
... PID CHECKS....
# Start the daemon
self.daemonize()
self.run()
#My code
class WebService(Daemon):
def run(self):
app.run()
if __name__ == "__main__":
if DEBUG:
app.run()
else:
service = WebService(os.path.join(DIR_ACTUAL,'ElAdministrador.pid'))
if len(sys.argv) == 2:
if 'start' == sys.argv[1]:
service.start()
elif 'stop' == sys.argv[1]:
service.stop()
elif 'restart' == sys.argv[1]:
service.restart()
else:
print "Unknown command"
sys.exit(2)
sys.exit(0)
else:
print "usage: %s start|stop|restart" % sys.argv[0]
sys.exit(2)
However, the web.py software not load (ie: The service no listen)
If I call it directly (ie: No using the daemon code) work fine.
I finally find the problem.
Web.py accept from command-line the optional port number:
python code.py 80
And the script also take input from the command-line:
python WebServer start
then web.py try to use "start" as port number and fail. I don't see the error because was in the bacground.
I fix this with a mini-hack:
if __name__ == "__main__":
if DEBUG:
app.run()
else:
service = WebService(os.path.join(DIR_ACTUAL,'ElAdministrador.pid'))
if len(sys.argv) == 2:
if 'start' == sys.argv[1]:
sys.argv[1] = '8080'
service.start()
you can start the web.py by using this command
/usr/bin/python index.py > log.txt 2>&1 &
I don't think you're telling the daemon to run. You need to instantiate a MyDaemon object and call o.run(). It looks like WebService only starts and stops the service interface to your web app, not the actual web app itself.
Instead of overwrite the second argument like you wrote here:
if __name__ == "__main__":
if DEBUG:
app.run()
else:
service = WebService(os.path.join(DIR_ACTUAL,'ElAdministrador.pid'))
if len(sys.argv) == 2:
if 'start' == sys.argv[1]:
sys.argv[1] = '8080'
service.start()
you could just delete the second argument on 'start|restart', like this:
if __name__ == "__main__":
if DEBUG:
app.run()
else:
service = WebService(os.path.join(DIR_ACTUAL,'ElAdministrador.pid'))
if len(sys.argv) == 2:
if 'start' == sys.argv[1]:
delete del sys.argv[1:2]
service.start()
In this way, the webpy will receive all the arguments you passed from command line except the daemon controller. Then you can run simply:
python WebServer start 8080