--- a/build/mobile/devicemanager.py
+++ b/build/mobile/devicemanager.py
@@ -492,79 +492,106 @@ class DeviceManager:
def catFile(self, remoteFile):
data = self.sendCMD(['cat ' + remoteFile])
if data == None:
return None
return self.stripPrompt(data)
def pullFile(self, remoteFile):
+ """Returns contents of remoteFile using the "pull" command.
+ The "pull" command is different from other commands in that DeviceManager
+ has to read a certain number of bytes instead of just reading to the
+ next prompt. This is more robust than the "cat" command, which will be
+ confused if the prompt string exists within the file being catted.
+ However it means we can't use the response-handling logic in sendCMD().
+ """
+
def err(error_msg):
- err_str = 'bad response to pull: %s!' % error_msg
+ err_str = 'error returned from pull: %s' % error_msg
print err_str
self._sock = None
raise FileError(err_str)
- def read(to_recv, error_msg):
+ # FIXME: We could possibly move these socket-reading functions up to
+ # the class level if we wanted to refactor sendCMD(). For now they are
+ # only used to pull files.
+
+ def uread(to_recv, error_msg):
+ """ unbuffered read """
data = self._sock.recv(to_recv)
if not data:
err(error_msg)
return None
return data
- self.sendCMD(['pull ' + remoteFile])
+ def read_until_char(c, buffer, error_msg):
+ """ read until 'c' is found; buffer rest """
+ while not '\n' in buffer:
+ data = uread(1024, error_msg)
+ if data == None:
+ err(error_msg)
+ return ('', '', '')
+ buffer += data
+ return buffer.partition(c)
+
+ def read_exact(total_to_recv, buffer, error_msg):
+ """ read exact number of 'total_to_recv' bytes """
+ while len(buffer) < total_to_recv:
+ to_recv = min(total_to_recv - len(buffer), 1024)
+ data = uread(to_recv, error_msg)
+ if data == None:
+ return None
+ buffer += data
+ return buffer
+
+ prompt = self.base_prompt + self.prompt_sep
buffer = ''
- while not '\n' in buffer:
- data = read(1024, 'could not find metadata')
- if data == None:
- return
- buffer += data
- nl = buffer.find('\n')
- metadata = buffer[:nl]
- print 'metadata: %s' % metadata
- filedata = buffer[nl+1:] # skip newline
- sep = metadata.rfind(',')
- if sep == -1:
- err('could not find file size')
+
+ # expected return value:
+ # <filename>,<filesize>\n<filedata>
+ # or, if error,
+ # <filename>,-1\n<error message>
+ self.sendCMD(['pull ' + remoteFile])
+ # read metadata; buffer the rest
+ metadata, sep, buffer = read_until_char('\n', buffer, 'could not find metadata')
+ if not metadata:
return None
- filename = metadata[:sep]
- filesizestr = metadata[sep+1:]
- prompt = self.base_prompt + self.prompt_sep
+ if self.debug >= 3:
+ print 'metadata: %s' % metadata
+
+ filename, sep, filesizestr = metadata.partition(',')
+ if sep == '':
+ err('could not find file size in returned metadata')
+ return None
try:
filesize = int(filesizestr)
except ValueError:
- err('invalid file size')
- return None
- if filesize == -1:
- while not '\n' in filedata:
- data = read(1024, 'could not find metadata')
- if data == None:
- return None
- filedata += data
- nl = filedata.find('\n')
- error_str = filedata[:nl]
- filedata = filedata[nl+1:]
- while filedata < len(prompt):
- data = read(1024, 'could not find metadata')
- if data == None:
- return None
- print 'error pulling file: %s' % error_str
+ err('invalid file size in returned metadata')
return None
- total_to_recv = filesize + len(prompt)
- while len(filedata) < total_to_recv:
- to_recv = min(total_to_recv - len(filedata), 1024)
- data = read(to_recv, 'could not get all file data')
- if data == None:
+ if filesize == -1:
+ # read error message
+ error_str, sep, buffer = read_until_char('\n', buffer, 'could not find error message')
+ if not error_str:
return None
- filedata += data
- if filedata[-len(prompt):] != prompt:
- err('no prompt')
- return filedata
- return filedata[:-len(prompt)]
+ # prompt should follow
+ read_exact(len(prompt), buffer, 'could not find prompt')
+ print 'DeviceManager: error pulling file: %s' % error_str
+ return None
+
+ # read file data
+ total_to_recv = filesize + len(prompt)
+ buffer = read_exact(total_to_recv, buffer, 'could not get all file data')
+ if buffer == None:
+ return None
+ if buffer[-len(prompt):] != prompt:
+ err('no prompt found after file data--DeviceManager may be out of sync with agent')
+ return buffer
+ return buffer[:-len(prompt)]
# copy file from device (remoteFile) to host (localFile)
def getFile(self, remoteFile, localFile = ''):
if localFile == '':
localFile = os.path.join(self.tempRoot, "temp.txt")
retVal = self.pullFile(remoteFile)
if retVal == None:
@@ -581,90 +608,89 @@ class DeviceManager:
def getDirectory(self, remoteDir, localDir):
if (self.debug >= 2): print "getting files in '" + remoteDir + "'"
filelist = self.listFiles(remoteDir)
if (filelist == None):
return None
if (self.debug >= 3): print filelist
if not os.path.exists(localDir):
os.makedirs(localDir)
-
+
for f in filelist:
if f == '.' or f == '..':
continue
remotePath = remoteDir + '/' + f
localPath = os.path.join(localDir, f)
- print 'remotePath is %s' % remotePath
- print 'localPath is %s' % localPath
try:
is_dir = self.isDir(remotePath)
except FileError:
- print 'bad file "%s"!' % remotePath
+ print 'isdir failed on file "%s"; continuing anyway...' % remotePath
continue
if is_dir:
if (self.getDirectory(remotePath, localPath) == None):
- print 'aborted when getting directory'
+ print 'failed to get directory "%s"' % remotePath
return None
else:
# It's sometimes acceptable to have getFile() return None, such as
# when the agent encounters broken symlinks.
# FIXME: This should be improved so we know when a file transfer really
# failed.
- self.getFile(remotePath, localPath)
+ if self.getFile(remotePath, localPath) == None:
+ print 'failed to get file "%s"; continuing anyway...' % remotePath
return filelist
def isDir(self, remotePath):
data = self.sendCMD(['isdir ' + remotePath])
retVal = self.stripPrompt(data).strip()
if not retVal:
raise FileError('isdir returned null')
return retVal == 'TRUE'
# true/false check if the two files have the same md5 sum
def validateFile(self, remoteFile, localFile):
remoteHash = self.getRemoteHash(remoteFile)
localHash = self.getLocalHash(localFile)
if (remoteHash == localHash):
- return True
+ return True
return False
# return the md5 sum of a remote file
def getRemoteHash(self, filename):
- data = self.sendCMD(['hash ' + filename])
- if (data == None):
- return ''
- retVal = self.stripPrompt(data)
- if (retVal != None):
- retVal = retVal.strip('\n')
- if (self.debug >= 3): print "remote hash returned: '" + retVal + "'"
- return retVal
+ data = self.sendCMD(['hash ' + filename])
+ if (data == None):
+ return ''
+ retVal = self.stripPrompt(data)
+ if (retVal != None):
+ retVal = retVal.strip('\n')
+ if (self.debug >= 3): print "remote hash returned: '" + retVal + "'"
+ return retVal
# return the md5 sum of a file on the host
def getLocalHash(self, filename):
- file = open(filename, 'rb')
- if (file == None):
- return None
+ file = open(filename, 'rb')
+ if (file == None):
+ return None
- try:
- mdsum = hashlib.md5()
- except:
- return None
+ try:
+ mdsum = hashlib.md5()
+ except:
+ return None
- while 1:
- data = file.read(1024)
- if not data:
- break
- mdsum.update(data)
+ while 1:
+ data = file.read(1024)
+ if not data:
+ break
+ mdsum.update(data)
- file.close()
- hexval = mdsum.hexdigest()
- if (self.debug >= 3): print "local hash returned: '" + hexval + "'"
- return hexval
+ file.close()
+ hexval = mdsum.hexdigest()
+ if (self.debug >= 3): print "local hash returned: '" + hexval + "'"
+ return hexval
# Gets the device root for the testing area on the device
# For all devices we will use / type slashes and depend on the device-agent
# to sort those out. The agent will return us the device location where we
# should store things, we will then create our /tests structure relative to
# that returned path.
# Structure on the device is as follows:
# /tests
@@ -872,28 +898,30 @@ class DeviceManager:
# Then we pass '' for processName
cmd += "'' " + appBundlePath
else:
cmd += processName + ' ' + appBundlePath
if (destPath):
cmd += " " + destPath
+ if (self.debug > 3): print "updateApp using command: " + str(cmd)
+
if (ipAddr is not None):
ip, port = self.getCallbackIpAndPort(ipAddr, port)
cmd += " %s %s" % (ip, port)
- if (self.debug > 3): print "updateApp using command: " + str(cmd)
-
# Set up our callback server
callbacksvr = callbackServer(ip, port, self.debug)
data = self.sendCMD([cmd])
status = callbacksvr.disconnect()
if (self.debug > 3): print "got status back: " + str(status)
+ else:
+ status = self.sendCMD([cmd])
return status
"""
return the current time on the device
"""
def getCurrentTime(self):
data = self.sendCMD(['clok'])