def shutdown(self): """ Shutdown minestorm server """ logging.getLogger('minestorm').info('Shutting down minestorm...') minestorm.get('server.networking').stop( ) # Stop the networking and close the port minestorm.get('server.servers').stop_all() # Stop all servers logging.getLogger('minestorm').info('Waiting for threads shutdown...')
def boot_3_requests(self): """ Boot the requests parser """ # Setup the resource validator = lambda resource: resource.name != '__base__' minestorm.get('resources').add('server.request_processors', subclass_of = minestorm.server.requests.BaseProcessor, name_attribute = 'name', validator = validator ) # Create the sorter manager = minestorm.server.requests.RequestSorter() minestorm.bind('server.requests', manager) # Register differend requests manager.register( minestorm.server.requests.PingProcessor() ) manager.register( minestorm.server.requests.NewSessionProcessor() ) manager.register( minestorm.server.requests.RemoveSessionProcessor() ) manager.register( minestorm.server.requests.StartServerProcessor() ) manager.register( minestorm.server.requests.StopServerProcessor() ) manager.register( minestorm.server.requests.StartAllServersProcessor() ) manager.register( minestorm.server.requests.StopAllServersProcessor() ) manager.register( minestorm.server.requests.CommandProcessor() ) manager.register( minestorm.server.requests.StatusProcessor() ) manager.register( minestorm.server.requests.RetrieveLinesProcessor() ) # Listen for events listener = lambda event: manager.sort(event.data['request']) minestorm.get('events').listen('server.networking.request_received', listener, 100)
def retrieve_lines(self, start, stop): """ Retrieve a specified amount of lines """ # Raise an error if the server is not running if self.status not in ("STARTING", "STARTED", "STOPPING"): raise SyncError("Unable to retrieve lines when the server is not running!") # Retrieve lines response = minestorm.get("console.networking").request( { "status": "retrieve_lines", "start": start, "stop": stop, "server": self.name, "sid": minestorm.get("console.networking").sid, } ) # Raise an exception if lines are not provided if response["status"] != "retrieve_lines_response": raise SyncError("Unable to retrieve lines") # Iterate over lines to check them and add to the lines list for identifier, line in response["lines"].items(): identifier = int(identifier) # Convert the identifier to the correct type # First check if the line was already downloaded if identifier in self._lines: continue self._lines[identifier] = line # Update the last line identifier if it greater than the actual one if identifier > self._last_line_identifier: self._last_line_identifier = identifier
def execute(self, arguments): # Try to switch server if arguments[0] in minestorm.get('console.servers').all(): minestorm.get('console.ui').focus = arguments[0] return 'Server switched' else: return 'Unknow server'
def process(self, request): try: minestorm.get('server.servers').start_all() except RuntimeError: request.reply({ 'status': 'failed', 'reason': str(e) }) else: request.reply({'status': 'ok'})
def _load_configuration(self, file_name): """ Handler for the -c option """ # If the path exists load it if os.path.exists(file_name): minestorm.get('configuration').load(file_name) else: print('Error: configuration file not found: {}'.format(file_name)) minestorm.shutdown()
def process(self, request): try: minestorm.get('server.servers').get( request.data['server'] ).start() except NameError: request.reply({ 'status': 'failed', 'reason': 'Server {} does not exist'.format(request.data['server']) }) except RuntimeError as e: request.reply({ 'status': 'failed', 'reason': str(e) }) else: request.reply({'status':'ok'})
def process(self, request): # Get the stop message stop_message = request.data['message'] if 'message' in request.data else None try: minestorm.get('server.servers').stop_all(stop_message) except RuntimeError: request.reply({ 'status': 'failed', 'reason': str(e) }) else: request.reply({'status': 'ok'})
def boot_2_networking(self): """ Boot the networking """ # Create events minestorm.get('events').create('server.networking.request_received') # Boot the networking manager = minestorm.server.networking.Listener() minestorm.bind('server.networking', manager) # Bind the console port manager.bind( minestorm.get('configuration').get('networking.port') )
def run(self, args): try: console = minestorm.console.MinestormConsole() if args.server: if args.server in minestorm.get('console.servers').all(): minestorm.get('console.ui').focus = args.server console.start() finally: curses.endwin()
def process(self, request): # Get the stop message stop_message = request.data['message'] if 'message' in request.data else None try: minestorm.get('server.servers').get( request.data['server'] ).stop(stop_message) except NameError: request.reply({ 'status': 'failed', 'reason': 'Server {} does not exist'.format(request.data['server']) }) except RuntimeError as e: request.reply({ 'status': 'failed', 'reason': str(e) }) else: request.reply({'status':'ok'})
def process(self, request): start, stop = int(request.data['start']), int(request.data['stop']) # Check if the server exists if request.data['server'] in minestorm.get('server.servers').servers: # Get requested lines lines = minestorm.get('server.servers').get( request.data['server'] ).retrieve_lines( start, stop ) # Build response result = { 'status': 'retrieve_lines_response' } result['lines'] = lines request.reply(result) else: request.reply({ 'status': 'failed', 'reason': 'Invalid server' })
def _render_server(self, line, name): """ Render a single server """ server = minestorm.get('console.servers').get(name) # Get the bullet colour if server.status in ('STARTING', 'STARTED', 'STOPPING'): bullet_colour = self.colours['green'] | curses.A_BOLD else: bullet_colour = self.colours['red'] | curses.A_BOLD # Render! if minestorm.get('console.ui').focus == name: self.component.addstr(line, 1, '>', self.colours['blue'] | curses.A_BOLD) self.component.addstr(line, 3, name) self.component.addstr(line, self.width-2, '⚫', bullet_colour)
def _update_boxes(self): """ Update boxes """ boxes = [( "RAM", "-" ), ( "Uptime", "-" )] focus = minestorm.get("console.ui").focus if focus: server = minestorm.get("console.servers").get(focus) if server.status in ('STARTING', 'STARTED', 'STOPPING'): ram = str(round(server.ram_used, 2))+"%" uptime = minestorm.common.seconds_to_string(round(server.uptime)) boxes = [( "RAM", ram ), ( "Uptime", uptime )] line = 1 for key, value in boxes: self._render_box(line, key, value) line += 1
def process(self, request): # If the passed server exists pick it if 'server' in request.data and request.data['server'] in minestorm.get('server.servers').servers: server = minestorm.get('server.servers').get(request.data['server']) # Else return an error else: request.reply({ 'status': 'failed', 'reason': 'Please specify a valid server' }) return # Execute the command only if the server is running if server.status == server.STATUS_STARTED: server.command( request.data['command'] ) # Execute the command request.reply({ 'status': 'ok' }) else: request.reply({ 'status': 'failed', 'reason': 'Server {} is not running'.format(server.details['name']) })
def _process(self, request): """ Procesor entry point """ # Check if sid is required but not passed if self.require_sid and 'sid' not in request.data: request.reply({'status': 'failed', 'reason': 'SID not provided'}) # Check if the sid is required but invalid (badly formatted or expired or never created) elif self.require_sid and not minestorm.get('server.sessions').is_valid( request.data['sid'] ): request.reply({'status': 'failed', 'reason': 'Invalid SID'}) else: # If a SID was provided, touch the relative session # to avoid expiration of it if 'sid' in request.data and minestorm.get('server.sessions').is_valid( request.data['sid'] ): minestorm.get('server.sessions').get( request.data['sid'] ).touch() self.process(request)
def update(self): """ Update the screen """ focus = minestorm.get('console.ui').focus if focus: lines = list( minestorm.get('console.servers').get( focus ).all_lines().values() ) else: lines = [] self.position = len(lines) # temp fix max_lines = self.height # If the number of lines is lower than the max number of lines # display all of them # Else display only the desidered chunk if ( len(lines) < max_lines ) or self.position-max_lines < 0: chunk = lines else: chunk = lines[ (self.position-max_lines) : self.position ] # Split lines into small parts # Needed to avoid lines truncating new_chunk = [] for line in chunk: # If the length of the line is greater than the screen size # split the line if len(line) > (self.width - 1): position = 0 while 1: position_max = position + self.width - 1 # If the expected part of the line is lower than the # total line length add the remaining part and break # the loop if position_max > len(line): new_chunk.append( line[ position: ] ) break # Else add the expected part else: new_chunk.append( line[ position:position_max ] ) position += self.width - 1 # If the line length is lower than the stream width # simply add it else: new_chunk.append( line ) # Now get only the last part of the chunk chunk = new_chunk[ :max_lines ] # Display the chunk i = 0 for line in chunk: self.erase(i) self.component.addstr(i, 0, line, self.colours['white']) # Add the line i += 1 self.refresh() # Refresh the screen
def _init_commands(self): """ Initialize the console manager """ # Setup the resource validator = lambda resource: resource.name != "__base__" minestorm.get("resources").add( "console.commands", subclass_of=minestorm.console.commands.Command, name_attribute="name", validator=validator, ) # Setup the manager commands = minestorm.console.commands.CommandsManager() commands.register(minestorm.console.commands.SwitchCommand()) commands.register(minestorm.console.commands.StartCommand()) commands.register(minestorm.console.commands.StopCommand()) minestorm.bind("console.commands", commands)
def boot_4_servers(self): """ Boot the servers manager """ manager = minestorm.server.servers.ServersManager() minestorm.bind('server.servers', manager) # Register all servers for section in minestorm.get('configuration').get('available_servers'): manager.register( section ) # Register the server
def execute(self, arguments): # Try to start the server result = minestorm.get('console.networking').request({'status': 'start_server', 'server': arguments[0], 'sid': minestorm.get('console.networking').sid }) if result['status'] == 'ok': return 'Server started' elif result['status'] == 'failed': return result['reason'] else: return 'Unknow reply from minestorm'
def listen_keys(self, key): """ Listen for keys """ if key == curses.KEY_BACKSPACE: # Don't remove the last character if the content is empty if self.content != "": before_part = self.content[:(self.cursor_position-1)] after_part = self.content[self.cursor_position:] self.content = before_part + after_part self.cursor_position -= 1 self.erase(0) elif key == 10: # Enter key if self.content != "": # It the command starts with "!", it's a console command if self.content[0] == "!": # Execute the command result = minestorm.get("console.commands").route( self.content ) if result: # If the command returned a result, display it minestorm.get("console.ui").infobar.message(result) else: # Send command to the backend sid = minestorm.get("console.networking").sid try: response = minestorm.get("console.networking").request({ 'status': 'command', 'server': minestorm.get("console.ui").focus, 'command': self.content, 'sid': sid }) except Exception as e: # If an exception occured display it in the infobar minestorm.get("console.ui").infobar.message("Exception: {!s}".format(e)) # If the request failed and a reason is returned display it if response['status'] == 'failed' and 'reason' in response: minestorm.get("console.ui").infobar.message(response['reason']) self.content = "" self.cursor_position = 0 self.erase(0) # Move the cursor back elif key == curses.KEY_LEFT: if self.cursor_position > 0: self.cursor_position -= 1 # Move the cursor forward elif key == curses.KEY_RIGHT: if self.cursor_position < len(self.content): self.cursor_position += 1 elif key <= 255: before_part = self.content[:self.cursor_position] after_part = self.content[self.cursor_position:] self.content = before_part+chr(key)+after_part self.cursor_position += 1 # Move the cursor self.screen.screen.addstr(self.start_y, 0, self.content, self.colours['white']) self.screen.screen.move(self.start_y, self.cursor_position) self.refresh()
def boot_1_cli(self): """ Boot the cli """ # Setup the resource validator = lambda resource: resource.name != '__base__' minestorm.get('resources').add('cli.commands', subclass_of = minestorm.cli.Command, name_attribute = 'name', validator = validator ) # Setup the manager manager = minestorm.cli.CommandsManager() minestorm.bind("cli", manager) # Register commands manager.register( minestorm.cli.ExecuteCommand() ) manager.register( minestorm.cli.StartCommand() ) manager.register( minestorm.cli.StopCommand() ) manager.register( minestorm.cli.StartAllCommand() ) manager.register( minestorm.cli.StopAllCommand() ) manager.register( minestorm.cli.CommandCommand() ) manager.register( minestorm.cli.ConsoleCommand() ) manager.register( minestorm.cli.StatusCommand() ) manager.register( minestorm.cli.TestCommand() ) manager.register( minestorm.cli.ConfigureCommand() )
def is_valid(self, sid): """ Check if a SID is valid """ # Get the expiration time expire_at = time.time() - minestorm.get('configuration').get('sessions.expiration.time') # First check if the SID exists if sid in self.sessions: # Now check if it isn't expired if self.sessions[sid].last_packet > expire_at: return True else: return False else: return False
def sync(self): """ Sync local copies of the servers objects with the backend """ new_servers = set() # Set of new servers response = minestorm.get("console.networking").request( {"status": "status", "sid": minestorm.get("console.networking").sid} ) # Raise an exception if the servers list was not provided if response["status"] != "status_response": raise SyncError("Unable to get servers list") # Iterate over received servers to for name, informations in response["servers"].items(): new_servers.add(name) # Append the server name to the synched servers list # If the server wasn't loaded create it if name not in self._servers: self._servers[name] = Server(name) # Update server informations self._servers[name].update(informations) # If there are servers in the servers list not present in the status # remove them diff = set(self._servers.keys()) - new_servers if len(diff) > 0: for server in diff: del self._servers[name]
def boot_1_logging(self): """ Boot the logging system """ logger = logging.getLogger('minestorm') # Get the logging level level = getattr( logging, str(minestorm.get('configuration').get('logging.level')).upper() ) logger.setLevel( level ) # Prepare the formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # Set the stream handler stream = logging.StreamHandler() stream.setLevel( level ) stream.setFormatter( formatter ) # Register all logger.addHandler( stream )
def run(self): while not self.stop: minestorm.get("console.servers").sync() # Sync the local cache # Retrieve last lines for server in minestorm.get("console.servers").all().values(): try: server.retrieve_last_lines() except SyncError: pass # Update the UI minestorm.get("console.ui").sidebar.update() # Update the sidebar minestorm.get("console.ui").stream.update() # Update the stream time.sleep(0.5)
def run(self, args): # Get the content of the default file bundled with setuptools content = pkg_resources.resource_string('minestorm', '_samples/configuration.json').decode('utf-8') # Get the destination directory destination = minestorm.get('configuration').default_file_path() # Copy it to its destination if not os.path.exists(destination): # Create base directories if needed os.makedirs( os.path.dirname(destination), exist_ok=True) # Save the configuration file with open(destination, 'w') as f: f.write(content) print('The minestorm configuration file is now at {}'.format(destination)) else: print('Error: minestorm: a file already exists at {}'.format(destination))
def _update_server_list(self): """ Update the server list """ servers_list = minestorm.get('console.servers').all() if len(servers_list) > 0: top = self.height-len(servers_list)-2 # Calculate the top line else: top = self.height-3 self.component.addstr(top, 3, 'Available servers:', curses.A_BOLD) # Get servers list servers = list(servers_list.keys()) servers.sort() servers.sort(key=lambda s: servers_list[s].status in ('STARTING', 'STARTED', 'STOPPING'), reverse=True) # Render each server if len(servers_list) > 0: i = 1 for server in servers: self._render_server(top+i, server ) i += 1 else: self.component.addstr(top+1, 3, "No server available")
def request(self, data): """ Make a request to the server """ try: # Create a new socket s = socket.socket() s.connect(( socket.gethostname(), minestorm.get('configuration').get('networking.port') )) # Prepare data to_send = json.dumps(data).encode('utf-8') length = struct.pack('I', len(to_send)) # Send the request minestorm.common.send_packet(s, length) minestorm.common.send_packet(s, to_send) # Receive response length = struct.unpack('I', minestorm.common.receive_packet( s, 4 ))[0] raw = minestorm.common.receive_packet( s, length ) # Load the response response = json.loads(raw.decode('utf-8')) # Shutdown the socket s.shutdown(socket.SHUT_RD) s.close() return response except socket.error: return
def start(self): """ Start minestorm server """ minestorm.get('server.networking').listen()