mirror of https://github.com/nocturn9x/nimd.git
Initial ground work on the init
This commit is contained in:
parent
d242a72a89
commit
c6f6fcb54e
|
@ -0,0 +1,13 @@
|
||||||
|
FROM nimlang/nim AS builder
|
||||||
|
|
||||||
|
COPY . /code
|
||||||
|
WORKDIR /code
|
||||||
|
|
||||||
|
RUN nim c -o:nimd --passL:"-static" src/main.nim
|
||||||
|
RUN cp /code/nimd /sbin/nimd
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
COPY --from=builder /code/nimd /sbin/nimd
|
||||||
|
# ENTRYPOINT ["/bin/sh", "-l"]
|
||||||
|
ENTRYPOINT [ "/sbin/nimd", "--extra"]
|
96
src/main.nim
96
src/main.nim
|
@ -11,33 +11,111 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
import parseopt
|
||||||
import strformat
|
import strformat
|
||||||
import posix
|
import posix
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
import util/logger as log
|
import util/[logging, constants, mount]
|
||||||
|
|
||||||
|
|
||||||
const NimdVersion*: tuple[major, minor, patch: int] = (major: 0, minor: 0, patch: 1)
|
proc sleepSeconds(amount: SomeInteger) = sleep(amount * 1000)
|
||||||
|
|
||||||
|
|
||||||
|
proc handleControlC {.noconv.} =
|
||||||
|
getDefaultLogger().warning("Main process received SIGINT: exiting") # TODO: Call exit point
|
||||||
|
quit(0)
|
||||||
|
|
||||||
|
|
||||||
|
proc mainLoop(logger: Logger) =
|
||||||
|
## NimD's main execution loop
|
||||||
|
try:
|
||||||
|
logger.info("Reading disk entries from /etc/fstab")
|
||||||
|
for entry in parseFileSystemTable(readFile("/etc/fstab")):
|
||||||
|
logger.debug(&"Mounting filesystem {entry.source} of type {entry.filesystemtype} at {entry.target} with mountflags {entry.mountflags} and mount options {entry.data}")
|
||||||
|
logger.trace("Calling mount()")
|
||||||
|
var retcode = mount(entry.source, entry.target, entry.filesystemtype, entry.mountflags, entry.data)
|
||||||
|
logger.trace(&"mount() returned {retcode}")
|
||||||
|
if retcode == -1:
|
||||||
|
logger.warning(&"Mounting disk {entry.source} has failed with error {posix.errno}: {posix.strerror(posix.errno)}")
|
||||||
|
else:
|
||||||
|
logger.debug(&"Mounted {entry.source} at {entry.target}")
|
||||||
|
posix.errno = cint(0)
|
||||||
|
except IndexDefect:
|
||||||
|
logger.fatal("Improperly formatted /etc/fstab, exiting")
|
||||||
|
quit(131)
|
||||||
|
logger.info("Disks mounted")
|
||||||
|
while true:
|
||||||
|
logger.info("NimD is running")
|
||||||
|
sleepSeconds(5)
|
||||||
|
|
||||||
|
|
||||||
proc main(logger: Logger) =
|
proc main(logger: Logger) =
|
||||||
## NimD entry point
|
## NimD entry point
|
||||||
|
logger.debug("Starting NimD: A minimal, self-contained dependency-based Linux init system written in Nim")
|
||||||
logger.info(&"NimD version {NimdVersion.major}.{NimdVersion.minor}.{NimdVersion.patch} is starting up...")
|
logger.info(&"NimD version {NimdVersion.major}.{NimdVersion.minor}.{NimdVersion.patch} is starting up...")
|
||||||
if posix.getuid() != 0:
|
logger.trace("Calling getCurrentProcessId()")
|
||||||
logger.error("NimD must run as root")
|
let pid = getCurrentProcessId()
|
||||||
quit(1) # EPERM - Operation not permitted
|
logger.trace(&"getCurrentProcessId() returned {pid}")
|
||||||
if (let pid = getCurrentProcessId(); pid) != 1:
|
if pid != 1:
|
||||||
logger.warning(&"Expecting to run as PID 1, but current process ID is {pid}")
|
logger.warning(&"Expecting to run as PID 1, but current process ID is {pid}")
|
||||||
|
logger.trace("Calling getuid()")
|
||||||
|
let uid = posix.getuid()
|
||||||
|
logger.trace(&"getuid() returned {uid}")
|
||||||
|
if uid != 0:
|
||||||
|
logger.fatal(&"NimD must run as root, but current user id is {uid}")
|
||||||
|
quit(EPERM) # EPERM - Operation not permitted
|
||||||
|
logger.debug("Starting uninterruptible mainloop")
|
||||||
|
mainLoop(logger)
|
||||||
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
setControlCHook(handleControlC)
|
||||||
var logger = getDefaultLogger()
|
var logger = getDefaultLogger()
|
||||||
|
var optParser = initOptParser(commandLineParams())
|
||||||
|
for kind, key, value in optParser.getopt():
|
||||||
|
case kind:
|
||||||
|
of cmdArgument:
|
||||||
|
discard
|
||||||
|
of cmdLongOption:
|
||||||
|
case key:
|
||||||
|
of "help":
|
||||||
|
echo helpMessage
|
||||||
|
quit(0)
|
||||||
|
of "version":
|
||||||
|
echo &"NimD version {NimdVersion.major}.{NimdVersion.minor}.{NimdVersion.patch} ({CompileDate}, {CompileTime}, {hostOS}, {hostCPU}) compiled with Nim {NimVersion}"
|
||||||
|
quit(0)
|
||||||
|
of "verbose":
|
||||||
|
logger.setLevel(LogLevel.Debug)
|
||||||
|
of "extra":
|
||||||
|
logger.setLevel(LogLevel.Trace)
|
||||||
|
else:
|
||||||
|
logger.error(&"Unkown command-line long option '{key}'")
|
||||||
|
quit(EINVAL) # EINVAL - Invalid argument
|
||||||
|
of cmdShortOption:
|
||||||
|
case key:
|
||||||
|
of "h":
|
||||||
|
echo helpMessage
|
||||||
|
quit(0)
|
||||||
|
of "v":
|
||||||
|
echo &"NimD version {NimdVersion.major}.{NimdVersion.minor}.{NimdVersion.patch} ({CompileDate}, {CompileTime}, {hostOS}, {hostCPU}) compiled with Nim {NimVersion}"
|
||||||
|
quit(0)
|
||||||
|
of "V":
|
||||||
|
logger.setLevel(LogLevel.Debug)
|
||||||
|
of "X":
|
||||||
|
logger.setLevel(LogLevel.Trace)
|
||||||
|
else:
|
||||||
|
logger.error(&"Unkown command-line short option '{key}'")
|
||||||
|
quit(EINVAL) # EINVAL - Invalid argument
|
||||||
|
else:
|
||||||
|
echo "Usage: nimd [options]"
|
||||||
|
quit(EINVAL) # EINVAL - Invalid argument
|
||||||
|
logger.debug("Calling NimD entry point")
|
||||||
try:
|
try:
|
||||||
main(logger)
|
main(logger)
|
||||||
except:
|
except:
|
||||||
logger.fatal(&"A fatal exception has occurred during startup and NimD cannot continue: {getCurrentExceptionMsg()}")
|
logger.fatal(&"A fatal unrecoverable error has occurred during startup and NimD cannot continue: {getCurrentExceptionMsg()}")
|
||||||
quit(131) # ENOTRECOVERABLE - State not recoverable
|
quit(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!",
|
# This will almost certainly cause the kernel to crash with an error the likes of "Kernel not syncing, attempted to kill init!",
|
||||||
# but there isn't much we can do if we can't even initialize *ourselves*, after all, is there?
|
# but, after all, there isn't much we can do if we can't even initialize *ourselves* is there?
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Copyright 2021 Mattia Giambirtone & All Contributors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
const NimdVersion*: tuple[major, minor, patch: int] = (major: 0, minor: 0, patch: 1)
|
||||||
|
const helpMessage* = """The NimD init system, Copyright (C) 2021 Mattia Giambirtone & All contributors
|
||||||
|
|
||||||
|
This program is free software, see the license distributed with this program or check
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0 for more info.
|
||||||
|
|
||||||
|
|
||||||
|
Command-line options
|
||||||
|
--------------------
|
||||||
|
-h, --help -> Shows this help text and exit
|
||||||
|
-v, --version -> Prints the NimD version number and exits
|
||||||
|
-V, --verbose -> Enables debug output
|
||||||
|
-X, --extra -> Enables extra verbose output (hint: you probably don't need it)"""
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
# A simple logging module inspired by python's own logging facility
|
# A simple logging module inspired by python's own logging facility
|
||||||
import strformat
|
import strformat
|
||||||
|
import times
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -61,7 +62,7 @@ proc removeHandler*(self: Logger, handler: LogHandler) = self.handlers.delete(se
|
||||||
proc log(self: Logger, level: LogLevel = defaultLevel, message: string) =
|
proc log(self: Logger, level: LogLevel = defaultLevel, message: string) =
|
||||||
## Generic utility for logging on any level
|
## Generic utility for logging on any level
|
||||||
for handler in self.handlers:
|
for handler in self.handlers:
|
||||||
if handler.level == level:
|
if handler.level == level and self.level <= level:
|
||||||
handler.code(handler, self, message)
|
handler.code(handler, self, message)
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,28 +70,29 @@ proc getDefaultLogger*(): Logger =
|
||||||
## Gets a simple logger with level set
|
## Gets a simple logger with level set
|
||||||
## to LogLevel.Info and one handler per
|
## to LogLevel.Info and one handler per
|
||||||
## level that writes the given message to the
|
## level that writes the given message to the
|
||||||
## standard error
|
## standard error with some basic info like the
|
||||||
|
## current date and time and the log level
|
||||||
|
|
||||||
proc logTrace(self: LogHandler, logger: Logger, message: string) =
|
proc logTrace(self: LogHandler, logger: Logger, message: string) =
|
||||||
stderr.write(&"[TRACE] {message}\n")
|
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - TRACE] {message}""")
|
||||||
|
|
||||||
proc logDebug(self: LogHandler, logger: Logger, message: string) =
|
proc logDebug(self: LogHandler, logger: Logger, message: string) =
|
||||||
stderr.write(&"[DEBUG] {message}\n")
|
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - DEBUG] {message}""")
|
||||||
|
|
||||||
proc logInfo(self: LogHandler, logger: Logger, message: string) =
|
proc logInfo(self: LogHandler, logger: Logger, message: string) =
|
||||||
stderr.write(&"[INFO] {message}\n")
|
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - INFO] {message}""")
|
||||||
|
|
||||||
proc logWarning(self: LogHandler, logger: Logger, message: string) =
|
proc logWarning(self: LogHandler, logger: Logger, message: string) =
|
||||||
stderr.write(&"[WARNING] {message}\n")
|
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - WARNING] {message}""")
|
||||||
|
|
||||||
proc logError(self: LogHandler, logger: Logger, message: string) =
|
proc logError(self: LogHandler, logger: Logger, message: string) =
|
||||||
stderr.write(&"[ERROR] {message}\n")
|
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - ERROR] {message}""")
|
||||||
|
|
||||||
proc logCritical(self: LogHandler, logger: Logger, message: string) =
|
proc logCritical(self: LogHandler, logger: Logger, message: string) =
|
||||||
stderr.write(&"[CRITICAL] {message}\n")
|
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - CRITICAL] {message}""")
|
||||||
|
|
||||||
proc logFatal(self: LogHandler, logger: Logger, message: string) =
|
proc logFatal(self: LogHandler, logger: Logger, message: string) =
|
||||||
stderr.write(&"[FATAL] {message}\n")
|
stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss")} - FATAL] {message}""")
|
||||||
|
|
||||||
|
|
||||||
result = newLogger()
|
result = newLogger()
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Copyright 2021 Mattia Giambirtone & All Contributors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
import strutils
|
||||||
|
import sequtils
|
||||||
|
|
||||||
|
|
||||||
|
proc parseFileSystemTable*(fstab: string): seq[tuple[source: cstring, target: cstring, filesystemtype: cstring, mountflags: culong, data: cstring]] =
|
||||||
|
## Parses the contents of the given file (the contents of /etc/fstab)
|
||||||
|
## and returns a sequence of tuples with elements source, target,
|
||||||
|
## filesystemtype, mountflags and data as required by mount in sys/mount.h
|
||||||
|
## which is wrapped below. An improperly formatted fstab will cause this
|
||||||
|
## function to error out with an IndexDefect exception (when an fstab entry is
|
||||||
|
## incomplete) that should be caught by the caller. No other checks other than
|
||||||
|
## very basic syntax are performed, as that job is delegated to the operating
|
||||||
|
## system.
|
||||||
|
var temp: seq[string] = @[]
|
||||||
|
var line: string
|
||||||
|
for l in fstab.splitlines():
|
||||||
|
if l.strip().startswith("#"):
|
||||||
|
continue
|
||||||
|
if l.strip().len() == 0:
|
||||||
|
continue
|
||||||
|
line = l.filterIt(it != ' ').join("")
|
||||||
|
temp = line.split(maxsplit=6)
|
||||||
|
result.add((source: cstring(temp[0]), target: cstring(temp[1]), filesystemtype: cstring(temp[2]), mountflags: culong(0), data: cstring(temp[3])))
|
||||||
|
|
||||||
|
|
||||||
|
proc mount*(source: cstring, target: cstring, filesystemtype: cstring,
|
||||||
|
mountflags: culong, data: pointer): cint {.header: "sys/mount.h", importc.}
|
Loading…
Reference in New Issue