diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 5588ef3c..a3b83413 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -30,7 +30,6 @@ import ( "github.com/kjake/cloudflared/connection" "github.com/kjake/cloudflared/credentials" "github.com/kjake/cloudflared/diagnostic" - "github.com/kjake/cloudflared/diagnostic/network" "github.com/kjake/cloudflared/edgediscovery" "github.com/kjake/cloudflared/ingress" "github.com/kjake/cloudflared/logger" diff --git a/diagnostic/diagnostic.go b/diagnostic/diagnostic.go index 6b25d890..c8992e79 100644 --- a/diagnostic/diagnostic.go +++ b/diagnostic/diagnostic.go @@ -15,7 +15,7 @@ import ( "github.com/rs/zerolog" - "github.com/kjake/cloudflared/diagnostic/network" + network "github.com/kjake/cloudflared/diagnostic/network" ) const ( diff --git a/diagnostic/network/collector.go b/diagnostic/network/collector.go index 78bbac98..8a3a0fd9 100644 --- a/diagnostic/network/collector.go +++ b/diagnostic/network/collector.go @@ -1,4 +1,4 @@ -package network +package diagnostic import ( "context" diff --git a/diagnostic/network/collector_unix.go b/diagnostic/network/collector_unix.go index 9f5202de..ce792d6f 100644 --- a/diagnostic/network/collector_unix.go +++ b/diagnostic/network/collector_unix.go @@ -1,6 +1,6 @@ //go:build darwin || linux || freebsd || openbsd || netbsd -package network +package diagnostic import ( "context" diff --git a/diagnostic/network/collector_unix_test.go b/diagnostic/network/collector_unix_test.go index d32c6e46..ddc7e7e2 100644 --- a/diagnostic/network/collector_unix_test.go +++ b/diagnostic/network/collector_unix_test.go @@ -1,6 +1,6 @@ //go:build darwin || linux || freebsd || openbsd || netbsd -package network_test +package diagnostic_test import ( "strings" diff --git a/diagnostic/network/collector_utils.go b/diagnostic/network/collector_utils.go index e6ccb1c8..bfc27849 100644 --- a/diagnostic/network/collector_utils.go +++ b/diagnostic/network/collector_utils.go @@ -1,4 +1,4 @@ -package network +package diagnostic import ( "bufio" diff --git a/diagnostic/network/collector_windows.go b/diagnostic/network/collector_windows.go index fe02b4ba..fe91a9de 100644 --- a/diagnostic/network/collector_windows.go +++ b/diagnostic/network/collector_windows.go @@ -1,6 +1,6 @@ //go:build windows -package network +package diagnostic import ( "context" diff --git a/diagnostic/network/collector_windows_test.go b/diagnostic/network/collector_windows_test.go index 777f1e13..1e3f5a85 100644 --- a/diagnostic/network/collector_windows_test.go +++ b/diagnostic/network/collector_windows_test.go @@ -1,6 +1,6 @@ //go:build windows -package network_test +package diagnostic_test import ( "strings" diff --git a/diagnostic/system_collector_unix.go b/diagnostic/system_collector_unix.go new file mode 100644 index 00000000..ebfaa64a --- /dev/null +++ b/diagnostic/system_collector_unix.go @@ -0,0 +1,168 @@ +//go:build !darwin || !linux || freebsd || openbsd || netbsd + +package diagnostic + +import ( + "context" + "fmt" + "os/exec" + "runtime" + "strconv" + "strings" +) + +type SystemCollectorImpl struct { + version string +} + +func NewSystemCollectorImpl( + version string, +) *SystemCollectorImpl { + return &SystemCollectorImpl{ + version, + } +} + +func (collector *SystemCollectorImpl) Collect(ctx context.Context) (*SystemInformation, error) { + memoryInfo, memoryInfoRaw, memoryInfoErr := collectMemoryInformation(ctx) + fdInfo, fdInfoRaw, fdInfoErr := collectFileDescriptorInformation(ctx) + disks, disksRaw, diskErr := collectDiskVolumeInformationUnix(ctx) + osInfo, osInfoRaw, osInfoErr := collectOSInformationUnix(ctx) + + var memoryMaximum, memoryCurrent, fileDescriptorMaximum, fileDescriptorCurrent uint64 + var osSystem, name, osVersion, osRelease, architecture string + gerror := SystemInformationGeneralError{} + + if memoryInfoErr != nil { + gerror.MemoryInformationError = SystemInformationError{ + Err: memoryInfoErr, + RawInfo: memoryInfoRaw, + } + } else { + memoryMaximum = memoryInfo.MemoryMaximum + memoryCurrent = memoryInfo.MemoryCurrent + } + + if fdInfoErr != nil { + gerror.FileDescriptorsInformationError = SystemInformationError{ + Err: fdInfoErr, + RawInfo: fdInfoRaw, + } + } else { + fileDescriptorMaximum = fdInfo.FileDescriptorMaximum + fileDescriptorCurrent = fdInfo.FileDescriptorCurrent + } + + if diskErr != nil { + gerror.DiskVolumeInformationError = SystemInformationError{ + Err: diskErr, + RawInfo: disksRaw, + } + } + + if osInfoErr != nil { + gerror.OperatingSystemInformationError = SystemInformationError{ + Err: osInfoErr, + RawInfo: osInfoRaw, + } + } else { + osSystem = osInfo.OsSystem + name = osInfo.Name + osVersion = osInfo.OsVersion + osRelease = osInfo.OsRelease + architecture = osInfo.Architecture + } + + cloudflaredVersion := collector.version + info := NewSystemInformation( + memoryMaximum, + memoryCurrent, + fileDescriptorMaximum, + fileDescriptorCurrent, + osSystem, + name, + osVersion, + osRelease, + architecture, + cloudflaredVersion, + runtime.Version(), + runtime.GOARCH, + disks, + ) + + return info, gerror +} + +func collectMemoryInformation(ctx context.Context) (*MemoryInformation, string, error) { + // FreeBSD uses `sysctl` to retrieve memory information. + const ( + memTotalKey = "hw.physmem" + memAvailableKey = "vm.stats.vm.v_free_count" + ) + + command := exec.CommandContext(ctx, "sysctl", "-n", memTotalKey) + totalOutput, err := command.Output() + if err != nil { + return nil, "", fmt.Errorf("error retrieving output from command '%s': %w", command.String(), err) + } + + command = exec.CommandContext(ctx, "sysctl", "-n", memAvailableKey) + availableOutput, err := command.Output() + if err != nil { + return nil, "", fmt.Errorf("error retrieving output from command '%s': %w", command.String(), err) + } + + total, err := strconv.ParseUint(strings.TrimSpace(string(totalOutput)), 10, 64) + if err != nil { + return nil, string(totalOutput), fmt.Errorf("error parsing memory total: %w", err) + } + + available, err := strconv.ParseUint(strings.TrimSpace(string(availableOutput)), 10, 64) + if err != nil { + return nil, string(availableOutput), fmt.Errorf("error parsing memory available: %w", err) + } + + memoryInfo := &MemoryInformation{ + MemoryMaximum: total, + MemoryCurrent: available * 4096, // FreeBSD reports pages; multiply by page size (4K). + } + + return memoryInfo, fmt.Sprintf("Total: %s, Available: %s", totalOutput, availableOutput), nil +} + +func collectFileDescriptorInformation(ctx context.Context) (*FileDescriptorInformation, string, error) { + // FreeBSD uses `sysctl` for file descriptor limits. + const ( + fdMaxKey = "kern.maxfiles" + fdCurrentKey = "kern.openfiles" + ) + + command := exec.CommandContext(ctx, "sysctl", "-n", fdMaxKey) + maxOutput, err := command.Output() + if err != nil { + return nil, "", fmt.Errorf("error retrieving output from command '%s': %w", command.String(), err) + } + + command = exec.CommandContext(ctx, "sysctl", "-n", fdCurrentKey) + currentOutput, err := command.Output() + if err != nil { + return nil, "", fmt.Errorf("error retrieving output from command '%s': %w", command.String(), err) + } + + max, err := strconv.ParseUint(strings.TrimSpace(string(maxOutput)), 10, 64) + if err != nil { + return nil, string(maxOutput), fmt.Errorf("error parsing max file descriptors: %w", err) + } + + current, err := strconv.ParseUint(strings.TrimSpace(string(currentOutput)), 10, 64) + if err != nil { + return nil, string(currentOutput), fmt.Errorf("error parsing current file descriptors: %w", err) + } + + fdInfo := &FileDescriptorInformation{ + FileDescriptorMaximum: max, + FileDescriptorCurrent: current, + } + + return fdInfo, fmt.Sprintf("Max: %s, Current: %s", maxOutput, currentOutput), nil +}