package main import ( "flag" "fmt" "gopkg.in/yaml.v3" "log" "net" "os" "path" "strconv" "strings" "syscall" "time" ) // Build-time variables var version = "dev" var sysconfdir = "/etc/" var runstatedir = "/var/run/" var socket net.Conn func main() { // Set and parse flags printVersion := flag.Bool("version", false, "print version and exit") flag.Parse() // Dial esvm socket dialSocket() defer socket.Close() if flag.NArg() < 1 { printUsage() os.Exit(1) } if *printVersion || flag.Args()[0] == "version" { fmt.Printf("Enit Control version %s\n", version) return } else if flag.Args()[0] == "help" { printUsage() return } else if flag.Args()[0] == "shutdown" || flag.Args()[0] == "poweroff" || flag.Args()[0] == "halt" { err := syscall.Kill(1, syscall.SIGUSR1) if err != nil { log.Fatalf("Could not send shutdown signal! Error: %s\n", err) } return } else if flag.Args()[0] == "reboot" || flag.Args()[0] == "restart" || flag.Args()[0] == "reset" { err := syscall.Kill(1, syscall.SIGTERM) if err != nil { log.Fatalf("Could not send shutdown signal! Error: %s\n", err) } return } else if flag.Args()[0] == "service" || flag.Args()[0] == "sv" { if len(flag.Args()) <= 1 { fmt.Println("Usage: ectl service [service]") return } else if flag.Args()[1] == "list" { fmt.Println("list") return } else if len(flag.Args()) <= 2 { fmt.Printf("Usage: ectl service %s \n", flag.Args()[1]) return } else if flag.Args()[1] == "start" { flag.Args()[2] = strings.TrimSuffix(flag.Args()[2], ".esv") if _, err := os.Stat(path.Join(sysconfdir, "esvm/services/", flag.Args()[2]+".esv")); 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" { flag.Args()[2] = strings.TrimSuffix(flag.Args()[2], ".esv") if _, err := os.Stat(path.Join(sysconfdir, "esvm/services/", flag.Args()[2]+".esv")); 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" { flag.Args()[2] = strings.TrimSuffix(flag.Args()[2], ".esv") if _, err := os.Stat(path.Join(sysconfdir, "esvm/services/", flag.Args()[2]+".esv")); err != nil { log.Fatalf("Could not stop 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 } file, err := os.ReadFile(path.Join(sysconfdir, "esvm/enabled_services")) if err != nil { log.Fatalf("Could not enable service! Error: %s\n", err) } for _, line := range strings.Split(string(file), "\n") { if strings.TrimSpace(line) == flag.Args()[2] { fmt.Println("Service is already enabled!") return } } err = os.WriteFile(path.Join(sysconfdir, "esvm/enabled_services"), []byte(string(file)+flag.Args()[2]+"\n"), 0644) if err != nil { log.Fatalf("Could not enable service! Error: %s\n", err) } fmt.Printf("Service (%s) has been enabled!\n", flag.Args()[2]) return } else if flag.Args()[1] == "disable" { if _, err := os.Stat(path.Join(sysconfdir, "esvm/enabled_services")); err != nil { fmt.Println("Service is already disabled!") return } file, err := os.ReadFile(path.Join(sysconfdir, "esvm/enabled_services")) if err != nil { log.Fatalf("Could not disable service! Error: %s\n", err) } 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 } err = os.WriteFile(path.Join(sysconfdir, "esvm/enabled_services"), []byte(strings.Join(lines, "\n")+"\n"), 0644) if err != nil { log.Fatalf("Could not disable service! Error: %s\n", err) } fmt.Printf("Service (%s) has been disabled!\n", flag.Args()[2]) return } else if flag.Args()[1] == "status" { flag.Args()[2] = strings.TrimSuffix(flag.Args()[2], ".esv") if _, err := os.Stat(path.Join(sysconfdir, "esvm/services/", flag.Args()[2]+".esv")); err != nil { log.Fatalf("Could not stop service! Error: %s\n", err) } var state uint64 bytes, err := os.ReadFile(path.Join(runstatedir, "esvm", flag.Args()[2], "state")) if err != nil { state = 0 } state, err = strconv.ParseUint(string(bytes), 10, 8) fmt.Println("Service name: " + flag.Args()[2]) switch state { case 0: fmt.Println("Service state: Unknown") case 1: fmt.Println("Service state: Unloaded") case 2: fmt.Println("Service state: Running") case 3: fmt.Println("Service state: Stopped") case 4: fmt.Println("Service state: Crashed") } return } } printUsage() os.Exit(1) } func printUsage() { fmt.Println("Available sucommands:") fmt.Println("ectl version | Show enit version") fmt.Println("ectl shutdown/poweroff/halt | Shutdown the system") fmt.Println("ectl reboot/restart | Reboot the system") fmt.Println("ectl help | Show command explanations") fmt.Println("ectl sv/service start | Start a service") fmt.Println("ectl sv/service stop | Stop a service") fmt.Println("ectl sv/service enable | Enable a service at startup") fmt.Println("ectl sv/service disable | Disable a service at startup") fmt.Println("ectl sv/service status | Show service status") fmt.Println("ectl sv/service list | Show all enabled services") } func dialSocket() { if _, err := os.Stat(path.Join(runstatedir, "esvm/esvm.sock")); err != nil { log.Fatalf("Could not find esvm.sock! Error: %s\n", err) } var err error socket, err = net.Dial("unix", path.Join(runstatedir, "esvm/esvm.sock")) if err != nil { log.Fatalf("Failed to connect to esvm.sock! Error: %s\n", err) } if err := socket.SetDeadline(time.Now().Add(5 * time.Second)); err != nil { log.Fatalf("Failed to set write deadline! Error: %s\n", err) } }