def remote_listing_cb(parent_path, listing): for (file_path, entry) in listing: entity = (stringify(entry.name), entry.modified_time, entry.size, entry.is_symlink) remote_entities.add(entity) remote_files.add(stringify(entry.name)) flags = (entry.is_regular, entry.is_symlink, entry.is_special) remote_attributes[stringify(entry.name)] = \ (entry.modified_time_dt, flags)
def shell(self, ready_cb, cols=80, rows=24): self.__log.debug("Starting RSP shell.") with SshChannel(self.__ssh_session) as sc: sc.open_session() sc.request_env('aa', 'bb') # sc.request_env('LANG', 'en_US.UTF-8') sc.request_pty() sc.change_pty_size(cols, rows) sc.request_shell() self.__log.debug("Waiting for shell welcome message.") welcome = bytearray() def welcome_received_cb(data): welcome.extend(bytify(data)) self.__sc = sc self.__wait_on_output_all(welcome_received_cb) self.__log.debug("RSP shell is ready.") ready_cb(sc, stringify(welcome)) self.__sc = None
def recurse(self, root_path, dir_cb, listing_cb, max_listing_size=0, max_depth=MAX_REMOTE_RECURSION_DEPTH): """Recursively iterate a directory. Invoke callbacks for directories and entries (both are optional, but it doesn't make sense unless one is provided). "max_listing_size" will allow for the file-listing to be chunked into manageable pieces. "max_depth" limited how deep recursion goes. This can be used to make it easy to simply read a single directory in chunks. """ q = deque([(root_path, 0)]) collected = [] def push_file(path, file_path, entry): collected.append((file_path, entry)) if max_listing_size > 0 and \ len(collected) >= max_listing_size: listing_cb(path, collected) # Clear contents on the list. We delete it this way so that # we're only -modifying- the list rather than replacing it (a # requirement of a closure). del collected[:] while q: (path, current_depth) = q.popleft() entries = self.listdir(path) for entry in entries: filename = stringify(entry.name) file_path = ('%s/%s' % (path, filename)) if entry.is_symlink: push_file(path, file_path, entry) elif entry.is_directory: if filename == '.' or filename == '..': continue if dir_cb is not None: dir_cb(path, file_path, entry) new_depth = current_depth + 1 if max_depth is None or new_depth <= max_depth: q.append((file_path, new_depth)) elif entry.is_regular: if listing_cb is not None: push_file(path, file_path, entry) if listing_cb is not None and (max_listing_size == 0 or len(collected) > 0): listing_cb(path, collected)
def _ssh_get_issue_banner(ssh_session): """Get the "issue banner" for the server. Note that this function may/will fail if the server isn't configured for such a message (like some/all Ubuntu installs). In the event of failure, we'll just return an empty string. """ message = c_ssh_get_issue_banner(c_void_p(ssh_session)) # TODO: Does "newly allocated" string have to be freed? We might have to reallocate it as a Python string. if message is None: return '' return stringify(message)
def remote_dir_cb(parent_path, full_path, entry): remote_dirs.add(stringify(entry.name))
def read_until_nl(self, read_cb): captured = ByteStream() i = 0 found = False nl = None done = False while found is False and done is False: position = self.__stream.tell() couplet = self.__stream.read(2) if len(couplet) < 2: logging.debug("Couplet is a dwarf of (%d) bytes." % (len(couplet))) more_data = read_cb() assert issubclass(more_data.__class__, bytes) logging.debug("Retrieved (%d) more bytes." % (len(more_data))) if more_data != b'': self.__stream.write(more_data) self.__stream.seek(position) # Re-read. couplet = self.__stream.read(2) logging.debug("Couplet is now (%d) bytes." % (len(couplet))) elif couplet == b'': done = True continue if len(couplet) == 2: # We represent a \r\n newline. if couplet == b'\r\n': nl = couplet found = True captured.write(couplet) # We represent a one-byte newline that's in the first position. elif couplet[0:1] == b'\r' or couplet[0:1] == b'\n': nl = couplet[0] found = True captured.write(couplet[0:1]) self.__stream.seek(-1, SEEK_CUR) # The first position is an ordinary character. If there's a # newline in the second position, we'll pick it up on the next # round. else: captured.write(couplet[0:1]) self.__stream.seek(-1, SEEK_CUR) elif len(couplet) == 1: # This is the last [odd] byte of the file. if couplet[0:1] == b'\r' or couplet[0:1] == b'\n': nl = couplet[0] found = True captured.write(couplet[0:1]) done = True i += 1 return (stringify(captured.get_bytes()), nl)