aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/cli/dispatch.py
blob: fc5712febc68a01972cd027a603ae28580eeb555 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""From the command line to the verb that runs it."""

import logging

from meshbay_node.cli import content, groups, lifecycle, members, settings, setup, status
from meshbay_node.cli.parser import build_parser
from meshbay_node.platform import config_dir


def start():
    """Set up the process and read its command line."""
    from meshbay_node.platform import configure_event_loop, force_utf8_stdio, load_node_env
    force_utf8_stdio()
    configure_event_loop()
    # Before anything reads the environment. On Linux systemd has usually loaded
    # the same file already via EnvironmentFile=; this is what makes a Windows
    # run (Startup-folder .vbs, no systemd) and a bare `meshbay-node` behave the
    # same. Already-set variables are left alone, so it cannot undo either.
    load_node_env(config_dir())

    parser = build_parser()
    args = parser.parse_args()

    # Query commands print a report; library logging would interleave with it.
    quiet = args.command in ("status", "gek-init", "gek", "operator",
                             "member", "group", "root", "file", "video", "chat",
                             "denylist", "stun", "reload", "restart-daemon",
                             "reset")
    logging.basicConfig(
        level=logging.ERROR if quiet else getattr(logging, args.log_level),
        format=LOG_FORMAT,
    )
    if args.command is None:
        add_log_file()
    return args


LOG_FORMAT = "%(asctime)s %(levelname)-8s %(name)s: %(message)s"


def add_log_file() -> None:
    """Also log the daemon to platform.log_file(), where there is one."""
    from logging.handlers import RotatingFileHandler

    from meshbay_node.platform import log_file
    path = log_file()
    if path is None:
        return
    try:
        path.parent.mkdir(parents=True, exist_ok=True)
        # delay: nothing is created until there is something to write.
        handler = RotatingFileHandler(path, maxBytes=5 * 1024 * 1024, backupCount=3,
                                      encoding="utf-8", delay=True)
    except OSError as e:
        logging.getLogger(__name__).warning("cannot open the log file %s: %s", path, e)
        return
    handler.setFormatter(logging.Formatter(LOG_FORMAT))
    logging.getLogger().addHandler(handler)


# Each verb and what runs it. A command line naming none of them starts the
# daemon, which is daemon.main's to do.
VERBS = {
    "init": setup.init,
    "reset": setup.reset,
    "status": status.status,
    "gek-init": groups.gek,
    "gek": groups.gek,
    "operator": members.operator,
    "member": members.member,
    "group": groups.group,
    "root": groups.root,
    "file": content.file,
    "video": content.video,
    "chat": content.chat,
    "denylist": settings.denylist,
    "stun": settings.stun,
    "transfers": settings.transfers,
    "reload": lifecycle.reload,
    "restart-daemon": lifecycle.restart_daemon,
    "autostart": lifecycle.autostart,
    "service": lifecycle.service,
    "calibrate-argon2": setup.calibrate,
}


def run(args) -> bool:
    """Run the verb on the command line; False when it names none."""
    verb = VERBS.get(args.command)
    if verb is None:
        return False
    verb(args)
    return True