#!/usr/bin/env python3
# This file is placed in the Public Domain.
# pylint: disable=C0413,W0212,W0718,E0401


"main"


import getpass
import os
import pathlib
import pwd
import sys
import time


from otpcr.cfg   import Config
from otpcr.cli   import CLI
from otpcr.defer import Errors, errors, later
from otpcr.disk  import Persist
from otpcr.log   import Logging
from otpcr.main  import init


from otpcr import modules
from otpcr import user


Cfg         = Config()
Cfg.dis     = ""
Cfg.mod     = "cmd,err,mod,thr"
Cfg.opts    = ""
Cfg.name    = "otpcr"
Cfg.user    = getpass.getuser()
Cfg.wdr     = os.path.expanduser(f"~/.{Cfg.name}")
Cfg.pidfile = os.path.join(Cfg.wdr, f"{Cfg.name}.pid")


Persist.workdir = Cfg.wdr


def daemon(pidfile, verbose=False):
    "switch to background."
    pid = os.fork()
    if pid != 0:
        os._exit(0)
    os.setsid()
    pid2 = os.fork()
    if pid2 != 0:
        os._exit(0)
    if not verbose:
        with open('/dev/null', 'r', encoding="utf-8") as sis:
            os.dup2(sis.fileno(), sys.stdin.fileno())
        with open('/dev/null', 'a+', encoding="utf-8") as sos:
            os.dup2(sos.fileno(), sys.stdout.fileno())
        with open('/dev/null', 'a+', encoding="utf-8") as ses:
            os.dup2(ses.fileno(), sys.stderr.fileno())
    os.umask(0)
    os.chdir("/")
    if os.path.exists(pidfile):
        os.unlink(pidfile)
    path = pathlib.Path(pidfile)
    path.parent.mkdir(parents=True, exist_ok=True)
    with open(pidfile, "w", encoding="utf-8") as fds:
        fds.write(str(os.getpid()))


def forever():
    "it never ends, until ctl-c."
    while True:
        time.sleep(1.0)


def modnames():
    "list all modules."
    return sorted({x for x in dir(modules) + dir(user) if not x.startswith("__")})


def privileges(username):
    "drop privileges."
    pwnam = pwd.getpwnam(username)
    os.setgid(pwnam.pw_gid)
    os.setuid(pwnam.pw_uid)


def wrap(func):
    "catch exceptions"
    try:
        func()
    except (KeyboardInterrupt, EOFError):
        print("")
    except Exception as ex:
        later(ex)
    errors()


def main():
    "main"
    daemon(Cfg.pidfile, "-v" in sys.argv)
    privileges(Cfg.user)
    CLI.out = Errors.out = Logging.out = None
    Cfg.mod = "," + ",".join(modnames())
    init(modules, Cfg.mod)
    init(user, Cfg.mod)
    forever()


if __name__ == "__main__":
    wrap(main)
