def IOStatEngine(sharedTupleQueue,entityId): tmpFil = lib_common.TmpFile("IOStat","log") filNam = tmpFil.Name fil = open(filNam,"w") # TODO: The delay could be a parameter. iostat_cmd = "iostat -d 1" fil.write( "iostat_cmd=%s\n" % ( iostat_cmd ) ) fil.flush() cnt = 0 for lin in os.popen(iostat_cmd): sys.stderr.write("cnt=%d:%s\n" % ( cnt, lin ) ) if lin: # We transfer also the header. spl = re.split(' +',lin) sharedTupleQueue.put( tuple( spl ) ) if cnt % 100 == 0: fil.write("cnt=%d:%s" % ( cnt, lin ) ) fil.flush() cnt += 1 fil.write( "Leaving after %d iterations\n" % ( cnt ) ) fil.close() return "Iostat execution end"
def Main(): paramkeyMaxDepth = "Maximum depth" paramkeyDispPackages = "Display packages" paramkeyDispFiles = "Display files" cgiEnv = lib_common.CgiEnv({ paramkeyMaxDepth: 1, paramkeyDispPackages: True, paramkeyDispFiles: False }) packageNam = cgiEnv.GetId() maxDepth = cgiEnv.GetParameters(paramkeyMaxDepth) dispPackages = cgiEnv.GetParameters(paramkeyDispPackages) dispFiles = cgiEnv.GetParameters(paramkeyDispFiles) packageNode = survol_python_package.MakeUri(packageNam) sys.stderr.write("packageNam=%s\n" % packageNam) grph = cgiEnv.GetGraph() tmpPyFil = lib_common.TmpFile("py_package_deps", "py") tmpPyFilName = tmpPyFil.Name # This creates a temporary file which imports the package. tmpFd = open(tmpPyFilName, "w") tmpFd.write("import %s\n" % packageNam) tmpFd.close() survol_python_package.AddImportedModules(grph, packageNode, tmpPyFilName, maxDepth, dispPackages, dispFiles) try: the_module = importlib.import_module(packageNam) except: exc = sys.exc_info()[0] lib_common.ErrorMessageHtml("Package:%s Unexpected error:%s" % (packageNam, str(exc))) try: initFilNam = the_module.__file__ filNode = lib_common.gUriGen.FileUri(initFilNam) grph.add( (packageNode, survol_python_package.propPythonPackage, filNode)) try: survol_python.AddAssociatedFiles(grph, filNode, initFilNam) except: exc = sys.exc_info()[0] lib_common.ErrorMessageHtml("File:%s Unexpected error:%s" % (initFilNam, str(exc))) except AttributeError: pass cgiEnv.OutCgiRdf("LAYOUT_SPLINE")
def IconToFile(hlib,group_name): DEBUG("IconToFile group_name=%s",str(group_name)) libc = ctypes.CDLL(ctypes.util.find_library('c')) libc.memcpy.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t] libc.memcpy.restype = ctypes.c_char_p # patch FindResourceW, ctypes.windll.kernel32.SizeofResource FindResourceW = ctypes.windll.kernel32.FindResourceW FindResourceW.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] FindResourceW.restype = ctypes.c_void_p SizeofResource = ctypes.windll.kernel32.SizeofResource SizeofResource.argtypes = [ctypes.c_void_p, ctypes.c_void_p] SizeofResource.restype = ctypes.c_size_t hRes = win32api.LoadResource(hlib, win32con.RT_GROUP_ICON, group_name) mem_icon_dir = ctypes.windll.kernel32.LockResource(hRes) # 32 bits color; 16 and 256 colors are too old icon_size = 256 icon_name = ctypes.windll.user32.LookupIconIdFromDirectoryEx(mem_icon_dir, True, icon_size, icon_size, 0x00000000); DEBUG("icon_name=%s",str(icon_name)) hResInfo = FindResourceW(hlib, icon_name, win32con.RT_ICON) size = ctypes.windll.kernel32.SizeofResource(hlib, hResInfo) rec = win32api.LoadResource(hlib, win32con.RT_ICON, icon_name) mem_icon = ctypes.windll.kernel32.LockResource(rec) # And this is some differ (copy data to Python buffer) binary_data = (ctypes.c_ubyte * size)() libc.memcpy(binary_data, mem_icon, size) hIconRet = ctypes.windll.user32.CreateIconFromResourceEx(binary_data, size, True, 0x00030000, 0, 0, 0x00000000); info = win32gui.GetIconInfo(hIconRet) bminfo = win32gui.GetObject(info[4]) # generate bitmap by drawing the icon hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0)) hbmp = win32ui.CreateBitmap() hbmp.CreateCompatibleBitmap(hdc, bminfo.bmWidth, bminfo.bmHeight) hcdc = hdc.CreateCompatibleDC() hcdc.SelectObject(hbmp) win32gui.DrawIconEx(hcdc.GetHandleOutput(), 0, 0, hIconRet, bminfo.bmWidth, bminfo.bmHeight, 0, 0, 0x0003) # MIME type is "image/bmp" # The group name might be a number: 110 etc... or a string such as 'ICO_MYCOMPUTER'. # This is the prefix of the temporary BMP file name containing the extracted icon. imgFilNamPrefix = "icon-%03dx%03d-%s-%03d" % (bminfo.bmWidth, bminfo.bmHeight, str(group_name), icon_name) # The destructor will remove the file. objTempFile = lib_common.TmpFile(imgFilNamPrefix,"bmp") imgFilNam = objTempFile.Name DEBUG("Generating %s",imgFilNam) hbmp.SaveBitmapFile(hcdc, imgFilNam) win32gui.DestroyIcon(hIconRet) return objTempFile
def RunGdbCommand(the_pid, command): tmpGdb = lib_common.TmpFile("gdbstack", "gdb") gdbFilNam = tmpGdb.Name gdbFil = open(gdbFilNam, "w") gdbFil.write(command + "\n") gdbFil.write("quit\n") gdbFil.close() # TODO: See python/__init__.py which also runs a gdb command. gdb_cmd = ["gdb", "-q", "-p", str(the_pid), "-x", gdbFilNam] DEBUG("gdb command=%s", " ".join(gdb_cmd)) try: gdb_pipe = lib_common.SubProcPOpen(gdb_cmd) #except FileNotFoundError: # lib_common.ErrorMessageHtml("gdb is not available") except Exception: exc = sys.exc_info()[1] lib_common.ErrorMessageHtml("Gdb:" + str(exc)) # TODO: How can we get the stderr message: "ptrace: Operation not permitted." which comes after "Attaching to process 6063" ???? (gdb_last_output, gdb_err) = gdb_pipe.communicate() resu = [] # Removes the heading information and the lines beginning with a prompt. # [rchateau@DuoLnx BY_process]$ gdb -q -p 6513 -x stack.gdb # Attaching to process 6513 # Reading symbols from /usr/bin/kdeinit...(no debugging symbols found)...done. for lin in gdb_last_output.split(b'\n'): if lib_util.is_py3: # This return a bytes. lin = lin.decode("utf-8") DEBUG("rungdb:%s", lin) # Not sure the prompt is displayed when in non-interactive mode. if lin.startswith("(gdb)"): continue if lin.startswith("Reading symbols "): continue if lin.startswith("Loaded symbols "): continue resu.append(lin) if len(gdb_err) != 0: DEBUG("Err:%s", gdb_err) lib_common.ErrorMessageHtml("No gdb output:" + gdb_err) return resu
def DoxygenMain(paramRecursiveExploration,fileParam): tmpDirObj = lib_common.TmpFile(prefix=None,suffix=None,subdir="DoxygenXml") doxyOUTPUT_DIRECTORY = tmpDirObj.TmpDirToDel if paramRecursiveExploration: doxyRECURSIVE = "YES" else: doxyRECURSIVE = "NO" try: RunDoxy(doxyOUTPUT_DIRECTORY, fileParam, doxyRECURSIVE) except: exc = sys.exc_info()[1] lib_common.ErrorMessageHtml("Doxygen: %s\n"%( str(exc) )) doxyResultDir = doxyOUTPUT_DIRECTORY + "/xml" objectsByLocation = DoTheStuff(doxyResultDir) return objectsByLocation
def TcpDumpEngine(sharedTupleQueue, entityId): tmpFil = lib_common.TmpFile("TcpDump", "log") filNam = tmpFil.Name fil = open(filNam, "w") tcpdump_cmd = GetTcmpDumpCommand() fil.write("TCPcommand=%s\n" % (tcpdump_cmd)) fil.flush() cnt = 0 for lin in os.popen(tcpdump_cmd): sys.stderr.write("cnt=%d:%s\n" % (cnt, lin)) if lin: TcpDumpEnqueue(sharedTupleQueue, lin) if cnt % 100 == 0: fil.write("cnt=%d:%s" % (cnt, lin)) fil.flush() cnt += 1 fil.write("Leaving after %d iterations\n" % (cnt)) fil.close() return "Tcpdump execution end"
def RunDoxy(doxyOUTPUT_DIRECTORY, doxyINPUT, doxyRECURSIVE): doxyFILE_PATTERNS = " ".join( "*.%s" % filExt for filExt in fileExtensionsDox ) # TODO: Create a tmp dir just for this purpose. filCo = myDoxyfile % (doxyOUTPUT_DIRECTORY, doxyINPUT, doxyFILE_PATTERNS, doxyRECURSIVE) tmpDoxyfileObj = lib_common.TmpFile("Doxygen") doxynam = tmpDoxyfileObj.Name doxyfi = open(doxynam, "w") doxyfi.write(filCo) doxyfi.close() # tmpDoxygenFil = lib_common.TmpFile("Doxygen","xml") # doxygen_out_filnam = tmpDoxygenFil.Name # https://www.stack.nl/~dimitri/doxygen/manual/customize.html doxygen_command = ["doxygen", doxynam] # TODO: Use lib_common.SubProcPOpen ret = lib_common.SubProcCall(doxygen_command) DEBUG("doxyOUTPUT_DIRECTORY=%s", doxyOUTPUT_DIRECTORY)
def Main(): paramkeyGroupByDirs = "Group by directories" cgiEnv = lib_common.CgiEnv(parameters={paramkeyGroupByDirs: True}) flagGroupByDirs = bool(cgiEnv.get_parameters(paramkeyGroupByDirs)) win_module = cgiEnv.GetId() lib_win32.CheckWindowsModule(win_module) # This has to be in the path. Is it the 32 bits or 64 bits one ? depends_bin = "depends.exe" DEBUG("depends_bin=%s", depends_bin) tmpFilObj = lib_common.TmpFile("depends") tmpOutFil = tmpFilObj.Name args = [depends_bin, "/c", "/OC:", tmpOutFil, win_module] DEBUG("Depends command=%s", str(args)) grph = cgiEnv.GetGraph() nodeDLL = lib_common.gUriGen.FileUri(win_module) # TODO: Check the return value. # http://www.dependencywalker.com/help/html/hidr_command_line_help.htm p = lib_common.SubProcPOpen(args) (nmap_last_output, nmap_err) = p.communicate() for lin in nmap_last_output: continue # Wait for the end, otherwise the file will not be ready. try: DEBUG("Depends tmpOutFil=%s", tmpOutFil) input_file = open(tmpOutFil, 'r') except Exception: exc = sys.exc_info()[1] lib_common.ErrorMessageHtml("Caught " + str(exc) + " when processing:" + tmpOutFil) # Status,Module,File Time Stamp,Link Time Stamp,File Size,Attr.,Link Checksum,Real Checksum,CPU,Subsystem,Symbols,Preferred Base,Actual Base,Virtual Size,Load Order,File Ver,Product Ver,Image Ver,Linker Ver,OS Ver,Subsystem Ver # ?,"MSVCR80D.DLL","Error opening file. The system cannot find the file specified (2).",,,,,,,,,,,,,,,,,, # D?,"XLCALL32.DLL","Error opening file. The system cannot find the file specified (2).",,,,,,,,,,,,,,,,,, # E6,"c:\windows\system32\ADVAPI32.DLL",2012-10-18 21:27:04,2012-10-18 21:27:12,876544,A,0x000D9B98,0x000D9B98,x64,Console,"CV",0x000007FF7FF10000,Unknown,0x000DB000,Not Loaded,6.1.7601.22137,6.1.7601.22137,6.1,9.0,6.1,6.1 # E6,"c:\windows\system32\API-MS-WIN-CORE-CONSOLE-L1-1-0.DLL",2013-08-02 03:12:18,2013-08-02 03:12:52,3072,HA,0x000081B6,0x000081B6,x64,Console,"CV",0x0000000000400000,Unknown,0x00003000,Not Loaded,6.1.7601.18229,6.1.7601.18229,6.1,9.0,6.1,6.1 # Used only if libraries are grouped by directory. dirsToNodes = {} for lin in input_file: # TODO: Beware of commas in file names!!!!! Maybe module shlex ? linargs = lin.split(',') module = linargs[1] # The library filename is enclosed in double-quotes, that we must remove. modulNam = module[1:-1] libNode = lib_common.gUriGen.SharedLibUri(modulNam) # If the libraries are displayed in groups belnging to a dir, this is clearer. if flagGroupByDirs: dirNam = os.path.dirname(modulNam) if dirNam == "": dirNam = "Unspecified dir" try: dirNod = dirsToNodes[dirNam] except KeyError: # TODO: Beware, in fact this is a directory. dirNod = lib_common.gUriGen.FileUri(dirNam) grph.add((nodeDLL, pc.property_library_depends, dirNod)) dirsToNodes[dirNam] = dirNod grph.add((dirNod, pc.property_library_depends, libNode)) else: grph.add((nodeDLL, pc.property_library_depends, libNode)) if linargs[0] != '?': cpu = linargs[8] if cpu not in ["", "CPU"]: grph.add((nodeDLL, pc.property_library_cpu, lib_common.NodeLiteral(cpu))) # Temporary file removed by constructor. input_file.close() cgiEnv.OutCgiRdf()
def Main(): cgiEnv = lib_common.CgiEnv() try: the_pid = int(cgiEnv.GetId()) except Exception: lib_common.ErrorMessageHtml("Must provide a pid") if not lib_util.isPlatformWindows: lib_common.ErrorMessageHtml("This works only on Windows platforms") grph = cgiEnv.GetGraph() # Starts a second session cdb_fil = lib_common.TmpFile("CdbCommand", "cdb") cdb_fd = open(cdb_fil.Name, "w") cdb_fd.write("lm\n") # List loaded modules cdb_fd.write("k\n") # Display stack backtrace. cdb_fd.write("qd\n") # Quit and detach. cdb_fd.close() cdb_cmd = "cdb -p " + str(the_pid) + " -cf " + cdb_fil.Name procNode = lib_common.gUriGen.PidUri(the_pid) callNodePrev = None modules_map = {} sys.stderr.write("Starting cdb_cmd=%s\n" % cdb_cmd) try: cdb_pipe = lib_common.SubProcPOpen(cdb_cmd) except WindowsError: exc = sys.exc_info()[1] lib_common.ErrorMessageHtml("cdb not available: Caught:%s" % str(exc)) sys.stderr.write("Started cdb_cmd=%s\n" % cdb_cmd) (cdb_output, cdb_err) = cdb_pipe.communicate() # Without decode, "TypeError: Type str does not support the buffer API" cdb_str = cdb_output.decode("utf-8") callDepth = 0 for dot_line in cdb_str.split('\n'): # sys.stderr.write("Line=%s\n" % dot_line ) err_match = re.match(".*parameter is incorrect.*", dot_line) if err_match: lib_common.ErrorMessageHtml("CDB:" + dot_line) # 76590000 766a0000 kernel32 (export symbols) C:\Windows\syswow64\kernel32.dll match_lm = re.match( "[0-9a-fA-F]+ [0-9a-fA-F]+ +([^ ]*) +\(export symbols\) +(.*)", dot_line) if match_lm: moduleName = match_lm.group(1) dllName = match_lm.group(2).strip().replace( "\\", "/") # .replace(":","COLON") sys.stderr.write("moduleName=%s dllName=%s\n" % (moduleName, dllName)) modules_map[moduleName] = dllName continue # 295cfb0c 00000000 ntdll!RtlInitializeExceptionChain+0x36 # Another format, maybe because of a 64 bits machine. # 00000000`02edff90 00000000`00000000 ntdll!RtlUserThreadStart+0x21 match_k = re.match("[`0-9a-fA-F]+ [`0-9a-fA-F]+ ([^!]*)!([^+]*)", dot_line) if match_k: moduleName = match_k.group(1) try: dllName = modules_map[moduleName] except KeyError: dllName = moduleName funcName = match_k.group(2).strip() sys.stderr.write("moduleName=%s dllName=%s funcName=%s\n" % (moduleName, dllName, funcName)) dllName = CDB.TestIfKnownDll(dllName) callNodePrev = survol_symbol.AddFunctionCall( grph, callNodePrev, procNode, funcName, dllName) grph.add((callNodePrev, lib_common.NodeLiteral("Depth"), lib_common.NodeLiteral(callDepth))) callDepth += 1 continue sys.stderr.write("dot_line=%s\n" % dot_line) sys.stderr.write("Parsed cdb result\n") callNodePrev = survol_symbol.AddFunctionCall(grph, callNodePrev, procNode, None, None) CIM_Process.AddInfo(grph, procNode, [the_pid]) # http://msdn.microsoft.com/en-us/library/windows/hardware/ff539058(v=vs.85).aspx # # This section describes how to perform basic debugging tasks using # the Microsoft Console Debugger (CDB) and Microsoft NT Symbolic Debugger (NTSD). # CDB and NTSD are identical in every way, except that NTSD spawns # a new text window when it is started, whereas CDB inherits # the Command Prompt window from which it was invoked. # The instructions in this section are given for CDB, # but they work equally well for NTSD. For a discussion # of when to use CDB or NTSD, see Debugging Environments. ################################################################################ # cgiEnv.OutCgiRdf() cgiEnv.OutCgiRdf("LAYOUT_SPLINE")
def Main(): cgiEnv = lib_common.CgiEnv() try: the_pid = int(cgiEnv.GetId()) except Exception: lib_common.ErrorMessageHtml("Must provide a pid") if not lib_util.isPlatformWindows: lib_common.ErrorMessageHtml("This works only on Windows platforms") grph = cgiEnv.GetGraph() # Starts a second session cdb_fil = lib_common.TmpFile("CdbCommand", "cdb") cdb_fd = open(cdb_fil.Name, "w") cdb_fd.write("lmv\n") # List loaded modules, verbose mode. cdb_fd.write("qd\n") # Quit and detach. cdb_fd.close() cdb_cmd = "cdb -p " + str(the_pid) + " -cf " + cdb_fil.Name procNode = lib_common.gUriGen.PidUri(the_pid) sys.stderr.write("Starting cdb_cmd=%s\n" % cdb_cmd) try: cdb_pipe = lib_common.SubProcPOpen(cdb_cmd) except WindowsError: exc = sys.exc_info()[1] lib_common.ErrorMessageHtml("cdb not available: Caught:%s" % str(exc)) sys.stderr.write("Started cdb_cmd=%s\n" % cdb_cmd) (cdb_output, cdb_err) = cdb_pipe.communicate() # Without decode, "TypeError: Type str does not support the buffer API" cdb_str = cdb_output.decode("utf-8", "ignore") PropLoadedModule = lib_common.MakeProp("Loaded module") for dot_line in cdb_str.split('\n'): # sys.stderr.write("Line=%s\n" % dot_line ) # moduleName=uDWM moduleStatus=deferred fileName= # dot_line= Image path: C:\windows\system32\uDWM.dll # dot_line= Image name: uDWM.dll # dot_line= Timestamp: Tue Jul 14 02:33:35 2009 (4A5BE06F) # dot_line= CheckSum: 0005E9A4 # dot_line= ImageSize: 00057000 # dot_line= File version: 6.1.7600.16385 # dot_line= Product version: 6.1.7600.16385 # dot_line= File flags: 0 (Mask 3F) # dot_line= File OS: 40004 NT Win32 # dot_line= File type: 2.0 Dll # dot_line= File date: 00000000.00000000 # dot_line= Translations: 0409.04b0 # dot_line= CompanyName: Microsoft Corporation # dot_line= ProductName: Microsoft Windows Operating System # dot_line= InternalName: udwm.dll # dot_line= OriginalFilename: udwm.dll # dot_line= ProductVersion: 6.1.7600.16385 # dot_line= FileVersion: 6.1.7600.16385 (win7_rtm.090713-1255) # dot_line= FileDescription: Microsoft Desktop Window Manager # dot_line= LegalCopyright: Microsoft Corporation. All rights reserved. match_lin = re.match(" *Image path: *(.*)", dot_line) if match_lin: fileName = match_lin.group(1) fileName = CDB.TestIfKnownDll(fileName) #fileName = fileName.strip().replace("\\","/") fileName = fileName.strip() fileNode = lib_common.gUriGen.FileUri(fileName) grph.add((procNode, PropLoadedModule, fileNode)) continue match_lin = re.match(" *CompanyName: *(.*)", dot_line) if match_lin: companyName = match_lin.group(1) grph.add((fileNode, lib_common.MakeProp("Company Name"), lib_common.NodeLiteral(companyName))) continue match_lin = re.match(" *File OS: *(.*)", dot_line) if match_lin: fileOS = match_lin.group(1) grph.add((fileNode, lib_common.MakeProp("File OS"), lib_common.NodeLiteral(fileOS))) continue match_lin = re.match(" *FileDescription: *(.*)", dot_line) if match_lin: fileDescription = match_lin.group(1) grph.add((fileNode, lib_common.MakeProp("Description"), lib_common.NodeLiteral(fileDescription))) continue # sys.stderr.write("dot_line=%s\n" % dot_line ) sys.stderr.write("Parsed cdb result\n") CIM_Process.AddInfo(grph, procNode, [the_pid]) # cgiEnv.OutCgiRdf() cgiEnv.OutCgiRdf("LAYOUT_RECT", [PropLoadedModule])
def STraceEngine(sharedTupleQueue,entityId): global logFil logTmp = lib_common.TmpFile("strace","log") logFil = open( logTmp.Name, "w" ) LogMsg("STraceEngine: entityId=" + str(entityId) ) # Maybe the process is not running, or the pid is not an integer. try: pidOk = psutil.pid_exists( int(entityId) ) except TypeError: pidOk = False if not pidOk: LogMsg("STraceEngine: entityId=" + str(entityId) + " not running") return "STraceEngine: Invalid process id:" + str(entityId) # This reduces the quantity of data. expressions = " -e trace=file -e trace=process -e trace=ipc -e trace=network " options = "-f -F -v -s100 " + expressions + " -p " + entityId # strace_cmd = "strace " + options + " 2>&1 | grep -vw poll" # strace_cmd = "strace " + options strace_cmd = "strace " + options + " 2>&1" LogMsg("STraceEngine: command=" + strace_cmd ) # TODO: Must reach the end of line. # callRegex = r'\[pid ([^).*) ([^ ]*)\((.*)\) = [0-9]*' callRegex = r'\[pid ([^]]*)\] ([^(]*)\((.*)\) = [0-9]*' for lin in os.popen(strace_cmd): if not lin: LogMsg("STraceEngine: leaving execution loop" ) break # TODO: What happens if we get. # Trace=attach: ptrace(PTRACE_ATTACH, ...): Operation not permitted LogMsg( "STraceEngine Lin=" + lin ) if re.match( ".*Operation not permitted.*", lin ): LogMsg("STraceEngine: Operation not permitted" ) return "STraceEngine: Operation not permitted pid=" + entityId matchCall = re.match( callRegex, lin, re.M|re.I) if not matchCall: continue # All the function calls are gathered under the same process, # even if this is the parent process. Could be a parameter. # Or we can carefully specify the actual process id. pid = matchCall.group(1) # Example: "getsockname" funcName = matchCall.group(2) # TODO: Later on we might store all functions calls to build or complete the call graph. # For the moment this just keeps track of functions calls # whose arguments can be parsed and whose results can generate # interesting RDF data. if not funcName in func_parsers: LogMsg( "Unknown function=" + lin ) continue # Example: "66, {sa_family=AF_NETLINK, pid=7593, groups=00000000}, [12]" args = matchCall.group(3) vecArgs = ParseArgs( args ) # The entity is the process id. lstResult = [ entityId, funcName ] + vecArgs # This builds a tuple from a list. sharedTupleQueue.put( tuple( lstResult ) ) # TODO: IL FAUDRAIT LAISSER UN MESSAGE POUR LE PROCESS LECTEUR. # PEUT ETRE QIE CONVENTIONNELLEMENT, SI ON LAISSE DANS LA QUEUE # AUTRE CHSOE QU UN TUPLE, C EST UN MESSAGE ??? LogMsg( "Leaving." ) return "THIS IS AN ERROR AND LEAVING MESSAGE"