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"))