mirror of
https://github.com/EnumeratedDev/enit.git
synced 2026-09-16 02:26:11 +00:00
Merge pull request 'Make ESVM socket accept json data instead of raw text commands' (#1) from json_switch into master
Reviewed-on: #1
This commit is contained in:
@@ -1,5 +1,3 @@
|
|||||||
module ectl
|
module ectl
|
||||||
|
|
||||||
go 1.23.4
|
go 1.23.4
|
||||||
|
|
||||||
require gopkg.in/yaml.v3 v3.0.1
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
|
|||||||
+168
-227
@@ -1,15 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gopkg.in/yaml.v3"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -19,17 +18,18 @@ var version = "dev"
|
|||||||
var sysconfdir = "/etc/"
|
var sysconfdir = "/etc/"
|
||||||
var runstatedir = "/var/run/"
|
var runstatedir = "/var/run/"
|
||||||
|
|
||||||
var socket net.Conn
|
var conn net.Conn
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// Set and parse flags
|
// Set and parse flags
|
||||||
printVersion := flag.Bool("version", false, "print version and exit")
|
printVersion := flag.Bool("version", false, "print version and exit")
|
||||||
|
printJson := flag.Bool("json", false, "print output in json format")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// Dial esvm socket
|
// Dial esvm socket
|
||||||
dialSocket()
|
dialSocket()
|
||||||
defer socket.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
if flag.NArg() < 1 {
|
if flag.NArg() < 1 {
|
||||||
printUsage()
|
printUsage()
|
||||||
@@ -58,202 +58,190 @@ func main() {
|
|||||||
if len(flag.Args()) <= 1 {
|
if len(flag.Args()) <= 1 {
|
||||||
fmt.Println("Usage: ectl service <start/stop/enable/disable/status/list> [service]")
|
fmt.Println("Usage: ectl service <start/stop/enable/disable/status/list> [service]")
|
||||||
return
|
return
|
||||||
} else if flag.Args()[1] == "list" {
|
} else if flag.Arg(1) == "start" || flag.Arg(1) == "stop" || flag.Arg(1) == "restart" || flag.Arg(1) == "enable" || flag.Arg(1) == "disable" {
|
||||||
if _, err := os.Stat(path.Join(runstatedir, "esvm")); err != nil {
|
// Ensure service name argument has been set
|
||||||
log.Fatalf("Could not list services! Error: %s\n", err)
|
if len(flag.Args()) <= 2 {
|
||||||
}
|
fmt.Printf("Usage: ectl service %s <service>\n", flag.Args()[1])
|
||||||
|
|
||||||
entries, err := os.ReadDir(path.Join(runstatedir, "esvm"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not list services! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range entries {
|
|
||||||
if !entry.IsDir() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
state := getServiceState(entry.Name())
|
|
||||||
enabled := strconv.FormatBool(isServiceEnabled(entry.Name()))
|
|
||||||
enabled = strings.ToUpper(enabled[:1]) + strings.ToLower(enabled[1:])
|
|
||||||
|
|
||||||
fmt.Println("Service name: " + entry.Name())
|
|
||||||
fmt.Printf(" State: %s\n", state)
|
|
||||||
fmt.Printf(" Enabled: %s\n", enabled)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else if len(flag.Args()) <= 2 {
|
|
||||||
fmt.Printf("Usage: ectl service %s <service>\n", flag.Args()[1])
|
|
||||||
return
|
|
||||||
} else if flag.Args()[1] == "start" {
|
|
||||||
if _, err := os.Stat(path.Join(runstatedir, "esvm", flag.Args()[2])); err != nil {
|
|
||||||
log.Fatalf("Could not start service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := socket.Write([]byte("start " + flag.Args()[2]))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not start service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
n, err := socket.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not start service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
if string(buf[:n]) != "ok" {
|
|
||||||
log.Fatalf("Could not start service! Error: expcted 'ok' got '%s'\n", string(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Service started successfully!")
|
|
||||||
return
|
|
||||||
} else if flag.Args()[1] == "stop" {
|
|
||||||
if _, err := os.Stat(path.Join(runstatedir, "esvm", flag.Args()[2])); err != nil {
|
|
||||||
log.Fatalf("Could not stop service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := socket.Write([]byte("stop " + flag.Args()[2]))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not stop service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
n, err := socket.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not stop service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
if string(buf[:n]) != "ok" {
|
|
||||||
log.Fatalf("Could not stop service! Error: expcted 'ok' got '%s'\n", string(buf))
|
|
||||||
}
|
|
||||||
fmt.Println("Service stopped successfully!")
|
|
||||||
return
|
|
||||||
} else if flag.Args()[1] == "restart" || flag.Args()[1] == "reload" {
|
|
||||||
if _, err := os.Stat(path.Join(runstatedir, "esvm", flag.Args()[2])); err != nil {
|
|
||||||
log.Fatalf("Could not restart service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := socket.Write([]byte("restart " + flag.Args()[2]))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not restart service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
n, err := socket.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not restart service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
if string(buf[:n]) != "ok" {
|
|
||||||
log.Fatalf("Could not restart service! Error: expcted 'ok' got '%s'\n", string(buf))
|
|
||||||
}
|
|
||||||
fmt.Println("Service restarted successfully!")
|
|
||||||
return
|
|
||||||
} else if flag.Args()[1] == "enable" {
|
|
||||||
// Check if service exists
|
|
||||||
found := false
|
|
||||||
entries, err := os.ReadDir(path.Join(sysconfdir, "esvm/services/"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not enable service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
type minimalServiceStruct struct {
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
}
|
|
||||||
for _, entry := range entries {
|
|
||||||
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".esv") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, err := os.ReadFile(path.Join(sysconfdir, "esvm/services", entry.Name()))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not enable service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sv := minimalServiceStruct{Name: ""}
|
|
||||||
err = yaml.Unmarshal(bytes, &sv)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not enable service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if sv.Name == flag.Args()[2] {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
log.Fatalf("Service does not exist!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(path.Join(sysconfdir, "esvm/enabled_services")); err != nil {
|
|
||||||
err := os.WriteFile(path.Join(sysconfdir, "esvm/enabled_services"), []byte(flag.Args()[2]+"\n"), 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not enable service! Error: %s\n", err)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.ReadFile(path.Join(sysconfdir, "esvm/enabled_services"))
|
type ServiceCommandJsonStruct struct {
|
||||||
if err != nil {
|
Command string `json:"command"`
|
||||||
log.Fatalf("Could not enable service! Error: %s\n", err)
|
Service string `json:"service"`
|
||||||
}
|
}
|
||||||
for _, line := range strings.Split(string(file), "\n") {
|
serviceCommandJson := ServiceCommandJsonStruct{
|
||||||
if strings.TrimSpace(line) == flag.Args()[2] {
|
Command: flag.Arg(1),
|
||||||
fmt.Println("Service is already enabled!")
|
Service: flag.Arg(2),
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(path.Join(sysconfdir, "esvm/enabled_services"), []byte(string(file)+flag.Args()[2]+"\n"), 0644)
|
// Encode struct to json string
|
||||||
|
jsonData, err := json.Marshal(serviceCommandJson)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not enable service! Error: %s\n", err)
|
log.Fatalf("Could not encode JSON data! Error: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Service (%s) has been enabled!\n", flag.Args()[2])
|
_, err = conn.Write(jsonData)
|
||||||
return
|
if err != nil {
|
||||||
} else if flag.Args()[1] == "disable" {
|
log.Fatalf("Could not write JSON data to socket! Error: %s\n", err)
|
||||||
if _, err := os.Stat(path.Join(sysconfdir, "esvm/enabled_services")); err != nil {
|
}
|
||||||
fmt.Println("Service is already disabled!")
|
|
||||||
|
// Create a buffer for incoming data.
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
|
||||||
|
// Read data from the connection.
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err == io.EOF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.ReadFile(path.Join(sysconfdir, "esvm/enabled_services"))
|
// Print json data if flag is set
|
||||||
if err != nil {
|
if *printJson {
|
||||||
log.Fatalf("Could not disable service! Error: %s\n", err)
|
fmt.Println(string(buf[:n]))
|
||||||
}
|
|
||||||
|
|
||||||
lines := strings.Split(string(file), "\n")
|
|
||||||
found := false
|
|
||||||
for i := len(lines) - 1; i >= 0; i-- {
|
|
||||||
line := strings.TrimSpace(lines[i])
|
|
||||||
if strings.TrimSpace(line) == flag.Args()[2] {
|
|
||||||
lines = append(lines[:i], lines[i+1:]...)
|
|
||||||
found = true
|
|
||||||
} else if strings.TrimSpace(line) == "" {
|
|
||||||
lines = append(lines[:i], lines[i+1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
fmt.Println("Service is already disabled!")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(path.Join(sysconfdir, "esvm/enabled_services"), []byte(strings.Join(lines, "\n")+"\n"), 0644)
|
// Decoode JSON data
|
||||||
|
var returnedJsonData map[string]any
|
||||||
|
err = json.Unmarshal(buf[:n], &returnedJsonData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not disable service! Error: %s\n", err)
|
log.Fatalf("Could not decode JSON data from connection!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, ok := returnedJsonData["error"]; ok {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else if msg, ok := returnedJsonData["success"]; ok {
|
||||||
|
fmt.Println(msg)
|
||||||
|
} else {
|
||||||
|
log.Fatal("Connection returned empty string!")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Service (%s) has been disabled!\n", flag.Args()[2])
|
|
||||||
return
|
return
|
||||||
} else if flag.Args()[1] == "status" {
|
} else if flag.Args()[1] == "status" {
|
||||||
if _, err := os.Stat(path.Join(runstatedir, "esvm", flag.Args()[2])); err != nil {
|
// Ensure service name argument has been set
|
||||||
log.Fatalf("Could not get service status! Error: %s\n", err)
|
if len(flag.Args()) <= 2 {
|
||||||
|
fmt.Printf("Usage: ectl service %s <service>\n", flag.Args()[1])
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state := getServiceState(flag.Args()[2])
|
type ServiceCommandJsonStruct struct {
|
||||||
enabled := strconv.FormatBool(isServiceEnabled(flag.Args()[2]))
|
Command string `json:"command"`
|
||||||
enabled = strings.ToUpper(enabled[:1]) + strings.ToLower(enabled[1:])
|
Service string `json:"service"`
|
||||||
|
}
|
||||||
|
serviceCommandJson := ServiceCommandJsonStruct{
|
||||||
|
Command: flag.Arg(1),
|
||||||
|
Service: flag.Arg(2),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode struct to json string
|
||||||
|
jsonData, err := json.Marshal(serviceCommandJson)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not encode JSON data! Error: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = conn.Write(jsonData)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not write JSON data to socket! Error: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a buffer for incoming data.
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
|
||||||
|
// Read data from the connection.
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err == io.EOF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print json data if flag is set
|
||||||
|
if *printJson {
|
||||||
|
fmt.Println(string(buf[:n]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoode JSON data
|
||||||
|
var returnedJsonData map[string]any
|
||||||
|
err = json.Unmarshal(buf[:n], &returnedJsonData)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not decode JSON data from connection!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, ok := returnedJsonData["error"]; ok {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceState := returnedJsonData["state"].(string)
|
||||||
|
serviceEnabled := returnedJsonData["is_enabled"].(bool)
|
||||||
|
|
||||||
|
fmt.Printf("Name: %s\n", flag.Arg(2))
|
||||||
|
fmt.Printf("State: %s\n", serviceState)
|
||||||
|
fmt.Printf("Enabled: %t\n", serviceEnabled)
|
||||||
|
|
||||||
|
return
|
||||||
|
} else if flag.Arg(1) == "list" {
|
||||||
|
type ServiceCommandJsonStruct struct {
|
||||||
|
Command string `json:"command"`
|
||||||
|
}
|
||||||
|
serviceCommandJson := ServiceCommandJsonStruct{
|
||||||
|
Command: flag.Arg(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode struct to json string
|
||||||
|
jsonData, err := json.Marshal(serviceCommandJson)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not encode JSON data! Error: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = conn.Write(jsonData)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not write JSON data to socket! Error: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a buffer for incoming data.
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
|
||||||
|
// Read data from the connection.
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err == io.EOF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print json data if flag is set
|
||||||
|
if *printJson {
|
||||||
|
fmt.Println(string(buf[:n]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoode JSON data
|
||||||
|
var returnedJsonData map[string]any
|
||||||
|
err = json.Unmarshal(buf[:n], &returnedJsonData)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not decode JSON data from connection!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, ok := returnedJsonData["error"]; ok {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, serviceMap := range returnedJsonData["services"].([]any) {
|
||||||
|
serviceName := serviceMap.(map[string]any)["name"].(string)
|
||||||
|
serviceState := serviceMap.(map[string]any)["state"].(string)
|
||||||
|
serviceEnabled := serviceMap.(map[string]any)["is_enabled"].(bool)
|
||||||
|
|
||||||
|
fmt.Printf("Name: %s\n", serviceName)
|
||||||
|
fmt.Printf("State: %s\n", serviceState)
|
||||||
|
fmt.Printf("Enabled: %t\n", serviceEnabled)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("Service name: " + flag.Args()[2])
|
|
||||||
fmt.Printf(" State: %s\n", state)
|
|
||||||
fmt.Printf(" Enabled: %s\n", enabled)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,53 +250,6 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServiceState(serviceName string) string {
|
|
||||||
if _, err := os.Stat(path.Join(runstatedir, "esvm", serviceName)); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var state uint64
|
|
||||||
bytes, err := os.ReadFile(path.Join(runstatedir, "esvm", serviceName, "state"))
|
|
||||||
if err != nil {
|
|
||||||
state = 0
|
|
||||||
}
|
|
||||||
state, err = strconv.ParseUint(string(bytes), 10, 8)
|
|
||||||
|
|
||||||
switch state {
|
|
||||||
case 1:
|
|
||||||
return "Unloaded"
|
|
||||||
case 2:
|
|
||||||
return "Running"
|
|
||||||
case 3:
|
|
||||||
return "Stopped"
|
|
||||||
case 4:
|
|
||||||
return "Crashed"
|
|
||||||
case 5:
|
|
||||||
return "Completed"
|
|
||||||
default:
|
|
||||||
return "Unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isServiceEnabled(serviceName string) bool {
|
|
||||||
if _, err := os.Stat(path.Join(sysconfdir, "esvm/enabled_services")); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := os.ReadFile(path.Join(sysconfdir, "esvm/enabled_services"))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, line := range strings.Split(string(file), "\n") {
|
|
||||||
if strings.TrimSpace(line) == serviceName {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func printUsage() {
|
func printUsage() {
|
||||||
fmt.Println("Available sucommands:")
|
fmt.Println("Available sucommands:")
|
||||||
fmt.Println("ectl version | Show enit version")
|
fmt.Println("ectl version | Show enit version")
|
||||||
@@ -329,12 +270,12 @@ func dialSocket() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
socket, err = net.Dial("unix", path.Join(runstatedir, "esvm/esvm.sock"))
|
conn, err = net.Dial("unix", path.Join(runstatedir, "esvm/esvm.sock"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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 := socket.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
|
if err := conn.SetDeadline(time.Now().Add(5 * 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
-16
@@ -8,7 +8,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
@@ -23,7 +22,6 @@ var runtimeServiceDir string
|
|||||||
var serviceConfigDir string
|
var serviceConfigDir string
|
||||||
|
|
||||||
var Services = make([]EnitService, 0)
|
var Services = make([]EnitService, 0)
|
||||||
var EnabledServices = make([]string, 0)
|
|
||||||
|
|
||||||
var logger *log.Logger
|
var logger *log.Logger
|
||||||
var socket net.Listener
|
var socket net.Listener
|
||||||
@@ -203,23 +201,10 @@ func Init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get enabled services
|
|
||||||
if _, err := os.Stat(path.Join(serviceConfigDir, "enabled_services")); err == nil {
|
|
||||||
file, err := os.ReadFile(path.Join(serviceConfigDir, "enabled_services"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, line := range strings.Split(string(file), "\n") {
|
|
||||||
if line != "" {
|
|
||||||
EnabledServices = append(EnabledServices, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get enabled services that meet their dependencies
|
// Get enabled services that meet their dependencies
|
||||||
servicesWithMetDepends := make([]EnitService, 0)
|
servicesWithMetDepends := make([]EnitService, 0)
|
||||||
for _, service := range Services {
|
for _, service := range Services {
|
||||||
if slices.Contains(EnabledServices, service.Name) && len(service.GetUnmetDependencies()) == 0 {
|
if service.isEnabled() && len(service.GetUnmetDependencies()) == 0 {
|
||||||
servicesWithMetDepends = append(servicesWithMetDepends, service)
|
servicesWithMetDepends = append(servicesWithMetDepends, service)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
@@ -21,6 +22,15 @@ const (
|
|||||||
EnitServiceCompleted
|
EnitServiceCompleted
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var EnitServiceStateNames map[EnitServiceState]string = map[EnitServiceState]string{
|
||||||
|
EnitServiceUnknown: "unknown",
|
||||||
|
EnitServiceUnloaded: "unloaded",
|
||||||
|
EnitServiceRunning: "running",
|
||||||
|
EnitServiceStopped: "stopped",
|
||||||
|
EnitServiceCrashed: "crashed",
|
||||||
|
EnitServiceCompleted: "completed",
|
||||||
|
}
|
||||||
|
|
||||||
type EnitService struct {
|
type EnitService struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Description string `yaml:"description,omitempty"`
|
Description string `yaml:"description,omitempty"`
|
||||||
@@ -308,3 +318,64 @@ func (service *EnitService) RestartService() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Functions will be rewritten at some point to allow enabling unloaded services
|
||||||
|
|
||||||
|
func (service *EnitService) isEnabled() bool {
|
||||||
|
contents, err := os.ReadFile(path.Join(serviceConfigDir, "enabled_services"))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(string(contents), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if line == service.Name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *EnitService) SetEnabled(isEnabled bool) error {
|
||||||
|
// Return if service is already in correct state
|
||||||
|
if service.isEnabled() == isEnabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create or open enabled_services file
|
||||||
|
file, err := os.OpenFile(path.Join(serviceConfigDir, "enabled_services"), os.O_CREATE|os.O_RDWR, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// Get enabled_services file contents
|
||||||
|
contents, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify contents
|
||||||
|
strContents := string(contents)
|
||||||
|
if isEnabled {
|
||||||
|
strContents += service.Name + "\n"
|
||||||
|
} else {
|
||||||
|
strContents = strings.ReplaceAll(strContents, service.Name+"\n", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write new contents to file
|
||||||
|
file.Truncate(0)
|
||||||
|
file.Seek(0, 0)
|
||||||
|
_, err = file.WriteString(strContents)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file.Sync()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
+256
-61
@@ -1,18 +1,30 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var commandHandlers = make(map[string]func(conn net.Conn, jsonData map[string]any))
|
||||||
|
|
||||||
func initSocket() (socket net.Listener, err error) {
|
func initSocket() (socket net.Listener, err error) {
|
||||||
socket, err = net.Listen("unix", path.Join(runtimeServiceDir, "esvm.sock"))
|
socket, err = net.Listen("unix", path.Join(runtimeServiceDir, "esvm.sock"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register command handlers
|
||||||
|
commandHandlers["start"] = handleStartServiceCommand
|
||||||
|
commandHandlers["stop"] = handleStopServiceCommand
|
||||||
|
commandHandlers["restart"] = handleRestartServiceCommand
|
||||||
|
commandHandlers["enable"] = handleEnableServiceCommand
|
||||||
|
commandHandlers["disable"] = handleDisableServiceCommand
|
||||||
|
commandHandlers["status"] = handleStatusServiceCommand
|
||||||
|
commandHandlers["list"] = handleListServicesCommand
|
||||||
|
|
||||||
return socket, nil
|
return socket, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,68 +47,251 @@ func listenToSocket() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(err)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
command := string(buf[:n])
|
// Decoode JSON data
|
||||||
commandSplit := strings.Split(command, " ")
|
var jsonData map[string]any
|
||||||
|
err = json.Unmarshal(buf[:n], &jsonData)
|
||||||
if len(commandSplit) >= 2 {
|
if err != nil {
|
||||||
if commandSplit[0] == "start" {
|
conn.Write(wrapErrorInJson(fmt.Errorf("Invalid JSON")))
|
||||||
service := GetServiceByName(commandSplit[1])
|
return
|
||||||
if service == nil {
|
|
||||||
_, err := conn.Write([]byte("service not found"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := service.StartService(); err != nil {
|
|
||||||
_, err := conn.Write([]byte("could not start service"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err := conn.Write([]byte("ok"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if commandSplit[0] == "stop" {
|
|
||||||
service := GetServiceByName(commandSplit[1])
|
|
||||||
if service == nil {
|
|
||||||
_, err := conn.Write([]byte("service not found"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := service.StopService(); err != nil {
|
|
||||||
_, err := conn.Write([]byte("could not stop service"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err := conn.Write([]byte("ok"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if commandSplit[0] == "restart" {
|
|
||||||
service := GetServiceByName(commandSplit[1])
|
|
||||||
if service == nil {
|
|
||||||
_, err := conn.Write([]byte("service not found"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := service.RestartService(); err != nil {
|
|
||||||
_, err := conn.Write([]byte("could not restart service"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err := conn.Write([]byte("ok"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get command to execute
|
||||||
|
command, ok := jsonData["command"]
|
||||||
|
if !ok {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("'command' field missing")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get command handler
|
||||||
|
commandHandler, ok := commandHandlers[command.(string)]
|
||||||
|
if !ok {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("command (%s) has not been implemented", command.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
commandHandler(conn, jsonData)
|
||||||
}(conn)
|
}(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleStartServiceCommand(conn net.Conn, jsonData map[string]any) {
|
||||||
|
// Get service name from json data
|
||||||
|
serviceName, ok := jsonData["service"]
|
||||||
|
if !ok {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("'service' field missing")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure service exists
|
||||||
|
service := GetServiceByName(serviceName.(string))
|
||||||
|
if service == nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Service (%s) not found", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the service
|
||||||
|
if err := service.StartService(); err != nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Service (%s) could not be started", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) has started sucessfully", serviceName.(string))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleStopServiceCommand(conn net.Conn, jsonData map[string]any) {
|
||||||
|
// Get service name from json data
|
||||||
|
serviceName, ok := jsonData["service"]
|
||||||
|
if !ok {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("'service' field missing")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure service exists
|
||||||
|
service := GetServiceByName(serviceName.(string))
|
||||||
|
if service == nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Service (%s) not found", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the service
|
||||||
|
if err := service.StopService(); err != nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Service (%s) could not be stopped", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) has stopped sucessfully", serviceName.(string))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRestartServiceCommand(conn net.Conn, jsonData map[string]any) {
|
||||||
|
// Get service name from json data
|
||||||
|
serviceName, ok := jsonData["service"]
|
||||||
|
if !ok {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("'service' field missing")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure service exists
|
||||||
|
service := GetServiceByName(serviceName.(string))
|
||||||
|
if service == nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Service (%s) not found", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart the service
|
||||||
|
if err := service.RestartService(); err != nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Service (%s) could not be restarted", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) has restarted sucessfully", serviceName.(string))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleEnableServiceCommand(conn net.Conn, jsonData map[string]any) {
|
||||||
|
// Get service name from json data
|
||||||
|
serviceName, ok := jsonData["service"]
|
||||||
|
if !ok {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("'service' field missing")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure service exists
|
||||||
|
service := GetServiceByName(serviceName.(string))
|
||||||
|
if service == nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Service (%s) not found", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if service is already enabled
|
||||||
|
if service.isEnabled() {
|
||||||
|
conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) is already enabled", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable service
|
||||||
|
err := service.SetEnabled(true)
|
||||||
|
if err != nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Could not enable service! Error: %s", err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) was enabled sucessfully", serviceName.(string))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleDisableServiceCommand(conn net.Conn, jsonData map[string]any) {
|
||||||
|
// Get service name from json data
|
||||||
|
serviceName, ok := jsonData["service"]
|
||||||
|
if !ok {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("'service' field missing")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure service exists
|
||||||
|
service := GetServiceByName(serviceName.(string))
|
||||||
|
if service == nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Service (%s) not found", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if service is already disabled
|
||||||
|
if !service.isEnabled() {
|
||||||
|
conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) is already disabled", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable service
|
||||||
|
err := service.SetEnabled(false)
|
||||||
|
if err != nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Could not disable service! Error: %s", err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) was disabled sucessfully", serviceName.(string))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleStatusServiceCommand(conn net.Conn, jsonData map[string]any) {
|
||||||
|
// Get service name from json data
|
||||||
|
serviceName, ok := jsonData["service"]
|
||||||
|
if !ok {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("'service' field missing")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure service exists
|
||||||
|
service := GetServiceByName(serviceName.(string))
|
||||||
|
if service == nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Service (%s) not found", serviceName.(string))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
statusMap := make(map[string]any)
|
||||||
|
statusMap["name"] = service.Name
|
||||||
|
statusMap["state"] = EnitServiceStateNames[service.GetCurrentState()]
|
||||||
|
statusMap["is_enabled"] = service.isEnabled()
|
||||||
|
|
||||||
|
// Encode map to json string
|
||||||
|
newJsonData, err := json.Marshal(statusMap)
|
||||||
|
if err != nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Could not encode JSON data")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Write(newJsonData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleListServicesCommand(conn net.Conn, _ map[string]any) {
|
||||||
|
servicesMap := make(map[string]any)
|
||||||
|
servicesMap["services"] = make([]map[string]any, 0)
|
||||||
|
|
||||||
|
// Loop through each service
|
||||||
|
for _, service := range Services {
|
||||||
|
statusMap := make(map[string]any)
|
||||||
|
statusMap["name"] = service.Name
|
||||||
|
statusMap["state"] = EnitServiceStateNames[service.GetCurrentState()]
|
||||||
|
statusMap["is_enabled"] = service.isEnabled()
|
||||||
|
servicesMap["services"] = append(servicesMap["services"].([]map[string]any), statusMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode map to json string
|
||||||
|
newJsonData, err := json.Marshal(servicesMap)
|
||||||
|
if err != nil {
|
||||||
|
conn.Write(wrapErrorInJson(fmt.Errorf("Could not encode JSON data")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Write(newJsonData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapErrorInJson(err error) []byte {
|
||||||
|
// Wrap error in struct
|
||||||
|
type jsonErrorStruct struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
jsonError := jsonErrorStruct{
|
||||||
|
Error: err.Error(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode struct to json string
|
||||||
|
jsonData, _err := json.Marshal(jsonError)
|
||||||
|
if _err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return jsonData
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapSuccessMsgInJson(msg string) []byte {
|
||||||
|
// Wrap message in struct
|
||||||
|
type jsonSuccessStruct struct {
|
||||||
|
Success string `json:"success"`
|
||||||
|
}
|
||||||
|
jsonSuccess := jsonSuccessStruct{
|
||||||
|
Success: msg,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode struct to json string
|
||||||
|
jsonData, _err := json.Marshal(jsonSuccess)
|
||||||
|
if _err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return jsonData
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user