blob: c4e097284606d0d437a29c6dd21401fe3188413b [file] [log] [blame]
#!/usr/bin/python
# Copyright 2012 Google Inc. All Rights Reserved.
#
"""A command-line tool for uploading to gfiber-dropbox.appspot.com."""
__author__ = 'apenwarr@google.com (Avery Pennarun)'
import errno
import os
import re
import subprocess
import sys
import options
optspec = """
upload-crash-log [options...]
--
s,server= The server URL (default: whatever upload-logs uses)
a,all Upload the entire log, not just the most recent part
l,logtype= The logtype to send to the server
stdout Print to stdout instead of calling upload-logs
"""
MARK_START = '*LOG_UPLOAD_START*'
MARK_END = '*LOG_UPLOAD_END*'
class SubprocError(Exception):
pass
def ReadPipe(argv, **kwargs):
p = subprocess.Popen(argv, stdout=subprocess.PIPE, **kwargs)
data = p.stdout.read()
retval = p.wait()
if retval:
raise SubprocError('%r returned exit code %d' % (argv, retval))
return data
def TryReadPipe(argv, **kwargs):
try:
return ReadPipe(argv, **kwargs)
except SubprocError:
return ''
except OSError, e:
if e.errno == errno.ENOENT:
return ''
else:
raise
def ReFind(regex, key, data):
matches = re.findall(regex, data)
return ['%s=%s' % (key, i) for i in matches]
def IfconfigData(ifcname):
"""Returns a list of key/value pairs for the given network interface."""
data = TryReadPipe(['ip', 'addr', 'show', ifcname])
out = []
out += ReFind(r'inet ([\d.]+/\d+)', 'ip', data)
out += ReFind(r'inet6 ([\da-fA-F:/.]+/\d+)', 'ip6', data)
out += ReFind(r'link/ether ([\da-fA-F:]+)', 'hw', data)
return out
def NvramData():
"""Returns a list of key/value pairs based on NVRAM contents."""
return ['serial=' + TryReadPipe(['/bin/serial']).rstrip()]
def ModelName():
try:
model = open('/etc/platform').read().rstrip()
return ['model=' + model]
except IOError:
return None
def Marker(s):
"""Print the given marker string to the dmesg log."""
open('/dev/kmsg', 'w').write('<7>%s\n' % s)
def UploadLog(server, filename, content, keys):
"""Upload a log file to the given server by calling upload-logs."""
extra_args = []
if server:
extra_args += ['--server', server]
p = subprocess.Popen(['upload-logs', '--stdin', filename] +
extra_args +
['-k%s' % i for i in keys],
stdin=subprocess.PIPE)
p.stdin.write(content)
p.stdin.close()
retval = p.wait()
if retval:
raise Exception('upload-logs returned exit code %d' % retval)
def FirstStamp(s):
regex = re.compile(r'^(<[0-9]+>)?\[\s*([0-9.]+\.[0-9]+)\s*\]', re.M)
g = re.search(regex, s)
if g:
return float(g.group(2))
return None
def main():
o = options.Options(optspec)
(opt, flags, extra) = o.parse(sys.argv[1:]) # pylint: disable-msg=W0612
if extra:
o.fatal('no filenames expected')
subprocess.call('logmark-once')
Marker(MARK_START)
# In case the system crashes after we finish uploading but before the next
# time this script runs, we want to make sure there's at least one logmark
# in the *next* segment as well. Otherwise the server won't be able to
# feel certain about what version that segment came from. So add it again
# now.
subprocess.call('logmark-once')
p = subprocess.Popen(['toolbox', 'dmesg'], stdout=subprocess.PIPE)
dmesg = p.stdout.read()
retval = p.wait()
if retval:
raise Exception('dmesg returned exit code %d' % retval)
interfaces = set(['br0', 'eth0', 'man', 'pon0'])
interfaces = interfaces.intersection(os.listdir('/sys/class/net'))
keys = []
for iface in interfaces:
keys += IfconfigData(iface)
model = ModelName()
if model:
keys += model
if opt.logtype:
keys.append('logtype=' + opt.logtype)
keys = set(keys + NvramData())
os.environ['PATH'] += ':.' # make sure PWD is searched for upload-logs
if not opt.all:
# we only want the text from after the last MARK_START that came *before*
# the last MARK_END. If we tried uploading previously but failed, it
# will have printed a MARK_START but not a MARK_END, and we have to ignore
# that one and retry. However, we can't just use MARK_END, because
# messages might have been printed after retrieving the dmesg text
# but before printing MARK_END.
#
# And we don't want anything that came after the final MARK_START, because
# that will be uploaded next time, and we don't want duplicate data.
start = dmesg.rfind(MARK_END)
if start < 0: start = 0
start = dmesg.rfind(MARK_START, 0, start)
if start < 0: start = 0
end = dmesg.rfind(MARK_START)
if end < 0:
end = len(dmesg)
dmesg = dmesg[start:end]
if start == 0 and FirstStamp(dmesg) != 0.0:
dmesg = ('\n\nWARNING: log buffer overflow. '
'Some messages were lost at this position.\n\n' +
dmesg)
if opt.stdout:
print dmesg
else:
UploadLog(opt.server, 'dmesg', dmesg, keys)
Marker(MARK_END)
if __name__ == '__main__':
main()