From 225f8aa90467dbc55b68e24b41d29665c2009d30 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 28 Jan 2021 21:26:02 -0800 Subject: [PATCH] Support some additional signal types for stopping a server; ref pterodactyl/panel#3042 --- environment/docker/power.go | 42 +++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/environment/docker/power.go b/environment/docker/power.go index 0663bab..20f4803 100644 --- a/environment/docker/power.go +++ b/environment/docker/power.go @@ -11,6 +11,7 @@ import ( "github.com/pterodactyl/wings/environment" "os" "strings" + "syscall" "time" ) @@ -115,23 +116,40 @@ func (e *Environment) Start() error { return e.Attach() } -// Stops the container that the server is running in. This will allow up to 30 seconds to pass -// before the container is forcefully terminated if we are trying to stop it without using a command -// sent into the instance. +// Stop stops the container that the server is running in. This will allow up to +// 30 seconds to pass before the container is forcefully terminated if we are +// trying to stop it without using a command sent into the instance. // -// You most likely want to be using WaitForStop() rather than this function, since this will return -// as soon as the command is sent, rather than waiting for the process to be completed stopped. +// You most likely want to be using WaitForStop() rather than this function, +// since this will return as soon as the command is sent, rather than waiting +// for the process to be completed stopped. +// +// TODO: pass context through from the server instance. func (e *Environment) Stop() error { e.mu.RLock() s := e.meta.Stop e.mu.RUnlock() + // A native "stop" as the Type field value will just skip over all of this + // logic and end up only executing the container stop command (which may or + // may not work as expected). if s.Type == "" || s.Type == api.ProcessStopSignal { if s.Type == "" { log.WithField("container_id", e.Id).Warn("no stop configuration detected for environment, using termination procedure") } - return e.Terminate(os.Kill) + signal := os.Kill + // Handle a few common cases, otherwise just fall through and just pass along + // the os.Kill signal to the process. + switch strings.ToUpper(s.Value) { + case "SIGABRT": + signal = syscall.SIGABRT + case "SIGINT": + signal = syscall.SIGINT + case "SIGTERM": + signal = syscall.SIGTERM + } + return e.Terminate(signal) } // If the process is already offline don't switch it back to stopping. Just leave it how @@ -147,26 +165,24 @@ func (e *Environment) Stop() error { } t := time.Second * 30 - if err := e.client.ContainerStop(context.Background(), e.Id, &t); err != nil { // If the container does not exist just mark the process as stopped and return without // an error. if client.IsErrNotFound(err) { e.SetStream(nil) e.SetState(environment.ProcessOfflineState) - return nil } - return err } return nil } -// Attempts to gracefully stop a server using the defined stop command. If the server -// does not stop after seconds have passed, an error will be returned, or the instance -// will be terminated forcefully depending on the value of the second argument. +// WaitForStop attempts to gracefully stop a server using the defined stop +// command. If the server does not stop after seconds have passed, an error will +// be returned, or the instance will be terminated forcefully depending on the +// value of the second argument. func (e *Environment) WaitForStop(seconds uint, terminate bool) error { if err := e.Stop(); err != nil { return err @@ -211,7 +227,7 @@ func (e *Environment) WaitForStop(seconds uint, terminate bool) error { return nil } -// Forcefully terminates the container using the signal passed through. +// Terminate forcefully terminates the container using the signal provided. func (e *Environment) Terminate(signal os.Signal) error { c, err := e.client.ContainerInspect(context.Background(), e.Id) if err != nil {