def stop(self): """ Stop the daemon """ # Get the pid from the pidfile try: pf = open(self.pidfile, 'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if pid is None: message = "pidfile {} does not exist. Daemon not running?\n".format(self.pidfile) logger.info(message) # sys.stderr.write(message) return # not an error in a restart # Try killing the daemon process try: while True: os.kill(pid, SIGTERM) time.sleep(1) except OSError as err: if err.errno == 3: # No such process if os.path.exists(self.pidfile): os.remove(self.pidfile) else: sys.stderr.write(err) sys.exit(1)
def clientMessageProcessor(self, msg, data): logger.debug('Got message {}'.format(msg)) if self.api is None: logger.info('Rest api not ready') return if msg == ipc.REQ_LOGIN: res = self.api.login(data).split('\t') # third parameter, if exists, sets maxSession duration to this. # First & second parameters are ip & hostname of connection source if len(res) >= 3: self.api.maxSession = int(res[2]) # Third parameter is max session duration msg = ipc.REQ_INFORMATION # Senf information, requested or not, to client on login notification elif msg == ipc.REQ_LOGOUT: self.api.logout(data) self.onLogout(data) elif msg == ipc.REQ_INFORMATION: info = {} if self.api.idle is not None: info['idle'] = self.api.idle if self.api.maxSession is not None: info['maxSession'] = self.api.maxSession self.ipc.sendInformationMessage(info) elif msg == ipc.REQ_TICKET: d = json.loads('data') try: result = self.api.getTicket(d['ticketId'], d['secure']) self.ipc.sendTicketMessage(result) except Exception: logger.exception('Getting ticket') self.ipc.sendTicketMessage({'error': 'invalid ticket'})
def rename(newName): distribution = platform.linux_distribution()[0].lower().strip() if distribution in renamers: return renamers[distribution](newName) # Try Debian renamer, simplest one logger.info('Renamer for platform "{0}" not found, tryin debian renamer'.format(distribution)) return renamers['debian'](newName)
def joinDomain(self, name, domain, ou, account, password): ver = operations.getWindowsVersion() ver = ver[0] * 10 + ver[1] logger.info('Starting joining domain {} with name {} (detected operating version: {})'.format( domain, name, ver)) # Accepts one step joinDomain, also remember XP is no more supported by # microsoft, but this also must works with it because will do a "multi # step" join if ver >= 60: self.oneStepJoin(name, domain, ou, account, password) else: self.multiStepJoin(name, domain, ou, account, password)
def testParameters(self): logger.debug('Testing connection') try: cfg = self._getCfg() api = REST.Api( cfg['host'], cfg['masterKey'], cfg['ssl']) api.test() QtGui.QMessageBox.information( self, 'Test Passed', 'The test was executed successfully', QtGui.QMessageBox.Ok) logger.info('Test was passed successfully') except Exception as e: logger.info('Test error: {}'.format(utils.exceptionToMessage(e))) QtGui.QMessageBox.critical(self, 'Test Error', utils.exceptionToMessage(e), QtGui.QMessageBox.Ok)
def initIPC(self): # ****************************************** # * Initialize listener IPC & REST threads * # ****************************************** logger.debug('Starting IPC listener at {}'.format(IPC_PORT)) self.ipc = ipc.ServerIPC(IPC_PORT, clientMessageProcessor=self.clientMessageProcessor) self.ipc.start() if self.api.mac in self.knownIps: address = (self.knownIps[self.api.mac], random.randrange(40000, 44000)) logger.info('Starting REST listener at {}'.format(address)) self.httpServer = httpserver.HTTPServerThread(address, self) self.httpServer.start() # And notify it to broker self.api.notifyComm(self.httpServer.getServerUrl())
def checkIpsChanged(self): if self.api.uuid is None: return # Not connected netInfo = tuple(operations.getNetworkInfo()) for i in netInfo: # If at least one ip has changed if i.mac in self.knownIps and self.knownIps[i.mac] != i.ip: logger.info('Notifying ip change to broker (mac {}, from {} to {})'.format(i.mac, self.knownIps[i.mac], i.ip)) try: # Notifies all interfaces IPs self.api.notifyIpChanges(((v.mac, v.ip) for v in netInfo)) # Regenerates Known ips self.knownIps = dict(((i.mac, i.ip) for i in netInfo)) except Exception as e: logger.warn('Got an error notifiying IPs to broker: {} (will retry in a bit)'.format(e.message.decode('windows-1250', 'ignore')))
def joinDomain(self, name, domain, ou, account, password): ver = operations.getWindowsVersion() ver = ver[0] * 10 + ver[1] logger.debug('Starting joining domain {} with name {} (detected operating version: {})'.format( domain, name, ver)) # If file c:\compat.bin exists, joind domain in two steps instead one # Accepts one step joinDomain, also remember XP is no more supported by # microsoft, but this also must works with it because will do a "multi # step" join if ver >= 60 and store.useOldJoinSystem() is False: self.oneStepJoin(name, domain, ou, account, password) else: logger.info('Using multiple step join because configuration requests to do so') self.multiStepJoin(name, domain, ou, account, password)
def execute(self, cmd, section): import os import subprocess import stat if os.path.isfile(cmd): if (os.stat(cmd).st_mode & stat.S_IXUSR) != 0: subprocess.call([cmd, ]) return True else: logger.info('{} file exists but it it is not executable (needs execution permission by admin/root)'.format(section)) else: logger.info('{} file not found & not executed'.format(section)) return False
def clientMessageProcessor(self, msg, data): logger.debug('Got message {}'.format(msg)) if self.api is None: logger.info('Rest api not ready') return if msg == ipc.REQ_LOGIN: self.api.login(data) elif msg == ipc.REQ_LOGOUT: self.api.logout(data) self.onLogout(data) elif msg == ipc.REQ_INFORMATION: info = {} if self.api.idle is not None: info['idle'] = self.api.idle self.ipc.sendInformationMessage(info)
def preConnect(self, user, protocol): logger.debug('Pre connect invoked') if protocol != 'rdp': # If connection is not using rdp, skip adding user return 'ok' # Well known SSID for Remote Desktop Users REMOTE_USERS_SID = 'S-1-5-32-555' p = win32security.GetBinarySid(REMOTE_USERS_SID) groupName = win32security.LookupAccountSid(None, p)[0] useraAlreadyInGroup = False resumeHandle = 0 while True: users, _, resumeHandle = win32net.NetLocalGroupGetMembers(None, groupName, 1, resumeHandle, 32768) if user.lower() in [u['name'].lower() for u in users]: useraAlreadyInGroup = True break if resumeHandle == 0: break if useraAlreadyInGroup is False: logger.debug('User not in group, adding it') self._user = user try: userSSID = win32security.LookupAccountName(None, user)[0] win32net.NetLocalGroupAddMembers(None, groupName, 0, [{'sid': userSSID}]) except Exception as e: logger.error('Exception adding user to Remote Desktop Users: {}'.format(e)) else: self._user = None logger.debug('User {} already in group'.format(user)) # Now try to run pre connect command try: pre_cmd = store.preApplication() if os.path.isfile(pre_cmd): if (os.stat(pre_cmd).st_mode & stat.S_IXUSR) != 0: subprocess.call([pre_cmd, user, protocol]) else: logger.info('PRECONNECT file exists but it it is not executable (needs execution permission by root)') else: logger.info('PRECONNECT file not found & not executed') except Exception as e: # Ignore output of execution command logger.error('Executing preconnect command give') return 'ok'
def multiStepJoin(self, name, domain, ou, account, password): currName = operations.getComputerName() if currName.lower() == name.lower(): currDomain = operations.getDomainName() logger.debug('Name: "{}" vs "{}", Domain: "{}" vs "{}"'.format(currName.lower(), name.lower(), currDomain.lower(), domain.lower())) if currDomain is not None: logger.info( 'Machine {} is part of domain {}'.format(name, domain)) self.setReady() else: operations.joinDomain( domain, ou, account, password, executeInOneStep=False) else: operations.renameComputer(name) logger.info( 'Rebooting computer got activate new name {}'.format(name)) self.reboot()
def preConnect(self, user, protocol): ''' Invoked when received a PRE Connection request via REST ''' # Execute script in /etc/udsactor/post after interacting with broker, if no reboot is requested ofc # This will be executed only when machine gets "ready" try: if os.path.isfile(PRECONNECT_CMD): if (os.stat(PRECONNECT_CMD).st_mode & stat.S_IXUSR) != 0: subprocess.call([PRECONNECT_CMD, user, protocol]) else: logger.info('PRECONNECT file exists but it it is not executable (needs execution permission by root)') else: logger.info('PRECONNECT file not found & not executed') except Exception as e: # Ignore output of execution command logger.error('Executing preconnect command give')
def information(self, info): ''' Invoked when received information from service ''' logger.info('Got information message: {}'.format(info)) if 'idle' in info: idle = int(info['idle']) operations.initIdleDuration(idle) self.maxIdleTime = idle logger.debug('Set screensaver launching to {}'.format(idle)) else: self.maxIdleTime = None if 'maxSession' in info: maxSession = int(info['maxSession']) # operations.initMaxSession(maxSession) self.maxSessionTime = maxSession logger.debug('Set maxsession to {}'.format(maxSession))
def execute(self, cmdLine, section): cmd = shlex.split(cmdLine, posix=False) if os.path.isfile(cmd[0]): if (os.stat(cmd[0]).st_mode & stat.S_IXUSR) != 0: try: res = subprocess.check_call(cmd) except Exception as e: logger.error('Got exception executing: {} - {}'.format(cmdLine, e)) return False logger.info('Result of executing cmd was {}'.format(res)) return True else: logger.error('{} file exists but it it is not executable (needs execution permission by admin/root)'.format(section)) else: logger.error('{} file not found & not executed'.format(section)) return False
def rename(self, name, user=None, oldPassword=None, newPassword=None): ''' Renames the computer, and optionally sets a password for an user before this ''' # Check for password change request for an user if user is not None: logger.info('Setting password for user {}'.format(user)) try: operations.changeUserPassword(user, oldPassword, newPassword) except Exception as e: # We stop here without even renaming computer, because the # process has failed raise Exception( 'Could not change password for user {} (maybe invalid current password is configured at broker): {} '.format(user, unicode(e))) renamer.rename(name) self.setReady()
def multiStepJoin(self, name, domain, ou, account, password): currName = operations.getComputerName() if currName.lower() == name.lower(): currDomain = operations.getDomainName() if currDomain is not None: # logger.debug('Name: "{}" vs "{}", Domain: "{}" vs "{}"'.format(currName.lower(), name.lower(), currDomain.lower(), domain.lower())) logger.info('Machine {} is part of domain {}'.format( name, domain)) self.setReady() else: operations.joinDomain(domain, ou, account, password, executeInOneStep=False) self.reboot() else: operations.renameComputer(name) logger.info( 'Rebooting computer got activate new name {}'.format(name)) self.reboot()
def execute(self, cmdLine, section): cmd = shlex.split(cmdLine, posix=False) if os.path.isfile(cmd[0]): if (os.stat(cmd[0]).st_mode & stat.S_IXUSR) != 0: try: res = subprocess.check_call(cmd) except Exception as e: logger.error('Got exception executing: {} - {}'.format( cmdLine, e)) return False logger.info('Result of executing cmd was {}'.format(res)) return True else: logger.error( '{} file exists but it it is not executable (needs execution permission by admin/root)' .format(section)) else: logger.error('{} file not found & not executed'.format(section)) return False
def rename(self, name, user=None, oldPassword=None, newPassword=None): ''' Renames the computer, and optionally sets a password for an user before this ''' hostName = operations.getComputerName() if hostName.lower() == name.lower(): logger.info('Computer name is now {}'.format(hostName)) self.setReady() return # Check for password change request for an user if user is not None: logger.info('Setting password for user {}'.format(user)) try: operations.changeUserPassword(user, oldPassword, newPassword) except Exception as e: # We stop here without even renaming computer, because the # process has failed raise Exception( 'Could not change password for user {} (maybe invalid current password is configured at broker): {} ' .format(user, unicode(e))) operations.renameComputer(name) # Reboot just after renaming logger.info('Rebooting computer to activate new name {}'.format(name)) self.reboot()
def rename(self, name, user=None, oldPassword=None, newPassword=None): ''' Renames the computer, and optionally sets a password for an user before this ''' hostName = operations.getComputerName() if hostName.lower() == name.lower(): logger.info('Computer name is already {}'.format(hostName)) self.setReady() return # Check for password change request for an user if user is not None: logger.info('Setting password for user {}'.format(user)) try: operations.changeUserPassword(user, oldPassword, newPassword) except Exception as e: # We stop here without even renaming computer, because the # process has failed raise Exception( 'Could not change password for user {} (maybe invalid current password is configured at broker): {} '.format(user, unicode(e))) renamer.rename(name) if self.rebootMachineAfterOp is False: self.setReady() else: logger.info('Rebooting computer to activate new name {}'.format(name)) self.reboot()
def preConnect(self, user, protocol): ''' Invoked when received a PRE Connection request via REST ''' # Execute script in /etc/udsactor/post after interacting with broker, if no reboot is requested ofc # This will be executed only when machine gets "ready" try: pre_cmd = store.preApplication() if os.path.isfile(pre_cmd): if (os.stat(pre_cmd).st_mode & stat.S_IXUSR) != 0: subprocess.call([pre_cmd, user, protocol]) else: logger.info( 'PRECONNECT file exists but it it is not executable (needs execution permission by root)' ) else: logger.info('PRECONNECT file not found & not executed') except Exception: # Ignore output of execution command logger.error('Executing preconnect command give') return 'ok'
def checkIdle(self): if self.maxIdleTime is None: # No idle check return if self.graceTimerShots > 0: self.graceTimerShots -= 1 return idleTime = operations.getIdleDuration() remainingTime = self.maxIdleTime - idleTime if remainingTime > 300: # Reset show Warning dialog if we have more than 5 minutes left self.showIdleWarn = True logger.debug('User has been idle for: {}'.format(idleTime)) if remainingTime <= 0: logger.info('User has been idle for too long, notifying Broker that service can be reclaimed') self.quit() if self.showIdleWarn is True and remainingTime < 120: # With two minutes, show a warning message self.showIdleWarn = False self.msgDlg.displayMessage("You have been idle for too long. The session will end if you don't resume operations")
def clientMessageProcessor(self, msg, data): logger.debug('Got message {}'.format(msg)) if self.api is None: logger.info('Rest api not ready') return if msg == ipc.REQ_LOGIN: res = self.api.login(data).split('\t') # third parameter, if exists, sets maxSession duration to this. # First & second parameters are ip & hostname of connection source if len(res) >= 3: self.api.maxSession = int( res[2]) # Third parameter is max session duration msg = ipc.REQ_INFORMATION # Senf information, requested or not, to client on login notification if msg == ipc.REQ_LOGOUT: self.api.logout(data) self.onLogout(data) if msg == ipc.REQ_INFORMATION: info = {} if self.api.idle is not None: info['idle'] = self.api.idle if self.api.maxSession is not None: info['maxSession'] = self.api.maxSession self.ipc.sendInformationMessage(info)
def checkIpsChanged(self): if self.api is None or self.api.uuid is None: return # Not connected netInfo = tuple(operations.getNetworkInfo()) for i in netInfo: # If at least one ip has changed if i.mac in self.knownIps and self.knownIps[i.mac] != i.ip: logger.info('Notifying ip change to broker (mac {}, from {} to {})'.format(i.mac, self.knownIps[i.mac], i.ip)) try: # Notifies all interfaces IPs self.api.notifyIpChanges(((v.mac, v.ip) for v in netInfo)) # Regenerates Known ips self.knownIps = dict(((v.mac, v.ip) for v in netInfo)) # And notify new listening address to broker address = (self.knownIps[self.api.mac], self.httpServer.getPort()) # And new listening address self.httpServer.restart(address) # sends notification self.api.notifyComm(self.httpServer.getServerUrl()) except Exception as e: logger.warn('Got an error notifiying IPs to broker: {} (will retry in a bit)'.format(e.message.decode('windows-1250', 'ignore')))
def interactWithBroker(self): ''' Returns True to continue to main loop, false to stop & exit service ''' # If no configuration is found, stop service if cfg is None: logger.fatal('No configuration found, stopping service') return False self.api = REST.Api(cfg['host'], cfg['masterKey'], cfg['ssl']) # Wait for Broker to be ready counter = 0 while self.isAlive: try: # getNetworkInfo is a generator function netInfo = tuple(operations.getNetworkInfo()) self.knownIps = dict(((i.mac, i.ip) for i in netInfo)) ids = ','.join([i.mac for i in netInfo]) if ids == '': # Wait for any network interface to be ready logger.debug('No valid network interfaces found, retrying in a while...') raise Exception() logger.debug('Ids: {}'.format(ids)) self.api.init(ids) # Set remote logger to notify log info to broker logger.setRemoteLogger(self.api) break except REST.InvalidKeyError: logger.fatal('Can\'t sync with broker: Invalid broker Master Key') return False except REST.UnmanagedHostError: # Maybe interface that is registered with broker is not enabled already? # Right now, we thing that the interface connected to broker is # the interface that broker will know, let's see how this works logger.fatal('This host is not managed by UDS Broker (ids: {})'.format(ids)) return False # On unmanaged hosts, there is no reason right now to continue running except Exception as e: logger.debug('Exception on network info: retrying') # Any other error is expectable and recoverable, so let's wait a bit and retry again # but, if too many errors, will log it (one every minute, for # example) counter += 1 if counter % 60 == 0: # Every 5 minutes, raise a log logger.info('Trying to inititialize connection with broker (last error: {})'.format(exceptionToMessage(e))) # Wait a bit before next check self.doWait(5000) # Now try to run the "runonce" element runOnce = store.runApplication() if runOnce is not None: if self.execute(runOnce, 'RunOnce') is True: # operations.reboot() return False # Broker connection is initialized, now get information about what to # do counter = 0 while self.isAlive: try: logger.debug('Requesting information of what to do now') info = self.api.information() data = info.split('\r') if len(data) != 2: logger.error('The format of the information message is not correct (got {})'.format(info)) raise Exception params = data[1].split('\t') if data[0] == 'rename': try: if len(params) == 1: # Simple rename logger.debug('Renaming computer to {}'.format(params[0])) self.rename(params[0]) # Rename with change password for an user elif len(params) == 4: logger.debug('Renaming computer to {}'.format(params)) self.rename(params[0], params[1], params[2], params[3]) else: logger.error('Got invalid parameter for rename operation: {}'.format(params)) return False break except Exception as e: logger.error('Error at computer renaming stage: {}'.format(e.message)) return None # Will retry complete broker connection if this point is reached elif data[0] == 'domain': if len(params) != 5: logger.error('Got invalid parameters for domain message: {}'.format(params)) return False # Stop running service self.joinDomain(params[0], params[1], params[2], params[3], params[4]) break else: logger.error('Unrecognized action sent from broker: {}'.format(data[0])) return False # Stop running service except REST.UserServiceNotFoundError: logger.error('The host has lost the sync state with broker! (host uuid changed?)') return False except Exception as err: if counter % 60 == 0: logger.warn('Too many retries in progress, though still trying (last error: {})'.format(exceptionToMessage(err))) counter += 1 # Any other error is expectable and recoverable, so let's wait # a bit and retry again # Wait a bit before next check self.doWait(5000) if self.rebootRequested: try: operations.reboot() except Exception as e: logger.error('Exception on reboot: {}'.format(e.message)) return False # Stops service return True
def SvcDoRun(self): ''' Main service loop ''' try: initCfg() logger.debug('running SvcDoRun') servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '')) # call the CoInitialize to allow the registration to run in an other # thread logger.debug('Initializing com...') pythoncom.CoInitialize() # ******************************************************** # * Ask brokers what to do before proceding to main loop * # ******************************************************** while True: brokerConnected = self.interactWithBroker() if brokerConnected is False: logger.debug( 'Interact with broker returned false, stopping service after a while' ) self.notifyStop() win32event.WaitForSingleObject(self.hWaitStop, 5000) return elif brokerConnected is True: break # If brokerConnected returns None, repeat the cycle self.doWait(16000) # Wait for a looong while if self.interactWithBroker() is False: logger.debug( 'Interact with broker returned false, stopping service after a while' ) self.notifyStop() win32event.WaitForSingleObject(self.hWaitStop, 5000) return if self.isAlive is False: logger.debug( 'The service is not alive after broker interaction, stopping it' ) self.notifyStop() return if self.rebootRequested is True: logger.debug('Reboot has been requested, stopping service') self.notifyStop() return self.initIPC() except Exception: # Any init exception wil be caught, service must be then restarted logger.exception() logger.debug('Exiting service with failure status') os._exit(-1) # pylint: disable=protected-access # ******************************** # * Registers SENS subscriptions * # ******************************** logevent('Registering ISensLogon') subscription_guid = '{41099152-498E-11E4-8FD3-10FEED05884B}' sl = SensLogon(self) subscription_interface = pythoncom.WrapObject(sl) event_system = win32com.client.Dispatch(PROGID_EventSystem) event_subscription = win32com.client.Dispatch(PROGID_EventSubscription) event_subscription.EventClassID = SENSGUID_EVENTCLASS_LOGON event_subscription.PublisherID = SENSGUID_PUBLISHER event_subscription.SubscriptionName = 'UDS Actor subscription' event_subscription.SubscriptionID = subscription_guid event_subscription.SubscriberInterface = subscription_interface event_system.Store(PROGID_EventSubscription, event_subscription) logger.debug('Registered SENS, running main loop') # Execute script in c:\\windows\\post-uds.bat after interacting with broker, if no reboot is requested ofc # This will be executed only when machine gets "ready" try: if os.path.isfile(POST_CMD): subprocess.call([ POST_CMD, ]) else: logger.info('POST file not found & not executed') except Exception as e: # Ignore output of execution command logger.error('Executing post command give') # ********************* # * Main Service loop * # ********************* # Counter used to check ip changes only once every 10 seconds, for # example counter = 0 while self.isAlive: counter += 1 # Process SENS messages, This will be a bit asyncronous (1 second # delay) pythoncom.PumpWaitingMessages() if counter >= 15: # Once every 15 seconds counter = 0 try: self.checkIpsChanged() except Exception as e: logger.error('Error checking ip change: {}'.format(e)) # In milliseconds, will break win32event.WaitForSingleObject(self.hWaitStop, 1000) logger.debug('Exited main loop, deregistering SENS') # ******************************************* # * Remove SENS subscription before exiting * # ******************************************* event_system.Remove(PROGID_EventSubscription, "SubscriptionID == " + subscription_guid) self.endIPC() # Ends IPC servers self.endAPI() # And deinitializes REST api if needed self.notifyStop()
def log_message(self, fmt, *args): logger.info('HTTP ' + fmt % args)
def SvcDoRun(self): # pylint: disable=too-many-statements, too-many-branches ''' Main service loop ''' try: initCfg() logger.debug('running SvcDoRun') servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '')) # call the CoInitialize to allow the registration to run in an other # thread logger.debug('Initializing com...') pythoncom.CoInitialize() # ******************************************************** # * Ask brokers what to do before proceding to main loop * # ******************************************************** while True: brokerConnected = self.interactWithBroker() if brokerConnected is False: logger.debug('Interact with broker returned false, stopping service after a while') self.notifyStop() win32event.WaitForSingleObject(self.hWaitStop, 5000) return elif brokerConnected is True: break # If brokerConnected returns None, repeat the cycle self.doWait(16000) # Wait for a looong while if self.interactWithBroker() is False: logger.debug('Interact with broker returned false, stopping service after a while') self.notifyStop() win32event.WaitForSingleObject(self.hWaitStop, 5000) return if self.isAlive is False: logger.debug('The service is not alive after broker interaction, stopping it') self.notifyStop() return if self.rebootRequested is True: logger.debug('Reboot has been requested, stopping service') self.notifyStop() return self.initIPC() except Exception: # Any init exception wil be caught, service must be then restarted logger.exception() logger.debug('Exiting service with failure status') os._exit(-1) # pylint: disable=protected-access # ******************************** # * Registers SENS subscriptions * # ******************************** logevent('Registering ISensLogon') subscription_guid = '{41099152-498E-11E4-8FD3-10FEED05884B}' sl = SensLogon(self) subscription_interface = pythoncom.WrapObject(sl) event_system = win32com.client.Dispatch(PROGID_EventSystem) event_subscription = win32com.client.Dispatch(PROGID_EventSubscription) event_subscription.EventClassID = SENSGUID_EVENTCLASS_LOGON event_subscription.PublisherID = SENSGUID_PUBLISHER event_subscription.SubscriptionName = 'UDS Actor subscription' event_subscription.SubscriptionID = subscription_guid event_subscription.SubscriberInterface = subscription_interface event_system.Store(PROGID_EventSubscription, event_subscription) logger.debug('Registered SENS, running main loop') # Execute script in c:\\windows\\post-uds.bat after interacting with broker, if no reboot is requested ofc # This will be executed only when machine gets "ready" try: if os.path.isfile(POST_CMD): subprocess.call([POST_CMD, ]) else: logger.info('POST file not found & not executed') except Exception as e: # Ignore output of execution command logger.error('Executing post command give') # ********************* # * Main Service loop * # ********************* # Counter used to check ip changes only once every 10 seconds, for # example counter = 0 while self.isAlive: counter += 1 # Process SENS messages, This will be a bit asyncronous (1 second # delay) pythoncom.PumpWaitingMessages() if counter >= 15: # Once every 15 seconds counter = 0 try: self.checkIpsChanged() except Exception as e: logger.error('Error checking ip change: {}'.format(e)) # In milliseconds, will break win32event.WaitForSingleObject(self.hWaitStop, 1000) logger.debug('Exited main loop, deregistering SENS') # ******************************************* # * Remove SENS subscription before exiting * # ******************************************* event_system.Remove( PROGID_EventSubscription, "SubscriptionID == " + subscription_guid) self.endIPC() # Ends IPC servers self.endAPI() # And deinitializes REST api if needed self.notifyStop()
def run(self): cfg = initCfg() # Gets a local copy of config to get "reboot" logger.debug('CFG: {}'.format(cfg)) if cfg is not None: self.rebootMachineAfterOp = cfg.get('reboot', True) else: self.rebootMachineAfterOp = False logger.info('Reboot after is {}'.format(self.rebootMachineAfterOp)) logger.debug('Running Daemon') set_proctitle('UDSActorDaemon') # Linux daemon will continue running unless something is requested to while True: brokerConnected = self.interactWithBroker() if brokerConnected is False: logger.debug('Interact with broker returned false, stopping service after a while') return elif brokerConnected is True: break # If brokerConnected returns None, repeat the cycle self.doWait(16000) # Wait for a looong while if self.isAlive is False: logger.debug('The service is not alive after broker interaction, stopping it') return if self.rebootRequested is True: logger.debug('Reboot has been requested, stopping service') return # Execute script in /etc/udsactor/post after interacting with broker, if no reboot is requested ofc # This will be executed only when machine gets "ready" try: if os.path.isfile(POST_CMD): if (os.stat(POST_CMD).st_mode & stat.S_IXUSR) != 0: subprocess.call([POST_CMD, ]) else: logger.info('POST file exists but it it is not executable (needs execution permission by root)') else: logger.info('POST file not found & not executed') except Exception as e: # Ignore output of execution command logger.error('Executing post command give') self.initIPC() # ********************* # * Main Service loop * # ********************* # Counter used to check ip changes only once every 10 seconds, for # example counter = 0 while self.isAlive: counter += 1 if counter % 10 == 0: self.checkIpsChanged() # In milliseconds, will break self.doWait(1000) self.endIPC() self.endAPI() self.notifyStop()
def interactWithBroker(self): ''' Returns True to continue to main loop, false to stop & exit service ''' # If no configuration is found, stop service if cfg is None: logger.fatal('No configuration found, stopping service') return False self.api = REST.Api(cfg['host'], cfg['masterKey'], cfg['ssl']) # Wait for Broker to be ready counter = 0 while self.isAlive: try: # getNetworkInfo is a generator function netInfo = tuple(operations.getNetworkInfo()) self.knownIps = dict(((i.mac, i.ip) for i in netInfo)) ids = ','.join([i.mac for i in netInfo]) if ids == '': # Wait for any network interface to be ready logger.debug('No valid network interfaces found, retrying in a while...') raise Exception() logger.debug('Ids: {}'.format(ids)) self.api.init(ids) # Set remote logger to notify log info to broker logger.setRemoteLogger(self.api) break except REST.InvalidKeyError: logger.fatal('Can\'t sync with broker: Invalid broker Master Key') return False except REST.UnmanagedHostError: # Maybe interface that is registered with broker is not enabled already? # Right now, we thing that the interface connected to broker is # the interface that broker will know, let's see how this works logger.fatal('This host is not managed by UDS Broker (ids: {})'.format(ids)) return False # On unmanaged hosts, there is no reason right now to continue running except Exception as e: logger.debug('Exception on network info: retrying') logger.exception() # Any other error is expectable and recoverable, so let's wait a bit and retry again # but, if too many errors, will log it (one every minute, for # example) counter += 1 if counter % 60 == 0: # Every 5 minutes, raise a log logger.info('Trying to inititialize connection with broker (last error: {})'.format(exceptionToMessage(e))) # Wait a bit before next check self.doWait(5000) # Broker connection is initialized, now get information about what to # do counter = 0 while self.isAlive: try: logger.debug('Requesting information of what to do now') info = self.api.information() data = info.split('\r') if len(data) != 2: logger.error('The format of the information message is not correct (got {})'.format(info)) raise Exception params = data[1].split('\t') if data[0] == 'rename': try: if len(params) == 1: # Simple rename logger.debug('Renaming computer to {}'.format(params[0])) self.rename(params[0]) # Rename with change password for an user elif len(params) == 4: logger.debug('Renaming computer to {}'.format(params)) self.rename(params[0], params[1], params[2], params[3]) else: logger.error('Got invalid parameter for rename operation: {}'.format(params)) return False break except Exception as e: logger.error('Error at computer renaming stage: {}'.format(e.message)) return False elif data[0] == 'domain': if len(params) != 5: logger.error('Got invalid parameters for domain message: {}'.format(params)) return False self.joinDomain(params[0], params[1], params[2], params[3], params[4]) break else: logger.error('Unrecognized action sent from broker: {}'.format(data[0])) return False # Stop running service except REST.UserServiceNotFoundError: logger.error('The host has lost the sync state with broker! (host uuid changed?)') return False except Exception: if counter % 60 == 0: logger.warn('Too many retries in progress, though still trying (last error: {})'.format(exceptionToMessage(e))) counter += 1 # Any other error is expectable and recoverable, so let's wait # a bit and retry again # Wait a bit before next check self.doWait(5000) if self.rebootRequested: try: operations.reboot() except Exception as e: logger.error('Exception on reboot: {}'.format(e.message)) return False # Stops service return True
def notifyStop(self): ''' Overriden to log stop ''' logger.info('Service is being stopped')
from PyQt5.QtCore import QTimer from udsactor.log import logger, DEBUG from udsactor.client import UDSClientQApp from udsactor.platform import operations if __name__ == "__main__": logger.setLevel(DEBUG) # Ensure idle operations is initialized on start operations.initIdleDuration(0) if 'linux' in sys.platform: os.environ['QT_X11_NO_MITSHM'] = '1' logger.info('Started UDS Client Actor') UDSClientQApp.setQuitOnLastWindowClosed(False) qApp = UDSClientQApp(sys.argv) qApp.init() # Crate a timer, so we can check signals from time to time by executing python interpreter # Note: Signals are only checked on python code execution, so we create a timer = QTimer(qApp) timer.start(1000) timer.timeout.connect(lambda *a: None) qApp.exec_()
def logevent(msg): logger.info(msg)