mirror of https://github.com/nocturn9x/nimd.git
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:
parent
1c6254b50f
commit
d055d876a1
|
@ -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()}")
|
logger.fatal(&"A fatal error has occurred while preparing filesystem, booting cannot continue. Error -> {getCurrentExceptionMsg()}")
|
||||||
nimDExit(logger, 131)
|
nimDExit(logger, 131)
|
||||||
logger.info("Disks mounted")
|
logger.info("Disks mounted")
|
||||||
|
logger.info("Setting hostname")
|
||||||
|
logger.debug(&"Hostname was set to '{setHostname(logger)}'")
|
||||||
logger.info("Processing boot runlevel")
|
logger.info("Processing boot runlevel")
|
||||||
# TODO
|
# TODO
|
||||||
logger.info("Processing default runlevel")
|
logger.info("Processing default runlevel")
|
||||||
|
@ -45,8 +47,8 @@ proc mainLoop*(logger: Logger, mountDisks: bool = true, fstab: string = "/etc/fs
|
||||||
# TODO
|
# TODO
|
||||||
sleepSeconds(5)
|
sleepSeconds(5)
|
||||||
except CtrlCException:
|
except CtrlCException:
|
||||||
logger.warning("Main process received SIGINT: exiting")
|
logger.warning("Main process received SIGINT: exiting") # TODO: Ignore this once we stop testing on our local machines lol
|
||||||
nimDExit(logger, 130) # 130 - Interrupted by SIGINT
|
nimDExit(logger, 130, emerg=false) # 130 - Interrupted by SIGINT
|
||||||
except:
|
except:
|
||||||
logger.critical(&"A critical error has occurred while running, restarting the mainloop! Error -> {getCurrentExceptionMsg()}")
|
logger.critical(&"A critical error has occurred while running, restarting the mainloop! Error -> {getCurrentExceptionMsg()}")
|
||||||
# We *absolutely* cannot die
|
# We *absolutely* cannot die
|
||||||
|
|
|
@ -36,6 +36,12 @@ proc main(logger: Logger) =
|
||||||
if uid != 0:
|
if uid != 0:
|
||||||
logger.fatal(&"NimD must run as root, but current user id is {uid}")
|
logger.fatal(&"NimD must run as root, but current user id is {uid}")
|
||||||
quit(EPERM) # EPERM - Operation not permitted
|
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")
|
logger.debug("Starting uninterruptible mainloop")
|
||||||
mainLoop(logger)
|
mainLoop(logger)
|
||||||
|
|
||||||
|
|
|
@ -145,6 +145,7 @@ proc checkDisksIsMounted(search: tuple[source, target, filesystemtype: string, m
|
||||||
|
|
||||||
proc mountRealDisks*(logger: Logger, fstab: string = "/etc/fstab") =
|
proc mountRealDisks*(logger: Logger, fstab: string = "/etc/fstab") =
|
||||||
## Mounts real disks from /etc/fstab
|
## Mounts real disks from /etc/fstab
|
||||||
|
var retcode = 0
|
||||||
try:
|
try:
|
||||||
logger.debug(&"Reading disk entries from {fstab}")
|
logger.debug(&"Reading disk entries from {fstab}")
|
||||||
for entry in parseFileSystemTable(readFile(fstab)):
|
for entry in parseFileSystemTable(readFile(fstab)):
|
||||||
|
@ -153,7 +154,7 @@ proc mountRealDisks*(logger: Logger, fstab: string = "/etc/fstab") =
|
||||||
continue
|
continue
|
||||||
logger.debug(&"Mounting filesystem {entry.source} ({entry.filesystemtype}) at {entry.target} with mount option(s) {entry.data}")
|
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}')")
|
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}")
|
logger.trace(&"mount('{entry.source}', '{entry.target}', '{entry.filesystemtype}', {entry.mountflags}, '{entry.data}') returned {retcode}")
|
||||||
if retcode == -1:
|
if retcode == -1:
|
||||||
logger.error(&"Mounting {entry.source} at {entry.target} has failed with error {posix.errno}: {posix.strerror(posix.errno)}")
|
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) =
|
proc mountVirtualDisks*(logger: Logger) =
|
||||||
## Mounts POSIX virtual filesystems/partitions,
|
## Mounts POSIX virtual filesystems/partitions,
|
||||||
## such as /proc and /sys
|
## such as /proc and /sys
|
||||||
|
|
||||||
|
var retcode = 0
|
||||||
for entry in virtualFileSystems:
|
for entry in virtualFileSystems:
|
||||||
if checkDisksIsMounted(entry):
|
if checkDisksIsMounted(entry):
|
||||||
logger.debug(&"Skipping mounting filesystem {entry.source} ({entry.filesystemtype}) at {entry.target}: already mounted")
|
logger.debug(&"Skipping mounting filesystem {entry.source} ({entry.filesystemtype}) at {entry.target}: already mounted")
|
||||||
continue
|
continue
|
||||||
logger.debug(&"Mounting filesystem {entry.source} ({entry.filesystemtype}) at {entry.target} with mount option(s) {entry.data}")
|
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}')")
|
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}")
|
logger.trace(&"mount('{entry.source}', '{entry.target}', '{entry.filesystemtype}', {entry.mountflags}, '{entry.data}') returned {retcode}")
|
||||||
if retcode == -1:
|
if retcode == -1:
|
||||||
logger.error(&"Mounting disk {entry.source} at {entry.target} has failed with error {posix.errno}: {posix.strerror(posix.errno)}")
|
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) =
|
proc unmountAllDisks*(logger: Logger, code: int) =
|
||||||
## Unmounts all currently mounted disks, including the ones that
|
## 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 flag: bool = false
|
||||||
|
var retcode = 0
|
||||||
try:
|
try:
|
||||||
logger.info("Detaching real filesystems")
|
logger.info("Detaching real filesystems")
|
||||||
logger.debug(&"Reading disk entries from /proc/mounts")
|
logger.debug(&"Reading disk entries from /proc/mounts")
|
||||||
for entry in parseFileSystemTable(readFile("/proc/mounts")):
|
for entry in parseFileSystemTable(readFile("/proc/mounts")):
|
||||||
flag = false
|
|
||||||
if entry.source in ["proc", "sys", "run", "dev", "devpts", "shm"]:
|
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"]:
|
for path in ["/proc", "/sys", "/run", "/dev", "/dev/pts", "/dev/shm"]:
|
||||||
if entry.target.startswith(path):
|
if entry.target.startswith(path):
|
||||||
flag = true
|
flag = true
|
||||||
if flag:
|
if flag:
|
||||||
|
flag = false
|
||||||
|
logger.debug(&"Skipping unmounting filesystem {entry.source} ({entry.filesystemtype}) from {entry.target} as it is a virtual filesystem")
|
||||||
continue
|
continue
|
||||||
if not checkDisksIsMounted(entry):
|
if not checkDisksIsMounted(entry):
|
||||||
logger.debug(&"Skipping unmounting filesystem {entry.source} ({entry.filesystemtype}) from {entry.target}: not mounted")
|
logger.debug(&"Skipping unmounting filesystem {entry.source} ({entry.filesystemtype}) from {entry.target}: not mounted")
|
||||||
continue
|
continue
|
||||||
logger.debug(&"Unmounting filesystem {entry.source} ({entry.filesystemtype}) from {entry.target}")
|
logger.debug(&"Unmounting filesystem {entry.source} ({entry.filesystemtype}) from {entry.target}")
|
||||||
logger.trace(&"Calling umount2('{entry.target}', MNT_DETACH)")
|
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}")
|
logger.trace(&"umount2('{entry.target}', MNT_DETACH) returned {retcode}")
|
||||||
if retcode == -1:
|
if retcode == -1:
|
||||||
logger.error(&"Unmounting disk {entry.source} from {entry.target} has failed with error {posix.errno}: {posix.strerror(posix.errno)}")
|
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)
|
posix.errno = cint(0)
|
||||||
else:
|
else:
|
||||||
logger.debug(&"Unmounted {entry.source} from {entry.target}")
|
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
|
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)
|
nimDExit(logger, 131)
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
# A simple logging module inspired by python's own logging facility
|
# A simple logging module inspired by python's own logging facility
|
||||||
|
import terminal
|
||||||
import strformat
|
import strformat
|
||||||
import times
|
import times
|
||||||
|
|
||||||
|
@ -72,28 +73,45 @@ proc getDefaultLogger*(): Logger =
|
||||||
## level that writes the given message to the
|
## level that writes the given message to the
|
||||||
## standard error with some basic info like the
|
## standard error with some basic info like the
|
||||||
## current date and time and the log level
|
## current date and time and the log level
|
||||||
|
|
||||||
|
setStdIoUnbuffered() # Just in case
|
||||||
|
|
||||||
proc logTrace(self: LogHandler, logger: Logger, message: string) =
|
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}""")
|
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) =
|
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}""")
|
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) =
|
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}""")
|
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) =
|
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}""")
|
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) =
|
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}""")
|
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) =
|
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}""")
|
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) =
|
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}""")
|
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - FATAL] {message}""")
|
||||||
|
setForegroundColor(fgDefault)
|
||||||
|
setBackgroundColor(bgDefault)
|
||||||
|
|
||||||
result = newLogger()
|
result = newLogger()
|
||||||
result.addHandler(createHandler(logTrace, LogLevel.Trace))
|
result.addHandler(createHandler(logTrace, LogLevel.Trace))
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
## to allow a clean shutdown of NimD
|
## to allow a clean shutdown of NimD
|
||||||
import os
|
import os
|
||||||
import strformat
|
import strformat
|
||||||
|
import strutils
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -36,8 +37,8 @@ proc removeShutdownHandler*(handler: proc (logger: Logger, code: int)) =
|
||||||
shutdownHandlers.delete(i)
|
shutdownHandlers.delete(i)
|
||||||
|
|
||||||
|
|
||||||
proc nimDExit*(logger: Logger, code: int) =
|
proc nimDExit*(logger: Logger, code: int, emerg: bool = true) =
|
||||||
logger.warning("The system is being shut down!")
|
logger.warning("The system is shutting down")
|
||||||
# TODO
|
# TODO
|
||||||
logger.info("Processing shutdown runlevel")
|
logger.info("Processing shutdown runlevel")
|
||||||
# TODO
|
# TODO
|
||||||
|
@ -48,13 +49,38 @@ proc nimDExit*(logger: Logger, code: int) =
|
||||||
except:
|
except:
|
||||||
logger.error(&"An error has occurred while calling shutdown handlers. Error -> {getCurrentExceptionMsg()}")
|
logger.error(&"An error has occurred while calling shutdown handlers. Error -> {getCurrentExceptionMsg()}")
|
||||||
# Note: continues calling handlers!
|
# Note: continues calling handlers!
|
||||||
logger.info("Terminating child processes with SIGINT")
|
if emerg:
|
||||||
# TODO
|
# We're in emergency mode: do not crash the kernel, spawn a shell and exit
|
||||||
logger.info("Terminating child processes with SIGKILL")
|
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")
|
||||||
# TODO
|
discard execShellCmd("/bin/sh") # TODO: Is this fine? maybe use execProcess
|
||||||
logger.warning("Shutdown procedure complete, sending final termination signal")
|
else:
|
||||||
# TODO
|
logger.info("Terminating child processes with SIGINT")
|
||||||
quit(code) # Replace with syscall(REBOOT, ...)
|
# 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)
|
proc sleepSeconds*(amount: SomeInteger) = sleep(amount * 1000)
|
||||||
|
@ -62,3 +88,6 @@ proc sleepSeconds*(amount: SomeInteger) = sleep(amount * 1000)
|
||||||
|
|
||||||
proc handleControlC* {.noconv.} =
|
proc handleControlC* {.noconv.} =
|
||||||
raise newException(CtrlCException, "Interrupted by Ctrl+C")
|
raise newException(CtrlCException, "Interrupted by Ctrl+C")
|
||||||
|
|
||||||
|
|
||||||
|
proc strsignal*(sig: cint): cstring {.header:"string.h", importc.}
|
||||||
|
|
Loading…
Reference in New Issue