Modularized the system further, added mounting of virtual filesystems

This commit is contained in:
Nocturn9x 2021-11-30 23:56:38 +01:00
parent c6f6fcb54e
commit 7652a2c1cf
7 changed files with 189 additions and 78 deletions

View File

@ -1,2 +1,2 @@
# nimd # nimd
A minimal init system written in Nim A minimal, self-contained dependency-based Linux init system written in Nim

49
src/core/mainloop.nim Normal file
View File

@ -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)

View File

@ -16,43 +16,13 @@ import strformat
import posix import posix
import os import os
# NimD's own stuff
import util/[logging, constants, mount] import util/[logging, constants, misc]
import core/mainloop
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's entry point
logger.debug("Starting NimD: A minimal, self-contained dependency-based Linux init system written in Nim") 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...")
logger.trace("Calling getCurrentProcessId()") logger.trace("Calling getCurrentProcessId()")
@ -84,7 +54,7 @@ when isMainModule:
echo helpMessage echo helpMessage
quit(0) quit(0)
of "version": of "version":
echo &"NimD version {NimdVersion.major}.{NimdVersion.minor}.{NimdVersion.patch} ({CompileDate}, {CompileTime}, {hostOS}, {hostCPU}) compiled with Nim {NimVersion}" echo NimDVersionString
quit(0) quit(0)
of "verbose": of "verbose":
logger.setLevel(LogLevel.Debug) logger.setLevel(LogLevel.Debug)
@ -99,7 +69,7 @@ when isMainModule:
echo helpMessage echo helpMessage
quit(0) quit(0)
of "v": of "v":
echo &"NimD version {NimdVersion.major}.{NimdVersion.minor}.{NimdVersion.patch} ({CompileDate}, {CompileTime}, {hostOS}, {hostCPU}) compiled with Nim {NimVersion}" echo NimDVersionString
quit(0) quit(0)
of "V": of "V":
logger.setLevel(LogLevel.Debug) logger.setLevel(LogLevel.Debug)

View File

@ -11,8 +11,11 @@
# 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 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 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 This program is free software, see the license distributed with this program or check

94
src/util/disks.nim Normal file
View File

@ -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}")

35
src/util/misc.nim Normal file
View File

@ -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

View File

@ -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.}