# # This file is distributed under the MIT License. See LICENSE.md for details. # import os import shlex from argparse import ArgumentParser, RawDescriptionHelpFormatter from sys import executable as py_executable from revng.internal.cli.commands_registry import Command, CommandsRegistry, Options from revng.internal.cli.support import collect_libraries, exec_run, handle_asan class DaemonCommand(Command): def __init__(self): super().__init__(("daemon",), "Run revng GraphQL server") def register_arguments(self, parser: ArgumentParser): parser.formatter_class = RawDescriptionHelpFormatter parser.description = """Run revng GraphQL server This command starts the execution of the revng graphql server with all installed analysis libraries and pipelines. Environment variables that are used: STARLETTE_DEBUG: if set to "1" enables debug mode, unset when using --production STARLETTE_MIDDLEWARES_{EARLY,LATE}: a comma-separated list of extra Middlewares to use in starlette. It follows the syntax: import.path.to.module.ObjectName,... REVNG_NOTIFY_FIFOS: list of fifo pipes to be notified of changes in the pipeline it follows the following format: :,:,... Event types that are available: begin, context REVNG_ORIGINS: comma-separated list of allowed CORS origins REVNG_EXPOSE_HEADERS: comma-separated list of response headers to expose via CORS REVNG_C_API_TRACE_PATH: path to file to use to save api tracing, useful for debugging Persistence: If the REVNG_DATA_DIR environment variable is set, the the data is persisted across restarts. Otherwise all progress will be lost on daemon shutdown. """ parser.add_argument("-p", "--port", type=int, default=8000, help="Port to use") parser.add_argument( "--uvicorn-args", type=str, default="", help="Extra arguments to pass to uvicorn" ) parser.add_argument( "--production", action="store_true", help="Start server in production mode, this runs the server on all interfaces", ) parser.add_argument( "-b", "--bind", type=str, help=( "Manually bind to the specified address. " "format: 'tcp::port'/'unix:' or 'none' to disable binding" ), ) def run(self, options: Options): _, dependencies = collect_libraries(options.search_prefixes) prefix = handle_asan(dependencies, options.search_prefixes) env = {**os.environ} args = ["--no-access-log", "--workers", "1", "--timeout-keep-alive", "600"] extra_args = shlex.split(options.parsed_args.uvicorn_args) host = "127.0.0.1" port = str(options.parsed_args.port) if bind := options.parsed_args.bind: if bind.startswith("tcp:"): _, host, port = bind.split(":", 2) args.extend(["--host", host, "--port", port]) elif bind.startswith("unix:"): _, path = bind.split(":", 1) args.extend(["--uds", path]) elif bind == "none": pass else: raise ValueError(f"Unknown bind address: {bind}") else: if options.parsed_args.production: host = "0.0.0.0" args.extend(["--host", host, "--port", port]) if not options.parsed_args.production: env["STARLETTE_DEBUG"] = "1" env["REVNG_ORIGINS"] = "*" exec_run( prefix + [ py_executable, "-m", "uvicorn", *args, *extra_args, "revng.internal.daemon:app", ], options, env, ) def setup(commands_registry: CommandsRegistry): commands_registry.register_command(DaemonCommand())