Files
James Gross 9534f16495 Make function graph drawing not take ten thousand years (#636)
* wip

* remove

* fixes

* more

* Fix

---------

Co-authored-by: atlas0fd00m <atlas@r4780y.com>
2024-02-15 14:40:12 -05:00

969 lines
31 KiB
Python

"""
The vivisect CLI.
"""
import re
import sys
import shlex
import pprint
import socket
import logging
import traceback
import collections
from getopt import getopt
import vtrace
import vivisect
import vivisect.vamp as viv_vamp
import vivisect.vector as viv_vector
import vivisect.reports as viv_reports
import vivisect.tools.graphutil as viv_graph
import vivisect.tools.fscope as v_t_fscope
import vivisect.tools.graphutil as v_t_graph
import visgraph.pathcore as vg_path
import vtrace.lockstep as vt_lockstep
import vdb
import envi.exc as e_exc
import envi.cli as e_cli
import envi.common as e_common
import envi.memory as e_memory
import envi.expression as e_expr
import envi.memcanvas as e_canvas
import envi.memcanvas.renderers as e_render
from vivisect.const import *
logger = logging.getLogger(__name__)
class VivCli(e_cli.EnviCli, vivisect.VivWorkspace):
'''
A class that builds upon the VivWorkspace to provide command line capabilities so that
things like the Vivisect UI can provide a cleaner interface that just a direct python
shell. It inherits the same parameters as the VivWorkspace (autosave and confdir currently)
To add a new command, simply add a new function called `do_<cmdname>`.
'''
def __init__(self, **kwargs):
e_cli.EnviCli.__init__(self, self, symobj=self)
vivisect.VivWorkspace.__init__(self, **kwargs)
self.canvas.addRenderer("bytes", e_render.ByteRend())
self.canvas.addRenderer("u_int_16", e_render.ShortRend())
self.canvas.addRenderer("u_int_32", e_render.LongRend())
self.canvas.addRenderer("u_int_64", e_render.QuadRend())
import vivisect.renderers as viv_rend
self.canvas.addRenderer("viv", viv_rend.WorkspaceRenderer(self))
self.prompt = "viv> "
self.addScriptPathEnvVar('VIV_SCRIPT_PATH')
def getExpressionLocals(self):
locs = e_cli.EnviCli.getExpressionLocals(self)
locs['vw'] = self
locs['vprint'] = self.vprint
locs['vivisect'] = vivisect
return locs
def do_report(self, line):
"""
Fire a report module by python path name.
Usage: report <python.path.to.report.module>
"""
if not line:
self.vprint("Report Modules")
for descr, modname in viv_reports.listReportModules():
self.vprint("Path: %32s (Name: %s)" % (modname, descr))
return
cols, results = viv_reports.runReportModule(self, line)
for va, row in results.items():
self.canvas.addVaText("%s:\n" % self.getName(va), va)
for indx in range(len(cols)):
valu = row[indx]
name, typename = cols[indx]
self.canvas.addVaText(" " + name, va)
self.canvas.addText(": %s\n" % valu)
self.canvas.addText("\n")
def do_pathcount(self, line):
'''
Mostly for testing the graph stuff... this will likely be removed.
(does not count paths with loops currently...)
Usage: pathcount <func_expr>
'''
if not line:
return self.do_help('pathcount')
try:
fva = self.parseExpression(line)
if not self.isFunction(fva):
self.vprint('Not a function!')
return
except Exception as e:
self.vprint(str(e))
return
g = v_t_graph.buildFunctionGraph(self, fva)
pathcnt = 0
for path in v_t_graph.getCodePaths(g):
self.vprint('Path through 0x%.8x: %s' % (fva, [hex(p[0]) for p in path]))
pathcnt += 1
self.vprint('Total Paths: %d' % pathcnt)
def do_symboliks(self, line):
'''
Use the new symboliks subsystem. (NOTE: i386 only for a bit...)
Usage: symboliks [ options ]
-A Run the emu and show the state of the machine for all found paths
to the given address
'''
if not line:
return self.do_help("symboliks")
watchaddr = None
argv = e_cli.splitargs(line)
try:
opts, argv = getopt(argv, 'A:')
except Exception:
return self.do_help('symboliks')
for opt, optarg in opts:
if opt == '-A':
# TODO: USE THIS
watchaddr = self.parseExpression(optarg)
va = self.parseExpression(argv[0])
fva = self.getFunction(va)
import vivisect.symboliks as viv_symboliks
import vivisect.symboliks.common as sym_common
import vivisect.symboliks.effects as viv_sym_effects
import vivisect.symboliks.analysis as vsym_analysis
symctx = vsym_analysis.getSymbolikAnalysisContext(self)
for emu, effects in symctx.getSymbolikPaths(fva):
self.vprint('PATH %s' % ('='*60))
for eff in effects:
eff.reduce(emu)
if eff.efftype in (EFFTYPE_CONSTRAIN, EFFTYPE_CALLFUNC):
self.vprint(str(eff))
for addrsym, valsym in emu._sym_mem.values():
addrsym = addrsym.reduce(emu=emu)
valsym = valsym.reduce(emu=emu)
if emu.isLocalMemory(addrsym):
continue
self.vprint('[ %s ] = %s' % (addrsym, valsym))
self.vprint('RETURN %r' % emu.getFunctionReturn().reduce())
def do_names(self, line):
'''
Show any names which contain the given argument.
Usage: names <name_regex>
FIXME unify do_sym from vdb into symbol context!
'''
if not line:
return self.do_help('names')
regex = re.compile(line, re.I)
for va, name in self.getNames():
if regex.search(name):
ftup = self.getFileAndOffset(va)
if ftup:
fname, fbase, off = ftup
self.vprint('0x%.8x: %s (%r + 0x%x)' % (va, name, fname, off))
else:
self.vprint("WARNING: 0x%x: %r doesn't exist in a file!" % (va, name))
def do_save(self, line):
"""
Save the current workspace.
Usage: save
"""
self.vprint("Saving workspace...")
self.saveWorkspace()
self.vprint("...save complete!")
def do_xrefs(self, line):
"""
Show xrefs for a particular location.
Usage: xrefs [options] <va_expr>
-T Show xrefs *to* the given address
-F Show xrefs *from* the given address (default)
"""
parser = e_cli.VOptionParser()
parser.add_option('-T', action='store_true', dest='xrto')
parser.add_option('-F', action='store_true', dest='xrfrom')
argv = shlex.split(line)
try:
options, argv = parser.parse_args(argv)
except Exception as e:
self.vprint(repr(e))
return self.do_help('xrefs')
if len(argv) < 1:
self.vprint('Supply a va_expr')
return self.do_help('xrefs')
va = self.parseExpression(argv[0])
fptr = []
if options.xrto:
fptr.append(self.getXrefsTo)
if options.xrfrom:
fptr.append(self.getXrefsFrom)
for func in fptr:
for xrfr, xrto, rtype, rflags in func(va):
tname = ref_type_names.get(rtype, 'Unknown')
self.vprint('\tFrom: 0x%.8x, To: 0x%.8x, Type: %s, Flags: 0x%.8x' % (xrfr, xrto, tname, rflags))
def do_searchopcodes(self, line):
'''
search opcodes/function for a pattern
searchopcodes [-f <funcva>] [options] <pattern>
-f [fva] - focus on one function
-c - search comments
-o - search operands
-t - search text
-M <color> - mark opcodes (default = orange)
-R - pattern is REGEX (otherwise just text)
'''
parser = e_cli.VOptionParser()
parser.add_option('-f', action='store', dest='funcva', type='int')
parser.add_option('-c', action='store_true', dest='searchComments')
parser.add_option('-o', action='store_true', dest='searchOperands')
parser.add_option('-t', action='store_true', dest='searchText')
parser.add_option('-M', action='store', dest='markColor', default='orange')
parser.add_option('-R', action='store_true', dest='is_regex')
argv = shlex.split(line)
try:
options, args = parser.parse_args(argv)
except Exception as e:
self.vprint(repr(e))
return self.do_help('searchopcodes')
pattern = ' '.join(args)
if len(pattern) == 0:
self.vprint('you must specify a pattern')
return self.do_help('searchopcodes')
# generate our interesting va list
valist = []
if options.funcva:
# setup valist from function data
try:
fva = options.funcva
graph = viv_graph.buildFunctionGraph(self, fva)
except Exception as e:
self.vprint(repr(e))
return
for nva, node in graph.getNodes():
va = nva
endva = nva + node.get('cbsize')
while va < endva:
lva, lsz, ltype, ltinfo = self.getLocation(va)
valist.append(va)
va += lsz
else:
# the whole workspace is our oyster
valist = [va for va, lvsz, ltype, ltinfo in self.getLocations(LOC_OP)]
res = []
canv = e_canvas.StringMemoryCanvas(self)
defaultSearchAll = True
for va in valist:
try:
addthis = False
op = self.parseOpcode(va)
# search comment
if options.searchComments:
defaultSearchAll = False
cmt = self.getComment(va)
if cmt is not None:
if options.is_regex:
if len(re.findall(pattern, cmt)):
addthis = True
else:
if pattern in cmt:
addthis = True
# search operands
if options.searchOperands:
defaultSearchAll = False
for opidx, oper in enumerate(op.opers):
# we're writing to a temp canvas, so clear it before each test
canv.clearCanvas()
oper = op.opers[opidx]
oper.render(canv, op, opidx)
operepr = canv.strval
if options.is_regex:
if len(re.findall(pattern, operepr)):
addthis = True
else:
if pattern in operepr:
addthis = True
# if we're doing non-regex, let's test against real numbers
# (instead of converting to hex and back)
numpattrn = pattern
try:
numpattrn = int(numpattrn, 0)
except:
pass
if numpattrn in vars(oper).values():
addthis = True
# search full text
if options.searchText or defaultSearchAll:
# search through the rendering of the opcode, as well as the comment
canv.clearCanvas()
op.render(canv)
oprepr = canv.strval
cmt = self.getComment(va)
if cmt is not None:
oprepr += " ; " + cmt
if options.is_regex:
if len(re.findall(pattern, oprepr)):
addthis = True
else:
if pattern in oprepr:
addthis = True
# only want one listing of each va, no matter how many times it matches
if addthis:
res.append(va)
except:
self.vprint(''.join(traceback.format_exception(*sys.exc_info())))
if len(res) == 0:
self.vprint('pattern not found: %s (%s)' % (pattern.encode('utf-8').hex(), repr(pattern)))
return
# set the color for each finding
color = options.markColor
colormap = {va: color for va in res}
if self._viv_gui is not None:
from vqt.main import vqtevent
vqtevent('viv:colormap', colormap)
self.vprint('matches for: %s (%s)' % (pattern.encode('utf-8').hex(), repr(pattern)))
for va in res:
mbase, msize, mperm, mfile = self.memobj.getMemoryMap(va)
pname = e_memory.reprPerms(mperm)
sname = self.reprPointer(va)
try:
op = self.parseOpcode(va)
self.canvas.renderMemory(va, len(op))
cmt = self.getComment(va)
if cmt is not None:
self.canvas.addText('\t\t; %s (Perms: %s, Smartname: %s)' % (cmt, pname, sname))
self.canvas.addText('\n')
except e_exc.SegmentationViolation as e:
logger.debug("segv at 0x%x", va)
self.vprint('done (%d results).' % len(res))
def do_imports(self, line):
"""
Show the imports in the workspace (or potentially just one file)
Usage: imports [fname]
"""
self.canvas.addText("Imports:\n")
for va, size, ltype, tinfo in self.getImports():
# FIXME warn them...
# (but should we though?)
if not tinfo.startswith(line):
continue
vastr = self.arch.pointerString(va)
self.canvas.addVaText(vastr, va)
self.canvas.addText(" ")
self.canvas.addNameText(tinfo, tinfo)
self.canvas.addText("\n")
def do_fscope(self, line):
'''
The fscope command can be used to enumerate things from the
scope of one function and down it's calling graph.
Usage: fscope [options] <func_addr_expr>
-I - Show import calls from this function scope
-S - Show strings from this function scope
Example: fscope -I kernel32.CreateFileW
(Show imports called by CreateFileW and down...)
'''
showimp = False
showstr = False
argv = e_cli.splitargs(line)
try:
opts, args = getopt(argv, 'IS')
except Exception:
return self.do_help('fscope')
if not len(args) or not len(opts):
return self.do_help('fscope')
for opt, optarg in opts:
if opt == '-I':
showimp = True
elif opt == '-S':
showstr = True
for expr in args:
va = self.parseExpression(expr)
if showimp:
for callva, impname in v_t_fscope.getImportCalls(self, va):
pstr = self.arch.pointerString(callva)
self.canvas.addVaText(pstr, callva)
# FIXME best name symbol etc?
self.canvas.addText(' %s\n' % impname)
if showstr:
for refva, strva, strbytes in v_t_fscope.getStringRefs(self, va):
pstr = self.arch.pointerString(refva)
self.canvas.addVaText(pstr, refva)
self.canvas.addText(' ')
self.canvas.addVaText(strbytes, strva)
self.canvas.addText('\n')
def do_exports(self, line):
"""
List the exports in the workspace (or in a specific file).
Usage: exports [fname]
"""
edict = collections.defaultdict(list)
for va, etype, name, filename in self.getExports():
edict[filename].append((name, va))
if line:
x = edict.get(line)
if x is None:
self.vprint("Unknown fname: %s" % line)
return
edict = {line: x}
fnames = list(edict.keys())
fnames.sort()
for fname in fnames:
self.canvas.addNameText(fname, fname)
self.canvas.addText(":\n")
exports = edict.get(fname)
exports.sort()
for ename, eva in exports:
pstr = self.arch.pointerString(eva)
self.canvas.addText(" ")
self.canvas.addVaText(pstr, eva)
self.canvas.addText(" ")
self.canvas.addNameText(ename, ename)
self.canvas.addText("\n")
def do_filemeta(self, line):
'''
Show/List file metadata.
Usage: filemeta [ fname [ keyname ] ]
Example: filemeta kernel32
Example: filemeta kernel32 md5
'''
argv = e_cli.splitargs(line)
if len(argv) == 0:
self.vprint('Loaded Files:')
for fname in self.getFiles():
self.vprint(' %s' % fname)
elif len(argv) == 1:
d = self.getFileMetaDict(argv[0])
self.vprint(pprint.pformat(d))
elif len(argv) == 2:
val = self.getFileMeta(argv[0], argv[1])
self.vprint('%s (%s):' % (argv[1], argv[0]))
self.vprint(pprint.pformat(val))
else:
self.do_help('filemeta')
def do_funcmeta(self, line):
"""
Show/Set function metadata.
Usage: funcmeta <func_expr> [key <value_expr>]
"""
# FIXME make a search thing here!
argv = e_cli.splitargs(line)
if len(argv) == 0:
return self.do_help("funcmeta")
if len(argv) == 1:
va = self.parseExpression(argv[0])
meta = self.getFunctionMetaDict(va)
self.vprint(pprint.pformat(meta))
elif len(argv) == 3:
va = self.parseExpression(argv[0])
name = argv[1]
locs = self.getExpressionLocals()
val = e_expr.evaluate(argv[2], locs)
self.setFunctionMeta(va, name, val)
def do_loc(self, line):
"""
Display the repr of a single location by va.
Usage: loc <va_expr>
"""
if not line:
return self.do_help("loc")
addr = self.parseExpression(line)
loc = self.getLocation(addr)
if loc is None:
s = self.arch.pointerString(addr)
self.vprint("Unknown location: %s" % s)
r = self.reprLocation(loc)
self.vprint(r)
def do_make(self, line):
"""
Create new instances of locations in the vivisect workspace.
Usage: make [options] <va_expr>
-c Make code
-f Make function
-s Make a string
-u Make a unicode string
-n <size> Make a number
-p <size> Make a pad
-S <structname> Make a structure
"""
argv = e_cli.splitargs(line)
try:
opts, args = getopt(argv, "csufn:p:S:")
except Exception as e:
logger.warning(str(e))
return self.do_help("make")
if len(args) != 1 or len(opts) != 1:
return self.do_help("make")
addr = self.parseExpression(args[0])
opt, optarg = opts[0]
if opt == "-f":
logger.debug('new function (manual-cli): 0x%x', addr)
self.makeFunction(addr)
elif opt == "-c":
self.makeCode(addr)
elif opt == "-s":
self.makeString(addr)
elif opt == "-u":
self.makeUnicode(addr)
elif opt == "-n":
size = self.parseExpression(optarg)
self.makeNumber(addr, size)
elif opt == "-p":
size = self.parseExpression(optarg)
self.makePad(addr, size)
elif opt == "-S":
self.makeStructure(addr, optarg)
else:
return self.do_help("make")
def do_emulate(self, line):
"""
Create an emulator for the given function, and drop into a vdb
interface to step through the code.
(vdb CLI will appear in controlling terminal...)
Usage: emulate <va_expr>
"""
if not line:
return self.do_help("emulate")
addr = self.parseExpression(line)
emu = self.getEmulator(va=addr)
trace = vt_lockstep.TraceEmulator(emu)
db = vdb.Vdb(trace=trace)
db.cmdloop()
def _do_argtrack(self, line):
#FIXME: this is currently broken and needs to be revamped to use
# apiCall and VivTaints
"""
Track input arguments to the given function by name or address.
(this is currently broken. sorry!)
Usage: argtrack <func_addr_expr> <arg_idx>
"""
if not line:
return self.do_help("argtrack")
argv = e_cli.splitargs(line)
if len(argv) != 2:
return self.do_help("argtrack")
try:
fva = self.parseExpression(argv[0])
except Exception:
self.vprint("Invalid Address Expression: %s" % argv[0])
return
try:
idx = self.parseExpression(argv[1])
except Exception:
self.vprint("Invalid Index Expression: %s" % argv[1])
return
if self.getFunction(fva) != fva:
self.vprint("Invalid Function Address: (0x%.8x) %s" % (fva, line))
for pleaf in viv_vector.trackArgOrigin(self, fva, idx):
self.vprint('='*80)
path = vg_path.getPathToNode(pleaf)
path.reverse()
for pnode in path:
fva = vg_path.getNodeProp(pnode, 'fva')
argv = vg_path.getNodeProp(pnode, 'argv')
callva = vg_path.getNodeProp(pnode, 'cva')
argidx = vg_path.getNodeProp(pnode, 'argidx')
if callva is not None:
aval, amagic = argv[argidx]
arepr = '0x%.8x' % aval
if amagic is not None:
arepr = repr(amagic)
frepr = 'UNKNOWN'
if fva is not None:
frepr = '0x%.8x' % fva
self.vprint('func: %s calls at: 0x%.8x with his own: %s' % (frepr, callva, arepr))
self.vprint("="*80)
def do_chat(self, line):
"""
Echo a message to any other users of a shared workspace.
Usage: chat oh hai! Checkout 0x7c778030
"""
if len(line) == 0:
return self.do_help('chat')
self.chat(line)
def do_codepath(self, line):
"""
Enumerate and show any known code paths from the specified
from address expression to the to address expression.
Usage: codepath <from_expr> <to_expr>
"""
if not line:
return self.do_help("codepath")
argv = e_cli.splitargs(line)
if len(argv) != 2:
return self.do_help("codepath")
try:
frva = self.parseExpression(argv[0])
except Exception:
self.vprint("Invalid From Va: %s" % argv[0])
return
try:
tova = self.parseExpression(argv[1])
except Exception:
self.vprint("Invalid To Va: %s" % argv[1])
return
self.vprint("Tracking Paths From 0x%.8x to 0x%.8x" % (frva, tova))
paths = viv_vector.getCodePaths(self, frva, tova)
self.vprint("Function VA\tBlock VA\tSize\tFunction Name")
count = 0
for blist in paths:
count += 1
self.vprint("="*30)
for bva, bsize, fva in blist:
fname = self.getName(fva)
self.vprint("0x%.8x\t0x%.8x\t%4d\t%s" % (fva, bva, bsize, fname))
if count == 0:
self.vprint("None!")
return
def do_vampsig(self, line):
"""
Generate a vamp signature string for the given function's first block.
"""
if not line:
return self.do_help("vampsig")
va = self.parseExpression(line)
fva = self.getFunction(va)
if fva is None:
self.vprint("Invalid Function Address: 0x%.8x (%s)" % (va, line))
sig, mask = viv_vamp.genSigAndMask(self, fva)
self.vprint("SIGNATURE: %s" % e_common.hexify(sig))
self.vprint("MASK: %s" % e_common.hexify(mask))
def do_vdb(self, line):
'''
Execute vdb GUI from within vivisect (allowing special hooks between them...)
(Optionally, specify a host to use for remote vdb debugging)
Usage: vdb [<remote_host>]
'''
if line:
try:
socket.gethostbyname(line)
except Exception:
self.vprint('Invalid Remote Host: %s' % line)
vtrace.remote = line
import vivisect.vdbext as viv_vdbext
viv_vdbext.runVdb(self._viv_gui)
def do_switch(self, line):
'''
Wire up a switch case.
Usage: switch <jmp_va> <count> [offset]
where:
jmp_va - the va of the "jmp reg" instruction
count - number of switch case indices are covered by this switch case jmp
offset - first switch case index.
eg: switch case handles i == 32 thru 47, count = 16, offset = 32
'''
if not line:
return self.do_help("switch")
argv = e_cli.splitargs(line)
if len(argv) < 2:
return self.do_help("switch")
offset = 0
try:
if argv[0].startswith("0x"):
jmpva = int(argv[0], 16)
else:
jmpva = int(argv[0])
if argv[1].startswith("0x"):
count = int(argv[1], 16)
else:
count = int(argv[1])
if len(argv) == 3:
if argv[2].startswith("0x"):
offset = int(argv[2], 16)
else:
offset = int(argv[2])
import vivisect.analysis.generic.symswitchcase as vagss
import vivisect.analysis.generic.codeblocks as vagc
sc = vagss.SwitchCase(self, jmpva)
sc.makeSwitch(count, offset)
funcva = self.getFunction(jmpva)
vagc.analyzeFunction(self, funcva)
except ValueError as e:
self.vprint("exception: %r" % e)
return self.do_help("switch")
def do_m_switch(self, line):
'''
Wire up a switch-case based on an array of pointers
Usage: m_switch <jmp_va> <array_va> <count> [baseva] [offset] [size]
where:
jmp_va - the va of the "jmp reg" instruction
array_va - the location of the first pointer in the array
count - number of switch case indices are covered by this switch case jmp
baseva - add the offset to this address (eg. _GOT_)
offset - first switch case index.
[size] - size (default: pointer size)
eg: switch case handles i == 32 thru 47, count = 16, offset = 32
'''
if not line:
return self.do_help("m_switch")
argv = e_cli.splitargs(line)
if len(argv) < 3:
return self.do_help("m_switch")
size = 0
baseva = 0
offset = 0
try:
jmpva = self.parseExpression(argv[0])
array = self.parseExpression(argv[1])
count = self.parseExpression(argv[2])
if len(argv) > 3:
baseva = self.parseExpression(argv[3])
if len(argv) > 4:
offset = self.parseExpression(argv[4])
if len(argv) > 5:
size = self.parseExpression(argv[5])
# link up the switchcase using data provided
import vivisect.analysis.generic.symswitchcase as vagss
import vivisect.analysis.generic.codeblocks as vagc
vagss.link_up(self, jmpva, array, count, offset, baseva, size)
# reanalyze codeblocks for the function (since it should have changed)
funcva = self.getFunction(jmpva)
if funcva is not None:
vagc.analyzeFunction(self, funcva)
except ValueError as e:
self.vprint("exception: %r" % e)
return self.do_help("m_switch")
def do_plt(self, line):
'''
Parse an entire PLT Section
Usage: plt <pltva> <pltsize>
'''
if not line:
return self.do_help("plt")
argv = e_cli.splitargs(line)
if len(argv) != 2:
return self.do_help("plt")
sva = self.parseExpression(argv[0])
ssize = self.parseExpression(argv[1])
import vivisect.analysis.elf.elfplt as vaee
vaee.analyzePLT(self, sva, ssize)
def do_plt_function(self, line):
'''
Make a PLT function at a virtual address
Usage: plt_function <va>
'''
if not line:
return self.do_help("plt_function")
fva = self.parseExpression(line)
import vivisect.analysis.elf.elfplt as vaee
vaee.analyzeFunction(self, fva)
def do_leaders(self, line):
'''
Manage Leader Sessions. This is useful if sessions are left hanging
if a leader disconnects.
Usage: leaders (list,kill,killall)
list - show leader sessions
kill <uuidhex> - kill a particular session
killall - kill all leader sessions for a given binary
mod <uuid> <user> <sessionname> - rename a leader session/user
Example:
leaders kill 197a7797ba1511ecbe41091ba860c051
'''
if not line:
return self.do_help("leaders")
argv = e_cli.splitargs(line)
try:
if argv[0] == 'list':
self.vprint("Current Leader Sessions:")
for uuid, (user, fname) in self.getLeaderSessions().items():
curva = self.getLeaderLoc(uuid)
self.vprint(" * %s (user: %s uuid: %s)" % (fname, user, uuid))
elif argv[0] == 'mod':
uuid = argv[1]
user = argv[2]
if len(argv) > 4:
sessionname = ' '.join(argv[3:])
else:
sessionname = argv[3]
self.modifyLeaderSession(uuid, user, sessionname)
elif argv[0] == 'kill':
if len(argv) < 2:
self.vprint('kill requires a valid <uuid> argument')
uuid = argv[1]
self.killLeaderSession(uuid)
elif argv[0] == 'killall':
for uuid, (user, fname) in self.getLeaderSessions().items():
self.vprint("*killing %r (%r, %r)" % (uuid, user, fname))
self.killLeaderSession(uuid)
except Exception as e:
self.vprint("Error: %r" % e)