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()}")
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -73,27 +74,44 @@ proc getDefaultLogger*(): Logger =
|
|||
## 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))
|
||||
|
|
|
@ -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!
|
||||
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) # Replace with syscall(REBOOT, ...)
|
||||
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.}
|
||||
|
|
Loading…
Reference in New Issue