def Main(): cgiEnv = lib_common.CgiEnv() entity_id = cgiEnv.m_entity_id entity_host = cgiEnv.GetHost() (nameSpace, entity_type, entity_namespace_type) = cgiEnv.GetNamespaceType() grph = cgiEnv.GetGraph() rootNode = lib_util.RootUri() entity_ids_arr = lib_util.EntityIdToArray(entity_type, entity_id) modeDisp = lib_util.GuessDisplayMode() sys.stderr.write("entity_mime.py entity_type=%s modeDisp=%s\n" % (entity_type, modeDisp)) if not entity_type: lib_common.ErrorMessageHtml("entity_mime.py needs an object") entity_module = lib_util.GetEntityModule(entity_type) if not entity_module: lib_common.ErrorMessageHtml( "entity_mime.py entity_type=%s needs a module" % (entity_type)) try: entity_module.DisplayAsMime(grph, rootNode, entity_ids_arr) except: exc = sys.exc_info()[1] sys.stderr.write("entity_mime.py No DisplayAsMime for %s %s: %s\n" % (entity_type, entity_id, str(exc))) lib_common.ErrorMessageHtml( "entity_mime.py No DisplayAsMime for %s %s: %s\n" % (entity_type, entity_id, str(exc)))
def _add_survol_node(grph, host_survol, url_survol_clean): logging.debug("AddSurvolNode hostSurvol=%s", host_survol) survol_host_node = lib_uris.gUriGen.HostnameUri(host_survol) curr_disp_mode = lib_util.GuessDisplayMode() # Several possibilities: # - Open a new HTML page with this URL. Or SVG, passed on the current mode. # - If we are in D3 mode, this should return a JSON object from the other agent. if curr_disp_mode == "json": server_box = lib_uris.OtherAgentBox(host_survol) # This is the URL of the remote host, on the remote agent. node_remote_host = server_box.HostnameUri(host_survol) grph.add((survol_host_node, lib_common.MakeProp("Survol host"), node_remote_host)) node_survol_url = lib_common.NodeUrl(url_survol_clean) grph.add((survol_host_node, lib_common.MakeProp("Survol agent"), node_survol_url)) else: url_survol_moded = lib_util.AnyUriModed(url_survol_clean, curr_disp_mode) node_survol_url = lib_common.NodeUrl(url_survol_moded) # Should check the URL to be sure it is valid. grph.add((survol_host_node, lib_common.MakeProp("Survol agent"), node_survol_url)) return node_survol_url
def Main(): cgiEnv = lib_common.ScriptEnvironment() entity_id = cgiEnv.m_entity_id name_space, entity_type = cgiEnv.get_namespace_type() grph = cgiEnv.GetGraph() root_node = lib_util.RootUri() entity_ids_arr = lib_util.EntityIdToArray(entity_type, entity_id) mode_disp = lib_util.GuessDisplayMode() logging.debug("entity_type=%s mode_disp=%s", entity_type, mode_disp) if not entity_type: lib_common.ErrorMessageHtml("entity_mime.py needs an object") entity_module = lib_util.GetEntityModule(entity_type) if not entity_module: lib_common.ErrorMessageHtml( "entity_mime.py entity_type=%s needs a module" % entity_type) try: entity_module.DisplayAsMime(grph, root_node, entity_ids_arr) except Exception as exc: lib_common.ErrorMessageHtml( __file__ + " DisplayAsMime fails: %s %s: %s. File=%s.\n" % (entity_type, entity_id, str(exc), entity_module.__file__))
def OutCgiRdf(self, dot_layout = "", collapsed_properties=[] ): global globalCgiEnvList # sys.stderr.write("OutCgiRdf globalMergeMode=%d len(globalCgiEnvList)=%d\n"%(globalMergeMode,len(globalCgiEnvList))) sys.stderr.write("OutCgiRdf m_calling_url=%s m_page_title=%s\n"%(self.m_calling_url,self.m_page_title)) self.m_layoutParams = MakeDotLayout( dot_layout, collapsed_properties ) mode = lib_util.GuessDisplayMode() topUrl = lib_util.TopUrl( self.m_entity_type, self.m_entity_id ) if self.m_page_title is None: self.m_page_title = "PAGE TITLE SHOULD BE SET" if globalMergeMode: # At the end, only one call to OutCgiMode() will be made. globalCgiEnvList.append(self) else: OutCgiMode( self, topUrl, mode )
def AddSurvolNode(grph, hostSurvol, urlSurvolClean): sys.stderr.write("AddSurvolNode hostSurvol=%s\n" % (hostSurvol)) survolHostNode = lib_common.gUriGen.HostnameUri(hostSurvol) currDispMode = lib_util.GuessDisplayMode() # Several possibilities: # - Open a new HTML page with this URL. Or SVG, passed on the current mode. # - If we are in D3 mode, this should return a JSON object from the other agent. if currDispMode == "json": if lib_util.IsLocalAddress(hostSurvol): machName_or_None = None serverBox = lib_common.gUriGen else: machName_or_None = hostSurvol serverBox = lib_common.OtherAgentBox(urlSurvolClean) # This is the URL of the remote host, on the remote agent. nodeRemoteHost = serverBox.HostnameUri(hostSurvol) grph.add((survolHostNode, lib_common.MakeProp("Survol host"), nodeRemoteHost)) nodeSurvolUrl = lib_common.NodeUrl(urlSurvolClean) grph.add((survolHostNode, lib_common.MakeProp("Survol agent"), nodeSurvolUrl)) else: urlSurvolModed = lib_util.AnyUriModed(urlSurvolClean, currDispMode) nodeSurvolUrl = lib_common.NodeUrl(urlSurvolModed) # Should check the URL to be sure it is valid. # sys.stderr.write("AddSurvolNode urlSurvolModed=%s\n"%(urlSurvolModed)) grph.add((survolHostNode, lib_common.MakeProp("Survol agent"), nodeSurvolUrl)) return nodeSurvolUrl
def OutCgiRdf(self, dot_layout="", collapsed_properties=[]): global globalCgiEnvList DEBUG("OutCgiRdf globalMergeMode=%d m_calling_url=%s m_page_title=%s", globalMergeMode, self.m_calling_url, self.m_page_title.replace("\n", "<NL>")) self.m_layoutParams = make_dot_layout(dot_layout, collapsed_properties) mode = lib_util.GuessDisplayMode() top_url = lib_util.TopUrl(self.m_entity_type, self.m_entity_id) if self.m_page_title is None: self.m_page_title = "PAGE TITLE SHOULD BE SET" self.m_page_subtitle = "PAGE SUBTITLE SHOULD BE SET" # See if this can be used in lib_client.py and merge_scritps.py. if globalMergeMode: # At the end, only one call to OutCgiMode() will be made. globalCgiEnvList.append(self) else: OutCgiMode(self, top_url, mode)
def OutCgiRdf(self, layout_style="", collapsed_properties=[]): global _global_cgi_env_list logging.debug("OutCgiRdf globalMergeMode=%d m_calling_url=%s m_page_title=%s", _global_merge_mode, self.m_calling_url, self.m_page_title.replace("\n", "<NL>")) # TODO: Get these values from the RDF document, if these were added on-the-fly by CGI scripts. if layout_style: self.m_layout_style = layout_style self.m_collapsed_properties.extend(collapsed_properties) mode = lib_util.GuessDisplayMode() top_url = lib_util.TopUrl(self.m_entity_type, self.m_entity_id) if self.m_page_title is None: self.m_page_title = "PAGE TITLE SHOULD BE SET" self.m_page_subtitle = "PAGE SUBTITLE SHOULD BE SET" # TODO: See if this can be used in lib_client.py and merge_scripts.py. if _global_merge_mode: # At the end, only one call to _out_cgi_mode() will be made. _global_cgi_env_list.append(self) else: _out_cgi_mode(self, top_url, mode)
def Main(): # This can process remote hosts because it does not call any script, just shows them. cgiEnv = lib_common.CgiEnv(can_process_remote=True, parameters={lib_util.paramkeyShowAll: False}) entity_id = cgiEnv.m_entity_id entity_host = cgiEnv.GetHost() flagShowAll = int(cgiEnv.GetParameters(lib_util.paramkeyShowAll)) (nameSpace, entity_type, entity_namespace_type) = cgiEnv.GetNamespaceType() grph = cgiEnv.GetGraph() rootNode = lib_util.RootUri() entity_ids_arr = lib_util.EntityIdToArray(entity_type, entity_id) # entity_info_only.AddInformation(grph,rootNode,entity_id, entity_type) # Each entity type ("process","file" etc... ) can have a small library # of its own, for displaying a rdf node of this type. if entity_type: entity_module = lib_util.GetEntityModule(entity_type) if entity_module: try: entity_module.AddInfo(grph, rootNode, entity_ids_arr) except AttributeError: exc = sys.exc_info()[1] sys.stderr.write("No AddInfo for %s %s: %s\n" % (entity_type, entity_id, str(exc))) else: sys.stderr.write("No lib_entities for %s %s\n" % (entity_type, entity_id)) # When displaying in json mode, the scripts are shown with a contextual menu, not with D3 modes.. if lib_util.GuessDisplayMode() not in ["json", "html"]: # This function is called for each script which applies to the given entity. # It receives a triplet: (subject,property,object) and the depth in the tree. # Here, this simply stores the scripts in a graph. The depth is not used yet, # but can help debugging. def CallbackGrphAdd(tripl, depthCall): grph.add(tripl) entity_dirmenu_only.DirToMenu(CallbackGrphAdd, rootNode, entity_type, entity_id, entity_host, flagShowAll) # This adds WBEM and WMI urls related to the current object. if entity_type != "": CIM_ComputerSystem.AddWbemWmiServers(grph, rootNode, entity_host, nameSpace, entity_type, entity_id) AddDefaultScripts(grph, rootNode, entity_host) # Special case if the currententity we are displaying, is a machine, # we might as well try to connect to its WMI or WBEM server, running on this machine. if entity_type == "CIM_ComputerSystem": AddDefaultScripts(grph, rootNode, entity_id) AddDefaultNodes(grph, rootNode, entity_host) cgiEnv.OutCgiRdf("LAYOUT_RECT", [pc.property_directory, pc.property_script])
def DoTheJob(TheEngine, Deserializer, AppName, Title, dot_layout="", collapsed_properties=[]): srvSingle = SrvSingleton(AppName) PortNum = srvSingle.m_PortNumber run_word = "run" GblLog("DoTheJob:" + str(sys.argv) + " title=" + Title + " dot_layout=" + dot_layout) if srvSingle.m_PortNumber != None: GblLog("Title=%s PortNum=%d" % (Title, PortNum)) else: GblLog("Title=%s PortNum not defined yet" % (Title)) # So we can answer immediately to "info" requests, without creating the subserver. cgiEnv = lib_common.CgiEnv(Title, "", {"port_number": PortNum}) # This is the subprocess of the specialised http server. if (len(sys.argv) > 1) and (sys.argv[1] == run_word): if srvSingle.GetRunning(): GblLog("Subprocess: Race condition: Port " + str(PortNum) + " allocated. Leaving.") sys.exit(0) # Called before creating the subserver because we will be stuck into it. if not srvSingle.SetRunning(): GblLog("CANNOT SET RUNNING STATE: Race condition ?") return # This time the port must be valid. PortNum = srvSingle.m_PortNumber GblLog("Subprocess: Port %d free. About to create subserver." % PortNum) try: # Normally we are stuck in this, answering HTTP requests and accumulating data. # TODO: It is probably not necessary to send Title and DotLayout. layoutParams = lib_common.make_dot_layout(dot_layout, collapsed_properties) # Now, will append log at the end. srvSingle.m_logFd.close() gServer = RdfStreamServer(PortNum, TheEngine, Deserializer, Title, layoutParams, srvSingle.m_logFilNam) GblLog("Should never be here") except Exception: exc = sys.exc_info()[1] msg = Task() + " Caught when starting RdfStreamServer:" + str(exc) GblLog(msg) srvSingle.CancelRunning() lib_common.ErrorMessageHtml("From RDFStreamServer, caught:" + msg) sys.exit(0) # if the subserver is not running, we start it, then redirects the browser # to initiate a new connection. After that, we exit() but this will never really # exit until our subserver subprocess, exits first. if not srvSingle.GetRunning(): GblLog("About to start:" + AppName) try: # Do not pipe the output otherwise it would not run in the background. # Si necessaire, on pourrait lancer un process avec l'user root ? # sub_proc = lib_common.SubProcPOpen( [ "python", AppName, run_word ] ) # Share standard error. sub_proc = lib_common.SubProcPOpen(["python", AppName, run_word], stderr=sys.stderr) GblLog("Started sub http server:" + AppName + " subpid=" + str(sub_proc.pid)) # We need the cgi arguments, especially the entity id. origUrl = os.environ["REQUEST_URI"] GblLog("About to redirect browser to:" + origUrl) # IMPORTANT: We leave time enough for the subprocess to start, # otherwise it will be started TWICE. time.sleep(0.1) GblLog("After pause") SendRedirection(sys.stdout, origUrl) # This blocks until the subprocess exists. GblLog("Waiting for end of subpid=" + str(sub_proc.pid)) sys.exit(0) except Exception: exc = sys.exc_info()[1] lib_common.ErrorMessageHtml("When starting server=" + AppName + ", caught:" + str(exc)) GblLog("Service exists. Port=" + str(PortNum)) # Here, we know that the subserver is running. url = SubServerUrl(PortNum, cgiEnv) # Do we have to stop the subserver ? if MustStop(): # Equivalent to 'ModedUrl("stop")' url_stop = lib_util.ConcatenateCgi(url, "mode=stop") # TODO: We could also load this URL, this would be nicer. SendRedirection(sys.stdout, url_stop) sys.exit(0) mode = lib_util.GuessDisplayMode() GblLog("mode:" + mode) # Equivalent to 'ModedUrl(mode)' url_mode = lib_util.ConcatenateCgi(url, "mode=" + mode) GblLog("url_mode:" + url_mode) SendRedirection(sys.stdout, url_mode) GblLog("Finished:" + AppName)
def Main(): # This can process remote hosts because it does not call any script, just shows them. cgiEnv = lib_common.ScriptEnvironment( can_process_remote=True, parameters={lib_util.paramkeyShowAll: False}) entity_id = cgiEnv.m_entity_id entity_host = cgiEnv.GetHost() logging.debug("entity_host=%s", entity_host) flag_show_all = int(cgiEnv.get_parameters(lib_util.paramkeyShowAll)) # This optional parameter must not be edited. # It contains the name of an associator, and the associated objects must be displayed. associator_attribute = cgiEnv.get_parameters("__associator_attribute__") logging.debug("associator_attribute=%s", associator_attribute) if associator_attribute and associator_attribute.find(".") <= 0: lib_common.ErrorMessageHtml( "Associator attribute '%s' should be 'associator.role'" % associator_attribute) name_space, entity_type = cgiEnv.get_namespace_type() grph = cgiEnv.GetGraph() root_node = lib_util.RootUri() logging.debug("root_node=%s", root_node) entity_ids_arr = lib_util.EntityIdToArray(entity_type, entity_id) # Each entity type ("process","file" etc... ) can have a small library # of its own, for displaying a rdf node of this type. if entity_type: entity_module = lib_util.GetEntityModule(entity_type) if entity_module: try: entity_module.AddInfo(grph, root_node, entity_ids_arr) except AttributeError as exc: logging.info("No AddInfo for %s %s: %s", entity_type, entity_id, str(exc)) except Exception as exc: logging.info("Unexpected exception for %s %s: %s", entity_type, entity_id, str(exc)) if associator_attribute: # Should we display the associated instances for this associator and role ? lib_associators.add_associated_instances(grph, root_node, entity_type, entity_id, associator_attribute) else: # This behaves as the top-level page, without instances to display. logging.info("No lib_entities for %s %s", entity_type, entity_id) # When displaying in json mode, the scripts are shown with a contextual menu, not with D3 modes.. if lib_util.GuessDisplayMode() not in ["json", "html"]: # This function is called for each script which applies to the given entity. # It receives a triplet: (subject,property,object) and the depth in the tree. # Here, this simply stores the scripts in a graph. The depth is not used yet, # but can help debugging. def callback_grph_add(tripl, depthCall): try: grph.add(tripl) except Exception as exc: logging.error("callback_grph_add: tripl=%s exception=%s" % (str(tripl), str(exc))) raise try: # This displays the scripts associated to this instance. entity_dirmenu_only.recursive_walk_on_scripts( callback_grph_add, root_node, entity_type, entity_id, entity_host, flag_show_all) except Exception as exc: logging.error("Caught in recursive_walk_on_scripts:%s. Trace=%s", exc, traceback.format_exc()) # This adds WBEM and WMI urls related to the current object. # They can display more information about this instance. if entity_type != "": # This solves the case where one of the values of the ontology predicates contains commas. # These commands were quoted, then separated of other arguments by a comma. # TODO: It would be probably be simpler to encode predicates values just like CGI arguments. _add_wbem_wmi_servers(grph, root_node, entity_host, name_space, entity_type, entity_id) _add_default_scripts(grph, root_node, entity_host) # Special case if the current entity we are displaying, is a machine, # we might as well try to connect to its WMI or WBEM server, running on this machine. if entity_type == "CIM_ComputerSystem": _add_default_scripts(grph, root_node, entity_id) _add_default_nodes(grph, root_node, entity_host) cgiEnv.OutCgiRdf("LAYOUT_RECT", [pc.property_directory, pc.property_script])
def __init__(self, parameters = {}, can_process_remote = False ): # TODO: This value is read again in OutCgiRdf, we could save time by making this object global. sys.stderr.write( "CgiEnv parameters=%s\n" % ( str(parameters) ) ) # TODO: When running from cgiserver.py, and if QUERY_STRING is finished by a dot ".", this dot # TODO: is removed. Workaround: Any CGI variable added after. # TODO: Also: Several slashes "/" are merged into one. # TODO: Example: "xid=http://192.168.1.83:5988/." becomes "xid=http:/192.168.1.83:5988/" # TODO: ... or "xx.py?xid=smbshr.Id=////WDMyCloudMirror///rchateau" become "xx.py?xid=smbshr.Id=/WDMyCloudMirror/rchateau" # TODO: Replace by "xid=http:%2F%2F192.168.1.83:5988/." # Maybe a bad collapsing of URL ? # sys.stderr.write("QUERY_STRING=%s\n" % os.environ['QUERY_STRING'] ) mode = lib_util.GuessDisplayMode() # Contains the optional arguments, needed by calling scripts. self.m_parameters = parameters self.m_parameterized_links = dict() # When in merge mode, the display parameters must be stored in a place accessible by the graph. docModuAll = GetCallingModuleDoc() # Take only the first non-empty line. See lib_util.FromModuleToDoc() docModuSplit = docModuAll.split("\n") self.m_page_title = docModuSplit[0] # Title page contains __doc__ plus object label. callingUrl = lib_util.RequestUri() self.m_calling_url = callingUrl sys.stderr.write("CgiEnv m_page_title=%s m_calling_url=%s\n"%(self.m_page_title,self.m_calling_url)) parsedEntityUri = lib_naming.ParseEntityUri(callingUrl,longDisplay=False,force_entity_ip_addr=None) if parsedEntityUri[2]: # If there is an object to display. # Practically, we are in the script "entity.py" and the single doc string is "Overview" fullTitle = parsedEntityUri[0] self.m_page_title += " " + fullTitle # We assume there is an object, and therefore a class and its description. entity_class = parsedEntityUri[1] # Similar code in objtypes.py entity_module = lib_util.GetEntityModule(entity_class) entDoc = entity_module.__doc__ # The convention is the first line treated as a title. if entDoc: self.m_page_title += "\n" + entDoc # If we can talk to a remote host to get the desired values. # Global CanProcessRemote has precedence over parameter can_process_remote # whcih should probably be deprecated, although they do not have exactly the same role: # * Global CanProcessRemote is used by entity.py to display scripts which have this capability. # * Parameter can_process_remote is used to inform, at execution time, of this capability. # Many scripts are not enumerated by entity.py so a global CanProcessRemote is not necessary. # For clarity, it might be fine to replace the parameter can_process_remote by the global value. # There cannot be nasty consequences except that some scripts might not be displayed # when they should be, and vice-versa. try: globalCanProcessRemote = globals()["CanProcessRemote"] except KeyError: globalCanProcessRemote = False if can_process_remote != globalCanProcessRemote: # sys.stderr.write("INCONSISTENCY CanProcessRemote\n") # ... which is not an issue. can_process_remote = True self.m_can_process_remote = can_process_remote self.m_arguments = cgi.FieldStorage() (self.m_entity_type,self.m_entity_id,self.m_entity_host) = self.GetXid() sys.stderr.write("CgiEnv m_entity_type=%s m_entity_id=%s m_entity_host=%s\n"%(self.m_entity_type,self.m_entity_id,self.m_entity_host)) self.m_entity_id_dict = lib_util.SplitMoniker(self.m_entity_id) # Depending on the caller module, maybe the arguments should be 64decoded. See "sql/query". # As the entity type is available, it is possible to import it and check if it encodes it arguments. # See presence of source_types.sql.query.DecodeCgiArg(keyWord,cgiArg) for example. # This is probably too generous to indicate a local host. self.TestRemoteIfPossible(can_process_remote) # TODO: HOW WILL WE RESTORE THE ORIGINAL DISPLAY MODE ? if mode == "edit": self.EditionMode()
def Main(): orig_req_uri = lib_util.RequestUri() # It initialises an implicit global object similar. # When in the mode of global merging, the method "cgiEnv.OutCgiRdf()" does not generate anything, # but simply stores the new cgiEnv in a global list.. # The script loops on the URLs passed as CGI parameters. # The URLs are loaded and their content merged into the container lib_common.globalGraph lib_common.CgiEnvMergeMode() arguments = cgi.FieldStorage() # The display mode is read now, otherwise the CGI arguments are later destroyed, in this script. the_mode = lib_util.GuessDisplayMode() logging.debug("the_mode=%s", the_mode) # Concatenation of error messages of each script. cumulated_error = "" # This logic might be needed in lib_client.py for urlfil in arguments.getlist("url"): # The parameters are coded in base64, although we leave the possibility not to encode them, # for compatibility with test scripts. complete_url = lib_util.Base64Decode(urlfil) logging.debug("complete_url=%s", complete_url) # Only the URL without the arguments. url_split = complete_url.split("?") url_no_args = url_split[0] if len(url_split) > 1: cgi_query_string = url_split[1] else: cgi_query_string = "" # The URL might be absolute or relative. Example: # "survol/sources_types/enumerate_CIM_Process.py?xid=." idx_htbin = url_no_args.find("sources_types/") if idx_htbin == -1: # This may be the main presentation page of a Survol, WMI or WBEM object. Example: # "http://127.0.0.1:80/Survol/survol/entity.py?xid=CIM_Process.Handle=640" survol_prefix = "survol/" idx_survol = url_no_args.find(survol_prefix) if idx_survol == -1: # TODO: This happens if the URL is a main presentation page of an object, # instead of a script: Something like "survol/entity.py/entity.py?xid=..." # This should be fixed but is not an issue. logging.warning("merge: SHOULD NOT HAPPEN url=%s", complete_url) url_path_short = "INVALID_MERGED_URL" else: # Just starts at the beginning of the script name: "entity.py", "entity_wmi.py", "entity_wbem.py". url_path_short = url_no_args[idx_survol + len(survol_prefix):] else: url_path_short = url_no_args[idx_htbin:] # url_path_short is the actual script to load. url_dir_nam = os.path.dirname(url_path_short) # The directory of the script is used to build a Python module name. modu_nam = url_dir_nam.replace("/", ".") url_fil_nam = os.path.basename(url_path_short) logging.debug( "url_path_short=%s url_dir_nam=%s modu_nam=%s url_fil_nam=%s", url_path_short, url_dir_nam, modu_nam, url_fil_nam) try: # argDir="sources_types.win32" urlFileNam="enumerate_top_level_windows.py" imported_mod = lib_util.GetScriptModule(modu_nam, url_fil_nam) except Exception as exc: logging.warning( "Caught %s when loading modu_nam=%s url_fil_nam=%s", exc, modu_nam, url_fil_nam) continue if not imported_mod: cumulated_error = "merge_scripts.py Cannot import complete_url=%s" % complete_url continue try: # The entire URL must be "injected" so the parameters will be properly parsed, # when Main() call lib_util.RequestUri(). # The script passed as CGI parameter, believes that it is loaded as a plain URL. url_unquote = lib_util.urllib_unquote(complete_url) os.environ["REQUEST_URI"] = url_unquote os.environ['SCRIPT_NAME'] = url_fil_nam # "xid=EURO%5CLONL00111310@process:16580" os.environ['QUERY_STRING'] = cgi_query_string lib_common.enable_error_message(False) # This executes the script: The new nodes and links are merged in a global RDF container. imported_mod.Main() except Exception as exc: logging.warning( "Caught %s when executing Main in modu_nam=%s url_fil_nam=%s", exc, modu_nam, url_fil_nam) if cumulated_error != "": cumulated_error += " ; " cumulated_error += " url=" + url_no_args + " / " + url_fil_nam + ":" + str( exc) continue lib_common.enable_error_message(True) os.environ["REQUEST_URI"] = orig_req_uri # OutCgiRdf has been called by each script without writing anything, # but the specific parameters per script are stored inside. # Here, all the RDF nodes and links, loaded from each URL, and then merged in lib_common.globalGraph, # are then transformed into the chosen output format. lib_common.MergeOutCgiRdf(the_mode, cumulated_error)
def Main(): origReqUri = lib_util.RequestUri() # There is only one cgiEnv and "cgiEnv.OutCgiRdf()" does not generate anything. lib_common.CgiEnvMergeMode() arguments = cgi.FieldStorage() # The display mode is read now, otherwise the CGI arguments are later destroyed, in this script. theMode = lib_util.GuessDisplayMode() sys.stderr.write("merge_scripts.py theMode=%s\n" % (theMode)) # Concatenation of error messages of each script. cumulatedError = "" for urlfil in arguments.getlist("url"): # The parameters are coded in base64, although we leave the possibility not to encode them, # for compatibility with test scripts. complete_url = lib_util.Base64Decode(urlfil) sys.stderr.write("complete_url=%s\n" % complete_url) # Only the URL without the arguments. urlSplit = complete_url.split("?") urlNoArgs = urlSplit[0] if len(urlSplit) > 1: cgiQueryString = urlSplit[1] else: cgiQueryString = "" # The URL might be absolute or relative. Example: # "survol/sources_types/enumerate_CIM_Process.py?xid=." idxHtbin = urlNoArgs.find("sources_types/") if idxHtbin == -1: # This may be the main presentation page of a Survol, WMI or WBEM object. Example: # "http://127.0.0.1:80/Survol/survol/entity.py?xid=CIM_Process.Handle=640" survolPrefix = "survol/" idxSurvol = urlNoArgs.find(survolPrefix) if idxSurvol == -1: # TODO: This happens if the URL is a main presentation page of an object, # instead of a script: Something like "survol/entity.py/entity.py?xid=..." # This should be fixed but is not an issue. sys.stderr.write("merge: SHOULD NOT HAPPEN url=%s\n" % complete_url) urlPathShort = "INVALID_MERGED_URL" else: # Just starts at the beginning of the script name: "entity.py", "entity_wmi.py", "entity_wbem.py". urlPathShort = urlNoArgs[idxSurvol + len(survolPrefix):] else: urlPathShort = urlNoArgs[idxHtbin:] # urlPathShort is the actual script to load. urlDirNam = os.path.dirname(urlPathShort) moduNam = urlDirNam.replace("/", ".") urlFilNam = os.path.basename(urlPathShort) sys.stderr.write( "urlPathShort=%s urlDirNam=%s moduNam=%s urlFilNam=%s\n" % (urlPathShort, urlDirNam, moduNam, urlFilNam)) try: # argDir="sources_types.win32" urlFileNam="enumerate_top_level_windows.py" importedMod = lib_util.GetScriptModule(moduNam, urlFilNam) except Exception: errorMsg = sys.exc_info()[1] sys.stderr.write( "Caught %s when loading moduNam=%s urlFilNam=%s\n" % (errorMsg, moduNam, urlFilNam)) continue if not importedMod: cumulatedError = "merge_scripts.py Cannot import complete_url=%s" % ( complete_url) continue try: # The entire URL must be "injected" so the parameters will be properly parsed, # when Main() call lib_util.RequestUri(). urlUnquote = lib_util.urllib_unquote(complete_url) os.environ["REQUEST_URI"] = urlUnquote os.environ['SCRIPT_NAME'] = urlFilNam # "xid=EURO%5CLONL00111310@process:16580" os.environ['QUERY_STRING'] = cgiQueryString lib_common.ErrorMessageEnable(False) importedMod.Main() except Exception: errorMsg = sys.exc_info()[1] sys.stderr.write( "Caught %s when executing Main in moduNam=%s urlFilNam=%s\n" % (errorMsg, moduNam, urlFilNam)) if cumulatedError != "": cumulatedError += " ; " cumulatedError += " url=" + urlNoArgs + " / " + urlFilNam + ":" + str( errorMsg) continue lib_common.ErrorMessageEnable(True) os.environ["REQUEST_URI"] = origReqUri # OutCgiRdf has been called by each script without writing anything, # but the specific parameters per script are stored inside. # TESTER AVEC CA: # http://127.0.0.1:8000/survol/merge_scripts.py?url=aHRiaW4vc291cmNlc190eXBlcy9hZGRyL3NvY2tldF9ob3N0LnB5P3hpZD1hZGRyLklkJTNEMTkyLjE2OC4xLjg4JTNBc3No&url=aHRiaW4vc291cmNlc190eXBlcy9DSU1fQ29tcHV0ZXJTeXN0ZW0vaG9zdG5hbWVfbm1hcC5weT94aWQ9Q0lNX0NvbXB1dGVyU3lzdGVtLk5hbWUlM0RVbmtub3duLTMwLWI1LWMyLTAyLTBjLWI1LTI&url=aHRiaW4vc291cmNlc190eXBlcy9hZGRyL3NvY2tldF9ob3N0LnB5P3hpZD1hZGRyLklkJTNEMTkyLjE2OC4xLjg4JTNBc3ZybG9j&url=aHRiaW4vZW50aXR5LnB5P3hpZD1zbWJzaHIuSWQ9Ly8vL1dETXlDbG91ZE1pcnJvci9yY2hhdGVhdQ&url=aHRiaW4vc291cmNlc190eXBlcy9DSU1fQ29tcHV0ZXJTeXN0ZW0vY29ubmVjdGVkX3NvY2tldHMucHk_eGlkPUNJTV9Db21wdXRlclN5c3RlbS5OYW1lJTNEVW5rbm93bi0zMC1iNS1jMi0wMi0wYy1iNS0y&url=aHRiaW4vc291cmNlc190eXBlcy9DSU1fQ29tcHV0ZXJTeXN0ZW0vaG9zdG5hbWVfbm1hcC5weT94aWQ9Q0lNX0NvbXB1dGVyU3lzdGVtLk5hbWUlM0RVbmtub3duLTMwLWI1LWMyLTAyLTBjLWI1LTI&url=aHRiaW4vc291cmNlc190eXBlcy9ncm91cC9saW51eF91c2VyX2dyb3VwLnB5P3hpZD1ncm91cC5OYW1lJTNEYXBhY2hl&url=aHRiaW4vc291cmNlc190eXBlcy91c2VyL3VzZXJfbGludXhfaWQucHk_eGlkPXVzZXIuRG9tYWluJTNETG9jYWxIb3N0JTJDTmFtZSUzRGFwYWNoZQ&url=aHRiaW4vc291cmNlc190eXBlcy9hZGRyL3NvY2tldF9jb25uZWN0ZWRfcHJvY2Vzc2VzLnB5P3hpZD1hZGRyLklkJTNEMTkyLjE2OC4xLjg4JTNBdGVsbmV0&url=aHRiaW4vc291cmNlc190eXBlcy91c2VyL3VzZXJfcHJvY2Vzc2VzLnB5P3hpZD11c2VyLkRvbWFpbiUzRExvY2FsSG9zdCUyQ05hbWUlM0RhcGFjaGU&url=aHRiaW4vc291cmNlc190eXBlcy9DSU1fUHJvY2Vzcy9wcm9jZXNzX2N3ZC5weT94aWQ9Q0lNX1Byb2Nlc3MuSGFuZGxlJTNEMTQ3MDU&url=aHRiaW4vc291cmNlc190eXBlcy9hZGRyL3NvY2tldF9jb25uZWN0ZWRfcHJvY2Vzc2VzLnB5P3hpZD1hZGRyLklkJTNEMTkyLjE2OC4xLjg4JTNBc3No&url=aHRiaW4vc291cmNlc190eXBlcy9DSU1fUHJvY2Vzcy9wcm9jZXNzX2N3ZC5weT94aWQ9Q0lNX1Byb2Nlc3MuSGFuZGxlJTNEMTQ3MDU&url=aHRiaW4vc291cmNlc190eXBlcy9hZGRyL3NvY2tldF9jb25uZWN0ZWRfcHJvY2Vzc2VzLnB5P3hpZD1hZGRyLklkJTNEMTkyLjE2OC4xLjg4JTNBdGVsbmV0&url=aHRiaW4vc291cmNlc190eXBlcy9hZGRyL3NvY2tldF9jb25uZWN0ZWRfcHJvY2Vzc2VzLnB5P3hpZD1hZGRyLklkJTNEMTkyLjE2OC4xLjg4JTNBdGVsbmV0&url=aHRiaW4vc291cmNlc190eXBlcy9hZGRyL3NvY2tldF9jb25uZWN0ZWRfcHJvY2Vzc2VzLnB5P3hpZD1hZGRyLklkJTNEMTkyLjE2OC4xLjg4JTNBc3No&url=aHR0cDovLzE5Mi4xNjguMS44ODo4MC9TdXJ2b2wvaHRiaW4vZW50aXR5LnB5P3hpZD1DSU1fUHJvY2Vzcy5IYW5kbGU9MjA1MTI lib_common.MergeOutCgiRdf(theMode, cumulatedError)
def __init__(self, parameters=None, can_process_remote=False, layout_style="", collapsed_properties=None): # It is possible to run these scripts as CGI scripts, so this transforms # command line arguments into CGI arguments. This is very helpful for debugging. # The HTTP server can set the logging level with the environment variable SURVOL_LOGGING_LEVEL. try: logging_level = os.environ["SURVOL_LOGGING_LEVEL"] logging.getLogger().setLevel(logging_level) logging.info("logging_level set with SURVOL_LOGGING_LEVEL=%s" % logging_level) except KeyError: logging.info("logging_level is not forced with SURVOL_LOGGING_LEVEL.") lib_command_line.command_line_to_cgi_args() assert "QUERY_STRING" in os.environ # Some limitations of cgiserver.py and Python2: # TODO: When running from cgiserver.py, and if QUERY_STRING is finished by a dot ".", this dot # TODO: is removed. Workaround: Any CGI variable added after. # TODO: Also: Several slashes "/" are merged into one. # TODO: Example: "xid=http://192.168.1.83:5988/." becomes "xid=http:/192.168.1.83:5988/" # TODO: ... or "xx.py?xid=smbshr.Id=////WDMyCloudMirror///jsmith" ... # TODO: ... becomes "xx.py?xid=smbshr.Id=/WDMyCloudMirror/jsmith" # TODO: Replace by "xid=http:%2F%2F192.168.1.83:5988/." mode = lib_util.GuessDisplayMode() logging.debug("mode=%s" % mode) # Contains the optional arguments of the script, entered as CGI arguments.. self.m_parameters = parameters if parameters else {} self.m_parameterized_links = dict() self.m_layout_style = layout_style self.m_collapsed_properties = collapsed_properties if collapsed_properties else [] # When in merge mode, the display parameters must be stored in a place accessible by the graph. doc_modu_all = _get_calling_module_doc() # Take only the first non-empty line. See lib_util.FromModuleToDoc() self.m_page_title, self.m_page_subtitle = lib_util.SplitTextTitleRest(doc_modu_all) # Title page contains __doc__ plus object label. # Example: REQUEST_URI=/Survol/survol/print_environment_variables.py # This does NOT contain the host and the port, which implies a confusion if severl Survol agents # use the same database. It makes sense, because the result should not depend in the agent. self.m_calling_url = lib_util.RequestUri() self.m_url_without_mode = lib_util.url_mode_replace(self.m_calling_url, "") full_title, entity_class, entity_id, entity_host = lib_naming.parse_entity_uri_with_host( self.m_calling_url, long_display=False, force_entity_ip_addr=None) # Here, the commas separating the CGI arguments are intact, but the commas in the arguments are encoded. entity_id_dict = lib_util.SplitMoniker(entity_id) self._concatenate_entity_documentation(full_title, entity_class, entity_id) # Global CanProcessRemote has precedence over parameter can_process_remote # which should probably be deprecated, although they do not have exactly the same role: # * Global CanProcessRemote is used by entity.py to display scripts which have this capability. # * Parameter can_process_remote is used to inform, at execution time, of this capability. # Many scripts are not enumerated by entity.py so a global CanProcessRemote is not necessary. # For clarity, it might be fine to replace the parameter can_process_remote by the global value. # There cannot be nasty consequences except that some scripts might not be displayed # when they should be, and vice-versa. try: globalCanProcessRemote = globals()["CanProcessRemote"] except KeyError: globalCanProcessRemote = False if can_process_remote != globalCanProcessRemote: # "INCONSISTENCY CanProcessRemote ... which is not an issue. can_process_remote = True self.m_can_process_remote = can_process_remote self.m_arguments = cgi.FieldStorage() self.m_entity_type = entity_class self.m_entity_id = entity_id self.m_entity_host = entity_host self.m_entity_id_dict = entity_id_dict self._create_or_get_graph() # Depending on the caller module, maybe the arguments should be 64decoded. See "sql/query". # As the entity type is available, it is possible to import it and check if it encodes it arguments. # See presence of source_types.sql.query.DecodeCgiArg(keyWord,cgiArg) for example. # This is probably too generous to indicate a local host. self.test_remote_if_possible(can_process_remote) if mode == "edit": self.enter_edition_mode() logging.critical("Should not be here because the HTML form is displayed.") assert False # Scripts which can run as events feeders must have their name starting with "events_feeder_". # This allows to use CGI programs as events genetors not written in Python. # TODO: Using the script name is enough, the module is not necessary. full_script_path, _, _ = self.m_calling_url.partition("?") script_basename = os.path.basename(full_script_path) daemonizable_script = os.path.basename(script_basename).startswith("events_feeder_") if not daemonizable_script: # This would be absurd to have a normal CGI script started in this mode. assert mode != "daemon", "Script is not an events generator:" + self.m_calling_url # Runs as usual as a CGI script. The script will fill the graph. return # The events graph must be specified because, from here, everything will access the events graph. set_events_credentials() # Maybe this is in the daemon. if mode == "daemon": # Just runs as usual. At the end of the script, OutCgiRdf will write the RDF graph in the events. # Here, this process is started by the supervisor process; It is not started by the HTTP server, # in CGI or WSGI. return try: # This may throw "[Errno 111] Connection refused" is_daemon_running = lib_daemon.is_events_feeder_daemon_running(self.m_url_without_mode) except Exception as exc: # Then display the content in snapshot mode, which is better than nothing. self.report_error_message("Cannot start daemon, caught:%s\n" % exc) logging.error("Cannot start daemon: When getting daemon status, caught:%s" % exc) return if not is_daemon_running: # This is the case of a daemonizable script, normally run. # TODO: Slight ambiguity here: The daemon could be intentionally stopped, and the user # TODO: would like to see the existing events stored in the persistent triplestore, # TODO: without restarting the daemon. We do not know how to do this yet. lib_daemon.start_events_feeder_daemon(self.m_url_without_mode) # After that, whether the daemon dedicated to the script and its parameters is started or not, # the script is then executed in normal, snapshot mode, as a CGI script. else: # Events are probably stored in the big events graph. The host and port are not used in the URL. lib_kbase.read_events_to_graph(self.m_url_without_mode, self.m_graph) # TODO: The layout parameters and any other display parameters of the calling script # TODO: must be in the constructor. # TODO: This, because the rest of the script is not executed. self.OutCgiRdf() # The rest of the script must not be executed because daemon scripts are organised so that # when the daemon is started, it writes all events in the database, to be read by the same script # run in CGI or WSGI. # The snapshot part of a daemon script is executed only when the deamon is not started. logging.info("Events are read from the events database because the deamon is running.") if _is_wsgi(): logging.info("Leaving the execution of the script run in a WSGI server.") # This is not an error. else: logging.info("Exiting the process of the script run in snapshot mode and CGI server.") # This raises SystemExit which can be handled. exit(0)