From 06f5e6564f0b2a50dee73877430f0565c5dac206 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 7 Dec 2025 18:03:13 +0200 Subject: [PATCH] ectl: move enable/disable service functionality back into ectl --- src/ectl/go.mod | 5 +- src/ectl/go.sum | 4 ++ src/ectl/sv_subcommand.go | 105 ++++++++++++++++++-------------------- src/ectl/utils.go | 88 ++++++++++++++++++++++++++++++++ src/esvm/service.go | 47 ----------------- src/esvm/socket.go | 62 ---------------------- 6 files changed, 146 insertions(+), 165 deletions(-) create mode 100644 src/ectl/utils.go diff --git a/src/ectl/go.mod b/src/ectl/go.mod index 705a7ec..024f90b 100644 --- a/src/ectl/go.mod +++ b/src/ectl/go.mod @@ -2,4 +2,7 @@ module ectl go 1.23.4 -require github.com/spf13/pflag v1.0.10 +require ( + github.com/spf13/pflag v1.0.10 + gopkg.in/yaml.v3 v3.0.1 +) diff --git a/src/ectl/go.sum b/src/ectl/go.sum index 8ec1276..bea0ee8 100644 --- a/src/ectl/go.sum +++ b/src/ectl/go.sum @@ -1,2 +1,6 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +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= diff --git a/src/ectl/sv_subcommand.go b/src/ectl/sv_subcommand.go index f05cac7..bd355db 100644 --- a/src/ectl/sv_subcommand.go +++ b/src/ectl/sv_subcommand.go @@ -45,10 +45,6 @@ func handleServiceSubcommand() { currentFlagSet.BoolP("json", "j", false, "Return output in json format") setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("ectl %s %s ", os.Args[1], subcommand), fmt.Sprintf("%s the specified service", strings.Title(subcommand)), os.Args[3:]) - // Dial esvm socket - dialSocket() - defer conn.Close() - enableDisableService(subcommand) case "status": // Setup flags and help @@ -158,67 +154,57 @@ func enableDisableService(subcommand string) { return } + service := currentFlagSet.Arg(0) + // Get service stage stage := 3 - if len(currentFlagSet.Args()) > 1 { + if subcommand == "disable" { + stage = 0 + } else if len(currentFlagSet.Args()) > 1 { flagStr := currentFlagSet.Arg(1) _stage, err := strconv.ParseInt(flagStr, 10, 32) if err != nil { log.Fatalf("Error: could not parse stage number: %s", err) } stage = int(_stage) - } else if subcommand == "disable" { - stage = 0 } - type ServiceCommandJsonStruct struct { - Command string `json:"command"` - Service string `json:"service"` - Stage int `json:"stage"` - } - serviceCommandJson := ServiceCommandJsonStruct{ - Command: "set_enabled", - Service: currentFlagSet.Arg(0), - Stage: stage, + verb := "enabled" + if stage == 0 { + verb = "disabled" } - // 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) - } - - // Read data from the connection. - data, err := readAllConn(conn) - if err != nil { - log.Fatalf("Could not read data from socket! Error: %s\n", err) + // Return if service is already enabled + if _, enabledStage := isServiceEnabled(service); enabledStage == stage { + if printJson { + fmt.Printf("{\"success\":\"Service (%s) is already %s\"}\n", service, verb) + } else { + fmt.Printf("Service (%s) is already %s\n", service, verb) + } return } - // Print json data if flag is set + // Enable service + err := setServiceEnabled(service, stage) + if err != nil { + verb := "enable" + if stage == 0 { + verb = "disable" + } + + if printJson { + fmt.Printf("{\"error\":\"Could not %s service! Error: %s\"}\n", verb, err) + } else { + fmt.Printf("Could not %s service! Error: %s\n", verb, err) + } + os.Exit(1) + } + if printJson { - fmt.Println(string(data)) + fmt.Printf("{\"success\":\"Service (%s) was %s sucessfully\"}\n", service, verb) return - } - - // Decoode JSON data - var returnedJsonData map[string]any - err = json.Unmarshal(data, &returnedJsonData) - if err != nil { - 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) was %s sucessfully\n", service, verb) } } @@ -259,12 +245,6 @@ func showServiceStatus() { return } - // Print json data if flag is set - if printJson { - fmt.Println(string(data)) - return - } - // Decoode JSON data var returnedJsonData map[string]any err = json.Unmarshal(data, &returnedJsonData) @@ -273,13 +253,28 @@ func showServiceStatus() { } if err, ok := returnedJsonData["error"]; ok { - log.Fatal(err) + if printJson { + fmt.Println(string(data)) + os.Exit(1) + } else { + log.Fatal(err) + } + } + + // Set is_enabled and stage fields in json data + returnedJsonData["is_enabled"], returnedJsonData["stage"] = isServiceEnabled(currentFlagSet.Arg(0)) + + // Print json data if flag is set + if printJson { + data, _ = json.Marshal(returnedJsonData) + fmt.Println(string(data)) + return } serviceState := returnedJsonData["state"].(string) serviceDescription := returnedJsonData["description"].(string) serviceEnabled := returnedJsonData["is_enabled"].(bool) - serviceStage := int(returnedJsonData["stage"].(float64)) + serviceStage := returnedJsonData["stage"].(int) processID := int(returnedJsonData["process_id"].(float64)) fmt.Printf("Name: %s\n", currentFlagSet.Arg(0)) diff --git a/src/ectl/utils.go b/src/ectl/utils.go new file mode 100644 index 0000000..16dd6c7 --- /dev/null +++ b/src/ectl/utils.go @@ -0,0 +1,88 @@ +package main + +import ( + "os" + "path" + "slices" + "strings" + + "gopkg.in/yaml.v3" +) + +func isServiceEnabled(service string) (bool, int) { + for stage, services := range readEnabledServices() { + if slices.Contains(services, service) { + return true, stage + } + } + + return false, 0 +} + +func setServiceEnabled(service string, stage int) error { + // Get current service enabled status + _, s := isServiceEnabled(service) + + // Return if service is already in correct state + if s == stage { + return nil + } + + EnabledServices := readEnabledServices() + + // Remove service from current stage + EnabledServices[s] = slices.DeleteFunc(EnabledServices[s], func(name string) bool { + return name == service + }) + if len(EnabledServices[s]) == 0 { + delete(EnabledServices, s) + } + + // Add service to stage + if stage != 0 { + EnabledServices[stage] = append(EnabledServices[stage], service) + } + + // Save enabled services to file + data, err := yaml.Marshal(EnabledServices) + if err != nil { + return err + } + err = os.WriteFile(path.Join(sysconfdir, "esvm/enabled_services"), data, 0644) + if err != nil { + return err + } + + return nil +} + +func readEnabledServices() (EnabledServices map[int][]string) { + EnabledServices = make(map[int][]string) + + data, err := os.ReadFile(path.Join(sysconfdir, "esvm/enabled_services")) + if err != nil { + return EnabledServices + } + + err = yaml.Unmarshal(data, &EnabledServices) + if err != nil { + // Assume old plain text format + for _, service := range strings.Split(strings.TrimSpace(string(data)), "\n") { + EnabledServices[3] = append(EnabledServices[3], service) + } + + // Update enabled_services file + data, err := yaml.Marshal(EnabledServices) + if err != nil { + return EnabledServices + } + err = os.WriteFile(path.Join(sysconfdir, "esvm/enabled_services"), data, 0644) + if err != nil { + return EnabledServices + } + + return EnabledServices + } + + return EnabledServices +} diff --git a/src/esvm/service.go b/src/esvm/service.go index d4dd216..a35e5c1 100644 --- a/src/esvm/service.go +++ b/src/esvm/service.go @@ -511,53 +511,6 @@ func (service *EnitService) RestartService() error { return nil } -func (service *EnitService) isEnabled() (bool, int) { - for stage, services := range ReadEnabledServices() { - if slices.Contains(services, service.Name) { - return true, stage - } - } - - return false, 0 -} - -func (service *EnitService) SetEnabled(stage int) error { - // Get current service enabled status - _, s := service.isEnabled() - - // Return if service is already in correct state - if s == stage { - return nil - } - - EnabledServices := ReadEnabledServices() - - // Remove service from current stage - EnabledServices[s] = slices.DeleteFunc(EnabledServices[s], func(name string) bool { - return name == service.Name - }) - if len(EnabledServices[s]) == 0 { - delete(EnabledServices, s) - } - - // Add service to stage - if stage != 0 { - EnabledServices[stage] = append(EnabledServices[stage], service.Name) - } - - // Save enabled services to file - data, err := yaml.Marshal(EnabledServices) - if err != nil { - return err - } - err = os.WriteFile(path.Join(serviceConfigDir, "enabled_services"), data, 0644) - if err != nil { - return err - } - - return nil -} - func ReadEnabledServices() (EnabledServices map[int][]string) { EnabledServices = make(map[int][]string) diff --git a/src/esvm/socket.go b/src/esvm/socket.go index ed222d5..4c2c390 100644 --- a/src/esvm/socket.go +++ b/src/esvm/socket.go @@ -22,7 +22,6 @@ func initSocket() (socket net.Listener, err error) { commandHandlers["start"] = handleStartServiceCommand commandHandlers["stop"] = handleStopServiceCommand commandHandlers["restart"] = handleRestartServiceCommand - commandHandlers["set_enabled"] = handleSetEnabledServiceCommand commandHandlers["status"] = handleStatusServiceCommand commandHandlers["list"] = handleListServicesCommand @@ -151,65 +150,6 @@ func handleRestartServiceCommand(conn net.Conn, jsonData map[string]any) { conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) has restarted sucessfully", serviceName.(string)))) } -func handleSetEnabledServiceCommand(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 - } - - // Get service stage from json json data - _serviceStage, ok := jsonData["stage"] - if !ok { - conn.Write(wrapErrorInJson(fmt.Errorf("'stage' field missing"))) - return - } - serviceStage, ok := _serviceStage.(float64) - if !ok { - conn.Write(wrapErrorInJson(fmt.Errorf("'stage' field is not a number"))) - return - } - - // Ensure service exists - service := GetServiceByName(serviceName.(string)) - if service == nil { - conn.Write(wrapErrorInJson(fmt.Errorf("Service (%s) not found", serviceName.(string)))) - return - } - - // Get current service enabled status - _, s := service.isEnabled() - - // Return if service is already in correct state - if s == int(serviceStage) { - if serviceStage == 0 { - conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) is already disabled", serviceName.(string)))) - } else { - conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) is already enabled", serviceName.(string)))) - } - return - } - - // Enable service - err := service.SetEnabled(int(serviceStage)) - if err != nil { - if serviceStage == 0 { - conn.Write(wrapErrorInJson(fmt.Errorf("Could not disable service! Error: %s", err))) - } else { - conn.Write(wrapErrorInJson(fmt.Errorf("Could not enable service! Error: %s", err))) - } - return - } - - if serviceStage == 0 { - conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) was disabled sucessfully", serviceName.(string)))) - } else { - conn.Write(wrapSuccessMsgInJson(fmt.Sprintf("Service (%s) was enabled sucessfully", serviceName.(string)))) - } - -} - func handleStatusServiceCommand(conn net.Conn, jsonData map[string]any) { // Get service name from json data serviceName, ok := jsonData["service"] @@ -230,7 +170,6 @@ func handleStatusServiceCommand(conn net.Conn, jsonData map[string]any) { statusMap["description"] = service.Description statusMap["state"] = EnitServiceStateNames[service.state] statusMap["process_id"] = service.processID - statusMap["is_enabled"], statusMap["stage"] = service.isEnabled() // Encode map to json string newJsonData, err := json.Marshal(statusMap) @@ -253,7 +192,6 @@ func handleListServicesCommand(conn net.Conn, _ map[string]any) { statusMap["description"] = service.Description statusMap["state"] = EnitServiceStateNames[service.state] statusMap["process_id"] = service.processID - statusMap["is_enabled"], statusMap["stage"] = service.isEnabled() servicesMap["services"] = append(servicesMap["services"].([]map[string]any), statusMap) }