mirror of https://github.com/nocturn9x/nimd.git
Added more entries to gitignore
This commit is contained in:
parent
539035f3a5
commit
3f3403170f
|
@ -11,3 +11,4 @@ packervm
|
|||
test
|
||||
initramfs-linux.img
|
||||
vmlinuz-linux
|
||||
debian*
|
||||
|
|
|
@ -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()}")
|
||||
|
|
|
@ -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()}")
|
||||
|
|
|
@ -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)
|
||||
|
|
103
src/main.nim
103
src/main.nim
|
@ -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?
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue