Adding current project state
This commit is contained in:
116
process.go
Normal file
116
process.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ProcessManager struct {
|
||||
sync.RWMutex
|
||||
ActiveInstances map[string]*exec.Cmd
|
||||
StdinPipes map[string]io.WriteCloser
|
||||
}
|
||||
|
||||
func NewProcessManager() *ProcessManager {
|
||||
return &ProcessManager{
|
||||
ActiveInstances: make(map[string]*exec.Cmd),
|
||||
StdinPipes: make(map[string]io.WriteCloser),
|
||||
}
|
||||
}
|
||||
|
||||
func (pm *ProcessManager) StartInstance(name string, version string, options VsServerConfigOptions, config *AppConfig) error {
|
||||
pm.Lock()
|
||||
defer pm.Unlock()
|
||||
|
||||
if _, running := pm.ActiveInstances[name]; running {
|
||||
return fmt.Errorf("Instance '%s' is already running", name)
|
||||
}
|
||||
|
||||
binaryPath := filepath.Join(config.Storage.InstallDir, version, "VintagestoryServer")
|
||||
instanceDataPath := filepath.Join(config.Storage.InstancesDir, name)
|
||||
|
||||
if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
|
||||
fmt.Printf("Server version '%s' not installed, attempting to install\n", version)
|
||||
install_err := DownloadAndExtractServer(version, config.Storage.InstallDir)
|
||||
if install_err != nil {
|
||||
return fmt.Errorf("Could not locate or download a server binary for version '%s'", version)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command(binaryPath, "--dataPath", instanceDataPath)
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
stdinPipe, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to open stdin pipe: %w", err)
|
||||
}
|
||||
|
||||
stdoutPipe, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
stdinPipe.Close()
|
||||
return fmt.Errorf("failed to open stdout conduit: %w", err)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
stdinPipe.Close()
|
||||
stdoutPipe.Close()
|
||||
return fmt.Errorf("Failed to start process thread: %w", err)
|
||||
}
|
||||
|
||||
pm.ActiveInstances[name] = cmd
|
||||
pm.StdinPipes[name] = stdinPipe
|
||||
|
||||
go pm.streamLogs(name, stdoutPipe)
|
||||
go pm.watchProcessExit(name, cmd)
|
||||
|
||||
fmt.Printf("Server instance '%s' successfully spawned under PID %d\n", name, cmd.Process.Pid)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pm *ProcessManager) streamLogs(name string, stdout io.ReadCloser) {
|
||||
defer stdout.Close()
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
|
||||
for scanner.Scan() {
|
||||
//stream straight to the manager console terminal,
|
||||
// but later dispatch payloads to our web UI or a log file
|
||||
fmt.Printf("[%s]: %s\n", name, scanner.Text())
|
||||
}
|
||||
}
|
||||
|
||||
func (pm *ProcessManager) watchProcessExit(name string, cmd *exec.Cmd) {
|
||||
_ = cmd.Wait()
|
||||
|
||||
pm.Lock()
|
||||
defer pm.Unlock()
|
||||
|
||||
delete(pm.ActiveInstances, name)
|
||||
if pipe, exists := pm.StdinPipes[name]; exists {
|
||||
pipe.Close()
|
||||
delete(pm.StdinPipes, name)
|
||||
}
|
||||
fmt.Printf("Server instance '%s' has shut down or terminated.\n", name)
|
||||
}
|
||||
|
||||
func (pm *ProcessManager) SendCommand(name string, command string) error {
|
||||
pm.RLock()
|
||||
pipe, exists := pm.StdinPipes[name]
|
||||
pm.RUnlock()
|
||||
|
||||
if !exists {
|
||||
return fmt.Errorf("Cannot send command, instance with name '%s' is not running", name)
|
||||
}
|
||||
|
||||
_, err := io.WriteString(pipe, command+"\n")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed writing to server process stdin conduit: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user