Skip to content

Instantly share code, notes, and snippets.

@fundor333
Created September 3, 2016 06:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fundor333/aaa0b1b46d1e961d6df4828eb82ec3f6 to your computer and use it in GitHub Desktop.
Save fundor333/aaa0b1b46d1e961d6df4828eb82ec3f6 to your computer and use it in GitHub Desktop.
Python Deamon + Service classes
import atexit
import os
import signal
import sys
import time
__author__ = 'fundor333'
class Daemon:
"""A generic daemon class.
Usage: subclass the daemon class and override the run() method."""
def __init__(self, pidfile):
self.pidfile = pidfile
def daemonize(self):
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(os.devnull, 'a+')
se = open(os.devnull, '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')
def delpid(self):
os.remove(self.pidfile)
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):
raise NotImplemented
from os.path import splitext, abspath
from sys import modules
import win32api
import win32event
import win32service
import win32serviceutil
class Service(win32serviceutil.ServiceFramework):
_svc_name_ = '_unNamed'
_svc_display_name_ = '_Service Template'
def __init__(self, *args):
win32serviceutil.ServiceFramework.__init__(self, *args)
self.log('init')
self.stop_event = win32event.CreateEvent(None, 0, 0, None)
def log(self, msg):
import servicemanager
servicemanager.LogInfoMsg(str(msg))
def sleep(self, sec):
win32api.Sleep(sec * 1000, True)
def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
try:
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
self.log('start')
self.start()
self.log('wait')
win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)
self.log('done')
except Exception as x:
self.log('Exception : %s' % x)
self.SvcStop()
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.log('stopping')
self.stop()
self.log('stopped')
win32event.SetEvent(self.stop_event)
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
# to be overridden
def start(self):
pass
# to be overridden
def stop(self):
pass
def run(self, name, display_name=None, stay_alive=True):
""" Install and Start (auto) a Service
cls : the class (derived from Service) that implement the Service
name : Service name
display_name : the name displayed in the service manager
stay_alive : Service will stop on logout if False
"""
self._svc_name_ = name
self._svc_display_name_ = display_name or name
try:
module_path = modules[self.__module__].__file__
except AttributeError:
# maybe py2exe went by
from sys import executable
module_path = executable
module_file = splitext(abspath(module_path))[0]
self._svc_reg_class_ = '%s.%s' % (module_file, self.__name__)
if stay_alive: win32api.SetConsoleCtrlHandler(lambda x: True, True)
try:
win32serviceutil.InstallService(
self._svc_reg_class_,
self._svc_name_,
self._svc_display_name_,
startType=win32service.SERVICE_AUTO_START
)
print('Install ok')
win32serviceutil.StartService(
self._svc_name_
)
print('Start ok')
except Exception as x:
print(str(x))
from winservice import Service
class Test(Service):
def start(self):
self.runflag = True
while self.runflag:
self.sleep(10)
self.log("I'm alive ...")
def stop(self):
self.runflag = False
self.log("I'm done")
startservice(Test, 'aTest', 'Python Service Test')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment