Improve service initialization and reloading

This commit is contained in:
2025-11-17 20:49:46 +02:00
parent 7bbfa9f198
commit 5ee42a5966
2 changed files with 85 additions and 90 deletions
+22 -63
View File
@@ -1,7 +1,6 @@
package main package main
import ( import (
"crypto/sha256"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@@ -15,8 +14,6 @@ import (
"strings" "strings"
"syscall" "syscall"
"time" "time"
"gopkg.in/yaml.v3"
) )
// Build-time variables // Build-time variables
@@ -136,64 +133,8 @@ func Init() {
// Read and initialize service files // Read and initialize service files
for _, entry := range dirEntries { for _, entry := range dirEntries {
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".esv") { if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".esv") {
logger.Printf("Initializing service (%s)...\n", entry.Name()) filepath := path.Join(serviceConfigDir, "services", entry.Name())
bytes, err := os.ReadFile(path.Join(serviceConfigDir, "services", entry.Name())) LoadService(filepath)
if err != nil {
logger.Printf("Error: Could not read service file (%s)", path.Join(serviceConfigDir, "services", entry.Name()))
continue
}
service := EnitService{
Name: "",
Description: "",
Type: "",
StartCmd: "",
ExitMethod: "",
StopCmd: "",
Restart: "",
Setpgid: true,
CrashOnSafeExit: true,
LogOutput: true,
Filepath: path.Join(serviceConfigDir, "services", entry.Name()),
filepathChecksum: sha256.Sum256(bytes),
restartCount: 0,
stopChannel: make(chan bool),
state: EnitServiceUnloaded,
}
if err := yaml.Unmarshal(bytes, &service); err != nil {
logger.Printf("Error: could not read service file %s", path.Join(serviceConfigDir, "services", entry.Name()))
continue
}
for _, sv := range Services {
if sv.Name == service.Name {
logger.Printf("Error: service with name (%s) has already been initialized", service.Name)
}
}
switch service.Type {
case "simple", "background":
default:
logger.Printf("Error: unknown service type (%s)", service.Type)
continue
}
switch service.ExitMethod {
case "stop_command", "kill":
default:
logger.Printf("Error: unknown exit method (%s)\n", service.ExitMethod)
continue
}
switch service.Restart {
case "true", "always":
default:
service.Restart = "false"
}
Services = append(Services, &service)
logger.Printf("Service (%s) has been initialized!\n", service.Name)
} }
} }
@@ -231,8 +172,26 @@ func Init() {
func Reload() { func Reload() {
logger.Println("Reloading all ESVM services...") logger.Println("Reloading all ESVM services...")
for _, service := range Services { dirEntries, err := os.ReadDir(path.Join(serviceConfigDir, "services"))
service.ReloadService() if err != nil {
logger.Fatalf("Error: Could not initialize ESVM: %s", err)
}
// Read and load service files
servicesToRemove := slices.Clone(Services)
for _, entry := range dirEntries {
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".esv") {
filepath := path.Join(serviceConfigDir, "services", entry.Name())
LoadService(filepath)
servicesToRemove = slices.DeleteFunc(servicesToRemove, func(sv *EnitService) bool {
return sv.Filepath == filepath
})
}
}
// Reload services that had their esv file removed
for _, service := range servicesToRemove {
LoadService(service.Filepath)
} }
logger.Println("All ESVM services have been reloaded!") logger.Println("All ESVM services have been reloaded!")
+55 -19
View File
@@ -101,9 +101,18 @@ func (service *EnitService) GetLogFile() (file *os.File, err error) {
return file, nil return file, nil
} }
func (service *EnitService) ReloadService() { func LoadService(filepath string) {
bytes, err := os.ReadFile(service.Filepath) bytes, err := os.ReadFile(filepath)
checksum := sha256.Sum256(bytes) checksum := sha256.Sum256(bytes)
var serviceToReload *EnitService
// Check if service is already loaded
for _, service := range Services {
if service.Filepath != filepath {
continue
}
if slices.Equal(checksum[:], service.filepathChecksum[:]) { if slices.Equal(checksum[:], service.filepathChecksum[:]) {
return return
} }
@@ -114,17 +123,29 @@ func (service *EnitService) ReloadService() {
return return
} }
service.shouldReload = false service.shouldReload = false
serviceToReload = service
logger.Printf("Reloading service (%s)...\n", service.Filepath) break
}
if serviceToReload == nil {
logger.Printf("Loading service (%s)...\n", filepath)
} else {
logger.Printf("Reloading service (%s)...\n", filepath)
}
if os.IsNotExist(err) { if os.IsNotExist(err) {
Services = slices.DeleteFunc(Services, func(sv *EnitService) bool { Services = slices.DeleteFunc(Services, func(sv *EnitService) bool {
return sv == service if sv.Filepath == filepath {
logger.Printf("Service (%s) has been removed\n", sv.Name)
return true
}
return false
}) })
logger.Printf("Service (%s) has been removed\n", service.Name)
return return
} else if err != nil { } else if err != nil {
logger.Printf("Error: Could not read service file (%s)", service.Filepath) logger.Printf("Error: Could not read service file (%s)", filepath)
return return
} }
@@ -139,20 +160,26 @@ func (service *EnitService) ReloadService() {
Setpgid: true, Setpgid: true,
CrashOnSafeExit: true, CrashOnSafeExit: true,
LogOutput: true, LogOutput: true,
Filepath: service.Filepath, Filepath: filepath,
filepathChecksum: checksum, filepathChecksum: sha256.Sum256(bytes),
restartCount: service.restartCount, restartCount: 0,
stopChannel: service.stopChannel, stopChannel: make(chan bool),
state: service.state, state: EnitServiceUnloaded,
}
if serviceToReload != nil {
newService.restartCount = serviceToReload.restartCount
newService.stopChannel = serviceToReload.stopChannel
newService.state = serviceToReload.state
} }
if err := yaml.Unmarshal(bytes, &newService); err != nil { if err := yaml.Unmarshal(bytes, &newService); err != nil {
logger.Printf("Error: could not read service file %s", service.Filepath) logger.Printf("Error: could not read service file %s", filepath)
return return
} }
for _, sv := range Services { for _, sv := range Services {
if sv.Name == newService.Name && sv != service { if sv.Name == newService.Name && sv != serviceToReload {
logger.Printf("Error: service with name (%s) has already been initialized", service.Name) logger.Printf("Error: service with name (%s) has already been loaded", newService.Name)
return
} }
} }
@@ -177,12 +204,15 @@ func (service *EnitService) ReloadService() {
} }
for i, sv := range Services { for i, sv := range Services {
if sv == service { if sv == serviceToReload {
Services[i] = &newService Services[i] = &newService
logger.Printf("Service (%s) has been reloaded!\n", newService.Name)
return
} }
} }
logger.Printf("Service (%s) has been reloaded!\n", newService.Name) Services = append(Services, &newService)
logger.Printf("Service (%s) has been loaded!\n", newService.Name)
} }
func (service *EnitService) StartService() (err error) { func (service *EnitService) StartService() (err error) {
@@ -294,7 +324,7 @@ func (service *EnitService) StartService() (err error) {
// Reload service if needed // Reload service if needed
if service.shouldReload { if service.shouldReload {
service.ReloadService() LoadService(service.Filepath)
if GetServiceByName(service.Name) == nil { if GetServiceByName(service.Name) == nil {
return return
} }
@@ -314,7 +344,7 @@ func (service *EnitService) StartService() (err error) {
// Reload service if needed // Reload service if needed
if service.shouldReload { if service.shouldReload {
service.ReloadService() LoadService(service.Filepath)
if GetServiceByName(service.Name) == nil { if GetServiceByName(service.Name) == nil {
return return
} }
@@ -362,7 +392,7 @@ func (service *EnitService) StopService() error {
// Reload service if needed // Reload service if needed
if service.shouldReload { if service.shouldReload {
service.ReloadService() LoadService(service.Filepath)
if GetServiceByName(service.Name) == nil { if GetServiceByName(service.Name) == nil {
return return
} }
@@ -422,6 +452,12 @@ func (service *EnitService) RestartService() error {
return err return err
} }
// Get service from list in case of a reload
if GetServiceByName(service.Name) == nil {
return fmt.Errorf("service was removed")
}
service = GetServiceByName(service.Name)
if err := service.StartService(); err != nil { if err := service.StartService(); err != nil {
return err return err
} }