5 Commits
5 changed files with 94 additions and 71 deletions
-1
View File
@@ -1,6 +1,5 @@
name: lo-interface name: lo-interface
description: Enable loopback interface on boot description: Enable loopback interface on boot
dependencies: ["/usr/sbin/ip"]
type: simple type: simple
start_cmd: ip link set lo up start_cmd: ip link set lo up
exit_method: kill exit_method: kill
+1 -1
View File
@@ -405,7 +405,7 @@ func dialSocket() {
log.Fatalf("Failed to connect to esvm.sock! Error: %s\n", err) log.Fatalf("Failed to connect to esvm.sock! Error: %s\n", err)
} }
if err := conn.SetDeadline(time.Now().Add(5 * time.Second)); err != nil { if err := conn.SetDeadline(time.Now().Add(30 * time.Second)); err != nil {
log.Fatalf("Failed to set write deadline! Error: %s\n", err) log.Fatalf("Failed to set write deadline! Error: %s\n", err)
} }
} }
+1 -1
View File
@@ -175,7 +175,7 @@ func stopServiceManager() {
case <-exited: case <-exited:
fmt.Println("Done.") fmt.Println("Done.")
return return
case <-time.After(60 * time.Second): case <-time.After(300 * time.Second):
log.Println("Could not stop service manager!") log.Println("Could not stop service manager!")
syscall.Kill(serviceManagerPid, syscall.SIGKILL) syscall.Kill(serviceManagerPid, syscall.SIGKILL)
return return
+15 -37
View File
@@ -280,25 +280,14 @@ func unmountFilesystems() {
// Unmount filesystem at mountpoint // Unmount filesystem at mountpoint
fmt.Printf("Unmounting %s...", mountpoint) fmt.Printf("Unmounting %s...", mountpoint)
tries := 0 err := unix.Unmount(mountpoint, 0)
for { if errors.Is(err, syscall.EBUSY) {
err := unix.Unmount(mountpoint, 0) fmt.Println(" Busy.")
if errors.Is(err, syscall.EBUSY) { time.Sleep(1 * time.Second)
fmt.Print(".") } else if err != nil {
tries++ fmt.Printf(" Error: %s\n", err.Error())
time.Sleep(1 * time.Second) } else {
if tries >= 60 { fmt.Println(" Done.")
unix.Unmount(mountpoint, syscall.MNT_FORCE)
fmt.Println(" Timeout.")
break
}
} else if err != nil {
fmt.Printf(" Error: %s\n", err.Error())
break
} else {
fmt.Println(" Done.")
break
}
} }
} }
} }
@@ -341,23 +330,12 @@ func remountRootReadonly() {
} }
} }
tries := 0 err = unix.Mount(source, "/", filesystem, syscall.MS_RDONLY|syscall.MS_REMOUNT, fsData)
for { if errors.Is(err, syscall.EBUSY) {
err := unix.Mount(source, "/", filesystem, syscall.MS_RDONLY|syscall.MS_REMOUNT, fsData) fmt.Println(" Busy.")
if errors.Is(err, syscall.EBUSY) { } else if err != nil {
fmt.Print(".") fmt.Printf(" Error: %s\n", err.Error())
tries++ } else {
time.Sleep(1 * time.Second) fmt.Println(" Done.")
if tries >= 60 {
fmt.Println(" Timeout.")
break
}
} else if err != nil {
fmt.Printf(" Error: %s\n", err.Error())
break
} else {
fmt.Println(" Done.")
break
}
} }
} }
+76 -30
View File
@@ -6,8 +6,10 @@ import (
"io" "io"
"os" "os"
"os/exec" "os/exec"
"os/user"
"path" "path"
"slices" "slices"
"strconv"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@@ -42,9 +44,9 @@ type EnitService struct {
Description string `yaml:"description,omitempty"` Description string `yaml:"description,omitempty"`
Type string `yaml:"type"` Type string `yaml:"type"`
StartCmd string `yaml:"start_cmd"` StartCmd string `yaml:"start_cmd"`
ExitMethod string `yaml:"exit_method"`
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"`
User string `yaml:"user,omitempty"`
Restart string `yaml:"restart,omitempty"` Restart string `yaml:"restart,omitempty"`
ReadyFd int `yaml:"ready_fd"` ReadyFd int `yaml:"ready_fd"`
Setpgid bool `yaml:"setpgid"` Setpgid bool `yaml:"setpgid"`
@@ -154,8 +156,8 @@ func LoadService(filepath string) {
Description: "", Description: "",
Type: "", Type: "",
StartCmd: "", StartCmd: "",
ExitMethod: "",
StopCmd: "", StopCmd: "",
User: "",
Restart: "", Restart: "",
Setpgid: true, Setpgid: true,
CrashOnSafeExit: true, CrashOnSafeExit: true,
@@ -190,13 +192,6 @@ func LoadService(filepath string) {
return return
} }
switch newService.ExitMethod {
case "stop_command", "kill":
default:
logger.Printf("Error: unknown exit method (%s)\n", newService.ExitMethod)
return
}
switch newService.Restart { switch newService.Restart {
case "true", "always": case "true", "always":
default: default:
@@ -236,10 +231,38 @@ func (service *EnitService) StartService() (err error) {
cmd := exec.Command("/bin/sh", "-c", "exec "+service.StartCmd) cmd := exec.Command("/bin/sh", "-c", "exec "+service.StartCmd)
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: service.Setpgid, Pgid: 0} cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: service.Setpgid, Pgid: 0}
// Setup service log file
if logFile != nil { if logFile != nil {
cmd.Stdout = logFile cmd.Stdout = logFile
cmd.Stderr = logFile cmd.Stderr = logFile
} }
// Setup command credentials
if service.User != "" && service.User != "root" {
// Lookup user in /etc/passwd
u, err := user.Lookup(service.User)
if err != nil {
return err
}
// Get user id and group id
uid, err := strconv.Atoi(u.Uid)
if err != nil {
return err
}
gid, err := strconv.Atoi(u.Gid)
if err != nil {
return err
}
cmd.SysProcAttr.Credential = &syscall.Credential{
Uid: uint32(uid),
Gid: uint32(gid),
}
}
// Setup command pipes
var pipeReader, pipeWriter *os.File var pipeReader, pipeWriter *os.File
if service.ReadyFd > 2 { if service.ReadyFd > 2 {
pipeReader, pipeWriter, err = os.Pipe() pipeReader, pipeWriter, err = os.Pipe()
@@ -267,6 +290,7 @@ func (service *EnitService) StartService() (err error) {
} }
cmd.ExtraFiles = append(cmd.ExtraFiles, pipeWriter) cmd.ExtraFiles = append(cmd.ExtraFiles, pipeWriter)
} }
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
// Close log file if not nil // Close log file if not nil
if logFile != nil { if logFile != nil {
@@ -319,18 +343,13 @@ func (service *EnitService) StartService() (err error) {
if service.Type == "simple" && err == nil { if service.Type == "simple" && err == nil {
service.restartCount = 0 service.restartCount = 0
if service.ExitMethod != "stop_command" { if strings.TrimSpace(service.StopCmd) == "" {
service.state = EnitServiceCompleted service.state = EnitServiceCompleted
// Reload service if needed // Reload service if needed
if service.shouldReload { if service.shouldReload {
LoadService(service.Filepath) LoadService(service.Filepath)
if GetServiceByName(service.Name) == nil {
return
}
} }
} else {
service.state = EnitServiceRunning
} }
return return
} }
@@ -400,7 +419,7 @@ func (service *EnitService) StopService() error {
} }
}() }()
if service.ExitMethod == "kill" { if strings.TrimSpace(service.StopCmd) == "" {
if err := service.GetProcess().Signal(syscall.Signal(0)); err != nil { if err := service.GetProcess().Signal(syscall.Signal(0)); err != nil {
newServiceStatus = EnitServiceStopped newServiceStatus = EnitServiceStopped
logger.Printf("Service (%s) has stopped (Process already dead)", service.Name) logger.Printf("Service (%s) has stopped (Process already dead)", service.Name)
@@ -418,27 +437,54 @@ func (service *EnitService) StopService() error {
go func() { service.stopChannel <- true }() go func() { service.stopChannel <- true }()
cmd := exec.Command("/bin/sh", "-c", service.StopCmd) cmd := exec.Command("/bin/sh", "-c", service.StopCmd)
// Setup command credentials
if service.User != "" && service.User != "root" {
// Lookup user in /etc/passwd
u, err := user.Lookup(service.User)
if err != nil {
return err
}
// Get user id and group id
uid, err := strconv.Atoi(u.Uid)
if err != nil {
return err
}
gid, err := strconv.Atoi(u.Gid)
if err != nil {
return err
}
cmd.SysProcAttr.Credential = &syscall.Credential{
Uid: uint32(uid),
Gid: uint32(gid),
}
}
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return err return err
} }
} }
// Check if the process has stopped gracefully, otherwise send sigkill on timeout if service.Type == "background" {
exited := make(chan bool) // Check if the process has stopped gracefully, otherwise send sigkill on timeout
go func() { exited := make(chan bool)
for { go func() {
if err := syscall.Kill(pid, syscall.Signal(0)); err != nil { for {
break if err := syscall.Kill(pid, syscall.Signal(0)); err != nil {
break
}
} }
} exited <- true
exited <- true }()
}()
select { select {
case <-exited: case <-exited:
case <-time.After(5 * time.Second): case <-time.After(15 * time.Second):
service.GetProcess().Signal(syscall.SIGKILL) service.GetProcess().Signal(syscall.SIGKILL)
return fmt.Errorf("could not stop process gracefully") return fmt.Errorf("could not stop process gracefully")
}
} }
newServiceStatus = EnitServiceStopped newServiceStatus = EnitServiceStopped