wings/server/console.go
2020-09-05 13:08:40 -06:00

107 lines
2.9 KiB
Go

package server
import (
"fmt"
"github.com/mitchellh/colorstring"
"github.com/pterodactyl/wings/config"
"sync"
"sync/atomic"
"time"
)
type ConsoleThrottler struct {
sync.RWMutex
config.ConsoleThrottles
// The total number of activations that have occurred thus far.
activations uint64
// The total number of lines processed so far during the given time period.
lines uint64
lastIntervalTime *time.Time
lastDecayTime *time.Time
}
// Increments the number of activations for a server.
func (ct *ConsoleThrottler) AddActivation() uint64 {
ct.Lock()
defer ct.Unlock()
ct.activations += 1
return ct.activations
}
// Decrements the number of activations for a server.
func (ct *ConsoleThrottler) RemoveActivation() uint64 {
ct.Lock()
defer ct.Unlock()
if ct.activations == 0 {
return 0
}
ct.activations -= 1
return ct.activations
}
// Increment the total count of lines that we have processed so far.
func (ct *ConsoleThrottler) IncrementLineCount() uint64 {
return atomic.AddUint64(&ct.lines, 1)
}
// Reset the line count to zero.
func (ct *ConsoleThrottler) ResetLineCount() {
atomic.SwapUint64(&ct.lines, 0)
}
// Handles output from a server's console. This code ensures that a server is not outputting
// an excessive amount of data to the console that could indicate a malicious or run-away process
// and lead to performance issues for other users.
//
// This was much more of a problem for the NodeJS version of the daemon which struggled to handle
// large volumes of output. However, this code is much more performant so I generally feel a lot
// better about it's abilities.
//
// However, extreme output is still somewhat of a DoS attack vector against this software since we
// are still logging it to the disk temporarily and will want to avoid dumping a huge amount of
// data all at once. These values are all configurable via the wings configuration file, however the
// defaults have been in the wild for almost two years at the time of this writing, so I feel quite
// confident in them.
func (ct *ConsoleThrottler) Handle() {
}
// Returns the throttler instance for the server or creates a new one.
func (s *Server) Throttler() *ConsoleThrottler {
s.throttleLock.RLock()
if s.throttler == nil {
// Release the read lock so that we can acquire a normal lock on the process and
// make modifications to the throttler.
s.throttleLock.RUnlock()
s.throttleLock.Lock()
s.throttler = &ConsoleThrottler{
ConsoleThrottles: config.Get().Throttles,
}
s.throttleLock.Unlock()
return s.throttler
} else {
defer s.throttleLock.RUnlock()
return s.throttler
}
}
// Sends output to the server console formatted to appear correctly as being sent
// from Wings.
func (s *Server) PublishConsoleOutputFromDaemon(data string) {
s.Events().Publish(
ConsoleOutputEvent,
colorstring.Color(fmt.Sprintf("[yellow][bold][Pterodactyl Daemon]:[default] %s", data)),
)
}