diff --git a/src/core/mainloop.nim b/src/core/mainloop.nim index 601e6c8..fa4b861 100644 --- a/src/core/mainloop.nim +++ b/src/core/mainloop.nim @@ -23,12 +23,13 @@ import shutdown -proc mainLoop*(logger: Logger, workers: int = 1) = +proc mainLoop*(logger: Logger, workers: int = 1, startServices: bool = true) = ## NimD's main execution loop - logger.info("Processing default runlevel") - startServices(logger, workers=workers, level=Default) - logger.debug(&"Unblocking signals") - unblockSignals(logger) + if startServices: + logger.info("Processing default runlevel") + startServices(logger, workers=workers, level=Default) + logger.debug(&"Unblocking signals") + unblockSignals(logger) logger.info("System initialization complete, idling on control socket") var opType: string try: @@ -37,10 +38,12 @@ proc mainLoop*(logger: Logger, workers: int = 1) = serverSocket.listen(5) var clientSocket = newSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP) logger.switchToFile() + logger.debug("Entering accept() loop") while true: serverSocket.accept(clientSocket) + logger.debug(&"Received connection on control socket") if clientSocket.recv(opType, size=1) == 0: - logger.debug(&"Client has disconnected, waiting for new connection") + logger.debug(&"Client has disconnected, waiting for new connections") continue logger.debug(&"Received operation type '{opType}' via control socket") # The operation type is a single byte: @@ -68,4 +71,4 @@ proc mainLoop*(logger: Logger, workers: int = 1) = logger.critical(&"A critical error has occurred while running, restarting the mainloop in 30 seconds! Error -> {getCurrentExceptionMsg()}") sleepSeconds(30) # We *absolutely* cannot die - mainLoop(logger) + mainLoop(logger, startServices=false) diff --git a/src/core/shutdown.nim b/src/core/shutdown.nim index e51cf13..fa8e08a 100644 --- a/src/core/shutdown.nim +++ b/src/core/shutdown.nim @@ -31,7 +31,7 @@ type ShutdownHandler* = ref object body*: proc (logger: Logger, code: int) -const reboot_codes = {"poweroff": 0x4321fedc'i64, "restart": 0x01234567'i64, "halt": 0xcdef0123}.toTable() +const reboot_codes = {"poweroff": 0x4321fedc'i64, "reboot": 0x01234567'i64, "halt": 0xcdef0123}.toTable() proc newShutdownHandler*(body: proc (logger: Logger, code: int)): ShutdownHandler = diff --git a/src/programs/halt.nim b/src/programs/halt.nim new file mode 100644 index 0000000..2e9c211 --- /dev/null +++ b/src/programs/halt.nim @@ -0,0 +1,25 @@ +# 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 net + + +when isMainModule: + var sock = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP) + try: + sock.connectUnix("/var/run/nimd.sock") + except OSError: + echo getCurrentExceptionMsg() + quit(-1) + echo sock.trySend("r") + sock.close() diff --git a/src/programs/poweroff.nim b/src/programs/poweroff.nim new file mode 100644 index 0000000..16f4ec6 --- /dev/null +++ b/src/programs/poweroff.nim @@ -0,0 +1,25 @@ +# 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 net + + +when isMainModule: + var sock = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP) + try: + sock.connectUnix("/var/run/nimd.sock") + except OSError: + echo getCurrentExceptionMsg() + quit(-1) + echo sock.trySend("p") + sock.close() diff --git a/src/programs/reboot.nim b/src/programs/reboot.nim new file mode 100644 index 0000000..2e9c211 --- /dev/null +++ b/src/programs/reboot.nim @@ -0,0 +1,25 @@ +# 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 net + + +when isMainModule: + var sock = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP) + try: + sock.connectUnix("/var/run/nimd.sock") + except OSError: + echo getCurrentExceptionMsg() + quit(-1) + echo sock.trySend("r") + sock.close() diff --git a/src/util/logging.nim b/src/util/logging.nim index 0e1ef8d..d1d3788 100644 --- a/src/util/logging.nim +++ b/src/util/logging.nim @@ -38,10 +38,21 @@ type level*: LogLevel handlers*: seq[LogHandler] + +proc dup3(a1, a2, a3: cint): cint {.importc.} + + var defaultLevel = LogLevel.Info var logFile = "/var/log/nimd" var logToFileOnly: bool = false +## This mess is needed to make sure stderr writes are mostly atomic. Sort of +## No error handling yet. Deal with it +var customStderrFd = dup(stderr.getFileHandle()) +discard dup3(stderr.getFileHandle(), customStderrFd, O_APPEND) +var customStderr: File +discard open(customStderr, customStderrFd, fmAppend) + proc log(self: Logger, level: LogLevel = defaultLevel, message: string) # Forward declaration @@ -79,132 +90,91 @@ proc log(self: Logger, level: LogLevel = defaultLevel, message: string) = # Do NOT touch the alignment offsets or your console output and logs will look like trash -proc lockFile(logger: Logger, handle: File) = - ## Locks the given file across the whole system for writing using fcntl() - if fcntl(handle.getFileHandle(), F_WRLCK) == -1: - setForegroundColor(fgRed) - stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} ERROR {"-":>3} ({posix.getpid():03})] Error while locking handle (code {posix.errno}, {posix.strerror(posix.errno)}): output may be mangled""") - setForegroundColor(fgDefault) - - -proc unlockFile(logger: Logger, handle: File) = - ## Unlocks the given file across the whole system for writing using fcntl() - if fcntl(handle.getFileHandle(), F_UNLCK) == -1: - setForegroundColor(fgRed) - stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} ERROR {"-":>3} ({posix.getpid():03})] Error while unlocking handle (code {posix.errno}, {posix.strerror(posix.errno)}): output may be missing""") - setForegroundColor(fgDefault) - proc logTraceStderr(self: LogHandler, logger: Logger, message: string) = - logger.lockFile(stderr) setForegroundColor(fgMagenta) - stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} TRACE {"-":>3} ({posix.getpid():03})] {message}""") + customStderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} TRACE {"-":>3} ({posix.getpid():03})] {message}""") setForegroundColor(fgDefault) - logger.unlockFile(stderr) proc logDebugStderr(self: LogHandler, logger: Logger, message: string) = - logger.lockFile(stderr) setForegroundColor(fgCyan) - stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} DEBUG {"-":>3} ({posix.getpid():03})] {message}""") + customStderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} DEBUG {"-":>3} ({posix.getpid():03})] {message}""") setForegroundColor(fgDefault) - logger.unlockFile(stderr) proc logInfoStderr(self: LogHandler, logger: Logger, message: string) = - logger.lockFile(stderr) setForegroundColor(fgGreen) - stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} INFO {"-":>4} ({posix.getpid():03})] {message}""") + customStderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} INFO {"-":>4} ({posix.getpid():03})] {message}""") setForegroundColor(fgDefault) - logger.unlockFile(stderr) proc logWarningStderr(self: LogHandler, logger: Logger, message: string) = - logger.lockFile(stderr) setForegroundColor(fgYellow) - stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} WARNING {"-":>1} ({posix.getpid():03})] {message}""") + customStderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} WARNING {"-":>1} ({posix.getpid():03})] {message}""") setForegroundColor(fgDefault) - logger.unlockFile(stderr) proc logErrorStderr(self: LogHandler, logger: Logger, message: string) = - logger.lockFile(stderr) setForegroundColor(fgRed) - stderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} ERROR {"-":>3} ({posix.getpid():03})] {message}""") + customStderr.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} ERROR {"-":>3} ({posix.getpid():03})] {message}""") setForegroundColor(fgDefault) - logger.unlockFile(stderr) - + proc logCriticalStderr(self: LogHandler, logger: Logger, message: string) = - logger.lockFile(stderr) setForegroundColor(fgYellow) setBackgroundColor(bgRed) - stderr.write(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<4} {"-":>1} CRITICAL {"-":>2} ({posix.getpid():03})]""") + customStderr.write(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<4} {"-":>1} CRITICAL {"-":>2} ({posix.getpid():03})]""") setBackgroundColor(bgDefault) - stderr.writeLine(&""" {message}""") + customStderr.writeLine(&""" {message}""") setForegroundColor(fgDefault) - logger.unlockFile(stderr) - + proc logFatalStderr(self: LogHandler, logger: Logger, message: string) = - logger.lockFile(stderr) setForegroundColor(fgBlack) setBackgroundColor(bgRed) - stderr.write(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<5} {"-":>1} {"":>1} FATAL {"-":>3} ({posix.getpid():03})]""") + customStderr.write(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<5} {"-":>1} {"":>1} FATAL {"-":>3} ({posix.getpid():03})]""") setForegroundColor(fgRed) setBackgroundColor(bgDefault) - stderr.writeline(&""" {message}""") + customStderr.writeline(&""" {message}""") setForegroundColor(fgDefault) - logger.unlockFile(stderr) proc logTraceFile(self: LogHandler, logger: Logger, message: string) = var self = StreamHandler(self) - logger.lockFile(self.file) self.file.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} TRACE {"-":>3} ({posix.getpid():03})] {message}""") - logger.unlockFile(self.file) - + proc logDebugFile(self: LogHandler, logger: Logger, message: string) = var self = StreamHandler(self) - logger.lockFile(self.file) self.file.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} DEBUG {"-":>3} ({posix.getpid():03})] {message}""") - logger.unlockFile(self.file) - + proc logInfoFile(self: LogHandler, logger: Logger, message: string) = var self = StreamHandler(self) - logger.lockFile(self.file) self.file.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} INFO {"-":>4} ({posix.getpid():03})] {message}""") - logger.unlockFile(self.file) - + proc logWarningFile(self: LogHandler, logger: Logger, message: string) = var self = StreamHandler(self) - logger.lockFile(self.file) self.file.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} WARNING {"-":>1} ({posix.getpid():03})] {message}""") - logger.unlockFile(self.file) proc logErrorFile(self: LogHandler, logger: Logger, message: string) = var self = StreamHandler(self) - logger.lockFile(self.file) self.file.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<10} {"-":>1} {"":>1} ERROR {"-":>3} ({posix.getpid():03})] {message}""") - logger.unlockFile(self.file) + proc logCriticalFile(self: LogHandler, logger: Logger, message: string) = var self = StreamHandler(self) - logger.lockFile(self.file) self.file.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<4} {"-":>1} CRITICAL {"-":>2} ({posix.getpid():03})] {message}""") - logger.unlockFile(self.file) - + proc logFatalFile(self: LogHandler, logger: Logger, message: string) = var self = StreamHandler(self) - logger.lockFile(self.file) self.file.writeLine(&"""[{fromUnix(getTime().toUnixFloat().int).format("d/M/yyyy HH:mm:ss"):<5} {"-":>1} {"":>1} FATAL {"-":>3} ({posix.getpid():03})] {message}""") - logger.unlockFile(self.file) + proc switchToFile*(self: Logger) =