NimD now checks if another instance is running before starting up

This commit is contained in:
Mattia Giambirtone 2022-03-12 17:22:40 +01:00
parent e34e48f87c
commit 99fd4171ed
6 changed files with 61 additions and 13 deletions

View File

@ -121,4 +121,5 @@ sigtermDelay = 90 # Delay (seconds) that nimd will wa
## Testing NimD ## 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 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. 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)

View File

@ -1,7 +1,10 @@
nim -o:rootfs/bin/nimd -d:release --gc:orc --opt:size --passL:"-static" compile src/main.nim # Build the environment
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 mkdir -p rootfs/etc/nimd
cp nimd.conf rootfs/etc/nimd/nimd.conf 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

View File

@ -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

View File

@ -28,8 +28,8 @@ proc mainLoop*(logger: Logger, config: NimDConfig, startServices: bool = true) =
if startServices: if startServices:
logger.info("Processing default runlevel") logger.info("Processing default runlevel")
startServices(logger, workers=config.workers, level=Default) startServices(logger, workers=config.workers, level=Default)
logger.debug(&"Unblocking signals") logger.debug(&"Unblocking signals")
unblockSignals(logger) unblockSignals(logger)
logger.info("System initialization complete, idling on control socket") logger.info("System initialization complete, idling on control socket")
var opType: string var opType: string
try: try:
@ -52,6 +52,7 @@ proc mainLoop*(logger: Logger, config: NimDConfig, startServices: bool = true) =
# - 'h' -> halt # - 'h' -> halt
# - 's' -> Services-related operations (start, stop, get status, etc.) # - 's' -> Services-related operations (start, stop, get status, etc.)
# - 'l' -> Reload in-memory configuration # - 'l' -> Reload in-memory configuration
# - 'c' -> Check NimD status (returns "1" if up)
case opType: case opType:
of "p": of "p":
logger.info("Received shutdown request") logger.info("Received shutdown request")
@ -63,11 +64,14 @@ proc mainLoop*(logger: Logger, config: NimDConfig, startServices: bool = true) =
logger.info("Received halt request") logger.info("Received halt request")
halt(logger) halt(logger)
of "s": of "s":
logger.info("Received service request") logger.info("Received service-related request")
# TODO: Operate on services # TODO: Operate on services
of "l": of "l":
logger.info("Received reload request") logger.info("Received reload request")
mainLoop(logger, parseConfig(logger, "/etc/nimd/nimd.conf"), startServices=false) mainLoop(logger, parseConfig(logger, "/etc/nimd/nimd.conf"), startServices=false)
of "c":
logger.info("Received check request, responding")
clientSocket.send("1")
else: else:
logger.warning(&"Received unknown operation type '{opType}' via control socket, ignoring it") logger.warning(&"Received unknown operation type '{opType}' via control socket, ignoring it")
discard discard

View File

@ -119,7 +119,6 @@ proc nimDExit*(logger: Logger, code: int, emerg: bool = true) =
logger.info("Terminating child processes with SIGKILL") logger.info("Terminating child processes with SIGKILL")
discard posix.kill(-1, SIGKILL) discard posix.kill(-1, SIGKILL)
logger.warning("Shutdown procedure complete, NimD is exiting") logger.warning("Shutdown procedure complete, NimD is exiting")
quit(-1)
proc reboot*(logger: Logger) = proc reboot*(logger: Logger) =

View File

@ -14,6 +14,7 @@
import parseopt import parseopt
import strformat import strformat
import posix import posix
import net
import os import os
# NimD's own stuff # NimD's own stuff
@ -68,10 +69,51 @@ proc addStuff =
addService(shell) 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) = proc main(logger: Logger, config: NimDConfig) =
## NimD's entry point and setup ## NimD's entry point and setup
## function ## function
if not checkControlSocket(logger, config):
return
logger.debug(&"Setting log file to '{config.logFile}'") logger.debug(&"Setting log file to '{config.logFile}'")
setLogFile(file=config.logFile) setLogFile(file=config.logFile)
logger.debug("Starting NimD: A minimal, self-contained, dependency-based Linux init system written in Nim") 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 quit(EINVAL) # EINVAL - Invalid argument
else: else:
echo "Usage: nimd [options]" echo "Usage: nimd [options]"
quit(EINVAL) # EINVAL - Invalid argument quit(EINVAL) # EINVAL - Invalid argument
setStdIoUnbuffered() # Colors don't work otherwise! setStdIoUnbuffered() # Colors don't work otherwise!
try: try:
main(logger, parseConfig(logger, "/etc/nimd/nimd.conf")) main(logger, parseConfig(logger, "/etc/nimd/nimd.conf"))