NimD now sets the machine's hostname, nimd has now colored logs. Added emergency mode (spawns root shell on fatal crash), stopped unmounting virtual filesystems

This commit is contained in:
Nocturn9x 2021-12-02 18:54:48 +01:00
parent 1c6254b50f
commit d055d876a1
5 changed files with 80 additions and 35 deletions

View File

@ -35,6 +35,8 @@ proc mainLoop*(logger: Logger, mountDisks: bool = true, fstab: string = "/etc/fs
logger.fatal(&"A fatal error has occurred while preparing filesystem, booting cannot continue. Error -> {getCurrentExceptionMsg()}")
nimDExit(logger, 131)
logger.info("Disks mounted")
logger.info("Setting hostname")
logger.debug(&"Hostname was set to '{setHostname(logger)}'")
logger.info("Processing boot runlevel")
# TODO
logger.info("Processing default runlevel")
@ -45,8 +47,8 @@ proc mainLoop*(logger: Logger, mountDisks: bool = true, fstab: string = "/etc/fs
# TODO
sleepSeconds(5)
except CtrlCException:
logger.warning("Main process received SIGINT: exiting")
nimDExit(logger, 130) # 130 - Interrupted by SIGINT
logger.warning("Main process received SIGINT: exiting") # TODO: Ignore this once we stop testing on our local machines lol
nimDExit(logger, 130, emerg=false) # 130 - Interrupted by SIGINT
except:
logger.critical(&"A critical error has occurred while running, restarting the mainloop! Error -> {getCurrentExceptionMsg()}")
# We *absolutely* cannot die

View File

@ -36,6 +36,12 @@ proc main(logger: Logger) =
if uid != 0:
logger.fatal(&"NimD must run as root, but current user id is {uid}")
quit(EPERM) # EPERM - Operation not permitted
logger.debug("Setting up dummy signal handlers")
onSignal(SIGABRT, SIGALRM, SIGHUP, SIGILL, SIGKILL, SIGQUIT, SIGSTOP, SIGSEGV,
SIGTRAP, SIGTERM, SIGPIPE, SIGUSR1, SIGUSR2, 6, SIGFPE, SIGBUS, SIGURG, SIGINT): # 6 is SIGIOT
# Can't capture local variables because this implicitly generates
# a noconv procedure
getDefaultLogger().warning(&"Ignoring signal {sig} ({strsignal(sig)})") # Nim injects the variable "sig" into the scope. Gotta love those macros
logger.debug("Starting uninterruptible mainloop")
mainLoop(logger)

View File

@ -145,6 +145,7 @@ proc checkDisksIsMounted(search: tuple[source, target, filesystemtype: string, m
proc mountRealDisks*(logger: Logger, fstab: string = "/etc/fstab") =
## Mounts real disks from /etc/fstab
var retcode = 0
try:
logger.debug(&"Reading disk entries from {fstab}")
for entry in parseFileSystemTable(readFile(fstab)):
@ -153,7 +154,7 @@ proc mountRealDisks*(logger: Logger, fstab: string = "/etc/fstab") =
continue
logger.debug(&"Mounting filesystem {entry.source} ({entry.filesystemtype}) at {entry.target} with mount option(s) {entry.data}")
logger.trace(&"Calling mount('{entry.source}', '{entry.target}', '{entry.filesystemtype}', {entry.mountflags}, '{entry.data}')")
var retcode = mount(entry.source, entry.target, entry.filesystemtype, entry.mountflags, entry.data)
retcode = mount(entry.source, entry.target, entry.filesystemtype, entry.mountflags, entry.data)
logger.trace(&"mount('{entry.source}', '{entry.target}', '{entry.filesystemtype}', {entry.mountflags}, '{entry.data}') returned {retcode}")
if retcode == -1:
logger.error(&"Mounting {entry.source} at {entry.target} has failed with error {posix.errno}: {posix.strerror(posix.errno)}")
@ -169,13 +170,15 @@ proc mountRealDisks*(logger: Logger, fstab: string = "/etc/fstab") =
proc mountVirtualDisks*(logger: Logger) =
## Mounts POSIX virtual filesystems/partitions,
## such as /proc and /sys
var retcode = 0
for entry in virtualFileSystems:
if checkDisksIsMounted(entry):
logger.debug(&"Skipping mounting filesystem {entry.source} ({entry.filesystemtype}) at {entry.target}: already mounted")
continue
logger.debug(&"Mounting filesystem {entry.source} ({entry.filesystemtype}) at {entry.target} with mount option(s) {entry.data}")
logger.trace(&"Calling mount('{entry.source}', '{entry.target}', '{entry.filesystemtype}', {entry.mountflags}, '{entry.data}')")
var retcode = mount(entry.source, entry.target, entry.filesystemtype, entry.mountflags, entry.data)
retcode = mount(entry.source, entry.target, entry.filesystemtype, entry.mountflags, entry.data)
logger.trace(&"mount('{entry.source}', '{entry.target}', '{entry.filesystemtype}', {entry.mountflags}, '{entry.data}') returned {retcode}")
if retcode == -1:
logger.error(&"Mounting disk {entry.source} at {entry.target} has failed with error {posix.errno}: {posix.strerror(posix.errno)}")
@ -189,26 +192,28 @@ proc mountVirtualDisks*(logger: Logger) =
proc unmountAllDisks*(logger: Logger, code: int) =
## Unmounts all currently mounted disks, including the ones that
## were not mounted trough fstab and virtual filesystems
## were not mounted trough fstab but excluding virtual filesystems
var flag: bool = false
var retcode = 0
try:
logger.info("Detaching real filesystems")
logger.debug(&"Reading disk entries from /proc/mounts")
for entry in parseFileSystemTable(readFile("/proc/mounts")):
flag = false
if entry.source in ["proc", "sys", "run", "dev", "devpts", "shm"]:
continue # We cannot detach the vfs just yet, we'll do it later
flag = true # We don't detach the vfs
for path in ["/proc", "/sys", "/run", "/dev", "/dev/pts", "/dev/shm"]:
if entry.target.startswith(path):
flag = true
if flag:
flag = false
logger.debug(&"Skipping unmounting filesystem {entry.source} ({entry.filesystemtype}) from {entry.target} as it is a virtual filesystem")
continue
if not checkDisksIsMounted(entry):
logger.debug(&"Skipping unmounting filesystem {entry.source} ({entry.filesystemtype}) from {entry.target}: not mounted")
continue
logger.debug(&"Unmounting filesystem {entry.source} ({entry.filesystemtype}) from {entry.target}")
logger.trace(&"Calling umount2('{entry.target}', MNT_DETACH)")
var retcode = umount2(entry.target, 2) # MNT_DETACH - Since we're shutting down, we need the disks to be *gone*!
# var retcode = umount2(entry.target, 2) # 2 = MNT_DETACH - Since we're shutting down, we need the disks to be *gone*!
logger.trace(&"umount2('{entry.target}', MNT_DETACH) returned {retcode}")
if retcode == -1:
logger.error(&"Unmounting disk {entry.source} from {entry.target} has failed with error {posix.errno}: {posix.strerror(posix.errno)}")
@ -216,21 +221,6 @@ proc unmountAllDisks*(logger: Logger, code: int) =
posix.errno = cint(0)
else:
logger.debug(&"Unmounted {entry.source} from {entry.target}")
logger.info("Detaching virtual filesystems")
for entry in virtualFileSystems:
if not checkDisksIsMounted(entry):
logger.debug(&"Skipping unmounting filesystem {entry.source} ({entry.filesystemtype}) from {entry.target}: not mounted")
continue
logger.debug(&"Unmounting filesystem {entry.source} ({entry.filesystemtype}) from {entry.target}")
logger.trace(&"Calling umount2('{entry.target}', MNT_DETACH)")
var retcode = umount2(entry.target, 2) # MNT_DETACH - Since we're shutting down, we need the disks to be *gone*!
logger.trace(&"umount('{entry.target}') returned {retcode}")
if retcode == -1:
logger.error(&"Unmounting disk {entry.source} from {entry.target} has failed with error {posix.errno}: {posix.strerror(posix.errno)}")
# Resets the error code
posix.errno = cint(0)
else:
logger.debug(&"Unmounted {entry.source} from {entry.target}")
except ValueError: # Check parseFileSystemTable for more info on this catch block
logger.fatal("Improperly formatted /etc/mtab, exiting")
logger.fatal(&"A fatal error occurred while unmounting disks: {getCurrentExceptionMsg()}")
nimDExit(logger, 131)

View File

@ -13,6 +13,7 @@
# limitations under the License.
# A simple logging module inspired by python's own logging facility
import terminal
import strformat
import times
@ -72,28 +73,45 @@ proc getDefaultLogger*(): Logger =
## level that writes the given message to the
## standard error with some basic info like the
## current date and time and the log level
setStdIoUnbuffered() # Just in case
proc logTrace(self: LogHandler, logger: Logger, message: string) =
setForegroundColor(fgMagenta)
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - TRACE] {message}""")
setForegroundColor(fgDefault)
proc logDebug(self: LogHandler, logger: Logger, message: string) =
setForegroundColor(fgCyan)
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - DEBUG] {message}""")
setForegroundColor(fgDefault)
proc logInfo(self: LogHandler, logger: Logger, message: string) =
setForegroundColor(fgGreen)
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - INFO] {message}""")
setForegroundColor(fgDefault)
proc logWarning(self: LogHandler, logger: Logger, message: string) =
setForegroundColor(fgYellow)
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - WARNING] {message}""")
setForegroundColor(fgDefault)
proc logError(self: LogHandler, logger: Logger, message: string) =
setForegroundColor(fgRed)
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - ERROR] {message}""")
setForegroundColor(fgDefault)
proc logCritical(self: LogHandler, logger: Logger, message: string) =
setForegroundColor(fgRed)
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - CRITICAL] {message}""")
setForegroundColor(fgDefault)
proc logFatal(self: LogHandler, logger: Logger, message: string) =
setForegroundColor(fgBlack)
setBackgroundColor(bgRed)
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - FATAL] {message}""")
setForegroundColor(fgDefault)
setBackgroundColor(bgDefault)
result = newLogger()
result.addHandler(createHandler(logTrace, LogLevel.Trace))

View File

@ -16,6 +16,7 @@
## to allow a clean shutdown of NimD
import os
import strformat
import strutils
import logging
@ -36,8 +37,8 @@ proc removeShutdownHandler*(handler: proc (logger: Logger, code: int)) =
shutdownHandlers.delete(i)
proc nimDExit*(logger: Logger, code: int) =
logger.warning("The system is being shut down!")
proc nimDExit*(logger: Logger, code: int, emerg: bool = true) =
logger.warning("The system is shutting down")
# TODO
logger.info("Processing shutdown runlevel")
# TODO
@ -48,13 +49,38 @@ proc nimDExit*(logger: Logger, code: int) =
except:
logger.error(&"An error has occurred while calling shutdown handlers. Error -> {getCurrentExceptionMsg()}")
# Note: continues calling handlers!
logger.info("Terminating child processes with SIGINT")
# TODO
logger.info("Terminating child processes with SIGKILL")
# TODO
logger.warning("Shutdown procedure complete, sending final termination signal")
# TODO
quit(code) # Replace with syscall(REBOOT, ...)
if emerg:
# 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")
discard execShellCmd("/bin/sh") # TODO: Is this fine? maybe use execProcess
else:
logger.info("Terminating child processes with SIGINT")
# TODO
logger.info("Terminating child processes with SIGKILL")
# TODO
logger.warning("Shutdown procedure complete, sending final termination signal")
# TODO
quit(code)
proc setHostname*(logger: Logger): string =
## Sets the machine's hostname. Returns
## the hostname that has been set or an
## empty string if an error occurs. If
## /etc/hostname doesn't exist, the hostname
## defaults to localhost
var hostname: string
try:
if not fileExists("/etc/hostname"):
logger.warning("/etc/hostname doesn't exist, defaulting to 'localhost'")
hostname = "localhost"
else:
hostname = readFile("/etc/hostname").strip(chars={'\n'})
writeFile("/proc/sys/kernel/hostname", hostname)
except:
logger.error(&"An error occurred while setting hostname -> {getCurrentExceptionMsg()}")
return ""
return hostname
proc sleepSeconds*(amount: SomeInteger) = sleep(amount * 1000)
@ -62,3 +88,6 @@ proc sleepSeconds*(amount: SomeInteger) = sleep(amount * 1000)
proc handleControlC* {.noconv.} =
raise newException(CtrlCException, "Interrupted by Ctrl+C")
proc strsignal*(sig: cint): cstring {.header:"string.h", importc.}