250 lines
7.0 KiB
Go
250 lines
7.0 KiB
Go
package httpapp
|
|
|
|
import (
|
|
"log"
|
|
"net/http"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// ManagedService represents a service with its status
|
|
type ManagedService struct {
|
|
Name string `json:"name"`
|
|
DisplayName string `json:"display_name"`
|
|
Status string `json:"status"`
|
|
Output string `json:"output,omitempty"`
|
|
}
|
|
|
|
// getServiceName extracts service name from query parameter or defaults to atlas-api
|
|
func getServiceName(r *http.Request) string {
|
|
serviceName := r.URL.Query().Get("service")
|
|
if serviceName == "" {
|
|
return "atlas-api"
|
|
}
|
|
return serviceName
|
|
}
|
|
|
|
// getAllServices returns list of all managed services
|
|
func getAllServices() []ManagedService {
|
|
services := []ManagedService{
|
|
{Name: "atlas-api", DisplayName: "AtlasOS API"},
|
|
{Name: "smbd", DisplayName: "SMB/CIFS (Samba)"},
|
|
{Name: "nfs-server", DisplayName: "NFS Server"},
|
|
{Name: "target", DisplayName: "iSCSI Target"},
|
|
}
|
|
return services
|
|
}
|
|
|
|
// getServiceStatus returns the status of a specific service
|
|
func getServiceStatus(serviceName string) (string, string, error) {
|
|
cmd := exec.Command("systemctl", "status", serviceName, "--no-pager", "-l")
|
|
output, err := cmd.CombinedOutput()
|
|
outputStr := string(output)
|
|
|
|
if err != nil {
|
|
// systemctl status returns non-zero exit code even when service is running
|
|
// Check if output contains "active (running)" to determine actual status
|
|
if strings.Contains(outputStr, "active (running)") {
|
|
return "running", outputStr, nil
|
|
}
|
|
if strings.Contains(outputStr, "inactive (dead)") {
|
|
return "stopped", outputStr, nil
|
|
}
|
|
if strings.Contains(outputStr, "failed") {
|
|
return "failed", outputStr, nil
|
|
}
|
|
// Service might not exist
|
|
if strings.Contains(outputStr, "could not be found") || strings.Contains(outputStr, "not found") {
|
|
return "not-found", outputStr, nil
|
|
}
|
|
return "unknown", outputStr, err
|
|
}
|
|
|
|
status := "unknown"
|
|
if strings.Contains(outputStr, "active (running)") {
|
|
status = "running"
|
|
} else if strings.Contains(outputStr, "inactive (dead)") {
|
|
status = "stopped"
|
|
} else if strings.Contains(outputStr, "failed") {
|
|
status = "failed"
|
|
}
|
|
|
|
return status, outputStr, nil
|
|
}
|
|
|
|
// handleListServices returns the status of all services
|
|
func (a *App) handleListServices(w http.ResponseWriter, r *http.Request) {
|
|
allServices := getAllServices()
|
|
servicesStatus := make([]ManagedService, 0, len(allServices))
|
|
|
|
for _, svc := range allServices {
|
|
status, output, err := getServiceStatus(svc.Name)
|
|
if err != nil {
|
|
log.Printf("error getting status for %s: %v", svc.Name, err)
|
|
status = "error"
|
|
}
|
|
servicesStatus = append(servicesStatus, ManagedService{
|
|
Name: svc.Name,
|
|
DisplayName: svc.DisplayName,
|
|
Status: status,
|
|
Output: output,
|
|
})
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
|
"services": servicesStatus,
|
|
})
|
|
}
|
|
|
|
// handleServiceStatus returns the status of a specific service
|
|
func (a *App) handleServiceStatus(w http.ResponseWriter, r *http.Request) {
|
|
serviceName := getServiceName(r)
|
|
status, output, err := getServiceStatus(serviceName)
|
|
|
|
if err != nil {
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{
|
|
"error": "failed to get service status",
|
|
"details": output,
|
|
})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
|
"service": serviceName,
|
|
"status": status,
|
|
"output": output,
|
|
})
|
|
}
|
|
|
|
// handleServiceStart starts a service
|
|
func (a *App) handleServiceStart(w http.ResponseWriter, r *http.Request) {
|
|
serviceName := getServiceName(r)
|
|
|
|
var cmd *exec.Cmd
|
|
// Special handling for SMB - use smbcontrol for reload, but systemctl for start/stop
|
|
if serviceName == "smbd" {
|
|
cmd = exec.Command("systemctl", "start", "smbd")
|
|
} else {
|
|
cmd = exec.Command("systemctl", "start", serviceName)
|
|
}
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
log.Printf("service start error for %s: %v", serviceName, err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
|
|
"error": "failed to start service",
|
|
"service": serviceName,
|
|
"details": string(output),
|
|
})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
|
"message": "service started successfully",
|
|
"service": serviceName,
|
|
})
|
|
}
|
|
|
|
// handleServiceStop stops a service
|
|
func (a *App) handleServiceStop(w http.ResponseWriter, r *http.Request) {
|
|
serviceName := getServiceName(r)
|
|
cmd := exec.Command("systemctl", "stop", serviceName)
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
log.Printf("service stop error for %s: %v", serviceName, err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
|
|
"error": "failed to stop service",
|
|
"service": serviceName,
|
|
"details": string(output),
|
|
})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
|
"message": "service stopped successfully",
|
|
"service": serviceName,
|
|
})
|
|
}
|
|
|
|
// handleServiceRestart restarts a service
|
|
func (a *App) handleServiceRestart(w http.ResponseWriter, r *http.Request) {
|
|
serviceName := getServiceName(r)
|
|
cmd := exec.Command("systemctl", "restart", serviceName)
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
log.Printf("service restart error for %s: %v", serviceName, err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
|
|
"error": "failed to restart service",
|
|
"service": serviceName,
|
|
"details": string(output),
|
|
})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
|
"message": "service restarted successfully",
|
|
"service": serviceName,
|
|
})
|
|
}
|
|
|
|
// handleServiceReload reloads a service
|
|
func (a *App) handleServiceReload(w http.ResponseWriter, r *http.Request) {
|
|
serviceName := getServiceName(r)
|
|
|
|
var cmd *exec.Cmd
|
|
// Special handling for SMB - use smbcontrol for reload
|
|
if serviceName == "smbd" {
|
|
cmd = exec.Command("smbcontrol", "all", "reload-config")
|
|
} else {
|
|
cmd = exec.Command("systemctl", "reload", serviceName)
|
|
}
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
log.Printf("service reload error for %s: %v", serviceName, err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
|
|
"error": "failed to reload service",
|
|
"service": serviceName,
|
|
"details": string(output),
|
|
})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
|
"message": "service reloaded successfully",
|
|
"service": serviceName,
|
|
})
|
|
}
|
|
|
|
// handleServiceLogs returns the logs of a service
|
|
func (a *App) handleServiceLogs(w http.ResponseWriter, r *http.Request) {
|
|
serviceName := getServiceName(r)
|
|
|
|
// Get number of lines from query parameter (default: 50)
|
|
linesStr := r.URL.Query().Get("lines")
|
|
lines := "50"
|
|
if linesStr != "" {
|
|
if n, err := strconv.Atoi(linesStr); err == nil && n > 0 && n <= 1000 {
|
|
lines = linesStr
|
|
}
|
|
}
|
|
|
|
cmd := exec.Command("journalctl", "-u", serviceName, "-n", lines, "--no-pager")
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
log.Printf("service logs error for %s: %v", serviceName, err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
|
|
"error": "failed to get service logs",
|
|
"service": serviceName,
|
|
"details": string(output),
|
|
})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
|
"service": serviceName,
|
|
"logs": string(output),
|
|
})
|
|
}
|