Hallo, dies ist ein Test.
PWD: /www/data-lst1/unixsoft/unixsoft/kaempfer/.public_html
Running in File Mode
Relative path: ./../../../../../../usr/dtrace/../lib/pkg.depotd
Real path: /usr/lib/pkg.depotd
Zurück
#!/usr/bin/python3.7 -Es # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007, 2023, Oracle and/or its affiliates. # import pkg.no_site_packages # pkg.depotd - package repository daemon # XXX The prototype pkg.depotd combines both the version management server that # answers to pkgsend(1) sessions and the HTTP file server that answers to the # various GET operations that a pkg(1) client makes. This split is expected to # be made more explicit, by constraining the pkg(1) operations such that they # can be served as a typical HTTP/HTTPS session. Thus, pkg.depotd will reduce # to a special purpose HTTP/HTTPS server explicitly for the version management # operations, and must manipulate the various state files--catalogs, in # particular--such that the pkg(1) pull client can operately accurately with # only a basic HTTP/HTTPS server in place. # XXX Although we pushed the evaluation of next-version, etc. to the pull # client, we should probably provide a query API to do same on the server, for # dumb clients (like a notification service). # The default path for static and other web content. CONTENT_PATH_DEFAULT = "/usr/share/lib/pkg" # cherrypy has a max_request_body_size parameter that determines whether the # server should abort requests with REQUEST_ENTITY_TOO_LARGE when the request # body is larger than the specified size (in bytes). The maximum size supported # by cherrypy is 2048 * 1024 * 1024 - 1 (just short of 2048MB), but the default # here is purposefully conservative. MAX_REQUEST_BODY_SIZE = 512 * 1024 * 1024 # The default host/port(s) to serve data from. HOST_DEFAULT = "0.0.0.0" PORT_DEFAULT = 80 SSL_PORT_DEFAULT = 443 # The minimum number of threads allowed. THREADS_MIN = 1 # The default number of threads to start. THREADS_DEFAULT = 60 # The maximum number of threads that can be started. THREADS_MAX = 5000 # The default server socket timeout in seconds. We want this to be longer than # the normal default of 10 seconds to accommodate clients with poor quality # connections. SOCKET_TIMEOUT_DEFAULT = 60 import getopt import gettext import locale import logging import os import os.path import OpenSSL.crypto as crypto import shlex import six import string import subprocess import sys import tempfile if sys.version_info[:2] >= (3, 4): from importlib import reload else: from imp import reload from six.moves.urllib.parse import urlparse, urlunparse import cherrypy import cherrypy.process.servers from cherrypy.process.plugins import Daemonizer from cherrypy._cpdispatch import Dispatcher from pkg.misc import msg, emsg, setlocale from pkg.client.debugvalues import DebugValues import pkg import pkg.client.api_errors as api_errors import pkg.config as cfg import pkg.portable.util as os_util import pkg.search_errors as search_errors import pkg.server.depot as ds import pkg.server.repository as sr try: import portend except ImportError: emsg("Python module: portend not found.") sys.exit(1) # Starting in CherryPy 3.2, its default dispatcher converts all punctuation to # underscore. Since publisher name can contain the hyphen symbol "-", in order # to let the dispatcher to find the correct page handler, we need to skip # converting the hyphen symbol. punc = string.punctuation.replace("-", "_") if six.PY2: translate = string.maketrans(punc, "_" * len(string.punctuation)) else: translate = str.maketrans(punc, "_" * len(string.punctuation)) class Pkg5Dispatcher(Dispatcher): def __init__(self, **args): Dispatcher.__init__(self, translate=translate) class LogSink(object): """This is a dummy object that we can use to discard log entries without relying on non-portable interfaces such as /dev/null.""" def write(self, *args, **kwargs): """Discard the bits.""" pass def flush(self, *args, **kwargs): """Discard the bits.""" pass def usage(text=None, retcode=2, full=False): """Optionally emit a usage message and then exit using the specified exit code.""" if text: emsg(text) if not full: # The full usage message isn't desired. emsg(_("Try `pkg.depotd --help or -?' for more " "information.")) sys.exit(retcode) print("""\ Usage: /usr/lib/pkg.depotd [-a address] [-d inst_root] [-p port] [-s threads] [-t socket_timeout] [--cfg] [--content-root] [--disable-ops op[/1][,...]] [--debug feature_list] [--image-root dir] [--log-access dest] [--log-errors dest] [--mirror] [--nasty] [--nasty-sleep] [--proxy-base url] [--readonly] [--ssl-cert-file] [--ssl-dialog] [--ssl-key-file] [--sort-file-max-size size] [--writable-root dir] -a address The IP address on which to listen for connections. The default value is 0.0.0.0 (INADDR_ANY) which will listen on all active interfaces. To listen on all active IPv6 interfaces, use '::'. -d inst_root The file system path at which the server should find its repository data. Required unless PKG_REPO has been set in the environment. -p port The port number on which the instance should listen for incoming package requests. The default value is 80 if ssl certificate and key information has not been provided; otherwise, the default value is 443. -s threads The number of threads that will be started to serve requests. The default value is 10. -t timeout The maximum number of seconds the server should wait for a response from a client before closing a connection. The default value is 60. --cfg The pathname of the file to use when reading and writing depot configuration data, or a fully qualified service fault management resource identifier (FMRI) of the SMF service or instance to read configuration data from. --content-root The file system path to the directory containing the the static and other web content used by the depot's browser user interface. The default value is '/usr/share/lib/pkg'. --disable-ops A comma separated list of operations that the depot should not configure. If, for example, you wanted to omit loading search v1, 'search/1' should be provided as an argument, or to disable all search operations, simply 'search'. --debug The name of a debug feature to enable; or a whitespace or comma separated list of features to enable. Possible values are: headers, hash=sha1+sha256, hash=sha256, hash=sha1+sha512t_256, hash=sha512t_256 --image-root The path to the image whose file information will be used as a cache for file data. --log-access The destination for any access related information logged by the depot process. Possible values are: stderr, stdout, none, or an absolute pathname. The default value is stdout if stdout is a tty; otherwise the default value is none. --log-errors The destination for any errors or other information logged by the depot process. Possible values are: stderr, stdout, none, or an absolute pathname. The default value is stderr. --mirror Package mirror mode; publishing and metadata operations disallowed. Cannot be used with --readonly or --rebuild. --nasty Instruct the server to misbehave. At random intervals it will time-out, send bad responses, hang up on clients, and generally be hostile. The option takes a value (1 to 100) for how nasty the server should be. --nasty-sleep In nasty mode (see --nasty), how many seconds to randomly sleep when a random sleep occurs. --proxy-base The url to use as the base for generating internal redirects and content. --readonly Read-only operation; modifying operations disallowed. Cannot be used with --mirror or --rebuild. --ssl-cert-file The absolute pathname to a PEM-encoded Certificate file. This option must be used with --ssl-key-file. Usage of this option will cause the depot to only respond to SSL requests on the provided port. --ssl-dialog Specifies what method should be used to obtain the passphrase needed to decrypt the file specified by --ssl-key-file. Supported values are: builtin, exec:/path/to/program, smf, or an SMF FMRI. The default value is builtin. If smf is specified, an SMF FMRI must be provided using the --cfg option. --ssl-key-file The absolute pathname to a PEM-encoded Private Key file. This option must be used with --ssl-cert-file. Usage of this option will cause the depot to only respond to SSL requests on the provided port. --sort-file-max-size The maximum size of the indexer sort file. Used to limit the amount of RAM the depot uses for indexing, or increase it for speed. --writable-root The path to a directory to which the program has write access. Used with --readonly to allow server to create needed files, such as search indices, without needing write access to the package information. Options: --help or -? Environment: PKG_REPO Used as default inst_root if -d not provided. PKG_DEPOT_CONTENT Used as default content_root if --content-root not provided.""") sys.exit(retcode) class OptionError(Exception): """Option exception. """ def __init__(self, *args): Exception.__init__(self, *args) @cherrypy.tools.register('before_finalize', priority=60) def secureheaders(): headers = cherrypy.response.headers headers['X-Frame-Options'] = 'SAMEORIGIN' headers['X-XSS-Protection'] = '1; mode=block' headers['Content-Security-Policy'] = "default-src 'self';" if __name__ == "__main__": setlocale(locale.LC_ALL, "") gettext.install("pkg", "/usr/share/locale", codeset=locale.getpreferredencoding()) add_content = False exit_ready = False rebuild = False reindex = False nasty = False # Track initial configuration values. ivalues = { "pkg": {}, "nasty": {} } if "PKG_REPO" in os.environ: ivalues["pkg"]["inst_root"] = os.environ["PKG_REPO"] try: content_root = os.environ["PKG_DEPOT_CONTENT"] ivalues["pkg"]["content_root"] = content_root except KeyError: try: content_root = os.path.join(os.environ['PKG_HOME'], 'share/lib/pkg') ivalues["pkg"]["content_root"] = content_root except KeyError: pass opt = None addresses = set() debug_features = [] disable_ops = [] repo_props = {} socket_path = "" user_cfg = None try: long_opts = ["add-content", "cfg=", "cfg-file=", "content-root=", "debug=", "disable-ops=", "exit-ready", "help", "image-root=", "log-access=", "log-errors=", "llmirror", "mirror", "nasty=", "nasty-sleep=", "proxy-base=", "readonly", "rebuild", "refresh-index", "set-property=", "ssl-cert-file=", "ssl-dialog=", "ssl-key-file=", "sort-file-max-size=", "writable-root="] opts, pargs = getopt.getopt(sys.argv[1:], "a:d:np:s:t:?", long_opts) show_usage = False for opt, arg in opts: if opt == "-a": addresses.add(arg) elif opt == "-n": sys.exit(0) elif opt == "-d": ivalues["pkg"]["inst_root"] = arg elif opt == "-p": ivalues["pkg"]["port"] = arg elif opt == "-s": threads = int(arg) if threads < THREADS_MIN: raise OptionError( "minimum value is {0:d}".format( THREADS_MIN)) if threads > THREADS_MAX: raise OptionError( "maximum value is {0:d}".format( THREADS_MAX)) ivalues["pkg"]["threads"] = threads elif opt == "-t": ivalues["pkg"]["socket_timeout"] = arg elif opt == "--add-content": add_content = True elif opt == "--cfg": user_cfg = arg elif opt == "--cfg-file": ivalues["pkg"]["cfg_file"] = arg elif opt == "--content-root": ivalues["pkg"]["content_root"] = arg elif opt == "--debug": if arg is None or arg == "": continue # A list of features can be specified using a # "," or any whitespace character as separators. if "," in arg: features = arg.split(",") else: features = arg.split() debug_features.extend(features) # We also allow key=value debug flags, which # get set in pkg.client.debugvalues for feature in features: try: key, val = feature.split("=", 1) DebugValues.set_value(key, val) except (AttributeError, ValueError): pass elif opt == "--disable-ops": if arg is None or arg == "": raise OptionError( "An argument must be specified.") disableops = arg.split(",") for s in disableops: if "/" in s: op, ver = s.rsplit("/", 1) else: op = s ver = "*" if op not in \ ds.DepotHTTP.REPO_OPS_DEFAULT: raise OptionError( "Invalid operation " "'{0}'.".format(s)) disable_ops.append(s) elif opt == "--exit-ready": exit_ready = True elif opt == "--image-root": ivalues["pkg"]["image_root"] = arg elif opt.startswith("--log-"): prop = "log_{0}".format(opt.lstrip("--log-")) ivalues["pkg"][prop] = arg elif opt in ("--help", "-?"): show_usage = True elif opt == "--mirror": ivalues["pkg"]["mirror"] = True elif opt == "--llmirror": ivalues["pkg"]["mirror"] = True ivalues["pkg"]["ll_mirror"] = True ivalues["pkg"]["readonly"] = True elif opt == "--nasty": # ValueError is caught by caller. nasty_value = int(arg) if (nasty_value > 100 or nasty_value < 1): raise OptionError("Invalid value " "for nasty option.\n Please " "choose a value between 1 and 100.") nasty = True ivalues["nasty"]["nasty_level"] = nasty_value elif opt == "--nasty-sleep": # ValueError is caught by caller. sleep_value = int(arg) ivalues["nasty"]["nasty_sleep"] = sleep_value elif opt == "--proxy-base": # Attempt to decompose the url provided into # its base parts. This is done so we can # remove any scheme information since we # don't need it. scheme, netloc, path, params, query, \ fragment = urlparse(arg, "http", allow_fragments=0) if not netloc: raise OptionError("Unable to " "determine the hostname from " "the provided URL; please use a " "fully qualified URL.") scheme = scheme.lower() if scheme not in ("http", "https"): raise OptionError("Invalid URL; http " "and https are the only supported " "schemes.") # Rebuild the url with the sanitized components. ivalues["pkg"]["proxy_base"] = \ urlunparse((scheme, netloc, path, params, query, fragment)) elif opt == "--readonly": ivalues["pkg"]["readonly"] = True elif opt == "--rebuild": rebuild = True elif opt == "--refresh-index": # Note: This argument is for internal use # only. # # This flag is purposefully omitted in usage. # The supported way to forcefully reindex is to # kill any pkg.depot using that directory, # remove the index directory, and restart the # pkg.depot process. The index will be rebuilt # automatically on startup. reindex = True exit_ready = True elif opt == "--set-property": try: prop, p_value = arg.split("=", 1) p_sec, p_name = prop.split(".", 1) except ValueError: usage(_("property arguments must be of " "the form '<section.property>=" "<value>'.")) repo_props.setdefault(p_sec, {}) repo_props[p_sec][p_name] = p_value elif opt == "--ssl-cert-file": if arg == "none" or arg == "": # Assume this is an override to clear # the value. arg = "" elif not os.path.isabs(arg): raise OptionError("The path to " "the Certificate file must be " "absolute.") elif not os.path.exists(arg): raise OptionError("The specified " "file does not exist.") elif not os.path.isfile(arg): raise OptionError("The specified " "pathname is not a file.") ivalues["pkg"]["ssl_cert_file"] = arg elif opt == "--ssl-key-file": if arg == "none" or arg == "": # Assume this is an override to clear # the value. arg = "" elif not os.path.isabs(arg): raise OptionError("The path to " "the Private Key file must be " "absolute.") elif not os.path.exists(arg): raise OptionError("The specified " "file does not exist.") elif not os.path.isfile(arg): raise OptionError("The specified " "pathname is not a file.") ivalues["pkg"]["ssl_key_file"] = arg elif opt == "--ssl-dialog": if arg != "builtin" and \ arg != "smf" and not \ arg.startswith("exec:/") and not \ arg.startswith("svc:"): raise OptionError("Invalid value " "specified. Expected: builtin, " "exec:/path/to/program, smf, or " "an SMF FMRI.") if arg.startswith("exec:"): if os_util.get_canonical_os_type() != \ "unix": # Don't allow a somewhat # insecure authentication method # on some platforms. raise OptionError("exec is " "not a supported dialog " "type for this operating " "system.") f = os.path.abspath(arg.split( "exec:")[1]) if not os.path.isfile(f): raise OptionError("Invalid " "file path specified for " "exec.") ivalues["pkg"]["ssl_dialog"] = arg elif opt == "--sort-file-max-size": ivalues["pkg"]["sort_file_max_size"] = arg elif opt == "--writable-root": ivalues["pkg"]["writable_root"] = arg # Set accumulated values. if debug_features: ivalues["pkg"]["debug"] = debug_features if disable_ops: ivalues["pkg"]["disable_ops"] = disable_ops if addresses: ivalues["pkg"]["address"] = list(addresses) if DebugValues: reload(pkg.digest) # Build configuration object. dconf = ds.DepotConfig(target=user_cfg, overrides=ivalues) except getopt.GetoptError as _e: usage("pkg.depotd: {0}".format(_e.msg)) except api_errors.ApiException as _e: usage("pkg.depotd: {0}".format(str(_e))) except OptionError as _e: usage("pkg.depotd: option: {0} -- {1}".format(opt, _e)) except (ArithmeticError, ValueError): usage("pkg.depotd: illegal option value: {0} specified " \ "for option: {1}".format(arg, opt)) if show_usage: usage(retcode=0, full=True) if not dconf.get_property("pkg", "log_errors"): dconf.set_property("pkg", "log_errors", "stderr") # If stdout is a tty, then send access output there by default instead # of discarding it. if not dconf.get_property("pkg", "log_access"): if os.isatty(sys.stdout.fileno()): dconf.set_property("pkg", "log_access", "stdout") else: dconf.set_property("pkg", "log_access", "none") # Check for invalid option combinations. image_root = dconf.get_property("pkg", "image_root") inst_root = dconf.get_property("pkg", "inst_root") mirror = dconf.get_property("pkg", "mirror") ll_mirror = dconf.get_property("pkg", "ll_mirror") readonly = dconf.get_property("pkg", "readonly") writable_root = dconf.get_property("pkg", "writable_root") if rebuild and add_content: usage("--add-content cannot be used with --rebuild") if rebuild and reindex: usage("--refresh-index cannot be used with --rebuild") if (rebuild or add_content) and (readonly or mirror): usage("--readonly and --mirror cannot be used with --rebuild " "or --add-content") if reindex and mirror: usage("--mirror cannot be used with --refresh-index") if reindex and readonly and not writable_root: usage("--readonly can only be used with --refresh-index if " "--writable-root is used") if image_root and not ll_mirror: usage("--image-root can only be used with --llmirror.") if image_root and writable_root: usage("--image_root and --writable-root cannot be used " "together.") if image_root and inst_root: usage("--image-root and -d cannot be used together.") # If the image format changes this may need to be reexamined. if image_root: inst_root = os.path.join(image_root, "var", "pkg") # Set any values using defaults if they weren't provided. # Only use the first value for now; multiple bind addresses may be # supported later. address = dconf.get_property("pkg", "address") if address: address = address[0] elif not address: dconf.set_property("pkg", "address", [HOST_DEFAULT]) address = dconf.get_property("pkg", "address")[0] if not inst_root: usage("Either PKG_REPO or -d must be provided") content_root = dconf.get_property("pkg", "content_root") if not content_root: dconf.set_property("pkg", "content_root", CONTENT_PATH_DEFAULT) content_root = dconf.get_property("pkg", "content_root") port = dconf.get_property("pkg", "port") ssl_cert_file = dconf.get_property("pkg", "ssl_cert_file") ssl_key_file = dconf.get_property("pkg", "ssl_key_file") if (ssl_cert_file and not ssl_key_file) or (ssl_key_file and not ssl_cert_file): usage("The --ssl-cert-file and --ssl-key-file options must " "must both be provided when using either option.") elif not port: if ssl_cert_file and ssl_key_file: dconf.set_property("pkg", "port", SSL_PORT_DEFAULT) else: dconf.set_property("pkg", "port", PORT_DEFAULT) port = dconf.get_property("pkg", "port") socket_timeout = dconf.get_property("pkg", "socket_timeout") if not socket_timeout: dconf.set_property("pkg", "socket_timeout", SOCKET_TIMEOUT_DEFAULT) socket_timeout = dconf.get_property("pkg", "socket_timeout") threads = dconf.get_property("pkg", "threads") if not threads: dconf.set_property("pkg", "threads", THREADS_DEFAULT) threads = dconf.get_property("pkg", "threads") # If the program is going to reindex, the port is irrelevant since # the program will not bind to a port. if not exit_ready: try: # Wait for the port to be free, time out in 1 sec. portend.free(address, port, 1.0) except Exception as e: emsg("pkg.depotd: unable to bind to the specified " "port: {0:d}. Reason: {1}".format(port, e)) sys.exit(1) else: # Not applicable if we're not going to serve content dconf.set_property("pkg", "content_root", "") # Any relative paths should be made absolute using pkg_root. 'pkg_root' # is a special property that was added to enable internal deployment of # multiple disparate versions of the pkg.depotd software. pkg_root = dconf.get_property("pkg", "pkg_root") repo_config_file = dconf.get_property("pkg", "cfg_file") if repo_config_file and not os.path.isabs(repo_config_file): repo_config_file = os.path.join(pkg_root, repo_config_file) if content_root and not os.path.isabs(content_root): content_root = os.path.join(pkg_root, content_root) if inst_root and not os.path.isabs(inst_root): inst_root = os.path.join(pkg_root, inst_root) if ssl_cert_file: if ssl_cert_file == "none": ssl_cert_file = None elif not os.path.isabs(ssl_cert_file): ssl_cert_file = os.path.join(pkg_root, ssl_cert_file) if ssl_key_file: if ssl_key_file == "none": ssl_key_file = None elif not os.path.isabs(ssl_key_file): ssl_key_file = os.path.join(pkg_root, ssl_key_file) if writable_root and not os.path.isabs(writable_root): writable_root = os.path.join(pkg_root, writable_root) # Setup SSL if requested. key_data = None ssl_dialog = dconf.get_property("pkg", "ssl_dialog") if not exit_ready and ssl_cert_file and ssl_key_file and \ ssl_dialog != "builtin": cmdline = None def get_ssl_passphrase(*ignored): p = None try: if cmdline: cmdargs = shlex.split(cmdline) else: cmdargs = [] p = subprocess.Popen(cmdargs, stdout=subprocess.PIPE, stderr=None) p.wait() except Exception as __e: emsg("pkg.depotd: an error occurred while " "executing [{0}]; unable to obtain the " "passphrase needed to decrypt the SSL " "private key file: {1}".format(cmdline, __e)) sys.exit(1) return p.stdout.read().strip(b"\n") if ssl_dialog.startswith("exec:"): exec_path = ssl_dialog.split("exec:")[1] if not os.path.isabs(exec_path): exec_path = os.path.join(pkg_root, exec_path) cmdline = "{0} {1} {2:d}".format(exec_path, "''", port) elif ssl_dialog == "smf" or ssl_dialog.startswith("svc:"): if ssl_dialog == "smf": # Assume the configuration target was an SMF # FMRI and let svcprop fail with an error if # it wasn't. svc_fmri = dconf.target else: svc_fmri = ssl_dialog cmdline = "/usr/bin/svcprop -p " \ "pkg_secure/ssl_key_passphrase {0}".format(svc_fmri) # The key file requires decryption, but the user has requested # exec-based authentication, so it will have to be decoded first # to an un-named temporary file. try: with open(ssl_key_file, "rb") as key_file: pkey = crypto.load_privatekey( crypto.FILETYPE_PEM, key_file.read(), get_ssl_passphrase) key_data = tempfile.NamedTemporaryFile(dir=pkg_root, delete=True) key_data.write(crypto.dump_privatekey( crypto.FILETYPE_PEM, pkey)) key_data.seek(0) except EnvironmentError as _e: emsg("pkg.depotd: unable to read the SSL private key " "file: {0}".format(_e)) sys.exit(1) except crypto.Error as _e: emsg("pkg.depotd: authentication or cryptography " "failure while attempting to decode\nthe SSL " "private key file: {0}".format(_e)) sys.exit(1) else: # Redirect the server to the decrypted key file. ssl_key_file = key_data.name # Setup our global configuration. gconf = { "checker.on": True, "environment": "production", "log.screen": False, "server.max_request_body_size": MAX_REQUEST_BODY_SIZE, "server.shutdown_timeout": 0, "server.socket_host": address, "server.socket_port": port, "server.socket_timeout": socket_timeout, "server.ssl_certificate": ssl_cert_file, "server.ssl_private_key": ssl_key_file, "server.thread_pool": threads, "tools.log_headers.on": True, "tools.encode.on": True, "tools.encode.encoding": "utf-8", "tools.secureheaders.on" : True, } if "headers" in dconf.get_property("pkg", "debug"): # Despite its name, this only logs headers when there is an # error; it's redundant with the debug feature enabled. gconf["tools.log_headers.on"] = False # Causes the headers of every request to be logged to the error # log; even if an exception occurs. gconf["tools.log_headers_always.on"] = True cherrypy.tools.log_headers_always = cherrypy.Tool( "on_start_resource", cherrypy.lib.cptools.log_request_headers) log_cfg = { "access": dconf.get_property("pkg", "log_access"), "errors": dconf.get_property("pkg", "log_errors") } # If stdin is not a tty and the pkgdepot controller isn't being used, # then assume process will be daemonized and redirect output. if not os.environ.get("PKGDEPOT_CONTROLLER") and \ not os.isatty(sys.stdin.fileno()): # Ensure log handlers are setup to use the file descriptors for # stdout and stderr as the Daemonizer (used for test suite and # SMF service) requires this. if log_cfg["access"] == "stdout": log_cfg["access"] = "/dev/fd/{0:d}".format( sys.stdout.fileno()) elif log_cfg["access"] == "stderr": log_cfg["access"] = "/dev/fd/{0:d}".format( sys.stderr.fileno()) elif log_cfg["access"] == "none": log_cfg["access"] = "/dev/null" if log_cfg["errors"] == "stderr": log_cfg["errors"] = "/dev/fd/{0:d}".format( sys.stderr.fileno()) elif log_cfg["errors"] == "stdout": log_cfg["errors"] = "/dev/fd/{0:d}".format( sys.stdout.fileno()) elif log_cfg["errors"] == "none": log_cfg["errors"] = "/dev/null" log_type_map = { "errors": { "param": "log.error_file", "attr": "error_log" }, "access": { "param": "log.access_file", "attr": "access_log" } } for log_type in log_type_map: dest = log_cfg[log_type] if dest in ("stdout", "stderr", "none"): if dest == "none": h = logging.StreamHandler(LogSink()) else: h = logging.StreamHandler(eval("sys.{0}".format( dest))) h.setLevel(logging.DEBUG) h.setFormatter(cherrypy._cplogging.logfmt) log_obj = eval("cherrypy.log.{0}".format( log_type_map[log_type]["attr"])) log_obj.addHandler(h) # Since we've replaced cherrypy's log handler with our # own, we don't want the output directed to a file. dest = "" elif dest: if not os.path.isabs(dest): dest = os.path.join(pkg_root, dest) gconf[log_type_map[log_type]["param"]] = dest cherrypy.config.update(gconf) # Now that our logging, etc. has been setup, it's safe to perform any # remaining preparation. # Initialize repository state. if not readonly: # Not readonly, so assume a new repository should be created. try: sr.repository_create(inst_root, properties=repo_props) except sr.RepositoryExistsError: # Already exists, nothing to do. pass except (api_errors.ApiException, sr.RepositoryError) as _e: emsg("pkg.depotd: {0}".format(_e)) sys.exit(1) try: sort_file_max_size = dconf.get_property("pkg", "sort_file_max_size") repo = sr.Repository(cfgpathname=repo_config_file, log_obj=cherrypy, mirror=mirror, properties=repo_props, read_only=readonly, root=inst_root, sort_file_max_size=sort_file_max_size, writable_root=writable_root) except (RuntimeError, sr.RepositoryError) as _e: emsg("pkg.depotd: {0}".format(_e)) sys.exit(1) except search_errors.IndexingException as _e: emsg("pkg.depotd: {0}".format(str(_e)), "INDEX") sys.exit(1) except api_errors.ApiException as _e: emsg("pkg.depotd: {0}".format(str(_e))) sys.exit(1) if not rebuild and not add_content and not repo.mirror and \ not (repo.read_only and not repo.writable_root): # Automatically update search indexes on startup if not already # told to, and not in readonly/mirror mode. reindex = True if reindex: try: # Only execute a index refresh here if --exit-ready was # requested; it will be handled later in the setup # process for other cases. if repo.root and exit_ready: repo.refresh_index() except (sr.RepositoryError, search_errors.IndexingException, api_errors.ApiException) as e: emsg(str(e), "INDEX") sys.exit(1) elif rebuild: try: repo.rebuild(build_index=True) except sr.RepositoryError as e: emsg(str(e), "REBUILD") sys.exit(1) except (search_errors.IndexingException, api_errors.UnknownErrors, api_errors.PermissionsException) as e: emsg(str(e), "INDEX") sys.exit(1) elif add_content: try: repo.add_content() repo.refresh_index() except sr.RepositoryError as e: emsg(str(e), "ADD_CONTENT") sys.exit(1) except (search_errors.IndexingException, api_errors.UnknownErrors, api_errors.PermissionsException) as e: emsg(str(e), "INDEX") sys.exit(1) # Ready to start depot; exit now if requested. if exit_ready: sys.exit(0) # Next, initialize depot. if nasty: depot = ds.NastyDepotHTTP(repo, dconf) else: depot = ds.DepotHTTP(repo, dconf) # Now build our site configuration. conf = { "/": {}, "/robots.txt": { "tools.staticfile.on": True, "tools.staticfile.filename": os.path.join(depot.web_root, "robots.txt") }, } conf["/"]["request.dispatch"] = Pkg5Dispatcher() proxy_base = dconf.get_property("pkg", "proxy_base") if proxy_base: # This changes the base URL for our server, and is primarily # intended to allow our depot process to operate behind Apache # or some other webserver process. # # Visit the following URL for more information: # http://cherrypy.org/wiki/BuiltinTools#tools.proxy proxy_conf = { "tools.proxy.on": True, "tools.proxy.local": "", "tools.proxy.base": proxy_base } # Now merge or add our proxy configuration information into the # existing configuration. for entry in proxy_conf: conf["/"][entry] = proxy_conf[entry] if ll_mirror: ds.DNSSD_Plugin(cherrypy.engine, gconf).subscribe() if reindex: # Tell depot to update search indexes when possible; # this is done as a background task so that packages # can be served immediately while search indexes are # still being updated. depot._queue_refresh_index() # If stdin is not a tty and the pkgdepot controller isn't being used, # then assume process should be daemonized. if not os.environ.get("PKGDEPOT_CONTROLLER") and \ not os.isatty(sys.stdin.fileno()): # Translate the values in log_cfg into paths. Daemonizer(cherrypy.engine, stderr=log_cfg["errors"], stdout=log_cfg["access"]).subscribe() try: root = cherrypy.Application(depot) cherrypy.quickstart(root, config=conf) except Exception as _e: emsg("pkg.depotd: unknown error starting depot server, " \ "illegal option value specified?") emsg(_e) sys.exit(1)