diff --git a/src/main.nim b/src/main.nim new file mode 100644 index 0000000..5c0a3c2 --- /dev/null +++ b/src/main.nim @@ -0,0 +1,43 @@ +# 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 strformat +import posix +import os + + +import util/logger as log + + +const NimdVersion*: tuple[major, minor, patch: int] = (major: 0, minor: 0, patch: 1) + + +proc main(logger: Logger) = + ## NimD entry point + logger.info(&"NimD version {NimdVersion.major}.{NimdVersion.minor}.{NimdVersion.patch} is starting up...") + if posix.getuid() != 0: + logger.error("NimD must run as root") + quit(1) # EPERM - Operation not permitted + if (let pid = getCurrentProcessId(); pid) != 1: + logger.warning(&"Expecting to run as PID 1, but current process ID is {pid}") + + +when isMainModule: + var logger = getDefaultLogger() + try: + main(logger) + except: + logger.fatal(&"A fatal exception has occurred during startup and NimD cannot continue: {getCurrentExceptionMsg()}") + 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!", + # but there isn't much we can do if we can't even initialize *ourselves*, after all, is there? \ No newline at end of file diff --git a/src/util/logger.nim b/src/util/logger.nim new file mode 100644 index 0000000..2eb11de --- /dev/null +++ b/src/util/logger.nim @@ -0,0 +1,103 @@ +# 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. + +# A simple logging module inspired by python's own logging facility +import strformat + + +type + LogLevel* = enum + Trace = 0, + Debug = 10, + Info = 20, + Warning = 30, + Error = 40, + Critical = 50, + Fatal = 60 + LogHandler* = ref object of RootObj + code: proc (self: LogHandler, logger: Logger, message: string) + level: LogLevel + StreamHandler* = ref object of LogHandler + filename*: string + file*: File + Logger* = ref object + level*: LogLevel + handlers*: seq[LogHandler] + +const defaultLevel = LogLevel.Info + +proc log(self: Logger, level: LogLevel = defaultLevel, message: string) # Forward declaration + +# Simple one-line procedures + +proc trace*(self: Logger, message: string) = self.log(LogLevel.Trace, message) +proc debug*(self: Logger, message: string) = self.log(LogLevel.Debug, message) +proc info*(self: Logger, message: string) = self.log(LogLevel.Info, message) +proc warning*(self: Logger, message: string) = self.log(LogLevel.Warning, message) +proc error*(self: Logger, message: string) = self.log(LogLevel.Error, message) +proc critical*(self: Logger, message: string) = self.log(LogLevel.Critical, message) +proc fatal*(self: Logger, message: string) = self.log(LogLevel.Fatal, message) + +proc newLogger*(level: LogLevel = defaultLevel, handlers: seq[LogHandler] = @[]): Logger = Logger(level: level, handlers: handlers) +proc setLevel*(self: Logger, level: LogLevel) = self.level = level +proc getLevel*(self: Logger): LogLevel = self.level +proc createHandler*(procedure: proc (self: LogHandler, logger: Logger, message: string), level: LogLevel): LogHandler = LogHandler(code: procedure, level: level) +proc createStreamHandler*(procedure: proc (self: LogHandler, logger: Logger, message: string), level: LogLevel, filename: string): StreamHandler = StreamHandler(code: procedure, level: level, filename: filename, file: open(filename, fmWrite)) +proc addHandler*(self: Logger, handler: LogHandler) = self.handlers.add(handler) +proc removeHandler*(self: Logger, handler: LogHandler) = self.handlers.delete(self.handlers.find(handler)) + + +proc log(self: Logger, level: LogLevel = defaultLevel, message: string) = + ## Generic utility for logging on any level + for handler in self.handlers: + if handler.level < level: + handler.code(handler, self, message) + + +proc getDefaultLogger*(): Logger = + ## Gets a simple logger with level set + ## to LogLevel.Info and one handler per + ## level that writes the given message to the + ## standard error + + proc logTrace(self: LogHandler, logger: Logger, message: string) = + stderr.write(&"[TRACE] {message}") + + proc logDebug(self: LogHandler, logger: Logger, message: string) = + stderr.write(&"[DEBUG] {message}") + + proc logInfo(self: LogHandler, logger: Logger, message: string) = + stderr.write(&"[INFO] {message}") + + proc logWarning(self: LogHandler, logger: Logger, message: string) = + stderr.write(&"[WARNING] {message}") + + proc logError(self: LogHandler, logger: Logger, message: string) = + stderr.write(&"[ERROR] {message}") + + proc logCritical(self: LogHandler, logger: Logger, message: string) = + stderr.write(&"[CRITICAL] {message}") + + proc logFatal(self: LogHandler, logger: Logger, message: string) = + stderr.write(&"[FATAL] {message}") + + + result = newLogger() + result.addHandler(createHandler(logTrace, LogLevel.Trace)) + result.addHandler(createHandler(logDebug, LogLevel.Debug)) + result.addHandler(createHandler(logInfo, LogLevel.Info)) + result.addHandler(createHandler(logWarning, LogLevel.Warning)) + result.addHandler(createHandler(logError, LogLevel.Error)) + result.addHandler(createHandler(logCritical, LogLevel.Critical)) + result.addHandler(createHandler(logFatal, LogLevel.Fatal)) \ No newline at end of file