158 lines
3.6 KiB
Go
158 lines
3.6 KiB
Go
package syscheck
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// GetDiskSpace returns available disk space in bytes for the root partition
|
|
func GetDiskSpace(ctx context.Context) (int64, error) {
|
|
var stat unix.Statfs_t
|
|
if err := unix.Statfs("/", &stat); err != nil {
|
|
return 0, fmt.Errorf("failed to get disk space: %w", err)
|
|
}
|
|
|
|
// Available space = Available blocks * Block size
|
|
availableSpace := int64(stat.Bavail) * int64(stat.Bsize)
|
|
return availableSpace, nil
|
|
}
|
|
|
|
// GetDiskSpeed measures disk read/write speed by writing and reading a 1GB test file
|
|
// Returns read speed and write speed in bytes per second
|
|
func GetDiskSpeed(ctx context.Context) (int64, int64, error) {
|
|
const (
|
|
testSize = 1024 * 1024 * 1024 // 1GB
|
|
bufferSize = 1024 * 1024 // 1MB buffer
|
|
)
|
|
|
|
tmpFile := "/tmp/diskspeed_test_" + strconv.FormatInt(time.Now().UnixNano(), 10)
|
|
defer func() {
|
|
// Clean up test file
|
|
_ = os.Remove(tmpFile)
|
|
}()
|
|
|
|
// Test write speed
|
|
writeSpeed, err := measureWriteSpeed(ctx, tmpFile, testSize, bufferSize)
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf("failed to measure write speed: %w", err)
|
|
}
|
|
|
|
// Test read speed
|
|
readSpeed, err := measureReadSpeed(ctx, tmpFile, bufferSize)
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf("failed to measure read speed: %w", err)
|
|
}
|
|
|
|
return readSpeed, writeSpeed, nil
|
|
}
|
|
|
|
// measureWriteSpeed writes test data to a file and measures the speed
|
|
func measureWriteSpeed(ctx context.Context, filename string, totalSize, bufferSize int64) (int64, error) {
|
|
// Create file
|
|
file, err := os.Create(filename)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to create test file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
// Prepare buffer with random data
|
|
buffer := make([]byte, bufferSize)
|
|
if _, err := rand.Read(buffer); err != nil {
|
|
return 0, fmt.Errorf("failed to generate random data: %w", err)
|
|
}
|
|
|
|
// Start timing
|
|
startTime := time.Now()
|
|
var written int64
|
|
|
|
// Write data in chunks
|
|
for written < totalSize {
|
|
// Check context cancellation
|
|
select {
|
|
case <-ctx.Done():
|
|
return 0, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
// Write buffer
|
|
n, err := file.Write(buffer)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to write data: %w", err)
|
|
}
|
|
written += int64(n)
|
|
}
|
|
|
|
// Sync to ensure data is written to disk
|
|
if err := file.Sync(); err != nil {
|
|
return 0, fmt.Errorf("failed to sync file: %w", err)
|
|
}
|
|
|
|
// Calculate speed
|
|
duration := time.Since(startTime)
|
|
speed := int64(float64(written) / duration.Seconds())
|
|
|
|
return speed, nil
|
|
}
|
|
|
|
// measureReadSpeed reads the test file and measures the speed
|
|
func measureReadSpeed(ctx context.Context, filename string, bufferSize int64) (int64, error) {
|
|
// Open file
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to open test file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
// Get file size
|
|
fileInfo, err := file.Stat()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to stat file: %w", err)
|
|
}
|
|
totalSize := fileInfo.Size()
|
|
|
|
// Prepare buffer
|
|
buffer := make([]byte, bufferSize)
|
|
|
|
// Start timing
|
|
startTime := time.Now()
|
|
var totalRead int64
|
|
|
|
// Read data in chunks
|
|
for {
|
|
// Check context cancellation
|
|
select {
|
|
case <-ctx.Done():
|
|
return 0, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
// Read buffer
|
|
n, err := file.Read(buffer)
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to read data: %w", err)
|
|
}
|
|
totalRead += int64(n)
|
|
}
|
|
|
|
// Calculate speed
|
|
duration := time.Since(startTime)
|
|
speed := int64(float64(totalRead) / duration.Seconds())
|
|
|
|
// Verify we read the entire file
|
|
if totalRead != totalSize {
|
|
return 0, fmt.Errorf("read size mismatch: expected %d, got %d", totalSize, totalRead)
|
|
}
|
|
|
|
return speed, nil
|
|
}
|