5 Commits
5 changed files with 94 additions and 71 deletions
+1 -2
View File
@@ -1,7 +1,6 @@
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
restart: false
+1 -1
View File
@@ -405,7 +405,7 @@ func dialSocket() {
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)
}
}
+1 -1
View File
@@ -175,7 +175,7 @@ func stopServiceManager() {
case <-exited:
fmt.Println("Done.")
return
case <-time.After(60 * time.Second):
case <-time.After(300 * time.Second):
log.Println("Could not stop service manager!")
syscall.Kill(serviceManagerPid, syscall.SIGKILL)
return
+15 -37
View File
@@ -280,25 +280,14 @@ func unmountFilesystems() {
// Unmount filesystem at mountpoint
fmt.Printf("Unmounting %s...", mountpoint)
tries := 0
for {
err := unix.Unmount(mountpoint, 0)
if errors.Is(err, syscall.EBUSY) {
fmt.Print(".")
tries++
time.Sleep(1 * time.Second)
if tries >= 60 {
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
}
err := unix.Unmount(mountpoint, 0)
if errors.Is(err, syscall.EBUSY) {
fmt.Println(" Busy.")
time.Sleep(1 * time.Second)
} else if err != nil {
fmt.Printf(" Error: %s\n", err.Error())
} else {
fmt.Println(" Done.")
}
}
}
@@ -341,23 +330,12 @@ func remountRootReadonly() {
}
}
tries := 0
for {
err := unix.Mount(source, "/", filesystem, syscall.MS_RDONLY|syscall.MS_REMOUNT, fsData)
if errors.Is(err, syscall.EBUSY) {
fmt.Print(".")
tries++
time.Sleep(1 * time.Second)
if tries >= 60 {
fmt.Println(" Timeout.")
break
}
} else if err != nil {
fmt.Printf(" Error: %s\n", err.Error())
break
} else {
fmt.Println(" Done.")
break
}
err = unix.Mount(source, "/", filesystem, syscall.MS_RDONLY|syscall.MS_REMOUNT, fsData)
if errors.Is(err, syscall.EBUSY) {
fmt.Println(" Busy.")
} else if err != nil {
fmt.Printf(" Error: %s\n", err.Error())
} else {
fmt.Println(" Done.")
}
}
+76 -30
View File
@@ -6,8 +6,10 @@ import (
"io"
"os"
"os/exec"
"os/user"
"path"
"slices"
"strconv"
"strings"
"syscall"
"time"
@@ -42,9 +44,9 @@ type EnitService struct {
Description string `yaml:"description,omitempty"`
Type string `yaml:"type"`
StartCmd string `yaml:"start_cmd"`
ExitMethod string `yaml:"exit_method"`
CrashOnSafeExit bool `yaml:"crash_on_safe_exit"`
StopCmd string `yaml:"stop_cmd,omitempty"`
User string `yaml:"user,omitempty"`
Restart string `yaml:"restart,omitempty"`
ReadyFd int `yaml:"ready_fd"`
Setpgid bool `yaml:"setpgid"`
@@ -154,8 +156,8 @@ func LoadService(filepath string) {
Description: "",
Type: "",
StartCmd: "",
ExitMethod: "",
StopCmd: "",
User: "",
Restart: "",
Setpgid: true,
CrashOnSafeExit: true,
@@ -190,13 +192,6 @@ func LoadService(filepath string) {
return
}
switch newService.ExitMethod {
case "stop_command", "kill":
default:
logger.Printf("Error: unknown exit method (%s)\n", newService.ExitMethod)
return
}
switch newService.Restart {
case "true", "always":
default:
@@ -236,10 +231,38 @@ func (service *EnitService) StartService() (err error) {
cmd := exec.Command("/bin/sh", "-c", "exec "+service.StartCmd)
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: service.Setpgid, Pgid: 0}
// Setup service log file
if logFile != nil {
cmd.Stdout = 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
if service.ReadyFd > 2 {
pipeReader, pipeWriter, err = os.Pipe()
@@ -267,6 +290,7 @@ func (service *EnitService) StartService() (err error) {
}
cmd.ExtraFiles = append(cmd.ExtraFiles, pipeWriter)
}
if err := cmd.Start(); err != nil {
// Close log file if not nil
if logFile != nil {
@@ -319,18 +343,13 @@ func (service *EnitService) StartService() (err error) {
if service.Type == "simple" && err == nil {
service.restartCount = 0
if service.ExitMethod != "stop_command" {
if strings.TrimSpace(service.StopCmd) == "" {
service.state = EnitServiceCompleted
// Reload service if needed
if service.shouldReload {
LoadService(service.Filepath)
if GetServiceByName(service.Name) == nil {
return
}
}
} else {
service.state = EnitServiceRunning
}
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 {
newServiceStatus = EnitServiceStopped
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 }()
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 {
return err
}
}
// Check if the process has stopped gracefully, otherwise send sigkill on timeout
exited := make(chan bool)
go func() {
for {
if err := syscall.Kill(pid, syscall.Signal(0)); err != nil {
break
if service.Type == "background" {
// Check if the process has stopped gracefully, otherwise send sigkill on timeout
exited := make(chan bool)
go func() {
for {
if err := syscall.Kill(pid, syscall.Signal(0)); err != nil {
break
}
}
}
exited <- true
}()
exited <- true
}()
select {
case <-exited:
case <-time.After(5 * time.Second):
service.GetProcess().Signal(syscall.SIGKILL)
return fmt.Errorf("could not stop process gracefully")
select {
case <-exited:
case <-time.After(15 * time.Second):
service.GetProcess().Signal(syscall.SIGKILL)
return fmt.Errorf("could not stop process gracefully")
}
}
newServiceStatus = EnitServiceStopped