def __init__(self, config): if sys.platform.startswith("win32"): self._python_runner = config['python_interpreter_windows'] self.command_paths = [ os.path.join(os.path.dirname(__file__), '../../..')] # Built-in commands (on root dir) tools_path = config.get('tools_path_windows') else: self._python_runner = config['python_interpreter_linux'] self.command_paths = [ os.path.join(os.path.dirname(__file__), '..', 'plugins')] # Built-in commands (absolute path) tools_path = config.get('tools_path_linux') self.timeout = int(config['timeout']) self.timeout_dc = None self.env = os.environ self.env['DEBIAN_FRONTEND'] = 'noninteractive' self.env['PYTHONPATH'] = os.path.dirname(__file__) self.env['LANG'] = 'en_US.utf8' self.env['PWD'] = '/root/' if tools_path: self.env["PATH"] += os.pathsep + tools_path log.debug("ENV: %s" % self.env) #reactor.callLater(0, self._loadCommands) self._commands = {} reactor.callWhenRunning(self._load_commands)
def _processCommand(self, message): log.debug('Process Command') if self.public_key: if not self._verify_message(message): log.critical('[RSA CHECK: Failed] Command from %s has bad signature (Ignored)' % message.from_) result = (_E_UNVERIFIED_COMMAND, '', 'Bad signature', 0) self._onCallFinished(result, message) return else: # No public key, RSA functions are not available on this system log.warn('[RSA CHECK: No available] WARNING: Running unverified Command from %s' % message.from_) flush_callback = self._Flush message.command_replaced = message.command.replace('.', '_') d = self.command_runner.run_command(message.command_replaced, message.command_args, flush_callback, message) if d: d.addCallbacks(self._onCallFinished, self._onCallFailed, callbackKeywords={'message': message}, errbackKeywords={'message': message}, ) return d else: log.info("Command Ignored: Unknown command: %s" % message.command) result = (_E_RUNNING_COMMAND, '', "Unknown command: %s" % message.command, 0) self._onCallFinished(result, message) return
def processEnded(self, status): log.debug("Process ended") self.flush_callback = None # Cancel flush callbacks self.flush_callback = None self._cancel_flush(self.flush_later) self._cancel_flush(self.flush_later_forced) # Get command retval t = type(status.value) if t is ProcessDone: exit_code = 0 elif t is ProcessTerminated: exit_code = status.value.exitCode else: raise status if not self.timeout_dc.called: self.timeout_dc.cancel() for d in self.deferreds: d.callback((exit_code, self.stdout, self.stderr, self.timeout_dc.called))
def _send(self, result, message): log.debug('Send Response') message.toResult(*result) del result self.send(message.toEtree())
def __init__(self, config): if sys.platform.startswith("win32"): self._python_runner = config["python_interpreter_windows"] self.command_paths = [ os.path.join(os.path.dirname(__file__), "../../..") ] # Built-in commands (on root dir) tools_path = config.get("tools_path_windows") else: self._python_runner = config["python_interpreter_linux"] self.command_paths = [ os.path.join(os.path.dirname(__file__), "..", "plugins") ] # Built-in commands (absolute path) tools_path = config.get("tools_path_linux") self.timeout = int(config["timeout"]) self.timeout_dc = None self.env = os.environ self.env["DEBIAN_FRONTEND"] = "noninteractive" self.env["LANG"] = "en_US.utf8" self.env["PWD"] = "/root/" if tools_path: self.env["PATH"] += os.pathsep + tools_path log.debug("ENV: %s" % self.env) self._commands = {} reactor.callWhenRunning(self._load_commands)
def __init__(self, config): """ XMPP agent class. """ log.info("Starting Agent...") log.info("Loading Commands...") self.command_runner = CommandRunner(config['Plugins']) log.info("Setting up Certificate") self.verify = ECVerify() log.info("Setting up Memory checker") self.running_commands = {} self.num_running_commands = 0 self.timestamp = 0 self.memory_checker = LoopingCall(self._check_memory, self.running_commands) self.memory_checker.start(_CHECK_RAM_INTERVAL) self.keepalive = LoopingCall(self._reconnect) log.debug("Loading XMPP...") Client.__init__( self, config['XMPP'], [("/iq[@type='set']", self.__onIq), ], resource='ecm_agent-%d' % AGENT_VERSION_PROTOCOL)
def _onCallFailed(self, failure, *argv, **kwargs): log.error("onCallFailed") log.debug(failure) if 'message' in kwargs: message = kwargs['message'] result = (2, '', failure, 0) self._onCallFinished(result, message)
def _get_config2(self, unique_id): hostname = self._get_hostname() address = self._get_ip() uuid = self._get_stored_uuid() account = self.get_stored_account() agent_groups = self.get_stored_agent_groups params = "?uuid=%s&ipaddress=%s&hostname=%s&unique_id=%s&account=%s" \ % (uuid, address, hostname, unique_id, account) auth_url = ECMANAGED_AUTH_URL + '/' + params auth_content = None try: auth_content = yield getPage(auth_url) except: log.debug("getPage failed") if not auth_content: try: auth_content = urllib2.urlopen(auth_url).read() except: pass if auth_content: for line in auth_content.splitlines(): if line: returnValue(line) returnValue('')
def __init__(self, config): """ XMPP agent class. """ log.info("Starting agent...") self.config = config log.info("Setting up certificate") self.public_key = None try: if Crypto.version_info[:2] >= (2, 2): _public_key = self._read_pub_key() if _public_key: key = PublicKey.RSA.importKey(_public_key) self.public_key = key.publickey() except: pass if not self.public_key: log.warn('PyCrypto not available or version is < 2.2: Please upgrade: http://www.pycrypto.org/') log.info("Loading commands...") self.command_runner = CommandRunner(config['Plugins']) log.debug("Loading XMPP...") observers = [ ('/iq', self.__onIq), ] Client.__init__(self, self.config['XMPP'], observers, resource='ecm_agent-%d' % AGENT_VERSION_PROTOCOL )
def _flush(self): if not self.flush_callback: return total_out = len(self.stdout) + len(self.stderr) if total_out - self.last_send_data_size > FLUSH_MIN_LENGTH: curr_time = time() if self.last_send_data_time + FLUSH_TIME < curr_time: self.last_send_data_size = total_out self.last_send_data_time = curr_time log.debug("Scheduling a flush response: %s" % self.stdout) self._cancel_flush(self.flush_later_forced) self.flush_later = reactor.callLater( 1, self.flush_callback, (None, self.stdout, self.stderr, 0, total_out), self.message ) if not self.flush_later: self._cancel_flush(self.flush_later_forced) self.flush_later_forced = reactor.callLater( FLUSH_TIME, self.flush_callback, (None, self.stdout, self.stderr, 0, total_out), self.message ) del total_out
def _send(self, result, message): log.debug('Send Response') mem_clean('agent._send') message.toResult(*result) del result mem_clean('agent._send') self.send(message.toEtree())
def connectionMade(self): log.debug("Process started.") self.timeout_dc = reactor.callLater(self.timeout, self.transport.signalProcess, 'KILL') # Pass the call arguments via stdin in json format self.transport.write(base64.b64encode(json.dumps(self.command_args))) # And close stdin to signal we are done writing args. self.transport.closeStdin()
def _onPossibleErrorIq(self, elem): sender = elem['from'] for el in elem.elements(): if el.name == 'error' and el['code'] == '404': log.warn('Received a 404 code from the server, setting the target user as offline') if sender in self._online_contacts: self._online_contacts.remove(sender) else: log.debug('Received a 404 from %s which not (anymore?) in the online contacts list.')
def mem_clean(where=''): _collect = collect() rss, vms = mem_usage() string = "_mem_clean: %s collected %d objects. (current mem: rss=%sMB | vms=%sMB)" % (where, _collect, rss, vms) log.debug(string) del _collect, where, vms, string return rss
def _add_command(self, data, **kwargs): (exit_code, stdout, stderr, timeout_called) = data if exit_code == 0: for line in stdout.splitlines(): self._commands[line.split()[0]] = kwargs['filename'] log.debug("Command %s added" % line.split()[0]) else: log.error('Error adding commands from %s: %s' % (kwargs['filename'], data))
def mem_clean(where="", dolog=False): _collect = collect() rss, vms = mem_usage() string = "_mem_clean: %s collected %d objects. (current mem: rss=%sMB | vms=%sMB)" % (where, _collect, rss, vms) if dolog: log.info(string) else: log.debug(string) del _collect, where, dolog, rss, vms, string
def run_command(self, message, flush_callback=None): if self._commands.get(message.command_name): log.debug("executing %s with args: %s" % (message.command_name, message.command_args)) return self._run_process( self._commands[message.command_name], message.command_name, message.command_args, flush_callback, message, ) return
def _read_pub_key(self): log.debug('Reading public certificate') public_key = None try: cert_file = os.path.join(os.path.dirname(__file__), _CERTIFICATE_FILE) if os.path.isfile(cert_file): f = open(cert_file, 'r') public_key = f.read() f.close() except: log.critical("Unable to read certificate file") return public_key
def check_config(self): uuid = self._get_stored_uuid() account_id = self.get_stored_account() if not uuid and not account_id: # Is not an update and no account is set log.error('Please configure agent with ./configure --account=XXXXX') raise Exception('Please configure agent with ./configure --account=XXXXX') unique_id = self._get_unique_id() if not unique_id: log.error('Could not obtain UNIQUE_ID. Please set up XMPP manually') raise Exception('Could not obtain UNIQUE_ID. Please set up XMPP manually') # Check all data valid for v3 if uuid and not '@' in uuid and account_id and self.is_unique_id_same(unique_id): log.debug('UNIQUE ID has not changed. Skip UUID check') else: # Try to get uuid (one hour and a half loop: 360x15) json_data = None for i in range(360): log.info("Trying to get UUID via URL (meta-data v2)") json_data = yield self._get_config(unique_id) if json_data: break sleep(15) # Decode metadata meta_data = self.parse_meta_data(json_data) if not meta_data: log.error('Could not obtain UUID. Please set up XMPP manually') raise Exception('Could not obtain UUID. Please set up XMPP manually') if not self['XMPP'].get('password'): self['XMPP']['password'] = hex(random.getrandbits(256))[2:-1] # Updates from v2 to v3 write account info if not account_id and meta_data.get('account'): self['XMPP']['account'] = meta_data.get('account') self['XMPP']['user'] = meta_data['uuid'] self['XMPP']['unique_id'] = unique_id self.write() returnValue(True)
def __onIq(self, msg): """ A new IQ message has been received and we should process it. """ log.debug('__onIq') message_type = msg['type'] log.debug("q Message received: \n%s" % msg.toXml()) log.debug("Message type: %s" % message_type) if message_type == 'set': #Parse and check message format message = IqMessage(msg) if hasattr(message, 'command') and hasattr(message, 'from_'): log.debug('online contacts: %s' % self._online_contacts) if message.from_ not in self._online_contacts: log.warn('IQ sender not in roster (%s), dropping message' % message.from_) else: self._processCommand(message) else: log.warn('Unknown ecm_message received: "%s" Full XML:\n%s' % (message_type, msg.toXml())) else: log.warn('Unknown IQ type received: "%s" Full XML:\n%s' % (message_type, msg.toXml()))
def __onIq(self, msg): """ A new IQ message has been received and we should process it. """ log.debug('__onIq') mem_clean('__onIq [start]') self.running_commands += 1 log.debug("q Message received: \n%s" % msg.toXml()) log.debug("Message type: %s" % msg['type']) if msg['type'] == 'set': message = IqMessage(msg) if hasattr(message, 'command') and hasattr(message, 'from_'): log.debug('online contacts: %s' % self._online_contacts) if message.from_ not in self._online_contacts: log.warn('IQ sender not in roster (%s), dropping message' % message.from_) else: self._processCommand(message) else: log.warn('Unknown ecm_message received: Full XML:\n%s' % (msg.toXml())) del message else: log.warn('Unknown IQ type received: Full XML:\n%s' % (msg.toXml())) del msg mem_clean('__onIq [end]')
def processEnded(self, status): log.debug("process ended") t = type(status.value) if t is ProcessDone: exit_code = 0 elif t is ProcessTerminated: exit_code = status.value.exitCode else: raise status for d in self.deferreds: d.callback((exit_code, self.stdout, self.stderr))
def _load_commands(self): for path in self.command_paths: log.debug("Processing dir: %s" % path) try: if os.path.isdir(path): for filename in os.listdir(path): if not filename.startswith("plugin_"): continue log.debug(" Queuing plugin %s for process." % filename) full_filename = os.path.join(path, filename) d = self._run_process(full_filename, "", {}) d.addCallback(self._add_command, filename=full_filename) except: print sys.exc_info()
def outReceived(self, data): log.debug("Out made: %s" % data) if _FINAL_OUTPUT_STRING in data: for line in data.split("\n"): if _FINAL_OUTPUT_STRING in line: # Skip this line and stop flush callback self.stdout = self.stderr = '' self.flush_callback = None else: self.stdout += line else: self.stdout += data self._flush()
def check_uuid(self): mac = self._get_mac() # Always generate a new password if not is set if not self['XMPP']['password']: self['XMPP']['password'] = hex(random.getrandbits(128))[2:-1] if mac: if str(mac) == str(self._getStoredMAC()): log.debug("MAC has not changed. Skip UUID check") else: # Try to get uuid uuid = None for i in range(30): try: uuid = yield self._getUUID() if uuid: break except: pass sleep(20) if not uuid: log.error("ERROR: Could not obtain UUID. please set up XMPP manually in %s" % self.filename) returnValue(False) if str(uuid) == str(self._getStoredUUID()): log.debug("UUID has not changed.") # Update mac self['XMPP']['mac'] = mac self.write() else: log.info("UUID has changed, reconfiguring XMPP user/pass") self['XMPP']['user'] = '******'.join((uuid, self['XMPP']['host'])) self['XMPP']['mac'] = mac self.write() returnValue(True) else: log.error("ERROR: Could not obtain MAC. please set up XMPP manually in %s" % self.filename) returnValue(False)
def check_uuid(self): unique_id = self._get_unique_id() if unique_id: if str(unique_id) == str(self._get_stored_unique_id()): log.debug("UNIQUE ID has not changed. Skip UUID check") else: # Try to get uuid (one hour and a half loop: 360x15) uuid = None for i in range(360): try: uuid = yield self._get_uuid() if uuid: break except Exception: pass sleep(15) if not uuid: log.error("ERROR: Could not obtain UUID. please set up XMPP manually in %s" % self.filename) raise Exception("Could not obtain UUID") if str(uuid) == str(self._get_stored_uuid()): log.debug("UUID has not changed.") self["XMPP"]["unique_id"] = unique_id self.write() else: log.info("UUID has changed, reconfiguring XMPP user/pass") self["XMPP"]["user"] = "******".join((uuid, self["XMPP"]["host"])) self["XMPP"]["unique_id"] = unique_id self.write() returnValue(True) else: log.error("ERROR: Could not obtain UNIQUE_ID. please set up XMPP manually in %s" % self.filename) raise Exception("Could not obtain UUID")
def _onPresence(self, elem): """ A new presence message has been received, let's see who has (dis)connected. """ log.debug('_onPresence') presence = XMPPPresence(elem) if presence.available: log.debug("%s is now available" % presence.sender) #Store full jid. self._online_contacts.add(presence.sender) else: log.debug("%s is not available anymore" % presence.sender) if presence.jid in self._online_contacts: self._online_contacts.remove(presence.sender)
def outReceived(self, data): log.debug("Out made") self.stdout += data
def errReceived(self, data): log.debug("Err made: %s" % data) self.stderr += data
def connectionMade(self): log.debug("Process started.")