From e29789114cfd79a741a7117da170018ce1a3f113 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 11 Oct 2020 15:02:37 -0700 Subject: [PATCH 1/2] Fix server disk usage not being reported properly; closes pterodactyl/panel#2445 --- server/filesystem/disk_space.go | 10 ++++++++++ server/resources.go | 26 +++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/server/filesystem/disk_space.go b/server/filesystem/disk_space.go index 4922b7a..3c7ef79 100644 --- a/server/filesystem/disk_space.go +++ b/server/filesystem/disk_space.go @@ -75,6 +75,16 @@ func (fs *Filesystem) HasSpaceAvailable(allowStaleValue bool) bool { return size <= fs.MaxDisk() } +// Returns the cached value for the amount of disk space used by the filesystem. Do not rely on this +// function for critical logical checks. It should only be used in areas where the actual disk usage +// does not need to be perfect, e.g. API responses for server resource usage. +func (fs *Filesystem) CachedUsage() int64 { + fs.mu.RLock() + defer fs.mu.RUnlock() + + return fs.diskUsed +} + // Internal helper function to allow other parts of the codebase to check the total used disk space // as needed without overly taxing the system. This will prioritize the value from the cache to avoid // excessive IO usage. We will only walk the filesystem and determine the size of the directory if there diff --git a/server/resources.go b/server/resources.go index db7bea8..d3a3d2c 100644 --- a/server/resources.go +++ b/server/resources.go @@ -1,6 +1,7 @@ package server import ( + "encoding/json" "github.com/pterodactyl/wings/environment" "sync" ) @@ -17,17 +18,34 @@ type ResourceUsage struct { // The current server status. State string `json:"state" default:"offline"` - // The current disk space being used by the server. This is cached to prevent slow lookup - // issues on frequent refreshes. + // The current disk space being used by the server. This value is not guaranteed to be accurate + // at all times. It is "manually" set whenever server.Proc() is called. This is kind of just a + // hacky solution for now to avoid passing events all over the place. Disk int64 `json:"disk_bytes"` } +// Alias the resource usage so that we don't infinitely recurse when marshaling the struct. +type IResourceUsage ResourceUsage + +// Custom marshaler to ensure that the object is locked when we're converting it to JSON in +// order to avoid race conditions. +func (ru *ResourceUsage) MarshalJSON() ([]byte, error) { + ru.mu.Lock() + defer ru.mu.Unlock() + + return json.Marshal(IResourceUsage(*ru)) +} + // Returns the resource usage stats for the server instance. If the server is not running, only the // disk space currently used will be returned. When the server is running all of the other stats will // be returned. // // When a process is stopped all of the stats are zeroed out except for the disk. func (s *Server) Proc() *ResourceUsage { + s.resources.SetDisk(s.Filesystem().CachedUsage()) + + // Get a read lock on the resources at this point. Don't do this before setting + // the disk, otherwise you'll cause a deadlock. s.resources.mu.RLock() defer s.resources.mu.RUnlock() @@ -35,11 +53,9 @@ func (s *Server) Proc() *ResourceUsage { } func (s *Server) emitProcUsage() { - s.resources.mu.RLock() - if err := s.Events().PublishJson(StatsEvent, s.resources); err != nil { + if err := s.Events().PublishJson(StatsEvent, s.Proc()); err != nil { s.Log().WithField("error", err).Warn("error while emitting server resource usage to listeners") } - s.resources.mu.RUnlock() } // Returns the servers current state. From 6c291d9b0e27ab11cfa5237af00d5316bc3e5751 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 11 Oct 2020 15:23:55 -0700 Subject: [PATCH 2/2] Minor tweaks to diagnostics for cleaner info, changes endpoint; closes pterodactyl/panel#2463 --- cmd/diagnostics.go | 41 ++++++++++++++++++++++------------------- config/config.go | 3 --- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/cmd/diagnostics.go b/cmd/diagnostics.go index 6092053..de9ec8f 100644 --- a/cmd/diagnostics.go +++ b/cmd/diagnostics.go @@ -13,6 +13,7 @@ import ( "path" "strconv" "strings" + "time" "github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2/terminal" @@ -25,8 +26,8 @@ import ( "github.com/spf13/cobra" ) -const DefaultHastebinUrl = "https://hastebin.com" -const DefaultLogLines = 50 +const DefaultHastebinUrl = "https://ptero.co" +const DefaultLogLines = 200 var ( diagnosticsArgs struct { @@ -102,23 +103,25 @@ func diagnosticsCmdRun(cmd *cobra.Command, args []string) { printHeader(output, "Wings Configuration") cfg, err := config.ReadConfiguration(config.DefaultLocation) if cfg != nil { - fmt.Fprintln(output, "Panel Location:", redact(cfg.PanelLocation)) - fmt.Fprintln(output, "Api Host:", redact(cfg.Api.Host)) - fmt.Fprintln(output, "Api Port:", cfg.Api.Port) - fmt.Fprintln(output, "Api Ssl Enabled:", cfg.Api.Ssl.Enabled) - fmt.Fprintln(output, "Api Ssl Certificate:", redact(cfg.Api.Ssl.CertificateFile)) - fmt.Fprintln(output, "Api Ssl Key:", redact(cfg.Api.Ssl.KeyFile)) - fmt.Fprintln(output, "Sftp Address:", redact(cfg.System.Sftp.Address)) - fmt.Fprintln(output, "Sftp Port:", cfg.System.Sftp.Port) - fmt.Fprintln(output, "Sftp Read Only:", cfg.System.Sftp.ReadOnly) - fmt.Fprintln(output, "Sftp Diskchecking Disabled:", cfg.System.Sftp.DisableDiskChecking) - fmt.Fprintln(output, "System Root Directory:", cfg.System.RootDirectory) - fmt.Fprintln(output, "System Logs Directory:", cfg.System.LogDirectory) - fmt.Fprintln(output, "System Data Directory:", cfg.System.Data) - fmt.Fprintln(output, "System Archive Directory:", cfg.System.ArchiveDirectory) - fmt.Fprintln(output, "System Backup Directory:", cfg.System.BackupDirectory) - fmt.Fprintln(output, "System Username:", cfg.System.Username) - fmt.Fprintln(output, "Debug Enabled:", cfg.Debug) + fmt.Fprintln(output, " Panel Location:", redact(cfg.PanelLocation)) + fmt.Fprintln(output, "") + fmt.Fprintln(output, "Internal Webserver:", redact(cfg.Api.Host) + ":", cfg.Api.Port) + fmt.Fprintln(output, " SSL Enabled:", cfg.Api.Ssl.Enabled) + fmt.Fprintln(output, " SSL Certificate:", redact(cfg.Api.Ssl.CertificateFile)) + fmt.Fprintln(output, " SSL Key:", redact(cfg.Api.Ssl.KeyFile)) + fmt.Fprintln(output, "") + fmt.Fprintln(output, " SFTP Server:", redact(cfg.System.Sftp.Address), ":", cfg.System.Sftp.Port) + fmt.Fprintln(output, " SFTP Read-Only:", cfg.System.Sftp.ReadOnly) + fmt.Fprintln(output, "") + fmt.Fprintln(output, " Root Directory:", cfg.System.RootDirectory) + fmt.Fprintln(output, " Logs Directory:", cfg.System.LogDirectory) + fmt.Fprintln(output, " Data Directory:", cfg.System.Data) + fmt.Fprintln(output, " Archive Directory:", cfg.System.ArchiveDirectory) + fmt.Fprintln(output, " Backup Directory:", cfg.System.BackupDirectory) + fmt.Fprintln(output, "") + fmt.Fprintln(output, " Username:", cfg.System.Username) + fmt.Fprintln(output, " Server Time:", time.Now().Format(time.RFC1123Z)) + fmt.Fprintln(output, " Debug Mode:", cfg.Debug) } else { fmt.Println("Failed to load configuration.", err) } diff --git a/config/config.go b/config/config.go index 0f6c3ce..e0027f1 100644 --- a/config/config.go +++ b/config/config.go @@ -73,9 +73,6 @@ type Configuration struct { // Defines the configuration of the internal SFTP server. type SftpConfiguration struct { - // If set to true disk checking will not be performed. This will prevent the SFTP - // server from checking the total size of a directory when uploading files. - DisableDiskChecking bool `default:"false" yaml:"disable_disk_checking"` // The bind address of the SFTP server. Address string `default:"0.0.0.0" json:"bind_address" yaml:"bind_address"` // The bind port of the SFTP server.