From d240d05cef9bd530fa2189e516c4e62718c8ec8c Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Sat, 12 Mar 2022 15:51:31 +0100 Subject: [PATCH 1/6] Minor changes to nimDExit and shutdown.nim --- src/core/shutdown.nim | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/shutdown.nim b/src/core/shutdown.nim index b74f24d..e4d029e 100644 --- a/src/core/shutdown.nim +++ b/src/core/shutdown.nim @@ -32,16 +32,14 @@ type ShutdownHandler* = ref object const reboot_codes = {"poweroff": 0x4321fedc'i64, "reboot": 0x01234567'i64, "halt": 0xcdef0123}.toTable() +var shutdownHandlers: seq[ShutdownHandler] = @[] +var sigTermDelay: float = 90 proc newShutdownHandler*(body: proc (logger: Logger, code: int)): ShutdownHandler = result = ShutdownHandler(body: body) -var shutdownHandlers: seq[ShutdownHandler] = @[] -var sigTermDelay: float = 90 - - proc setSigTermDelay*(delay: int = 90) = # Sets the sigtermDelay variable sigTermDelay = float(delay) @@ -97,8 +95,8 @@ proc nimDExit*(logger: Logger, code: int, emerg: bool = true) = # We're in emergency mode: do not crash the kernel, spawn a shell and exit logger.fatal("NimD has entered emergency mode and cannot continue. You will be now (hopefully) dropped in a root shell: you're on your own. May the force be with you") logger.info("Terminating child processes with SIGKILL") - discard execCmd(os.getEnv("SHELL", "/bin/sh")) # TODO: Is this fine? maybe use execProcess discard posix.kill(-1, SIGKILL) + discard execCmd(os.getEnv("SHELL", "/bin/sh")) # TODO: Is this fine? maybe use execProcess quit(-1) logger.warning("The system is shutting down") logger.info("Processing shutdown runlevel") @@ -121,6 +119,7 @@ proc nimDExit*(logger: Logger, code: int, emerg: bool = true) = logger.info("Terminating child processes with SIGKILL") discard posix.kill(-1, SIGKILL) logger.warning("Shutdown procedure complete, NimD is exiting") + quit(-1) proc reboot*(logger: Logger) = From df1371c13f31e2fa03842ee2844b782a1ce22deb Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Sat, 12 Mar 2022 15:53:31 +0100 Subject: [PATCH 2/6] Added build and startup scripts --- .gitignore | 3 -- scripts/boot.sh | 115 +++++++++++++++++++++++++++++++++++++++++++++ scripts/rebuild.sh | 7 +++ scripts/start.sh | 1 + 4 files changed, 123 insertions(+), 3 deletions(-) create mode 100755 scripts/boot.sh create mode 100755 scripts/rebuild.sh create mode 100755 scripts/start.sh diff --git a/.gitignore b/.gitignore index bb3dc13..3c841b7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ main *.iso nimd main -boot.sh rootfs packervm test @@ -14,7 +13,5 @@ initrd* vmlinuz-linux vmlinuz* debian* -start.sh -rebuild.sh vm.qcow2 *.tar.* diff --git a/scripts/boot.sh b/scripts/boot.sh new file mode 100755 index 0000000..6c9064c --- /dev/null +++ b/scripts/boot.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail +trap cleanup SIGINT SIGTERM ERR EXIT + +script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) + +usage() { + cat << EOF +Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-m] [-b] -k kernel -i initrd -r rootfs + +Alpinemini vm + +Available options: + +-h, --help Print this help and exit +-v, --verbose Print script debug info +-k, --kernel Specify kernel file +-i, --initrd Specify initrd file +-m, --memory Set maximum vm memory +-b, --build Build a new disk (and then boot) +EOF + exit +} + +cleanup() { + trap - SIGINT SIGTERM ERR EXIT +} + +setup_colors() { + if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then + NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m' + else + NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW='' + fi +} + +msg() { + echo >&2 -e "${1-}" +} + +die() { + local msg=$1 + local code=${2-1} + msg "$msg" + exit "$code" +} + +parse_params() { + build=0 + rootfs='' + kernel='' + initrd='' + memory='' + + while :; do + case "${1-}" in + -h | --help) usage ;; + -v | --verbose) set -x ;; + --no-color) NO_COLOR=1 ;; + -b | --build) build=1 ;; # build disk + -k | --kernel) # kernel file + kernel="${2-}" + shift + ;; + -i | --initrd) # initrd file + initrd="${2-}" + shift + ;; + -m | --memory) # max memory + memory="${2-}" + shift + ;; + -?*) die "Unknown option: $1" ;; + *) break ;; + esac + shift + done + + args=("$@") + + # check required params and arguments + [[ -z "${kernel-}" ]] && die "${RED}Missing required parameter:${NOFORMAT} kernel" + [[ -z "${initrd-}" ]] && die "${RED}Missing required parameter:${NOFORMAT} initrd" + + return 0 +} +setup_colors +parse_params "$@" + +if ! [ -x "$(command -v qemu-system-x86_64)" ]; then + echo '${RED}Error:${NOFORMAT} unable to find qemu-system-x86_64, please install it first.' >&2 + exit 1 +fi + +if ! [ -x "$(command -v qemu-img)" ]; then + echo '${RED}Error:${NOFORMAT} unable to find qemu-img, please install it first.' >&2 + exit 1 +fi + + +if [[ $build -eq 1 ]] +then + msg "${CYAN}Building disk... Please wait${NOFORMAT}" + qemu-img create -f qcow2 vm.qcow2 800M + qemu-system-x86_64 -m 256M -smp 1 -drive file=packervm/packer.qcow2,if=virtio,readonly=on -drive file=vm.qcow2,if=virtio -enable-kvm -fsdev local,id=rootfs_dev,path=rootfs,security_model=none -device virtio-9p-pci,fsdev=rootfs_dev,mount_tag=rootfs -display none +fi + +qemumem='' +if ! [[ $memory -eq "" ]] + then + qemumem="-m ${memory}" +fi + +qemu-system-x86_64 -net nic,model=virtio,netdev=user.0 -netdev user,id=user.0 -drive file=vm.qcow2,if=virtio -enable-kvm -kernel ${kernel} -initrd ${initrd} ${qemumem} -append 'root=/dev/vda1 rw quiet modules=ext4 console=ttyS0,9600 console=ttyS0 init=/bin/nimd' diff --git a/scripts/rebuild.sh b/scripts/rebuild.sh new file mode 100755 index 0000000..61b6012 --- /dev/null +++ b/scripts/rebuild.sh @@ -0,0 +1,7 @@ +nim -o:rootfs/bin/nimd -d:release --gc:orc --opt:size --passL:"-static" compile src/main.nim +nim -o:rootfs/bin/nimdown -d:release --gc:orc --opt:size --passL:"-static" compile src/programs/poweroff.nim +nim -o:rootfs/bin/nimhalt -d:release --gc:orc --opt:size --passL:"-static" compile src/programs/halt.nim +nim -o:rootfs/bin/nimreboot -d:release --gc:orc --opt:size --passL:"-static" compile src/programs/reboot.nim +mkdir -p rootfs/etc/nimd +cp nimd.conf rootfs/etc/nimd/nimd.conf +./boot.sh --kernel vmlinuz-linux --initrd initrd-linux.img --memory 1G --build diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100755 index 0000000..5372785 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1 @@ +./boot.sh --kernel vmlinuz-linux --initrd initrd-linux.img --memory 1G From e34e48f87c91c2b592db5f21023fd9c2fa6a1685 Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Sat, 12 Mar 2022 15:55:50 +0100 Subject: [PATCH 3/6] Updated README with note to scripts folder for testing --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b41147a..6811e91 100644 --- a/README.md +++ b/README.md @@ -116,4 +116,9 @@ workers = 1 # Number of worker processes to use restartDelay = 10 # Delay (seconds) that nimd will wait before restarting itself after crashes sigtermDelay = 90 # Delay (seconds) that nimd will wait before terminating child processes with # SIGKILL after sending a more gentle SIGTERM upon shutdown or exit -``` \ No newline at end of file +``` + +## Testing NimD + +NimD is not quite ready for production yet, but in the `scripts` folder you can find a few simple bash scripts to test NimD +in a minimal Alpine Linux VM using QEMU. \ No newline at end of file From 99fd4171edc299c8a9b383b99074bab2c84078bc Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Sat, 12 Mar 2022 17:22:40 +0100 Subject: [PATCH 4/6] NimD now checks if another instance is running before starting up --- README.md | 3 ++- scripts/rebuild.sh | 13 ++++++++----- scripts/start.sh | 2 +- src/core/mainloop.nim | 10 +++++++--- src/core/shutdown.nim | 1 - src/main.nim | 45 +++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 61 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 6811e91..f7654ba 100644 --- a/README.md +++ b/README.md @@ -121,4 +121,5 @@ sigtermDelay = 90 # Delay (seconds) that nimd will wa ## Testing NimD NimD is not quite ready for production yet, but in the `scripts` folder you can find a few simple bash scripts to test NimD -in a minimal Alpine Linux VM using QEMU. \ No newline at end of file +in a minimal Alpine Linux VM using QEMU. Note that due to weirdness in how stdout is handled on the VGA port, the VM will use +the serial port (ttyS0) as output by default (you can change this in the kernel parameters) \ No newline at end of file diff --git a/scripts/rebuild.sh b/scripts/rebuild.sh index 61b6012..97112a3 100755 --- a/scripts/rebuild.sh +++ b/scripts/rebuild.sh @@ -1,7 +1,10 @@ -nim -o:rootfs/bin/nimd -d:release --gc:orc --opt:size --passL:"-static" compile src/main.nim -nim -o:rootfs/bin/nimdown -d:release --gc:orc --opt:size --passL:"-static" compile src/programs/poweroff.nim -nim -o:rootfs/bin/nimhalt -d:release --gc:orc --opt:size --passL:"-static" compile src/programs/halt.nim -nim -o:rootfs/bin/nimreboot -d:release --gc:orc --opt:size --passL:"-static" compile src/programs/reboot.nim +# Build the environment mkdir -p rootfs/etc/nimd cp nimd.conf rootfs/etc/nimd/nimd.conf -./boot.sh --kernel vmlinuz-linux --initrd initrd-linux.img --memory 1G --build +nim -o:rootfs/bin/nimd -d:release --gc:orc --opt:size --passL:"-static" compile src/main.nim +nim -o:rootfs/bin/halt -d:release --gc:orc --opt:size --passL:"-static" compile src/programs/halt.nim +nim -o:rootfs/bin/reboot -d:release --gc:orc --opt:size --passL:"-static" compile src/programs/reboot.nim +nim -o:rootfs/bin/poweroff -d:release --gc:orc --opt:size --passL:"-static" compile src/programs/poweroff.nim + +# Start the VM +./scripts/boot.sh --kernel vmlinuz-linux --initrd initrd-linux.img --memory 1G --build diff --git a/scripts/start.sh b/scripts/start.sh index 5372785..4bb6653 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -1 +1 @@ -./boot.sh --kernel vmlinuz-linux --initrd initrd-linux.img --memory 1G +./scripts/boot.sh --kernel vmlinuz-linux --initrd initrd-linux.img --memory 1G diff --git a/src/core/mainloop.nim b/src/core/mainloop.nim index d79cf43..20e7417 100644 --- a/src/core/mainloop.nim +++ b/src/core/mainloop.nim @@ -28,8 +28,8 @@ proc mainLoop*(logger: Logger, config: NimDConfig, startServices: bool = true) = if startServices: logger.info("Processing default runlevel") startServices(logger, workers=config.workers, level=Default) - logger.debug(&"Unblocking signals") - unblockSignals(logger) + logger.debug(&"Unblocking signals") + unblockSignals(logger) logger.info("System initialization complete, idling on control socket") var opType: string try: @@ -52,6 +52,7 @@ proc mainLoop*(logger: Logger, config: NimDConfig, startServices: bool = true) = # - 'h' -> halt # - 's' -> Services-related operations (start, stop, get status, etc.) # - 'l' -> Reload in-memory configuration + # - 'c' -> Check NimD status (returns "1" if up) case opType: of "p": logger.info("Received shutdown request") @@ -63,11 +64,14 @@ proc mainLoop*(logger: Logger, config: NimDConfig, startServices: bool = true) = logger.info("Received halt request") halt(logger) of "s": - logger.info("Received service request") + logger.info("Received service-related request") # TODO: Operate on services of "l": logger.info("Received reload request") mainLoop(logger, parseConfig(logger, "/etc/nimd/nimd.conf"), startServices=false) + of "c": + logger.info("Received check request, responding") + clientSocket.send("1") else: logger.warning(&"Received unknown operation type '{opType}' via control socket, ignoring it") discard diff --git a/src/core/shutdown.nim b/src/core/shutdown.nim index e4d029e..aaf2996 100644 --- a/src/core/shutdown.nim +++ b/src/core/shutdown.nim @@ -119,7 +119,6 @@ proc nimDExit*(logger: Logger, code: int, emerg: bool = true) = logger.info("Terminating child processes with SIGKILL") discard posix.kill(-1, SIGKILL) logger.warning("Shutdown procedure complete, NimD is exiting") - quit(-1) proc reboot*(logger: Logger) = diff --git a/src/main.nim b/src/main.nim index 8265bb8..a689764 100644 --- a/src/main.nim +++ b/src/main.nim @@ -14,6 +14,7 @@ import parseopt import strformat import posix +import net import os # NimD's own stuff @@ -68,10 +69,51 @@ proc addStuff = addService(shell) +proc checkControlSocket(logger: Logger, config: NimDConfig): bool = + ## Performs some startup checks on nim's control + ## socket + result = true + var stat_result: Stat + if posix.stat(cstring(config.sock), stat_result) == -1: + logger.warning(&"Could not stat() {config.sock}, assuming NimD instance isn't running") + # I stole this from /usr/lib/python3.10/stat.py + elif (int(stat_result.st_mode) and 0o170000) != 0o140000: + logger.fatal(&"{config.sock} exists and is not a socket") + result = false + elif dirExists(config.sock): + logger.info("Control socket path is a directory, appending nimd.sock to it") + config.sock = config.sock.joinPath("nimd.sock") + else: + logger.debug("Trying to reach current NimD instance") + var sock = newSocket(Domain.AF_UNIX, SockType.SOCK_STREAM, Protocol.IPPROTO_IP) + try: + sock.connectUnix(config.sock) + logger.info("Control socket already exists, trying to reach current NimD instance") + except OSError: + logger.warning(&"Could not connect to control socket at {config.sock} ({getCurrentExceptionMsg()}), assuming NimD instance isn't running") + try: + removeFile(config.sock) + except OSError: + logger.warning(&"Could not delete dangling control socket at {config.sock} ({getCurrentExceptionMsg()})") + if sock.trySend("c"): + try: + if sock.recv(1, timeout=5) == "1": + logger.error("Another NimD instance is running! Exiting") + result = false + except OSError: + logger.warning(&"Could not read from control socket at {config.sock} ({getCurrentExceptionMsg()}), assuming NimD instance isn't running") + except TimeoutError: + logger.warning(&"Could not read from control socket at {config.sock} ({getCurrentExceptionMsg()}), assuming NimD instance isn't running") + else: + logger.fatal(&"Could not write on control socket at {config.sock}") + result = false + proc main(logger: Logger, config: NimDConfig) = ## NimD's entry point and setup ## function + if not checkControlSocket(logger, config): + return logger.debug(&"Setting log file to '{config.logFile}'") setLogFile(file=config.logFile) logger.debug("Starting NimD: A minimal, self-contained, dependency-based Linux init system written in Nim") @@ -173,8 +215,7 @@ when isMainModule: quit(EINVAL) # EINVAL - Invalid argument else: echo "Usage: nimd [options]" - quit(EINVAL) # EINVAL - Invalid argument - + quit(EINVAL) # EINVAL - Invalid argument setStdIoUnbuffered() # Colors don't work otherwise! try: main(logger, parseConfig(logger, "/etc/nimd/nimd.conf")) From 5f965826ea25a325b1228a1d3f2c2916949a9a52 Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Sun, 13 Mar 2022 12:46:39 +0100 Subject: [PATCH 5/6] Shell has stderr working now --- src/main.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.nim b/src/main.nim index a689764..164bcfb 100644 --- a/src/main.nim +++ b/src/main.nim @@ -61,7 +61,7 @@ proc addStuff = var shell = newService(name="login", description="A simple login shell", kind=Simple, workDir=getCurrentDir(), runlevel=Boot, exec="/bin/login -f root", supervised=true, restart=Always, restartDelay=0, depends=(@[]), provides=(@[]), - useParentStreams=true, stdin="/dev/null", stderr="", stdout="" + useParentStreams=true, stdin="/dev/null", stderr="/proc/self/fd/2", stdout="/proc/self/fd/1" ) addService(errorer) addService(echoer) From 38698ec72a2df6b818cd5efc65855e34ac3ed941 Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Sun, 13 Mar 2022 13:04:40 +0100 Subject: [PATCH 6/6] Minor changes to error handling in main loop + changes to boot script and config --- nimd.conf | 2 +- scripts/boot.sh | 2 +- src/core/mainloop.nim | 79 +++++++++++++++++++++++-------------------- src/main.nim | 8 +++-- 4 files changed, 50 insertions(+), 41 deletions(-) diff --git a/nimd.conf b/nimd.conf index 3627f6c..9d2634d 100644 --- a/nimd.conf +++ b/nimd.conf @@ -7,7 +7,7 @@ restartDelay = 10 sigtermDelay = 90 [Logging] -level = info +level = debug logFile = /var/log/nimd [Filesystem] diff --git a/scripts/boot.sh b/scripts/boot.sh index 6c9064c..fc9ede1 100755 --- a/scripts/boot.sh +++ b/scripts/boot.sh @@ -112,4 +112,4 @@ if ! [[ $memory -eq "" ]] qemumem="-m ${memory}" fi -qemu-system-x86_64 -net nic,model=virtio,netdev=user.0 -netdev user,id=user.0 -drive file=vm.qcow2,if=virtio -enable-kvm -kernel ${kernel} -initrd ${initrd} ${qemumem} -append 'root=/dev/vda1 rw quiet modules=ext4 console=ttyS0,9600 console=ttyS0 init=/bin/nimd' +qemu-system-x86_64 -net nic,model=virtio,netdev=user.0 -netdev user,id=user.0 -drive file=vm.qcow2,if=virtio -enable-kvm -kernel ${kernel} -initrd ${initrd} ${qemumem} -append 'root=/dev/vda1 rw quiet modules=ext4 console=ttyS0 init=/bin/nimd' diff --git a/src/core/mainloop.nim b/src/core/mainloop.nim index 20e7417..764b4f9 100644 --- a/src/core/mainloop.nim +++ b/src/core/mainloop.nim @@ -40,44 +40,49 @@ proc mainLoop*(logger: Logger, config: NimDConfig, startServices: bool = true) = logger.switchToFile() logger.debug("Entering accept() loop") while true: - serverSocket.accept(clientSocket) - logger.debug(&"Received connection on control socket") - if clientSocket.recv(opType, size=1) == 0: - logger.debug(&"Client has disconnected, waiting for new connections") - continue - logger.debug(&"Received operation type '{opType}' via control socket") - # The operation type is a single byte: - # - 'p' -> poweroff - # - 'r' -> reboot - # - 'h' -> halt - # - 's' -> Services-related operations (start, stop, get status, etc.) - # - 'l' -> Reload in-memory configuration - # - 'c' -> Check NimD status (returns "1" if up) - case opType: - of "p": - logger.info("Received shutdown request") - shutdown(logger) - of "r": - logger.info("Received reboot request") - reboot(logger) - of "h": - logger.info("Received halt request") - halt(logger) - of "s": - logger.info("Received service-related request") - # TODO: Operate on services - of "l": - logger.info("Received reload request") - mainLoop(logger, parseConfig(logger, "/etc/nimd/nimd.conf"), startServices=false) - of "c": - logger.info("Received check request, responding") - clientSocket.send("1") - else: - logger.warning(&"Received unknown operation type '{opType}' via control socket, ignoring it") - discard - clientSocket.close() + try: + serverSocket.accept(clientSocket) + logger.debug(&"Received connection on control socket") + if clientSocket.recv(opType, size=1) == 0: + logger.debug(&"Client has disconnected, waiting for new connections") + continue + logger.debug(&"Received operation type '{opType}' via control socket") + # The operation type is a single byte: + # - 'p' -> poweroff + # - 'r' -> reboot + # - 'h' -> halt + # - 's' -> Services-related operations (start, stop, get status, etc.) + # - 'l' -> Reload in-memory configuration + # - 'c' -> Check NimD status (returns "1" if up) + case opType: + of "p": + logger.info("Received shutdown request") + shutdown(logger) + of "r": + logger.info("Received reboot request") + reboot(logger) + of "h": + logger.info("Received halt request") + halt(logger) + of "s": + logger.info("Received service-related request") + # TODO: Operate on services + of "l": + logger.info("Received reload request") + mainLoop(logger, parseConfig(logger, "/etc/nimd/nimd.conf"), startServices=false) + of "c": + logger.info("Received check request, responding") + clientSocket.send("1") + else: + logger.warning(&"Received unknown operation type '{opType}' via control socket, ignoring it") + discard + except: + logger.error(&"An error occurred while idling on control socket: {getCurrentExceptionMsg()}") + finally: + clientSocket.close() + clientSocket = newSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP) except: - logger.critical(&"A critical error has occurred while running, restarting the mainloop in {config.restartDelay} seconds! Error -> {getCurrentExceptionMsg()}") + logger.critical(&"A critical error has occurred while running, restarting the main loop in {config.restartDelay} seconds! Error -> {getCurrentExceptionMsg()}") sleepSeconds(config.restartDelay) # We *absolutely* cannot die mainLoop(logger, config, startServices=false) diff --git a/src/main.nim b/src/main.nim index 164bcfb..3e77f62 100644 --- a/src/main.nim +++ b/src/main.nim @@ -59,7 +59,7 @@ proc addStuff = depends=(@[newDependency(Other, errorer)]), provides=(@[]), stdin="/dev/null", stderr="", stdout="") var shell = newService(name="login", description="A simple login shell", kind=Simple, - workDir=getCurrentDir(), runlevel=Boot, exec="/bin/login -f root", + workDir=getCurrentDir(), runlevel=Default, exec="/bin/login -f root", supervised=true, restart=Always, restartDelay=0, depends=(@[]), provides=(@[]), useParentStreams=true, stdin="/dev/null", stderr="/proc/self/fd/2", stdout="/proc/self/fd/1" ) @@ -74,8 +74,12 @@ proc checkControlSocket(logger: Logger, config: NimDConfig): bool = ## socket result = true var stat_result: Stat - if posix.stat(cstring(config.sock), stat_result) == -1: + if posix.stat(cstring(config.sock), stat_result) == -1 and posix.errno != 2: logger.warning(&"Could not stat() {config.sock}, assuming NimD instance isn't running") + elif posix.errno == 2: + logger.debug(&"Control socket path is clear, starting up") + posix.errno = 0 + # 2 is ENOENT, which means the file does not exist # I stole this from /usr/lib/python3.10/stat.py elif (int(stat_result.st_mode) and 0o170000) != 0o140000: logger.fatal(&"{config.sock} exists and is not a socket")