"""
Cobbler daemon for logging remote syslog traffic during automatic installation
"""
import binascii
import logging
import os
import pwd
import sys
import time
import systemd.daemon # type: ignore
from cobbler import remote, utils
from cobbler.api import CobblerAPI
try:
import psutil # type: ignore
except ModuleNotFoundError:
# pylint: disable-next=invalid-name
psutil = None # type: ignore
logger = logging.getLogger()
[docs]
def core(cobbler_api: CobblerAPI) -> None:
"""
Starts Cobbler.
:param cobbler_api: The cobbler_api instance which is used for this method.
"""
settings = cobbler_api.settings()
xmlrpc_port = settings.xmlrpc_port
regen_ss_file()
do_xmlrpc_rw(cobbler_api, xmlrpc_port)
[docs]
def regen_ss_file() -> None:
"""
This is only used for Kerberos auth at the moment. It identifies XMLRPC requests from Apache that have already been
cleared by Kerberos.
"""
ssfile = "/var/lib/cobbler/web.ss"
data = os.urandom(512)
with open(ssfile, "w", 0o660, encoding="UTF-8") as ss_file_fd:
ss_file_fd.write(str(binascii.hexlify(data)))
http_user = "apache"
family = utils.get_family()
if family == "debian":
http_user = "www-data"
elif family == "suse":
http_user = "wwwrun"
os.lchown(ssfile, pwd.getpwnam(http_user)[2], -1)
[docs]
def do_xmlrpc_rw(cobbler_api: CobblerAPI, port: int) -> None:
"""
This trys to bring up the Cobbler xmlrpc_api and restart it if it fails.
:param cobbler_api: The cobbler_api instance which is used for this method.
:param port: The port where the xmlrpc api should run on.
"""
xinterface = remote.ProxiedXMLRPCInterface(
cobbler_api, remote.CobblerXMLRPCInterface
)
server = remote.CobblerXMLRPCServer(("127.0.0.1", port))
# don't log requests; ignore mypy due to multiple inheritance & protocols being 3.8+
server.logRequests = False # type: ignore[attr-defined]
logger.debug("XMLRPC running on %s", port)
server.register_instance(xinterface)
start_time = ""
if psutil is not None:
ps_util = psutil.Process(os.getpid())
start_time = f" in {str(time.time() - ps_util.create_time())} seconds"
systemd.daemon.notify("READY=1") # type: ignore
while True:
try:
logger.info("Cobbler startup completed %s", start_time)
# Start background load_items task
xinterface.proxied.background_load_items()
server.serve_forever()
except IOError:
# interrupted? try to serve again
time.sleep(0.5)
[docs]
def daemonize_self() -> None:
"""
Deamonizes the current process.
"""
# daemonizing code: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012
logger.info("cobblerd started")
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as e:
logger.error("fork #1 failed: %s (%s)", e.errno, e.strerror)
sys.exit(1)
# decouple from parent environment
os.chdir("/")
os.setsid()
os.umask(0o22)
# do second fork
try:
pid = os.fork()
if pid > 0:
logger.info("Daemon PID %d", pid)
sys.exit(0)
except OSError as e:
logger.error("fork #2 failed: %s (%s)", e.errno, e.strerror)
sys.exit(1)
with open("/dev/null", "r+", encoding="UTF-8") as dev_null:
os.dup2(dev_null.fileno(), sys.stdin.fileno())
os.dup2(dev_null.fileno(), sys.stdout.fileno())
os.dup2(dev_null.fileno(), sys.stderr.fileno())