- Windows Service written in python, not detecting shutdown events and stopping gracefully
- from os.path import splitext, abspath
- from sys import modules
- import win32serviceutil
- import win32service
- import win32event
- import win32api
- import win32security
- import win32ts
- class Service(win32serviceutil.ServiceFramework):
- _svc_name_ = '_unNamed'
- _svc_display_name_ = '_Service Template'
- def __init__(self, *args):
- win32serviceutil.ServiceFramework.__init__(self, *args)
- self.log('Initializing Service')
- self.stop_event = win32event.CreateEvent(None, 0, 0, None)
- self.server = None
- def log(self, msg):
- import servicemanager
- servicemanager.LogInfoMsg(str(msg))
- def logErr(self, msg):
- import servicemanager
- servicemanager.LogErrorMsg(str(msg))
- def logWarn(self, msg):
- import servicemanager
- servicemanager.LogWarningMsg(str(msg))
- def sleep(self, sec):
- win32api.Sleep(sec*1000, True)
- def GetAcceptedControls(self):
- # Accept SESSION_CHANGE control
- rc = win32serviceutil.ServiceFramework.GetAcceptedControls(self)
- rc |= win32service.SERVICE_ACCEPT_SESSIONCHANGE
- rc |= win32service.SERVICE_ACCEPT_SHUTDOWN
- return rc
- def GetUserInfo(self, sess_id):
- sessions = win32security.LsaEnumerateLogonSessions()[:-5]
- for sn in sessions:
- sn_info = win32security.LsaGetLogonSessionData(sn)
- if sn_info['Session'] == sess_id:
- return sn_info
- def getUserSessionInfo(self, sess_id):
- msg = ""
- try:
- for key, val in self.GetUserInfo(sess_id).items():
- msg += '%s : %sn'%(key, val)
- if key == "UserName":
- self.server.username = val
- except Exception, e:
- msg += '%s'%e
- return msg
- # All extra events are sent via SvcOtherEx (SvcOther remains as a
- # function taking only the first args for backwards compatability)
- def SvcOtherEx(self, control, event_type, data):
- # This is only showing a few of the extra events - see the MSDN
- # docs for "HandlerEx callback" for more info.
- if control == win32service.SERVICE_CONTROL_SESSIONCHANGE:
- sess_id = data[0]
- msg = ""
- if event_type == 5: # logon
- msg = "Logon event: type=%s, sessionid=%sn" % (event_type, sess_id)
- # user_token = win32ts.WTSQueryUserToken(int(sess_id))
- self.server.status = 1 #logon event
- self.getUserSessionInfo(sess_id)
- self.sendHeartbeat()
- self.server.status = 2 #active user
- elif event_type == 6: # logoff
- msg = "Logoff event: type=%s, sessionid=%sn" % (event_type, sess_id)
- self.server.status = 3 #logoff event
- self.sendHeartbeat()
- self.server.username = ""
- self.server.status = 0 #no user
- elif event_type == 7: # lock
- msg = "Lock event: type=%s, sessionid=%sn" % (event_type, sess_id)
- self.server.status = 1 #logon event
- self.getUserSessionInfo(sess_id)
- self.sendHeartbeat()
- self.server.status = 2 #active user
- elif event_type == 8: # unlock
- self.server.status = 3 #logoff event
- self.server.username = ""
- self.sendHeartbeat()
- self.server.status = 0 #no user
- else:
- msg = "Other session event: type=%s, sessionid=%sn" % (event_type, sess_id)
- # msg += self.getUserSessionInfo(sess_id)
- self.log(msg)
- # elif control == win32service.SERVICE_CONTROL_SHUTDOWN:
- # msg = "Server being shutdown..."
- # self.log(msg)
- def SvcDoRun(self):
- self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
- try:
- self.ReportServiceStatus(win32service.SERVICE_RUNNING)
- self.log('Starting Service')
- self.start()
- self.log('Waiting')
- win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)
- self.log('Done')
- except Exception, x:
- self.logErr('Exception : %s' % x)
- self.SvcStop()
- def SvcStop(self):
- self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
- self.log('Stopping Service')
- self.stop()
- self.log('Stopped')
- win32event.SetEvent(self.stop_event)
- self.ReportServiceStatus(win32service.SERVICE_STOPPED)
- def sendHeartbeat(self):
- # 0 = no user, standard beat (format: 0||hostname)
- # 1 = login event (format: 1|username|hostname)
- # 2 = active user session
- # 3 = logout event (format: 2|username|hostname)
- # 4 = Service exception (format 3||hostname)
- return self.server.sendHeartbeat(category=self.server.status, username=self.server.username)
- def start(self): pass
- # to be overridden
- def stop(self): pass
- # to be overridden
- #reboot/halt makes a different call than 'net stop mytestservice'
- def SvcShutdown(self):
- msg = "Server being shutdown..."
- self.log(msg)
- self.SvcStop()
- def instart(cls, name, display_name=None, stay_alive=True, exe_name="caedmSAM.exe"):
- '''
- 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
- '''
- cls._svc_name_ = name
- cls._svc_display_name_ = display_name or name
- cls._exe_name_ = exe_name
- cls._svc_description_ = "CAEDM SAM Server registration and montioring service"
- try:
- module_path=modules[cls.__module__].__file__
- except AttributeError:
- # maybe py2exe went by
- from sys import executable
- module_path=executable
- module_file=splitext(abspath(module_path))[0]
- cls._svc_reg_class_ = '%s.%s' % (module_file, cls.__name__)
- if stay_alive: win32api.SetConsoleCtrlHandler(lambda x: True, True)
- try:
- win32serviceutil.InstallService(
- cls._svc_reg_class_,
- cls._svc_name_,
- cls._svc_display_name_,
- startType=win32service.SERVICE_AUTO_START
- )
- # print 'Install: OK'
- win32serviceutil.StartService(
- cls._svc_name_
- )
- # print 'Start: OK'
- except Exception, x:
- print str(x)