Added more entries to gitignore

This commit is contained in:
nocturn9x 2021-12-05 20:53:44 +01:00
parent 539035f3a5
commit 3f3403170f
7 changed files with 157 additions and 79 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ packervm
test
initramfs-linux.img
vmlinuz-linux
debian*

View File

@ -350,9 +350,44 @@ proc createSymlinks*(logger: Logger) =
createDir(sym.dest.splitPath().head)
createSymlink(sym.source, sym.dest)
except:
logger.warning(&"Failed to create symbolic link from {sym.dest} to {sym.source}: {getCurrentExceptionMsg()}")
logger.error(&"Failed to create symbolic link from {sym.dest} to {sym.source}: {getCurrentExceptionMsg()}")
proc createDirectories*(logger: Logger) =
## Creates standard directories that
## Linux software expects to be present.
## Linux software expects to be present.
## Note that this has to run after the
## filesystem has been initialized.
## If a chmod binary is found, it is used
## to set directory permissions as specified
## in their config. Note that the entire path
## of the directory is created if it does not
## exist yet
var hasChmod = false
try:
if findExe("chmod").isEmptyOrWhitespace():
logger.warning("Could not find chmod binary, directory permissions will default to OS configuration")
hasChmod = true
except:
logger.error(&"Failed to search for chmod binary: {getCurrentExceptionMsg()}")
for dir in directories:
try:
if exists(dir.path):
if dirExists(dir.path):
logger.warning(&"Creation of directory {dir.path} skipped: directory already exists")
elif fileExists(dir.path):
logger.warning(&"Creation of directory {dir.path} skipped: path is a file")
elif symlinkExists(dir.path):
logger.warning(&"Creation of directory {dir.path} skipped: path is a symlink to {expandSymlink(dir.path)}")
else:
# Catch-all
logger.warning(&"Creation of directory {dir.path} skipped: destination already exists")
else:
createDir(dir.path)
logger.debug(&"Created new directory at {dir.path}")
if hasChmod:
logger.debug(&"Setting permissions to {dir.permissions} for {dir.path}")
if (let code = execShellCmd(&"chmod -R {dir.permissions} {dir.path}"); code) != 0:
logger.warning(&"Command 'chmod -R {dir.permissions}' exited non-zero status code {code}")
except:
logger.error(&"Failed to create directory at {dir.path}: {getCurrentExceptionMsg()}")

View File

@ -17,10 +17,13 @@ import tables
import osproc
import posix
import shlex
import os
proc strsignal(sig: cint): cstring {.header: "string.h", importc.}
import ../util/logging
import ../util/misc
type
@ -31,6 +34,9 @@ type
## Enumerates all service
## types
Oneshot, Simple
RestartKind* = enum
## Enum of possible restart modes
Always, OnFailure, Never
Service* = ref object of RootObj
## A service object
name: string
@ -40,14 +46,14 @@ type
runlevel: RunLevel
exec: string
supervised: bool
restartOnFailure: bool
restart: RestartKind
restartDelay: int
proc newService*(name, description: string, kind: ServiceKind, workDir: string, runlevel: RunLevel, exec: string, supervised, restartOnFailure: bool, restartDelay: int): Service =
proc newService*(name, description: string, kind: ServiceKind, workDir: string, runlevel: RunLevel, exec: string, supervised: bool, restart: RestartKind, restartDelay: int): Service =
## Creates a new service object
result = Service(name: name, description: description, kind: kind, workDir: workDir, runLevel: runLevel,
exec: exec, supervised: supervised, restartOnFailure: restartOnFailure, restartDelay: restartDelay)
exec: exec, supervised: supervised, restart: restart, restartDelay: restartDelay)
var services: seq[Service] = @[]
@ -114,25 +120,42 @@ proc supervisorWorker(logger: Logger, service: Service, pid: int) =
sig = WTERMSIG(status)
else:
sig = -1
if sig > 0 and service.restartOnFailure:
logger.info(&"Service '{service.name}' ({returnCode}) has crashed (terminated by signal {sig}: {strsignal(cint(sig))}), sleeping {service.restartDelay} seconds before restarting it")
removeManagedProcess(pid)
sleepSeconds(service.restartDelay)
var split = shlex(service.exec)
if split.error:
logger.error(&"Error while restarting service '{service.name}': invalid exec syntax")
case service.restart:
of Never:
logger.info(&"Service '{service.name}' ({returnCode}) has exited, shutting down controlling process")
break
var arguments = split.words
let progName = arguments[0]
arguments = arguments[1..^1]
process = startProcess(progName, workingDir=service.workDir, args=arguments)
pid = process.processID()
elif sig > 0:
logger.info(&"Service '{service.name}' ({returnCode}) has crashed (terminated by signal {sig}: {strsignal(cint(sig))}), shutting down controlling process")
break
else:
logger.info(&"Service '{service.name}' ({returnCode}) has exited, shutting down controlling process")
break
of Always:
if sig > 0:
logger.info(&"Service '{service.name}' ({returnCode}) has crashed (terminated by signal {sig}: {strsignal(cint(sig))}), sleeping {service.restartDelay} seconds before restarting it")
elif sig == 0:
logger.info(&"Service '{service.name}' has exited gracefully, sleeping {service.restartDelay} seconds before restarting it")
else:
logger.info(&"Service '{service.name}' has exited, sleeping {service.restartDelay} seconds before restarting it")
removeManagedProcess(pid)
sleep(service.restartDelay * 1000)
var split = shlex(service.exec)
if split.error:
logger.error(&"Error while restarting service '{service.name}': invalid exec syntax")
break
var arguments = split.words
let progName = arguments[0]
arguments = arguments[1..^1]
process = startProcess(progName, workingDir=service.workDir, args=arguments)
pid = process.processID()
of OnFailure:
if sig > 0:
logger.info(&"Service '{service.name}' ({returnCode}) has crashed (terminated by signal {sig}: {strsignal(cint(sig))}), sleeping {service.restartDelay} seconds before restarting it")
removeManagedProcess(pid)
sleep(service.restartDelay * 1000)
var split = shlex(service.exec)
if split.error:
logger.error(&"Error while restarting service '{service.name}': invalid exec syntax")
break
var arguments = split.words
let progName = arguments[0]
arguments = arguments[1..^1]
process = startProcess(progName, workingDir=service.workDir, args=arguments)
pid = process.processID()
if process != nil:
process.close()
@ -140,7 +163,8 @@ proc supervisorWorker(logger: Logger, service: Service, pid: int) =
proc startService(logger: Logger, service: Service) =
## Starts a single service (this is called by
## startServices below until all services have
## been started)
## been started). This function is supposed to
## be called from a forked process!
var process: Process
try:
var split = shlex(service.exec)
@ -152,7 +176,9 @@ proc startService(logger: Logger, service: Service) =
arguments = arguments[1..^1]
process = startProcess(progName, workingDir=service.workDir, args=arguments)
if service.supervised:
supervisorWorker(logger, service, process.processID)
var pid = posix.fork()
if pid == 0:
supervisorWorker(logger, service, process.processID)
# If the service is unsupervised we just exit
except:
logger.error(&"Error while starting service {service.name}: {getCurrentExceptionMsg()}")

View File

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import osproc
import posix
import glob
import strutils
@ -20,6 +21,7 @@ import times
import ../util/logging
import services
type ShutdownHandler* = ref object
@ -79,17 +81,15 @@ proc nimDExit*(logger: Logger, code: int, emerg: bool = true) =
## as cleanly as possible. When emerg equals true, it will
## try to spawn a root shell and exit
if emerg:
var status: cint
# 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")
logger.info("Terminating child processes with SIGKILL")
discard posix.kill(SIGKILL, -1)
discard posix.waitPid(-1, status, 0)
discard execShellCmd("/bin/sh") # TODO: Is this fine? maybe use execProcess
discard execCmd("/bin/sh") # TODO: Is this fine? maybe use execProcess
discard posix.kill(-1, SIGKILL)
quit(-1)
logger.warning("The system is shutting down")
logger.info("Processing shutdown runlevel")
# TODO
startServices(logger, Shutdown)
logger.info("Running shutdown handlers")
try:
for handler in shutdownHandlers:
@ -99,13 +99,13 @@ proc nimDExit*(logger: Logger, code: int, emerg: bool = true) =
# Note: continues calling handlers!
logger.info("Terminating child processes with SIGTERM")
logger.debug(&"Waiting up to {sigTermDelay} seconds for the kernel to deliver signals")
discard posix.kill(SIGTERM, -1) # The kernel handles this for us asynchronously
discard posix.kill(-1, SIGTERM) # The kernel handles this for us asynchronously
var t = cpuTime()
# We wait some time for the signals to propagate
while anyUserlandProcessLeft() or cpuTime() - t >= sigTermDelay:
sleep(int(0.25 * 1000))
if anyUserlandProcessLeft():
logger.info("Terminating child processes with SIGKILL")
discard posix.kill(SIGKILL, -1)
discard posix.kill(-1, SIGKILL)
logger.warning("Shutdown procedure complete, sending final termination signal")
quit(code)

View File

@ -21,6 +21,50 @@ import util/[logging, constants, misc]
import core/[mainloop, fs, shutdown, services]
proc addStuff =
## Adds stuff to test NimD. This is
## a temporary procedure
# Adds symlinks
addSymlink(newSymlink(dest="/dev/fd", source="/proc/self/fd"))
addSymlink(newSymlink(dest="/dev/fd/0", source="/proc/self/fd/0"))
addSymlink(newSymlink(dest="/dev/fd/1", source="/proc/self/fd/1"))
addSymlink(newSymlink(dest="/dev/fd/2", source="/proc/self/fd/2"))
addSymlink(newSymlink(dest="/dev/std/in", source="/proc/self/fd/0"))
addSymlink(newSymlink(dest="/dev/std/out", source="/proc/self/fd/1"))
addSymlink(newSymlink(dest="/dev/std/err", source="/proc/self/fd/2"))
# Tests here. Check logging output (debug) to see if
# they work as intended
addSymlink(newSymlink(dest="/dev/std/err", source="/")) # Should say link already exists and points to /proc/self/fd/2
addSymlink(newSymlink(dest="/dev/std/in", source="/does/not/exist")) # Shuld say destination does not exist
addSymlink(newSymlink(dest="/dev/std/in", source="/proc/self/fd/0")) # Should say link already exists
# Adds virtual filesystems
addVFS(newFilesystem(source="proc", target="/proc", fstype="proc", mountflags=0u64, data="nosuid,noexec,nodev", dump=0u8, pass=0u8))
addVFS(newFilesystem(source="sys", target="/sys", fstype="sysfs", mountflags=0u64, data="nosuid,noexec,nodev", dump=0u8, pass=0u8))
addVFS(newFilesystem(source="run", target="/run", fstype="tmpfs", mountflags=0u64, data="mode=0755,nosuid,nodev", dump=0u8, pass=0u8))
addVFS(newFilesystem(source="dev", target="/dev", fstype="devtmpfs", mountflags=0u64, data="mode=0755,nosuid", dump=0u8, pass=0u8))
addVFS(newFilesystem(source="devpts", target="/dev/pts", fstype="devpts", mountflags=0u64, data="mode=0620,gid=5,nosuid,noexec", dump=0u8, pass=0u8))
addVFS(newFilesystem(source="shm", target="/dev/shm", fstype="tmpfs", mountflags=0u64, data="mode=1777,nosuid,nodev", dump=0u8, pass=0u8))
addDirectory(newDirectory("test", 777)) # Should create a directory
addDirectory(newDirectory("/dev/disk", 123)) # Should say directory already exists
addDirectory(newDirectory("/dev/test/owo", 000)) # Should say path does not exist
# Shutdown handler to unmount disks
addShutdownHandler(newShutdownHandler(unmountAllDisks))
# Adds test services
addService(newService(name="echoer", description="prints owo", exec="/bin/echo owo",
runlevel=Boot, kind=Oneshot, workDir=getCurrentDir(),
supervised=false, restart=Never, restartDelay=0))
addService(newService(name="errorer", description="la mamma di gavd",
exec="/bin/false", supervised=true, restart=OnFailure,
restartDelay=5, runlevel=Boot, workDir="/", kind=Simple))
addService(newService(name="exiter", description="la mamma di licenziat",
exec="/bin/true", supervised=true, restart=Always,
restartDelay=5, runlevel=Boot, workDir="/", kind=Simple))
addService(newService(name="sleeper", description="la mamma di danieloz",
exec="/usr/bin/sleep", supervised=true, restart=Always,
restartDelay=5, runlevel=Boot, workDir="/", kind=Simple))
proc main(logger: Logger, mountDisks: bool = true, fstab: string = "/etc/fstab") =
## NimD's entry point and setup
## function
@ -53,26 +97,7 @@ proc main(logger: Logger, mountDisks: bool = true, fstab: string = "/etc/fstab")
onSignal(SIGINT):
# Temporary
nimDExit(getDefaultLogger(), 131, emerg=true)
addSymlink(newSymlink(dest="/dev/fd", source="/proc/self/fd"))
addSymlink(newSymlink(dest="/dev/fd/0", source="/proc/self/fd/0"))
addSymlink(newSymlink(dest="/dev/fd/1", source="/proc/self/fd/1"))
addSymlink(newSymlink(dest="/dev/fd/2", source="/proc/self/fd/2"))
addSymlink(newSymlink(dest="/dev/std/in", source="/proc/self/fd/0"))
addSymlink(newSymlink(dest="/dev/std/out", source="/proc/self/fd/1"))
addSymlink(newSymlink(dest="/dev/std/err", source="/proc/self/fd/2"))
# Tests here. Check logging output (debug) to see if
# they work as intended
addSymlink(newSymlink(dest="/dev/std/err", source="/")) # Should say link already exists and points to /proc/self/fd/2
addSymlink(newSymlink(dest="/dev/std/in", source="/does/not/exist")) # Shuld say destination does not exist
addSymlink(newSymlink(dest="/dev/std/in", source="/proc/self/fd/0")) # Should say link already exists
# Adds virtual filesystems
addVFS(newFilesystem(source="proc", target="/proc", fstype="proc", mountflags=0u64, data="nosuid,noexec,nodev", dump=0u8, pass=0u8))
addVFS(newFilesystem(source="sys", target="/sys", fstype="sysfs", mountflags=0u64, data="nosuid,noexec,nodev", dump=0u8, pass=0u8))
addVFS(newFilesystem(source="run", target="/run", fstype="tmpfs", mountflags=0u64, data="mode=0755,nosuid,nodev", dump=0u8, pass=0u8))
addVFS(newFilesystem(source="dev", target="/dev", fstype="devtmpfs", mountflags=0u64, data="mode=0755,nosuid", dump=0u8, pass=0u8))
addVFS(newFilesystem(source="devpts", target="/dev/pts", fstype="devpts", mountflags=0u64, data="mode=0620,gid=5,nosuid,noexec", dump=0u8, pass=0u8))
addVFS(newFilesystem(source="shm", target="/dev/shm", fstype="tmpfs", mountflags=0u64, data="mode=1777,nosuid,nodev", dump=0u8, pass=0u8))
addShutdownHandler(newShutdownHandler(unmountAllDisks))
addStuff()
try:
if mountDisks:
logger.info("Mounting filesystem")
@ -82,31 +107,22 @@ proc main(logger: Logger, mountDisks: bool = true, fstab: string = "/etc/fstab")
mountRealDisks(logger, fstab)
else:
logger.info("Skipping disk mounting, assuming this has already been done")
logger.info("Creating symlinks")
createSymlinks(logger)
logger.info("Creating directories")
createDirectories(logger)
logger.info("Filesystem preparation complete")
logger.debug("Calling sync() just in case")
doSync(logger)
except:
logger.fatal(&"A fatal error has occurred while preparing filesystem, booting cannot continue. Error -> {getCurrentExceptionMsg()}")
nimDExit(logger, 131, emerg=false)
logger.info("Disks mounted")
logger.debug("Calling sync() just in case")
doSync(logger)
logger.info("Setting hostname")
logger.debug(&"Hostname was set to '{setHostname(logger)}'")
logger.info("Creating symlinks")
createSymlinks(logger)
logger.info("Creating directories")
createDirectories(logger)
logger.debug("Entering critical fork() section: blocking signals")
blockSignals(logger)
blockSignals(logger) # They are later unblocked in mainLoop
logger.info("Processing boot runlevel")
addService(newService(name="echoer", description="prints owo", exec="/bin/echo owo",
runlevel=Boot, kind=Oneshot, workDir=getCurrentDir(),
supervised=false, restartOnFailure=false, restartDelay=0))
addService(newService(name="sleeper", description="la mamma di licenziato",
exec="/usr/bin/sleep 10", supervised=true, restartOnFailure=true,
restartDelay=5, runlevel=Boot, workDir="/home", kind=Simple))
addService(newService(name="errorer", description="la mamma di gavd",
exec="/bin/false", supervised=true, restartOnFailure=true,
restartDelay=5, runlevel=Boot, workDir="/", kind=Simple))
startServices(logger, workers=2, level=Boot)
startServices(logger, workers=1, level=Boot)
logger.debug("Starting main loop")
mainLoop(logger)
@ -117,7 +133,8 @@ when isMainModule:
for kind, key, value in optParser.getopt():
case kind:
of cmdArgument:
discard
echo "Error: unexpected argument"
quit(EINVAL)
of cmdLongOption:
case key:
of "help":
@ -131,7 +148,7 @@ when isMainModule:
of "extra":
logger.setLevel(LogLevel.Trace)
else:
logger.error(&"Unkown command-line long option '{key}'")
echo &"Unkown command-line long option '{key}'"
quit(EINVAL) # EINVAL - Invalid argument
of cmdShortOption:
case key:
@ -146,7 +163,9 @@ when isMainModule:
of "X":
logger.setLevel(LogLevel.Trace)
else:
logger.error(&"Unkown command-line short option '{key}'")
echo &"Unkown command-line short option '{key}'"
echo "Usage: nimd [options]"
echo "Try nimd --help for more info"
quit(EINVAL) # EINVAL - Invalid argument
else:
echo "Usage: nimd [options]"
@ -158,4 +177,4 @@ when isMainModule:
logger.fatal(&"A fatal unrecoverable error has occurred during startup and NimD cannot continue: {getCurrentExceptionMsg()}")
nimDExit(logger, 131) # ENOTRECOVERABLE - State not recoverable
# This will almost certainly cause the kernel to crash with an error the likes of "Kernel not syncing, attempted to kill init!",
# but, after all, there isn't much we can do if we can't even initialize *ourselves* is there?
# but, after all, there isn't much we can do if we can't even initialize *ourselves* is there?

View File

@ -75,7 +75,7 @@ proc getDefaultLogger*(): Logger =
## standard error with some basic info like the
## current date and time and the log level
setStdIoUnbuffered() # Just in case
setStdIoUnbuffered() # Doesn't work otherwise!
proc logTrace(self: LogHandler, logger: Logger, message: string) =
setForegroundColor(fgMagenta)

View File

@ -1,3 +0,0 @@
#!/bin/bash
docker build -t nimd:latest .
docker run --rm -it nimd