Compare commits

..

4 Commits

Author SHA1 Message Date
b77147434e Add loopback interface startup service 2025-03-19 17:23:27 +02:00
fe33725c09 Add file dependencies to services 2025-03-19 17:22:18 +02:00
aec103063f Add simple service logging 2025-03-19 15:34:47 +02:00
b4847da839 Add swap fstab entry support 2025-03-15 09:23:01 +02:00
5 changed files with 125 additions and 13 deletions

View File

@ -1,10 +1,12 @@
package main package main
import ( import (
"fmt"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"os" "os"
"slices" "slices"
"strings" "strings"
"unsafe"
) )
var flagsEquivalence = map[string]uintptr{ var flagsEquivalence = map[string]uintptr{
@ -111,6 +113,8 @@ func mountFstabEntries() error {
return err return err
} }
swapPriority := -2
for _, line := range strings.Split(string(bytes), "\n") { for _, line := range strings.Split(string(bytes), "\n") {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
if strings.HasPrefix(line, "#") || line == "" { if strings.HasPrefix(line, "#") || line == "" {
@ -128,6 +132,18 @@ func mountFstabEntries() error {
continue continue
} }
if fstype == "swap" {
b := append([]byte(source), 0)
const SwapFlagPrioShift = 0
const SwapFlagPrioMask = 0x7fff
_, _, err := unix.Syscall(unix.SYS_SWAPON, uintptr(unsafe.Pointer(&b[0])), uintptr((swapPriority<<SwapFlagPrioShift)&SwapFlagPrioMask), 0)
swapPriority--
if err != 0 {
return fmt.Errorf("swapon syscall returned none-zero error code: %d", err)
}
continue
}
if isMountpoint(target) && !slices.Contains(flags, unix.MS_REMOUNT) { if isMountpoint(target) && !slices.Contains(flags, unix.MS_REMOUNT) {
flags = append(flags, unix.MS_REMOUNT) flags = append(flags, unix.MS_REMOUNT)
} }

View File

@ -2,4 +2,4 @@ module esvm
go 1.23.4 go 1.23.4
require gopkg.in/yaml.v3 v3.0.1 // indirect require gopkg.in/yaml.v3 v3.0.1

View File

@ -1,3 +1,4 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -39,6 +39,7 @@ type EnitService struct {
CrashOnSafeExit bool `yaml:"crash_on_safe_exit"` CrashOnSafeExit bool `yaml:"crash_on_safe_exit"`
StopCmd string `yaml:"stop_cmd,omitempty"` StopCmd string `yaml:"stop_cmd,omitempty"`
Restart string `yaml:"restart,omitempty"` Restart string `yaml:"restart,omitempty"`
LogOutput bool `yaml:"log_output,omitempty"`
ServiceRunPath string ServiceRunPath string
restartCount int restartCount int
stopChannel chan bool stopChannel chan bool
@ -57,13 +58,12 @@ var logger *log.Logger
var socket net.Listener var socket net.Listener
func main() { func main() {
loggerFile, err := os.OpenFile("/var/log/esvm.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) // Setup main logger
err := setupESVMLogger()
if err != nil { if err != nil {
log.Fatalf("Error opening /var/log/esvm/esvm.log: %v", err) log.Printf("Could not setup main ESVM logger! Error: %s\n", err)
logger = log.Default()
} }
logger = log.New(loggerFile, "[ESVM] ", log.Lshortfile|log.LstdFlags)
// Print an empty line as separator
logger.Println()
// Parse flags // Parse flags
printVersion := flag.Bool("version", false, "print version and exit") printVersion := flag.Bool("version", false, "print version and exit")
@ -93,7 +93,6 @@ func main() {
go func() { go func() {
<-sigc <-sigc
Destroy() Destroy()
loggerFile.Close()
os.Exit(0) os.Exit(0)
}() }()
@ -102,6 +101,22 @@ func main() {
} }
} }
func setupESVMLogger() error {
err := os.MkdirAll("/var/log/esvm", 0755)
if err != nil {
return err
}
loggerFile, err := os.OpenFile("/var/log/esvm/esvm.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return err
}
logger = log.New(loggerFile, "[ESVM] ", log.Lshortfile|log.LstdFlags)
// Print an empty line as separator
_, err = loggerFile.WriteString("------ " + time.Now().Format(time.UnixDate) + " ------\n")
return nil
}
func Init() { func Init() {
logger.Println("Initializing ESVM...") logger.Println("Initializing ESVM...")
@ -152,6 +167,7 @@ func Init() {
ServiceRunPath: "", ServiceRunPath: "",
restartCount: 0, restartCount: 0,
stopChannel: make(chan bool), stopChannel: make(chan bool),
LogOutput: true,
} }
if err := yaml.Unmarshal(bytes, &service); err != nil { if err := yaml.Unmarshal(bytes, &service); err != nil {
logger.Printf("Could not read service file at %s!\n", path.Join(serviceConfigDir, "services", entry.Name())) logger.Printf("Could not read service file at %s!\n", path.Join(serviceConfigDir, "services", entry.Name()))
@ -232,9 +248,18 @@ func Init() {
service := servicesWithMetDepends[i] service := servicesWithMetDepends[i]
canStart := true canStart := true
for _, dependency := range service.Dependencies { for _, dependency := range service.Dependencies {
if GetServiceByName(dependency).GetCurrentState() != EnitServiceRunning && GetServiceByName(dependency).GetCurrentState() != EnitServiceCompleted { if strings.HasPrefix(dependency, "/") {
canStart = false // File dependency
break if _, err := os.Stat(dependency); err != nil {
canStart = false
break
}
} else {
// Service dependency
if GetServiceByName(dependency).GetCurrentState() != EnitServiceRunning && GetServiceByName(dependency).GetCurrentState() != EnitServiceCompleted {
canStart = false
break
}
} }
} }
if canStart { if canStart {
@ -277,9 +302,17 @@ func GetServiceByName(name string) *EnitService {
func (service *EnitService) GetUnmetDependencies() (missingDependencies []string) { func (service *EnitService) GetUnmetDependencies() (missingDependencies []string) {
for _, dependency := range service.Dependencies { for _, dependency := range service.Dependencies {
depService := GetServiceByName(dependency) if strings.HasPrefix(dependency, "/") {
if depService == nil { // File dependency
missingDependencies = append(missingDependencies, dependency) if _, err := os.Stat(dependency); err != nil {
missingDependencies = append(missingDependencies, dependency)
}
} else {
// Service dependency
depService := GetServiceByName(dependency)
if depService == nil {
missingDependencies = append(missingDependencies, dependency)
}
} }
} }
@ -332,6 +365,26 @@ func (service *EnitService) setCurrentState(state EnitServiceState) error {
return nil return nil
} }
func (service *EnitService) GetLogFile() (file *os.File, err error) {
err = os.MkdirAll(path.Join("/var/log/esvm/"), 0755)
if err != nil {
return nil, err
}
file, err = os.OpenFile(path.Join("/var/log/esvm/", service.Name+".log"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return nil, err
}
_, err = file.WriteString("------ " + time.Now().Format(time.UnixDate) + " ------\n")
if err != nil {
file.Close()
return nil, err
}
return file, nil
}
func (service *EnitService) StartService() error { func (service *EnitService) StartService() error {
if service == nil { if service == nil {
return nil return nil
@ -342,23 +395,58 @@ func (service *EnitService) StartService() error {
logger.Printf("Starting service (%s)...\n", service.Name) logger.Printf("Starting service (%s)...\n", service.Name)
// Get log file if service logs output
var logFile *os.File
if service.LogOutput {
var err error
logFile, err = service.GetLogFile()
if err != nil {
return err
}
}
cmd := exec.Command("/bin/sh", "-c", "exec "+service.StartCmd) cmd := exec.Command("/bin/sh", "-c", "exec "+service.StartCmd)
if logFile != nil {
cmd.Stdout = logFile
cmd.Stderr = logFile
}
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
// Close log file if not nil
if logFile != nil {
logFile.Close()
}
return err return err
} }
err := service.setProcessID(cmd.Process.Pid) err := service.setProcessID(cmd.Process.Pid)
if err != nil { if err != nil {
// Close log file if not nil
if logFile != nil {
logFile.Close()
}
return err return err
} }
err = service.setCurrentState(EnitServiceRunning) err = service.setCurrentState(EnitServiceRunning)
if err != nil { if err != nil {
// Close log file if not nil
if logFile != nil {
logFile.Close()
}
return err return err
} }
go func() { go func() {
err := cmd.Wait() err := cmd.Wait()
// Close log file if not nil
if logFile != nil {
logFile.Close()
}
select { select {
case <-service.stopChannel: case <-service.stopChannel:
service.restartCount = 0 service.restartCount = 0

View File

@ -0,0 +1,7 @@
name: lo-interface
description: Enable loopback interface on boot
dependencies: ["/usr/sbin/ip"]
type: simple
start_cmd: ip link set lo up
exit_method: kill
restart: false