From 349db46da0b1d24572709be78c67050ba85c0232 Mon Sep 17 00:00:00 2001 From: vishnuocv Date: Mon, 30 Jan 2017 10:53:52 +0900 Subject: [PATCH 1/4] SCP kind of Behavioral addition Recursive copy, Wild card copy etc has been added Retain the permissions of the destination file as it is having in source. If source is a file and destination is a folder, a file with the name of source will be created in destination with the same name. The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. Signed-off-by: Vishnu Sankar vishnuocv@gmail.com --- serio | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 12 deletions(-) diff --git a/serio b/serio index 84d80c2..bd2eebe 100755 --- a/serio +++ b/serio @@ -31,6 +31,7 @@ import hashlib import StringIO import uu from getopt import GetoptError, getopt as GetOpt +import glob, stat # verbose # 1 bytes uploaded/downloaded @@ -246,6 +247,7 @@ class RemoteExecute: j = 0 data_size = len(data) + # try to ensure at shell prompt # also, required before calls to execute() self.prime_before_first_execute() @@ -332,6 +334,47 @@ class RemoteExecute: result = result.replace("\r\n", "\n") return remote_status, result + def recursive_copy(self, src_dir, dst_dir): + if src_dir.endswith('/'): + if verbose > 0: + print src_dir + raise Exception("omitting directory: " + src_dir) + else: + head, tail = os.path.split(src_dir) + destin = os.path.join(dst_dir, tail) + if verbose > 0: + print destin + self.execute("mkdir -p '%s' 2> /dev/null" % destin) + self.execute("[[ -d '%s' ]] ; echo $? " % destin) + data = self.fp.readline() + if (int(data) == 0): + dst_dir = destin + else: + raise Exception("unable to create remote directory: " + data) + + for root, dirs, files in os.walk(src_dir): + for item in dirs: + src_path = os.path.join(root, item) + rel_path = os.path.relpath(root, src_dir) + if rel_path is not '.': + dst_path = os.path.join(dst_dir, os.path.join(rel_path, item)) + else: + dst_path = os.path.join(dst_dir, item) + self.cmd('mkdir -p %s' % dst_path) + + for item in files: + relative = os.path.relpath(root, src_dir) + if relative is not '.': + dst_path = os.path.join(dst_dir, os.path.join(relative, item)) + else: + dst_path = os.path.join(dst_dir, item) + src_path = os.path.join(root, item) + size = self.putfile(open(src_path, "rb").read(), dst_path) + mode = oct(stat.S_IMODE(os.lstat(src_path).st_mode)) + self.execute("chmod '%s' '%s' " % (mode, dst_path)) + if verbose > 0: + print 'Uploaded %d bytes from %s to %s' % (size, src_dir, dst_path) + class SerialTerminal(RemoteExecute): @@ -370,6 +413,9 @@ Side-effects: - if exception occurs, shell echo may be disabled on remote system (remote system can be fixed with command "stty echo") + - put path in double quotes (especially for wild cards) + --get + - put path in double quotes (especially for wild cards) Remote System Dependencies: - May hang if the shell prompt is zero bytes long @@ -385,6 +431,8 @@ Known Problems: - will hang for command "bash" - will hang for command "exit" (exiting bash) - can not be used to login to a shell + - Put with wildcard support + - wildcard put dont supports recursive copy -Python 2.7 glob.glob limitation Return: If --cmd and --remote-shell then the exit status of the remote shell. @@ -503,25 +551,88 @@ def main(): if action == 'put': try: - host_file = open(source, "rb") - size = sterm.putfile(host_file.read(), destination) - - if verbose > 0: - print 'Uploaded %d bytes from %s to %s' % (size, source, destination) + if "*" in source: + print "wildcard" + for filename in glob.glob(source): + if verbose > 1: + print os.path.basename(filename) + dest = os.path.join(destination, os.path.basename(filename)) + if os.path.isdir(filename): + if verbose > 1: + print "directory for * is ", os.path.basename(filename) + dst_path = os.path.join(destination, os.path.basename(filename)) + sterm.recursive_copy(os.path.basename(filename), destination) + elif os.path.isfile(filename): + if verbose > 1: + print "filename for * is ", os.path.basename(filename) + dest = os.path.join(destination, os.path.basename(filename)) + size = sterm.putfile(open(filename, "rb").read(), dest) + mode = oct(stat.S_IMODE(os.lstat(filename).st_mode)) + sterm.execute("chmod '%s' '%s'" % (mode, dest)) + if verbose > 0: + print 'Uploaded %d bytes from %s to %s' % (size, filename, dest) + elif os.path.isfile(source): + if verbose > 1: + print "source file is ", source + file_name = os.path.basename(source) + remote_status, data = sterm.cmd('[[ -d "%s" ]] ; echo $? ' % destination) + if (int(data) == 0): + destination = os.path.join(destination, file_name) + size = sterm.putfile(open(source, "rb").read(), destination) + if verbose > 0: + print 'Uploaded %d bytes from %s to %s' % (size, source, destination) + mode = oct(stat.S_IMODE(os.lstat(source).st_mode)) + sterm.execute("chmod '%s' '%s' " % (mode, destination)) + + elif os.path.isdir(source): + if verbose > 1: + print "source directory is ", source + sterm.recursive_copy(source, destination) + except Exception, e: print "ERROR:", e sys.exit(2) elif action == 'get': try: - mode, data = sterm.getfile(source) - if len(data): - open(destination, "wb").write(data) - if mode != -1: - os.chmod(destination, mode) - + if "*" in source: + if verbose > 1: + print "Wildcard entry" + data = sterm.cmd("ls -1 %s " % source) + if verbose > 1: + print data + for line in data.splitlines(): + if verbose > 1: + print line + head, tail = os.path.split(line) + des = os.path.abspath(destination) + des = os.path.join(des, tail) + if verbose > 1: + print des + mode, contents = sterm.getfile(str(line)) + if len(contents): + open(des, "wb").write(contents) + if mode != -1: + os.chmod(des_file, mode) + else: + mode, data = sterm.getfile(source) + if len(data): + des = os.path.abspath(destination) + if verbose > 1: + print des + if os.path.isdir(des): + head, tail = os.path.split(source) + des_file = os.path.join(des, tail) + open(des_file, "wb").write(data) + if mode != -1: + os.chmod(des_file, mode) + + else: + open(des, "wb").write(data) + if mode != -1: + os.chmod(des, mode) if verbose > 0: - print 'Downloaded %d bytes from %s to %s' % (len(data), source, destination) + print 'Downloaded %d bytes from %s to %s' % (len(data), source, des) except Exception, e: print "\nERROR:", e sys.exit(3) From c11f846ef2d2ec97489825ad505b01c68a82b3f8 Mon Sep 17 00:00:00 2001 From: lineo Date: Fri, 3 Feb 2017 16:51:05 +0900 Subject: [PATCH 2/4] Added multiple file input --- serio | 85 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/serio b/serio index bd2eebe..d281b35 100755 --- a/serio +++ b/serio @@ -344,9 +344,8 @@ class RemoteExecute: destin = os.path.join(dst_dir, tail) if verbose > 0: print destin - self.execute("mkdir -p '%s' 2> /dev/null" % destin) - self.execute("[[ -d '%s' ]] ; echo $? " % destin) - data = self.fp.readline() + self.fp.write("mkdir -p '%s' 2> /dev/null \n" % destin) + remote_status, data = self.cmd("[[ -d '%s' ]] ; echo $? " % destin) if (int(data) == 0): dst_dir = destin else: @@ -413,7 +412,7 @@ Side-effects: - if exception occurs, shell echo may be disabled on remote system (remote system can be fixed with command "stty echo") - - put path in double quotes (especially for wild cards) + - put path in double quotes (especially for wild cards and recursive) --get - put path in double quotes (especially for wild cards) @@ -551,43 +550,51 @@ def main(): if action == 'put': try: - if "*" in source: - print "wildcard" - for filename in glob.glob(source): + source_check = source.split() + for source_path in source_check: + if verbose > 1: + print source_path + if os.path.exists(source_path): + if "*" in source_path: if verbose > 1: - print os.path.basename(filename) - dest = os.path.join(destination, os.path.basename(filename)) - if os.path.isdir(filename): + print "wildcard" + for filename in glob.glob(source_path): if verbose > 1: - print "directory for * is ", os.path.basename(filename) - dst_path = os.path.join(destination, os.path.basename(filename)) - sterm.recursive_copy(os.path.basename(filename), destination) - elif os.path.isfile(filename): - if verbose > 1: - print "filename for * is ", os.path.basename(filename) + print os.path.basename(filename) dest = os.path.join(destination, os.path.basename(filename)) - size = sterm.putfile(open(filename, "rb").read(), dest) - mode = oct(stat.S_IMODE(os.lstat(filename).st_mode)) - sterm.execute("chmod '%s' '%s'" % (mode, dest)) - if verbose > 0: - print 'Uploaded %d bytes from %s to %s' % (size, filename, dest) - elif os.path.isfile(source): - if verbose > 1: - print "source file is ", source - file_name = os.path.basename(source) - remote_status, data = sterm.cmd('[[ -d "%s" ]] ; echo $? ' % destination) - if (int(data) == 0): - destination = os.path.join(destination, file_name) - size = sterm.putfile(open(source, "rb").read(), destination) - if verbose > 0: - print 'Uploaded %d bytes from %s to %s' % (size, source, destination) - mode = oct(stat.S_IMODE(os.lstat(source).st_mode)) - sterm.execute("chmod '%s' '%s' " % (mode, destination)) - - elif os.path.isdir(source): - if verbose > 1: - print "source directory is ", source - sterm.recursive_copy(source, destination) + if os.path.isdir(filename): + if verbose > 1: + print "directory for * is ", os.path.basename(filename) + dst_path = os.path.join(destination, os.path.basename(filename)) + sterm.recursive_copy(os.path.basename(filename), destination) + elif os.path.isfile(filename): + if verbose > 1: + print "filename for * is ", os.path.basename(filename) + dest = os.path.join(destination, os.path.basename(filename)) + size = sterm.putfile(open(filename, "rb").read(), dest) + mode = oct(stat.S_IMODE(os.lstat(filename).st_mode)) + sterm.execute("chmod '%s' '%s'" % (mode, dest)) + if verbose > 0: + print 'Uploaded %d bytes from %s to %s' % (size, filename, dest) + + elif os.path.isfile(source_path): + if verbose > 1: + print "source file is ", source_path + destin_path = destination + file_name = os.path.basename(source_path) + remote_status, data = sterm.cmd('[[ -d "%s" ]] ; echo $? ' % destination) + if (int(data) == 0): + destin_path = os.path.join(destin_path, file_name) + size = sterm.putfile(open(source_path, "rb").read(), destin_path) + if verbose > 0: + print 'Uploaded %d bytes from %s to %s' % (size, source_path, destin_path) + mode = oct(stat.S_IMODE(os.lstat(source_path).st_mode)) + sterm.execute("chmod '%s' '%s' " % (mode, destin_path)) + + elif os.path.isdir(source_path): + if verbose > 1: + print "source directory is ", source_path + sterm.recursive_copy(source_path, destination) except Exception, e: print "ERROR:", e @@ -647,7 +654,7 @@ def main(): print "ERROR:", e else: print "ERROR: unknown action '%s'" % action - + sterm.close() sys.exit(int(exit_status)) From 2f5ba46ca8e457ee75fd529cb01e32c292f2b773 Mon Sep 17 00:00:00 2001 From: lineo Date: Mon, 6 Feb 2017 18:49:26 +0900 Subject: [PATCH 3/4] Suport for Maintaining the permission of file/Folder in Target wrt Source Fix for erroneous filename handling in recursive_put Function Signed-off-by: Vishnu Sankar vishnuocv@gmail.com --- serio | 721 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 476 insertions(+), 245 deletions(-) diff --git a/serio b/serio index d281b35..ae07ffe 100755 --- a/serio +++ b/serio @@ -13,12 +13,12 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. @@ -30,8 +30,9 @@ import sys, os.path, math, time import hashlib import StringIO import uu -from getopt import GetoptError, getopt as GetOpt -import glob, stat +import getopt +import glob +import stat # verbose # 1 bytes uploaded/downloaded @@ -76,14 +77,13 @@ class RemoteExecute: DATA_PER_WRITE = 100 PRINT_INTERVAL = 256 - def __init__(self, fp, io_time=None): + def __init__(self, fp): global verbose if callable(fp.read) and callable(fp.write) and callable(fp.readline) and callable (fp.readlines): self.fp = fp self.current = 0 self.showprogress = verbose > 1 self.last_size = 0 - self.IO_TIME = io_time else: raise Exception("bad fp object!") @@ -108,12 +108,19 @@ class RemoteExecute: self.last_size = current def readuntil(self, count=None, lines=None, delim=None): + # FIXME - consider implications of read timeout + # - case count will return fewer bytes than requested + # - case count will return fewer lines than requested + # - case delim will spin forever + # Should timeout be detected and set a different + # exit code? + data = "" if count is not None: for i in range(0, count): - data = self.fp.read(1) + data += self.fp.read(1) self.progress(len(data), count) elif lines is not None: @@ -129,15 +136,29 @@ class RemoteExecute: self.progress((len(data)-len(delim)), (len(data)-len(delim))) data = data[0:-len(delim)] + # FIXME - all users of delim do this fix after calling readuntil(), + # consider moving it here: + # consume '\n' leftover from readuntil() + # self.fp.readline() self.progress(len(data), len(data)) if self.showprogress > 0: print return data + # read data from the serial port, for a fixed amount of time + # intended for short reads (e.g. to drain a small amount of + # data from the port, or to get the prompt) + def readuntil_time(self, duration): + saved_timeout = self.fp.timeout + self.fp.timeout = duration + data = self.fp.read(10000) + self.fp.timeout = saved_timeout + return data + def prime_before_first_execute(self): - # required before first call of execute(), after this - # program begins + # required after this program begins, + # before first call of execute() cmd = "#" self.fp.write(cmd + "\n") @@ -145,14 +166,36 @@ class RemoteExecute: # remote system shell echos command, slurp it up cmd_echo = self.fp.readline() - # The probably will not be a prompt, becuase the remote - # shell probably sent it before serio started execution. - # Check for the command with carriage return, newline. - # If the echoed command does not include this then - # repeat the readline(). + ### FIXME - this readline() should not be needed if the + ### 'Try to synch up' is done here + ## There probably will not be a prompt, becuase the remote + ## shell probably sent it before serio started execution. + ## Check for the command with carriage return, newline. + ## If the echoed command does not include this then + ## repeat the readline(). + ## + #if not cmd_echo.endswith(cmd + "\r\n"): + # cmd_echo = self.fp.readline() + + # If serio is run quickly back to back then there might be + # some remaining cruft to clear out. This problem was + # detected with: + # for k in `seq 1 10` ; do serio -p -s file_1 -y /dev/ttyACM2 ; serio -p -s file_1 -y /dev/ttyACM2 ; done + # The resulting error is: ERROR: invalid literal for int() with base 10: "root@beaglebone:~# [ -d '.' ] ; echo $? \r\n" # - if not cmd_echo.endswith(cmd + "\r\n"): - cmd_echo = self.fp.readline() + # Try to synch up + # Need to split DELIM into separate parts so that readuntil() will + # not stop when it sees the entire value in the command line. + self.execute("echo '%s''%s'" % (self.DELIM[0:1], self.DELIM[1:])) + data = self.readuntil(delim=self.DELIM) + # consume '\n' leftover from readuntil() + self.fp.readline() + + + def get_prompt(self): + self.fp.write("# gp\n") + data = self.readuntil_time(1) + return data.strip() def execute(self, cmd): # This program MUST send a newline to the remote system once @@ -178,16 +221,17 @@ class RemoteExecute: # If the echoed command does not include the prompt then # repeat the readline(). # + # FIXME - this does not work for remote shell: busybox sh if cmd_echo == cmd + "\r\n": cmd_echo = self.fp.readline() def getfile(self, filename): - # required before calls to execute() - self.prime_before_first_execute() if self.basic: self.execute("cat '%s' && echo '%s'" % (filename, self.DELIM)) data = self.readuntil(delim=self.DELIM) + # consume '\n' leftover from readuntil() + self.fp.readline() # Catting data over a terminal turns all \n's to \r\n's data = data.replace("\r\n", "\n") @@ -207,6 +251,8 @@ class RemoteExecute: self.execute("uuencode '%s' /dev/stdout" % (filename)) data = self.readuntil(delim="end") + # consume '\n' leftover from readuntil() + self.fp.readline() # be careful about format of uuencode header @@ -242,89 +288,120 @@ class RemoteExecute: return mode, outfile.getvalue() - def putfile(self, data, filename): - i = 0 - j = 0 - data_size = len(data) - - # try to ensure at shell prompt - # also, required before calls to execute() - self.prime_before_first_execute() + def put_file(self, name, destination): + global verbose - # disable shell echo - # Otherwise shell echos from a large --put may still be in - # progress when putfile() returns. Even with disabled echo, - # remote system will send a shell prompt each time it receives - # a newline, so this does not eliminate all traffic from the - # remote system, it just reduces it. - self._safe_write('stty -echo\n') + self.execute("[ -d '%s' ] ; echo $? " % destination) + status = self.fp.readline() + if (int(status) == 0): + filename = destination + '/' + os.path.basename(name) + else: + filename = destination + local_file = open(name, "rb") + data = local_file.read() + sent = 0 + data_size = len(data) # Create/zero the file - self._safe_write('echo -ne > %s\n' % filename) + self.execute('echo -ne > %s' % filename) + + # Disable shell echo + # Echoing the data doubles the time required to put the file. + self.fp.write('stty -echo\n') + + # drain the response from the serial buffer + junk = self.readuntil_time(1) + + prompt = self.get_prompt() + + # -------------------- self.execute() will NOT work after this point + # until after 'stty echo' # Loop through all the bytes in the source data and append them # to the destination file. Limit size of data in the echo # command to DATA_PER_WRITE characters. - while i < data_size: - j = 0 + while sent < data_size: + line_size = 0 dpart = '' - while j < self.DATA_PER_WRITE and i < data_size: - data_i = data[i] + while line_size < self.DATA_PER_WRITE and sent < data_size: + data_i = data[sent] odata = ord(data_i) if 'a' <= data_i <= 'z' or 'A' <= data_i <= 'Z' or '0' <= data_i <= '9': dpart += data_i - j += 1 + line_size += 1 else: - if j + 5 > self.DATA_PER_WRITE: + if line_size + 5 > self.DATA_PER_WRITE: break dpart += '\\x%.2X' % int(odata) - j += 5 - i += 1 + line_size += 5 + sent += 1 - self._safe_write('echo -ne "%s" >> %s\n' % (dpart, filename)) + self.fp.write('echo -ne \"%s\" >> %s\n' % (dpart, filename)) # Show upload status - self.progress(i, data_size) + self.progress(sent, data_size) + + # wait for shell prompt before sending next line + recv_data = self.readuntil(delim=prompt) if self.showprogress: print # enable shell echo - self._safe_write('stty echo\n') + self.fp.write('\n') + self.fp.write('stty echo\n') + self.fp.write('\n') + + # -------------------- self.execute() will work after this point + + # Try to synch up + # Need to split DELIM into separate parts so that readuntil() will + # not stop when it sees the entire value in the command line. + self.execute("echo '%s''%s'" % (self.DELIM[0:1], self.DELIM[1:])) + data = self.readuntil(delim=self.DELIM) + # consume '\n' leftover from readuntil() + self.fp.readline() - return i + local_file.close() - def _safe_write(self, data): - self.fp.write(data) - if data.endswith('\n'): - # give the remote system time for disk/flash I/O - if self.IO_TIME is not None: - time.sleep(self.IO_TIME) + mode = oct(stat.S_IMODE(os.stat(name).st_mode)) + # FIXME - check for chmod error + self.execute("chmod '%s' '%s' 2> /dev/null" % (mode, destination)) + if verbose > 0: + print 'Uploaded %d bytes from %s to %s' % (sent, name, destination) def close(self): self.fp.close() def cmd(self, cmd_str, remote_shell=None, timeout=None): - # required before calls to execute() - self.prime_before_first_execute() if remote_shell: # FIXME - add option to choose what shell to use # have minimally tested sh and bash - remote_cmd = 'sh -c \'' + cmd_str + '\' ; r=$?; echo "%s"; echo "${r}"' % self.DELIM + # + # FIXME - remote_cmd works ok if the remote shell that executes + # remote_cmd is bash, but not if the remote shell that + # executes remote_cmd is busybox sh. + # This appears to be due to sh echoing a '\n' when the + # input line is too long due to a large prompt.A + # For example, if PS1='\w \$ ', and in the directory + # /usr/data/frowand/tmp, then when remote_cmd is: + # sh -c 'ls .' ; r=$?; echo '[[&&serio_cmd_done&&]]'; echo ${r} + # sh splits the input with a '\n' before ' ${r}' + # + remote_cmd = "sh -c \'" + cmd_str + "\' ; r=$?; echo '%s'; echo ${r}" % self.DELIM else: - remote_cmd = cmd_str + ' ; echo "%s"\n' % self.DELIM + remote_cmd = cmd_str + " ; echo '%s'\n" % self.DELIM self.execute(remote_cmd) # should do this in a thread so we can output while # the data is coming back - # FIXME - use timeout here result = self.readuntil(None, None, self.DELIM) + # consume '\n' leftover from readuntil() + self.fp.readline() if remote_shell: - # consume '\n' leftover from readuntil() - self.fp.readline() remote_status = self.fp.readline() remote_status = remote_status.replace("\r\n", "") else: @@ -334,157 +411,260 @@ class RemoteExecute: result = result.replace("\r\n", "\n") return remote_status, result - def recursive_copy(self, src_dir, dst_dir): - if src_dir.endswith('/'): - if verbose > 0: - print src_dir - raise Exception("omitting directory: " + src_dir) - else: - head, tail = os.path.split(src_dir) - destin = os.path.join(dst_dir, tail) - if verbose > 0: - print destin - self.fp.write("mkdir -p '%s' 2> /dev/null \n" % destin) - remote_status, data = self.cmd("[[ -d '%s' ]] ; echo $? " % destin) - if (int(data) == 0): - dst_dir = destin - else: - raise Exception("unable to create remote directory: " + data) - - for root, dirs, files in os.walk(src_dir): - for item in dirs: - src_path = os.path.join(root, item) - rel_path = os.path.relpath(root, src_dir) + def recursive_put(self, src_dir, dst_dir): + head, tail = os.path.split(src_dir) + dest = dst_dir + '/' + tail + self.execute("mkdir -p '%s' 2> /dev/null" % dest) + self.execute("[ -d '%s' ] ; echo $? " % dest) + data = self.fp.readline() + if (int(data) != 0): + raise Exception("unable to create remote directory '%s': " %dest + data) + mode = oct(stat.S_IMODE(os.lstat(src_dir).st_mode)) + self.execute("chmod '%s' '%s' " % (mode, dest)) + mode = oct(stat.S_IMODE(os.lstat(src_dir).st_mode)) + self.execute("chmod '%s' '%s' " % (mode, dest)) + + for dirpath, dirs, files in os.walk(src_dir, followlinks=True): + + for dir in dirs: + rel_path = os.path.relpath(dirpath, src_dir) if rel_path is not '.': - dst_path = os.path.join(dst_dir, os.path.join(rel_path, item)) + dst_path = os.path.join(dest, os.path.join(rel_path, dir)) else: - dst_path = os.path.join(dst_dir, item) - self.cmd('mkdir -p %s' % dst_path) + dst_path = dest + '/' + dir + self.execute("mkdir -p '%s' 2> /dev/null" % dst_path) + self.execute("[ -d '%s' ] ; echo $? " % dst_path) + data = self.fp.readline() + if (int(data) != 0): + raise Exception("unable to create remote directory '%s': " % dst_path + data) + mode = oct(stat.S_IMODE(os.lstat(src_dir).st_mode)) + self.execute("chmod '%s' '%s' " % (mode, dst_path)) - for item in files: - relative = os.path.relpath(root, src_dir) - if relative is not '.': - dst_path = os.path.join(dst_dir, os.path.join(relative, item)) - else: - dst_path = os.path.join(dst_dir, item) - src_path = os.path.join(root, item) - size = self.putfile(open(src_path, "rb").read(), dst_path) + for file in files: + rel_path = os.path.relpath(dirpath, src_dir) + if rel_path is not '.': + dst_path = os.path.join(dest, os.path.join(rel_path, file)) + else: + dst_path = dest + '/' + file + src_path = dirpath + '/' + file + size = self.put_file(src_path, dst_path) mode = oct(stat.S_IMODE(os.lstat(src_path).st_mode)) self.execute("chmod '%s' '%s' " % (mode, dst_path)) - if verbose > 0: - print 'Uploaded %d bytes from %s to %s' % (size, src_dir, dst_path) + class SerialTerminal(RemoteExecute): - def __init__(self, basic, checksum, port, baudrate, io_time): + def __init__(self, basic, checksum, port, baudrate, timeout): self.basic = basic self.checksum = checksum - self.serial = serial.Serial(port=port, baudrate=baudrate) + self.serial = serial.Serial(port=port, baudrate=baudrate, timeout=timeout) already_open = self.serial.isOpen() if not already_open: self.serial.open() - RemoteExecute.__init__(self, fp=self.serial, io_time=io_time) + RemoteExecute.__init__(self, fp=self.serial) -def usage(): - print '\nUsage: %s [OPTIONS]' % os.path.basename(sys.argv[0]) +def usage_sersh(): + + print + print 'Usage: %s [OPTIONS] [user @]hostname command' % os.path.basename(sys.argv[0]) print ''' -b, --baudrate= Serial port baud rate [115200] -B, --basic Use basic (minimal) remote system commands - -d, --destination= Path to destination file - -g, --get Get a file from the remote system -h, --help Show help - -p, --put Put a file to the remote system - -c, --cmd="command with args" Execute a command on the remote system - -M, --md5sum verify file transfer with md5sum -m, --minicom= Name of the minicom config file to use -N, --no-remote-shell Do not invoke a remote shell to execute --cmd - -P, --paranoid Assume fragile remote system, be conservative - -s, --source= Path to source file - -t, --time= delay in _safe_write() [if -P ''' + str(paranoid_time) + ''' else 0] + -T, --timeout= Set read timout on the serial port (float) + -v, --verbose Print more information (cumulative) + +Notes: + - user is ignored + - --timeout is a big stick. Setting to a small value will result + in errors or unstable behaviour. must be long enough to allow + the entire command to be echoed. + +Remote System Dependencies: + - May hang if the shell prompt is zero bytes long + - Remote system shell echos \\r\\n at end of command input + +Known Problems: + - will hang for command "bash" + - will hang for command "exit" (exiting bash) + - can not be used to login to a shell + +Return: + If --remote-shell then the exit status of the remote shell. + Otherwise: + 0 no error + 1 --help or argument error + + ''' + + +def usage_sercp(): + + print + print 'Usage: %s [OPTIONS] [[user1@]host1:]file1 ... [[user2@]host2:]file2' % os.path.basename(sys.argv[0]) + print ''' + -b, --baudrate= Serial port baud rate [115200] + -B, --basic Use basic (minimal) remote system commands + -h, --help Show help + -M, --md5sum verify file transfer with md5sum + -m, --minicom= Name of the minicom config file to use + -r, --recurse Recursively copy directories + -T, --timeout= Set read timout on the serial port (float) -v, --verbose Print more information (cumulative) - -y, --port= Serial port to use [/dev/ttyUSB0] + +Notes: + - user1 and user2 are ignored + - scp distinguished a filename containing ":" from a host by finding + a "/" before the ":". This means that "host1" and "host2" may not + contains a "/". Due to this constraint "host1" and "host2" are device + names relative to "/dev/". + - All source files ("file1 ...") must be on the same host. + - The source files ("file1 ...") and the destination location ("file2") + must be on different hosts. Local copies and copies between remote + hosts are not supported. + - Behaviour of a list of --source files and of --recurse are intended to + be as consistent as possible with scp. + FIXME - default of scp is verbose, list of files is silenced by '-q' + serio is the opposite + - Put 'file path' in double quotes when multiple files are transferred + This is different syntax than scp and will change if scp style source + and destination is implemented. + - --timeout is a big stick. Setting to a small value will result + in errors or unstable behaviour. For --cmd, must be long + enough to allow the entire command to be echoed. Side-effects: - --put + --put --basic - shell echo will be enabled on remote system (stty echo) - if exception occurs, shell echo may be disabled on remote system (remote system can be fixed with command "stty echo") - - put path in double quotes (especially for wild cards and recursive) - --get - - put path in double quotes (especially for wild cards) Remote System Dependencies: - May hang if the shell prompt is zero bytes long - - Remote system shell echos \\r\\n at end of command input - Remote system shell built-ins or programs: - --get --basic : cat, echo - --get : uuencode - --get --md5sum : stat, md5sum, uuencode - --put : stty, echo + file get --basic : cat, echo + file get : uuencode + file get --md5sum : stat, md5sum, uuencode + file put : stty, echo Known Problems: - - execute a command (--cmd) - - will hang for command "bash" - - will hang for command "exit" (exiting bash) - - can not be used to login to a shell - - Put with wildcard support - - wildcard put dont supports recursive copy -Python 2.7 glob.glob limitation + - the entire file is copied into memory before being written out. Return: - If --cmd and --remote-shell then the exit status of the remote shell. - Otherwise: 0 no error 1 --help or argument error - 2 --put error - 3 --get error + 2 file put error + 3 file get error ''' -paranoid_time = 0.1 +def usage(): + if os.path.basename(sys.argv[0]) == 'sersh': + usage_sersh() + if os.path.basename(sys.argv[0]) == 'sercp': + usage_sercp() + def main(): + def parse_arg_for_files(args): + host = None + files = [] + + for arg in args[0:len(args)]: + # print arg + + # from usage: + # - host1 and host2 are device names relative to /dev/ + # This is because scp allows a file name to contain + # a ':'. scp determines that [host1] or [host2] is + # a file instead of a host because there is a '/' + # before the ':'. For serio to follow this rule, + # [host1] and [host2] can not contain a '/', so + # serio requires [host1] and [host2] to be located + # in '/dev/'. The '/dev/' portion of [host1] and + # [host2] is implied. + + host_in_arg = 1 + if ':' in arg and '/' in arg: + index_colon = arg.find(':') + index_slash = arg.find('/') + if index_slash < index_colon: + host_in_arg = 0 + elif not ':' in arg: + host_in_arg = 0 + + if host_in_arg: + host_end = arg.find(':') + file_start = host_end + 1 + if '@' in arg: + # strip off leading [user@] + host_start = arg.find('@') + 1 + else: + host_start = 0 + new_host = arg[host_start:host_end] + if not '/' in new_host: + new_host = '/dev/' + new_host + if host is None: + host = new_host + elif host != new_host: + print 'ERROR: all source files must be on the same host' + sys.exit(1) + file = arg[file_start:] + files.append(file) + + else: + if host is not None: + print 'ERROR: all source files must be on the same host' + sys.exit(1) + file = arg + files.append(file) + + return host, files + + global verbose - exit_status = 0 + exit_status = 0 + def_port = '/dev/ttyUSB0' action = None basic = None baudrate = 115200 checksum = CHECKSUM_NONE destination = None - io_time = None + timeout = None minicom = None - paranoid = None - port = '/dev/ttyUSB0' + port = None + recurse = None remote_shell = 1 source = None try: - opts, args = GetOpt(sys.argv[1:], - 'b:Bc:d:ghMNm:pPs:t:vy:', + opts, args = getopt.getopt(sys.argv[1:], + 'b:Bc:d:hMNm:rs:T:vy:', ['baudrate=', 'basic', 'cmd=', 'destination=', - 'get', 'help', 'md5sum', - 'minicom=', - 'put', - 'paranoid', 'no-remote-shell', + 'minicom=', + 'recurse', 'source=', - 'time=', + 'timeout=', 'verbose', 'port=', ] ) - except GetoptError, e: + except getopt.GetoptError, e: print 'Usage error:', e print 'Help available via: \'%s -h\'' % os.path.basename(sys.argv[0]) sys.exit(1) @@ -499,8 +679,6 @@ def main(): cmd_str = arg elif opt in ('-d', '--destination'): destination = arg - elif opt in ('-g', '--get'): - action = 'get' elif opt in ('-h', '--help'): usage() sys.exit(1) @@ -508,93 +686,168 @@ def main(): checksum = CHECKSUM_MD5SUM elif opt in ('-m', '--minicom'): minicom = ParseMinicom(arg) - elif opt in ('-p', '--put'): - action = 'put' - elif opt in ('-P', '--paranoid'): - paranoid = 1 elif opt in ('-N', '--no-remote-shell'): remote_shell = 0 + elif opt in ('-r', '--recurse'): + recurse = 1 elif opt in ('-s', '--source'): source = arg - elif opt in ('-t', '--time'): - io_time = float(arg) + elif opt in ('-T', '--timeout'): + timeout = float(arg) elif opt in ('-v', '--verbose'): verbose += 1 elif opt in ('-y', '--port'): port = arg - if paranoid: - if io_time is None: - io_time = paranoid_time + + if os.path.basename(sys.argv[0]) == 'sersh': + action = 'cmd' + tmp_cmd_str = None + for arg in args: + if tmp_cmd_str is None: + tmp_cmd_str = arg + else: + tmp_cmd_str = tmp_cmd_str + ' ' + arg + + host_end = tmp_cmd_str.find(' ') + if '@' in tmp_cmd_str: + # strip off leading [user@] + host_start = tmp_cmd_str.find('@') + 1 + else: + host_start = 0 + host = tmp_cmd_str[host_start:host_end] + if '/' in host: + port = host + else: + port = '/dev/' + host + cmd_str = tmp_cmd_str[host_end:] + + elif action is None: + + if len(args) < 2: + print 'ERROR: file1 and file2 required' + sys.exit(1) + + src_host, source = parse_arg_for_files(args[0:len(args) - 1]) + dst_host, dst = parse_arg_for_files(args[len(args) - 1:]) + + if src_host: + if dst_host: + print 'ERROR: file1 xor file2 must be remote' + sys.exit(1) + action = 'get' + port = src_host + + # zzz there must be a more elegent way to do this: + # way (1) + # doing it this way results in hang during file get in + # serio-test.sh ttyUSB0 + # destination = dst[0:1] + # way (2) + # this works + for x in dst[0:1]: + destination = x + if destination == '': + destination = '.' + if dst_host: + if src_host: + print 'ERROR: file1 xor file2 must be remote' + sys.exit(1) + action = 'put' + port = dst_host + + elif len(args) > 0: + print 'ERROR: unrecognized parameters' + sys.exit(0) + if action is None: - print 'ERROR: must specify an action option' - print ' --cmd' - print ' --get' - print ' --put' + print 'ERROR: must specify either a copy or --cmd' sys.exit(1) if action in ['get','put']: - if not source: - print 'ERROR: must specify the -s option' - sys.exit(1) - - if destination is None or destination == ".": - destination = os.path.basename(source) + if destination is None: + destination = "." if minicom is not None: - port = minicom.port() - baudrate = minicom.baudrate() - sterm = SerialTerminal(basic, checksum, port, baudrate, io_time) + m_port = minicom.port() + if port is None: + port = m_port + else: + if port != m_port: + print 'ERROR: host in filespec does not match port from --minicom' + sys.exit(1) + + m_baudrate = minicom.baudrate() + if not m_baudrate is None: + baudrate = m_baudrate + + if timeout == 0: + + # FIXME - add test for minimum reasonable value based on baud + # rate (and maybe length of command). + # execute() must have enough time to be able + # to readline() the echoed command. + # + # FIXME - --timeout is a big hammer when used as a global + # timeout. Consider using timeout() to vary + # the value at various points of this program. For + # example, the value while in execute() could be + # adjusted upwards to account for the length of the + # command string passed for each specific call to + # execute(). [[ sterm.fp.timeout ]] + + print 'ERROR: "--timeout 0" will result in incorrect behaviour' + sys.exit(1) + + if port is None: + port = def_port + + sterm = SerialTerminal(basic, checksum, port, baudrate, timeout) + + # required before calls to execute() + sterm.prime_before_first_execute() if action == 'put': try: - source_check = source.split() - for source_path in source_check: - if verbose > 1: - print source_path - if os.path.exists(source_path): - if "*" in source_path: - if verbose > 1: - print "wildcard" - for filename in glob.glob(source_path): - if verbose > 1: - print os.path.basename(filename) - dest = os.path.join(destination, os.path.basename(filename)) - if os.path.isdir(filename): - if verbose > 1: - print "directory for * is ", os.path.basename(filename) - dst_path = os.path.join(destination, os.path.basename(filename)) - sterm.recursive_copy(os.path.basename(filename), destination) - elif os.path.isfile(filename): - if verbose > 1: - print "filename for * is ", os.path.basename(filename) - dest = os.path.join(destination, os.path.basename(filename)) - size = sterm.putfile(open(filename, "rb").read(), dest) - mode = oct(stat.S_IMODE(os.lstat(filename).st_mode)) - sterm.execute("chmod '%s' '%s'" % (mode, dest)) - if verbose > 0: - print 'Uploaded %d bytes from %s to %s' % (size, filename, dest) - - elif os.path.isfile(source_path): - if verbose > 1: - print "source file is ", source_path - destin_path = destination - file_name = os.path.basename(source_path) - remote_status, data = sterm.cmd('[[ -d "%s" ]] ; echo $? ' % destination) - if (int(data) == 0): - destin_path = os.path.join(destin_path, file_name) - size = sterm.putfile(open(source_path, "rb").read(), destin_path) - if verbose > 0: - print 'Uploaded %d bytes from %s to %s' % (size, source_path, destin_path) - mode = oct(stat.S_IMODE(os.lstat(source_path).st_mode)) - sterm.execute("chmod '%s' '%s' " % (mode, destin_path)) - - elif os.path.isdir(source_path): - if verbose > 1: - print "source directory is ", source_path - sterm.recursive_copy(source_path, destination) + + # FIXME - need to handle space inside name, either with + # quotes of '\' delimeter + + # if multiple sources then destination must be a directory + + names = 0 + directories = 0 + for name in source: + names += 1 + if os.path.isdir(name): + directories += 1 + + if names > 1 or directories: + + sterm.execute('[ ! -e "%s" ] ; echo $?' % destination) + status = sterm.fp.readline() + if int(status) == 0: + raise Exception(destination + ": No such file or directory") + + sterm.execute('[ ! -d "%s" ] ; echo $?' % destination) + status = sterm.fp.readline() + if int(status) == 0: + raise Exception(destination + ": Not a directory") + + for name in source: + + if os.path.isdir(name): + + if recurse: + sterm.recursive_put(name, destination) + else: + # follow scp behaviour, do not raise exception + print 'ERROR: ' + name + ': not a regular file' + + elif os.path.isfile(name): + sterm.put_file(name, destination) except Exception, e: print "ERROR:", e @@ -602,44 +855,21 @@ def main(): elif action == 'get': try: - if "*" in source: - if verbose > 1: - print "Wildcard entry" - data = sterm.cmd("ls -1 %s " % source) - if verbose > 1: - print data - for line in data.splitlines(): - if verbose > 1: - print line - head, tail = os.path.split(line) - des = os.path.abspath(destination) - des = os.path.join(des, tail) - if verbose > 1: - print des - mode, contents = sterm.getfile(str(line)) - if len(contents): - open(des, "wb").write(contents) - if mode != -1: - os.chmod(des_file, mode) - else: - mode, data = sterm.getfile(source) + # FIXME - implement multi-file get + if len(source) > 1: + raise Exception('multi-file get not implemented') + + for src in source: + mode, data = sterm.getfile(src) if len(data): - des = os.path.abspath(destination) - if verbose > 1: - print des - if os.path.isdir(des): - head, tail = os.path.split(source) - des_file = os.path.join(des, tail) - open(des_file, "wb").write(data) - if mode != -1: - os.chmod(des_file, mode) + open(destination, "wb").write(data) + if mode != -1: + os.chmod(destination, mode) + + if verbose > 0: + print 'Downloaded %d bytes from %s to %s' % (len(data), src, destination) + mode, data = sterm.getfile(src) - else: - open(des, "wb").write(data) - if mode != -1: - os.chmod(des, mode) - if verbose > 0: - print 'Downloaded %d bytes from %s to %s' % (len(data), source, des) except Exception, e: print "\nERROR:", e sys.exit(3) @@ -652,9 +882,10 @@ def main(): print data except Exception, e: print "ERROR:", e + else: print "ERROR: unknown action '%s'" % action - + sterm.close() sys.exit(int(exit_status)) From 4b692298560ccc0bfb485a9f0198f5809f2a1daf Mon Sep 17 00:00:00 2001 From: lineo Date: Mon, 6 Feb 2017 19:00:49 +0900 Subject: [PATCH 4/4] Test app from Master Relocating app --- serio-test.sh | 128 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 88 insertions(+), 40 deletions(-) diff --git a/serio-test.sh b/serio-test.sh index f7c8278..fa183e9 100755 --- a/serio-test.sh +++ b/serio-test.sh @@ -14,21 +14,18 @@ function vecho { function usage { echo " -usage: ${script_name} [-v] [--debug] [-h] [-nc] +usage: ${script_name} [-v] [--debug] [-h] [-nc] serial_port -B, --basic *Use basic (minimal) remote system commands - --debug Print each shell line as it is executed - -F, --numfile Number of random data test files + --debug Print each shell line as it is executed + -F, --numfiles Number of random data test files -h, -help, --help Show help - -nc Don't clean up test files + --nc Don't clean up test files -s, --sleep Time to sleep between --get and --put tests [0.0] - -t, --time *Delay in _safe_write() - -y, --port *Serial port to use [/dev/ttyUSB0] - --remote-dir Directory on remote system to put files into [.] + -T, --timeout *Read timout on the serial port (float) + -R, --remote-dir Directory on remote system to put files into [.] -v Be verbose - * Sets a serio command line option - " >&2 } @@ -40,14 +37,15 @@ script_name=`basename $0` #PARANOID="-P" unset BASIC -unset SERIAL_DEV unset PARANOID +unset PORT +unset SERIAL_DEV unset SLEEP -unset TIME +unset TIMEOUT do_cleanup=1 help=0 -numfile=1 +numfiles=2 remote_dir="." verbose=0 while [ $# -gt 0 ] ; do @@ -64,9 +62,9 @@ while [ $# -gt 0 ] ; do set -x ;; - -F | --numfile ) + -F | --numfiles ) shift - numfile=$1 + numfiles=$1 shift ;; @@ -91,18 +89,16 @@ while [ $# -gt 0 ] ; do shift ;; - -t | --time ) + -T | --timeout ) shift - TIME="-t $1" + TIMEOUT="-T $1" shift - verbose=1 ;; - -T | --remote-dir ) + -R | --remote-dir ) shift remote_dir="$1" shift - verbose=1 ;; -v ) @@ -110,11 +106,10 @@ while [ $# -gt 0 ] ; do verbose=1 ;; - -y | --port ) - shift - SERIAL_DEV="-y $1" - shift - verbose=1 + -* ) + echo "ERROR: unrecognized option: $1" >&2 + exit 1 + break ;; * ) @@ -124,6 +119,11 @@ while [ $# -gt 0 ] ; do esac done +if [ $# -gt 0 ] ; then + PORT="$1" + SERIAL_DEV="-y /dev/$1" + shift +fi if [ $help -eq 1 ] ; then usage @@ -131,8 +131,12 @@ if [ $help -eq 1 ] ; then fi if [ $# -ne 0 ] ; then - echo "ERROR: unexpected parameter:" >&2 - echo " $@" >&2 + echo "ERROR: unexpected parameter: $@" >&2 + exit 1 +fi + +if [ "${PORT}" == "" ] ; then + echo "ERROR: argument 'serial_port' is required" >&2 exit 1 fi @@ -148,27 +152,46 @@ fi tmp_dir=$( mktemp --tmpdir -d serio-test.XXXXXXXXXX ) -FILE_LIST="file_1" -for k in $( seq 2 $(( ${numfile} + 1 )) ) ; do +FILE_LIST="" +for k in $(seq ${numfiles}) ; do FILE_LIST="${FILE_LIST} file_${k}" done -SERIO_ARGS="$SERIAL_DEV $PARANOID $BASIC $TIME" +SERCP_ARGS="$PARANOID $BASIC $TIMEOUT" +SERSH_ARGS="$PARANOID $BASIC $TIMEOUT" # shell builtin 'time' does not recognize -f TIME="/usr/bin/time" # respect $PATH +SERCP=$( which sercp ) SERIO=$( which serio ) +SERSH=$( which sersh ) + +# if serio is not on the PATH, then try using it from +# the current directory. This way, even if serio is +# not installed, there's a chance that serio-test.sh will work +if [ -z "${SERCP}" ] ; then + SERCP=./sercp +fi + +if [ -z "${SERIO}" ] ; then + SERIO=./serio +fi +if [ -z "${SERSH}" ] ; then + SERSH=./sersh +fi vecho "remote-dir = ${remote_dir}" vecho "FILE_LIST = ${FILE_LIST}" vecho "SERIAL_DEV = ${SERIAL_DEV}" vecho "SERIO_ARGS = ${SERIO_ARGS}" vecho "SLEEP = ${SLEEP}" +vecho "SERCP = ${SERCP}" vecho "SERIO = ${SERIO}" +vecho "SERSH = ${SERSH}" ######################### @@ -177,7 +200,7 @@ vecho "SERIO = ${SERIO}" echo "Creating files..." # make a super-small, super-safe file for file1 -echo "this is the first test file for serio" >file1 +echo "this is the first test file for serio" >${tmp_dir}/file_short if [ $verbose -gt 0 ] ; then ddout="/dev/stdout" @@ -194,6 +217,9 @@ for f in ${FILE_LIST} ; do i=$(( $i + 1 )) done +FILE_LIST="file_short ${FILE_LIST}" + + ######################### # test put and get # run some put and get tests, timing them @@ -218,10 +244,12 @@ echo "Putting files: $FILE_LIST" for f in $FILE_LIST ; do f_full="${tmp_dir}/$f" vecho "Putting file ${f}" - ${TIME} -f " time for put of $f: %E" $SERIO $SERIO_ARGS -p --source=$f_full --destination="${remote_dir}/$f" ; + ${TIME} -f " time for put of $f: %E" \ + $SERCP $SERCP_ARGS $f_full "${PORT}:${remote_dir}/$f" $SLEEP done + ## get some files echo "Getting files: $FILE_LIST" @@ -229,7 +257,8 @@ for f in $FILE_LIST ; do ret_f="${f}-return" ret_f_full="${tmp_dir}/${ret_f}" vecho " Getting file ${f} (into $ret_f)" - ${TIME} -f " time for get of $f: %E" $SERIO $SERIO_ARGS -g --source="${remote_dir}/$f" --destination=$ret_f_full ; + ${TIME} -f " time for get of $f: %E" \ + $SERCP $SERCP_ARGS "${PORT}:${remote_dir}/$f" $ret_f_full $SLEEP if [ -e "$ret_f_full" ] ; then @@ -269,25 +298,44 @@ done ######################### # test some commands -#$SERIO $SERIO_ARGS -c "echo hello there!" +#$SERSH $SERSH_ARGS -c "echo hello there!" echo "Executing some commands" vecho " Execute 'ls -l $remote_dir'" -$SERIO $SERIO_ARGS -c "ls -l $remote_dir" +$SERSH $SERSH_ARGS ${PORT} "ls -l $remote_dir" vecho " Execute 'echo hello there'" -res1=$($SERIO $SERIO_ARGS -c "echo hello there") -exp1=$'hello there' +res=$($SERSH $SERSH_ARGS ${PORT} "echo hello there") +rcode=$? +exp=$'hello there' -echo "expected : [$exp1]" -echo "got result: [$res1]" +echo "expected : [$exp], rcode=[0]" +echo "got result: [$res], rcode=[$rcode]" desc="$test_num - run 'echo hello there' on remote" -if [ "$res1" = "$exp1" ] ; then +if [ "$res" = "$exp" -a "$rcode" = "0" ] ; then + echo "ok $desc" +else + echo "not ok $desc" +fi +test_num=$(( $test_num + 1 )) + +vecho " Execute 'echo foo ; false'" +res=$($SERSH $SERSH_ARGS ${PORT} "echo foo ; false") +rcode=$? +exp=$'foo' +expcode=1 + +echo "expected : [$exp], rcode=[$expcode]" +echo "got result: [$res], rcode=[$rcode]" + +desc="$test_num - run 'echo foo ; false' on remote" +if [ "$res" = "$exp" -a "$rcode" = "$expcode" ] ; then echo "ok $desc" else echo "not ok $desc" fi +test_num=$(( $test_num + 1 )) ######################### # test cleanup @@ -295,7 +343,7 @@ fi function cleanup { # remove test files - $SERIO $SERIAL_DEV -c "rm ${remote_dir}/file_[1-9]*" + $SERSH ${PORT} "rm ${remote_dir}/file_[1-9]* ${remote_dir}/file_short" rm -r ${tmp_dir} }