diff --git a/README.md b/README.md index 4be71a5..fe4c1ae 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # nimd -A minimal init system written in Nim +A minimal, self-contained dependency-based Linux init system written in Nim diff --git a/src/core/mainloop.nim b/src/core/mainloop.nim new file mode 100644 index 0000000..865cb70 --- /dev/null +++ b/src/core/mainloop.nim @@ -0,0 +1,49 @@ +# 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 segfaults # Makes us catch segfaults as NilAccessDefect exceptions! +import strformat +import posix +import os + + +import ../util/[logging, disks, misc] + + +proc mainLoop*(logger: Logger, mountDisks: bool = true) = + ## NimD's main execution loop + try: + if mountDisks: + logger.info("Mounting virtual disks") + mountVirtualDisks(logger) + logger.info("Mounting real disks") + mountRealDisks(logger) + else: + logger.info("Skipping disk mounting, did we restart after a critical error?") + except IndexDefect: # Check parseFileSystemTable for more info on this catch block + logger.fatal("Improperly formatted /etc/fstab, exiting") + nimDExit(logger, 131) + logger.info("Disks mounted") + logger.info("Processing boot runlevel") + # TODO + logger.info("Processing default runlevel") + # TODO + logger.info("System initialization complete, going idle") + while true: + try: + # TODO + sleepSeconds(5) + except: + logger.critical(&"A critical error has occurred while running, restarting the mainloop! Error -> {getCurrentExceptionMsg()}") + # We *absolutely* cannot die + mainLoop(logger, mountDisks=false) diff --git a/src/main.nim b/src/main.nim index 21f9730..0759f17 100644 --- a/src/main.nim +++ b/src/main.nim @@ -16,43 +16,13 @@ import strformat import posix import os - -import util/[logging, constants, mount] - - -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) +# NimD's own stuff +import util/[logging, constants, misc] +import core/mainloop proc main(logger: Logger) = - ## NimD entry point + ## NimD's 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.trace("Calling getCurrentProcessId()") @@ -84,7 +54,7 @@ when isMainModule: echo helpMessage quit(0) of "version": - echo &"NimD version {NimdVersion.major}.{NimdVersion.minor}.{NimdVersion.patch} ({CompileDate}, {CompileTime}, {hostOS}, {hostCPU}) compiled with Nim {NimVersion}" + echo NimDVersionString quit(0) of "verbose": logger.setLevel(LogLevel.Debug) @@ -99,7 +69,7 @@ when isMainModule: echo helpMessage quit(0) of "v": - echo &"NimD version {NimdVersion.major}.{NimdVersion.minor}.{NimdVersion.patch} ({CompileDate}, {CompileTime}, {hostOS}, {hostCPU}) compiled with Nim {NimVersion}" + echo NimDVersionString quit(0) of "V": logger.setLevel(LogLevel.Debug) diff --git a/src/util/constants.nim b/src/util/constants.nim index 864d99f..4eb176d 100644 --- a/src/util/constants.nim +++ b/src/util/constants.nim @@ -11,8 +11,11 @@ # 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 -const NimdVersion*: tuple[major, minor, patch: int] = (major: 0, minor: 0, patch: 1) + +const NimDVersion*: tuple[major, minor, patch: int] = (major: 0, minor: 0, patch: 1) +const NimDVersionString* = &"NimD version {NimDVersion.major}.{NimDVersion.minor}.{NimDVersion.patch} ({CompileDate}, {CompileTime}, {hostOS}, {hostCPU}) compiled with Nim {NimVersion}" 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 diff --git a/src/util/disks.nim b/src/util/disks.nim new file mode 100644 index 0000000..78415b5 --- /dev/null +++ b/src/util/disks.nim @@ -0,0 +1,94 @@ +# 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 +import strformat +import posix + +import logging +import misc + +const virtualFileSystems: seq[tuple[source: cstring, target: cstring, filesystemtype: cstring, mountflags: culong, data: cstring]] = @[ + (source: cstring("proc"), target: cstring("/proc"), filesystemtype: cstring("proc"), mountflags: culong(0), data: cstring("nosuid,noexec,nodev")), + (source: cstring("sys"), target: cstring("/sys"), filesystemtype: cstring("sysfs"), mountflags: culong(0), data: cstring("nosuid,noexec,nodev")), + (source: cstring("run"), target: cstring("/run"), filesystemtype: cstring("tmpfs"), mountflags: culong(0), data: cstring("mode=0755,nosuid,nodev")), + (source: cstring("dev"), target: cstring("/dev"), filesystemtype: cstring("devtmpfs"), mountflags: culong(0), data: cstring("mode=0755,nosuid")), + (source: cstring("devpts"), target: cstring("/dev/pts"), filesystemtype: cstring("devpts"), mountflags: culong(0), data: cstring("mode=0620,gid=5,nosuid,noexec")), + (source: cstring("shm"), target: cstring("/dev/shm"), filesystemtype: cstring("tmpfs"), mountflags: culong(0), data: cstring("mode=1777,nosuid,nodev")), + +] + +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(): + line = l.strip().replace("\t", " ") + if line.startswith("#"): + continue + if line.isEmptyOrWhitespace(): + continue + # This madness will make sure we only get (hopefully) 6 entries + # in our temporary list + temp = line.split().filterIt(it != "").join(" ").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.} + + +proc mountRealDisks*(logger: Logger) = + ## Mounts real disks from /etc/fstab + try: + logger.info("Reading disk entries from /etc/fstab") + for entry in parseFileSystemTable(readFile("/etc/fstab")): + 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) + logger.trace(&"mount({entry.source}, {entry.target}, {entry.filesystemtype}, {entry.mountflags}, {entry.data}) returned {retcode}") + if retcode == -1: + logger.error(&"Mounting disk {entry.source} has failed with error {posix.errno}: {posix.strerror(posix.errno)}") + # Resets the error code + posix.errno = cint(0) + else: + logger.debug(&"Mounted {entry.source} at {entry.target}") + except IndexDefect: # Check parseFileSystemTable for more info on this catch block + logger.fatal("Improperly formatted /etc/fstab, exiting") + nimDExit(logger, 131) + + +proc mountVirtualDisks*(logger: Logger) = + ## Mounts POSIX virtual filesystems/partitions, + ## such as /proc and /sys + for entry in virtualFileSystems: + 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) + logger.trace(&"mount({entry.source}, {entry.target}, {entry.filesystemtype}, {entry.mountflags}, {entry.data}) returned {retcode}") + if retcode == -1: + logger.error(&"Mounting disk {entry.source} has failed with error {posix.errno}: {posix.strerror(posix.errno)}") + # Resets the error code + posix.errno = cint(0) + logger.fatal("Failed mounting vital system disk partition, system is likely corrupted, booting cannot continue") + nimDExit(logger, 131) # ENOTRECOVERABLE - State not recoverable + else: + logger.debug(&"Mounted {entry.source} at {entry.target}") \ No newline at end of file diff --git a/src/util/misc.nim b/src/util/misc.nim new file mode 100644 index 0000000..2cc77b2 --- /dev/null +++ b/src/util/misc.nim @@ -0,0 +1,35 @@ +# 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 os + +import logging + + + +proc nimDExit*(logger: Logger, code: int) = + logger.warning("The system is being shut down, beginning child process termination") + # TODO + logger.info("Processing shutdown runlevel") + # TODO + logger.warning("Process termination complete, sending final shutdown signal") + # TODO + quit(code) + + +proc sleepSeconds*(amount: SomeInteger) = sleep(amount * 1000) + + +proc handleControlC* {.noconv.} = + getDefaultLogger().warning("Main process received SIGINT: exiting") # TODO: Call exit point + nimDExit(getDefaultLogger(), 130) # Exit code 130 indicates a SIGINT \ No newline at end of file diff --git a/src/util/mount.nim b/src/util/mount.nim deleted file mode 100644 index 54f3bff..0000000 --- a/src/util/mount.nim +++ /dev/null @@ -1,40 +0,0 @@ -# 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.}