Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/checks/commands/check_files.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Naemon Config

| Argument | Description |
| ---------------------------- | -------------------------------------------------------------------------------------- |
| add-disk-size | Add the disk size used on disk for each file and directory. On unix systems this is calculated from the allocated blocks, on windows an additional API call is required for each entry. This calculation may be expensive. Default: false |
| add-files-only-once | When following symlinks, same file can be added multiple times. Enable this to track added files and stop adding them twice. Files will be added using the first path they were encountered with. Default: false |
| calculate-subdirectory-sizes | For subdirectories that are found under the search paths, calculate the subdirectory sizes based on found files. This calculation may be expensive. Default: false |
| file | Alias for path |
Expand Down Expand Up @@ -89,13 +90,16 @@ these can be used in filters and thresholds (along with the default attributes):
| access | Unix timestamp of last access time |
| creation | Unix timestamp when file was created |
| size | File size in bytes |
| disksize | File size on disk in bytes |
| written | Unix timestamp when file was last written to |
| write | Alias for written |
| age | Seconds since file was last written |
| version | Windows exe/dll file version (windows only) |
| line_count | Number of lines in the files (text files) |
| total_bytes | Total size over all files in bytes |
| total_size | Total size over all files as human readable bytes |
| total_diskbytes | Total disk size over all files in bytes |
| total_disksize | Total disk size over all files as human readable bytes |
| md5_checksum | MD5 checksum of the file |
| sha1_checksum | SHA1 checksum of the file |
| sha256_checksum | SHA256 checksum of the file |
Expand Down
21 changes: 0 additions & 21 deletions pkg/snclient/check_drivesize_windows_vhdx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,6 @@ func hasElevatedPrivileges() bool {
return token.IsElevated()
}

// resolveLongPath expands 8.3 short names (ex.: C:\Users\RUNNER~1) to their long form.
// diskpart stores the vhd backing file path and the mount point as given, so later
// select/detach calls need to use the exact same path string.
func resolveLongPath(path string) (string, error) {
pathPtr, err := windows.UTF16PtrFromString(path)
if err != nil {
return "", fmt.Errorf("GetLongName error when creating UTF16 string pointer from path %w", err)
}
size, _ := windows.GetLongPathName(pathPtr, nil, 0)
if size == 0 {
return "", fmt.Errorf("GetLongPathName returned no size for %s", path)
}
buf := make([]uint16, size)
res, _ := windows.GetLongPathName(pathPtr, &buf[0], size)
if res == 0 {
return "", fmt.Errorf("GetLongPathName returned 0 for %s", path)
}

return windows.UTF16ToString(buf[:res]), nil
}

func execDiskpart(t *testing.T, script string) (output string, err error) {
t.Helper()

Expand Down
57 changes: 57 additions & 0 deletions pkg/snclient/check_files.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type CheckFiles struct {
calculateSubdirectorySizes bool // constructor NewCheckFiles sets this as false
followSymlinks bool
addFilesOnlyOnce bool
addDiskSize bool // constructor NewCheckFiles sets this as false
maxFiles int64 // maximum number of files
}

Expand All @@ -62,6 +63,7 @@ func NewCheckFiles() CheckHandler {
calculateSubdirectorySizes: false,
followSymlinks: CheckFilesDefaultFollowSymlinks,
addFilesOnlyOnce: CheckFilesDefaultAddFilesOnlyOnce,
addDiskSize: false,
maxFiles: CheckFilesDefaultMaxFiles,
}
}
Expand All @@ -88,6 +90,8 @@ func (l *CheckFiles) Build() *CheckData {
" Enable this to track added files and stop adding them twice. Files will be added using the first path they were encountered with. Default: %t", CheckFilesDefaultAddFilesOnlyOnce)},
"calculate-subdirectory-sizes": {value: &l.calculateSubdirectorySizes, description: "For subdirectories that are found under the search paths, " +
"calculate the subdirectory sizes based on found files. This calculation may be expensive. Default: false"},
"add-disk-size": {value: &l.addDiskSize, description: "Add the disk size used on disk for each file and directory. On unix systems this is " +
"calculated from the allocated blocks, on windows an additional API call is required for each entry. This calculation may be expensive. Default: false"},
"max-files": {value: &l.maxFiles, description: fmt.Sprintf("Maximum number of files to process. Default: %d", CheckFilesDefaultMaxFiles)},
},
detailSyntax: "%(name)",
Expand All @@ -106,13 +110,16 @@ func (l *CheckFiles) Build() *CheckData {
{name: "access", description: "Unix timestamp of last access time", unit: UDate},
{name: "creation", description: "Unix timestamp when file was created", unit: UDate},
{name: "size", description: "File size in bytes", unit: UByte},
{name: "disksize", description: "File size on disk in bytes", unit: UByte},
{name: "written", description: "Unix timestamp when file was last written to", unit: UDate},
{name: "write", description: "Alias for written", unit: UDate},
{name: "age", description: "Seconds since file was last written", unit: UDuration},
{name: "version", description: "Windows exe/dll file version (windows only)"},
{name: "line_count", description: "Number of lines in the files (text files)"},
{name: "total_bytes", description: "Total size over all files in bytes", unit: UByte},
{name: "total_size", description: "Total size over all files as human readable bytes", unit: UByte},
{name: "total_diskbytes", description: "Total disk size over all files in bytes", unit: UByte},
{name: "total_disksize", description: "Total disk size over all files as human readable bytes", unit: UByte},
{name: "md5_checksum", description: "MD5 checksum of the file"},
{name: "sha1_checksum", description: "SHA1 checksum of the file"},
{name: "sha256_checksum", description: "SHA256 checksum of the file"},
Expand Down Expand Up @@ -518,6 +525,14 @@ func (l *CheckFiles) populateEntryDetails(check *CheckData, entry map[string]str
entry["write"] = fmt.Sprintf("%d", fileInfoSys.Mtime.Unix())
entry["written"] = fmt.Sprintf("%d", fileInfoSys.Mtime.Unix())

if l.addDiskSize {
if diskSize, diskErr := getFileDiskSize(fileInfo, path); diskErr != nil {
log.Debugf("could not get disk size for %s: %s", path, diskErr.Error())
} else {
entry["disksize"] = fmt.Sprintf("%d", diskSize)
}
}

needVersion := check.HasThreshold("version") || check.HasMacro("version")
if needVersion {
version, err := getFileVersion(path)
Expand Down Expand Up @@ -623,6 +638,34 @@ func (l *CheckFiles) addGeneralMetrics(check *CheckData) {
}
}

// only calculate the total disk size when the attribute has been populated for the entries
if l.addDiskSize {
totalDiskSize := uint64(0)
for _, data := range check.listData {
if data["type"] == "file" {
totalDiskSize += convert.UInt64(data["disksize"])
}
}

if len(check.listData) > 0 || check.emptySyntax == "" {
check.details["total_diskbytes"] = fmt.Sprintf("%d", totalDiskSize)
check.details["total_disksize"] = humanize.IBytesF(convert.UInt64(totalDiskSize), 2)
}

if check.HasThreshold("total_disksize") {
check.result.Metrics = append(check.result.Metrics,
&CheckMetric{
ThresholdName: "total_disksize",
Name: "total_disksize",
Value: totalDiskSize,
Unit: "B",
Warning: check.warnThreshold,
Critical: check.critThreshold,
Min: &Zero,
})
}
}

// files do not have a 'count' atrribute, so this wont collide like 'size' would. No need for 'totalCount'
if check.HasThreshold("count") {
check.result.Metrics = append(check.result.Metrics,
Expand Down Expand Up @@ -735,8 +778,10 @@ func (l *CheckFiles) addSubDirMetrics(check *CheckData) {
}
}

//nolint:funlen // the length is ok, function is not complex
func (l *CheckFiles) addFileMetrics(check *CheckData) {
needSize := check.HasThreshold("size")
needDiskSize := l.addDiskSize && check.HasThreshold("disksize")
needAge := check.HasThreshold("age")
needAccess := check.HasThreshold("access")
needWritten := check.HasThreshold("written")
Expand All @@ -755,6 +800,18 @@ func (l *CheckFiles) addFileMetrics(check *CheckData) {
Min: &Zero,
})
}
if needDiskSize {
check.result.Metrics = append(check.result.Metrics,
&CheckMetric{
ThresholdName: "disksize",
Name: data["filename"] + " " + "disksize",
Value: convert.UInt64(data["disksize"]),
Unit: "B",
Warning: check.warnThreshold,
Critical: check.critThreshold,
Min: &Zero,
})
}
if needAge {
check.result.Metrics = append(check.result.Metrics,
&CheckMetric{
Expand Down
15 changes: 15 additions & 0 deletions pkg/snclient/check_files_helper_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,18 @@ func getFileInode(fi fs.FileInfo) (uint64, bool) {

return stat.Ino, true
}

// POSIX st_blocks is always reported in 512 byte units, this is independent of the filesystem block size
const statBlockSizeBytes = 512

func getFileDiskSize(fileInfo fs.FileInfo, _ string) (uint64, error) {
stat, ok := fileInfo.Sys().(*syscall.Stat_t)
if !ok {
return 0, fmt.Errorf("type assertion for fileInfo.Sys() failed")
}
if stat.Blocks < 0 {
return 0, fmt.Errorf("invalid negative block count: %d", stat.Blocks)
}

return uint64(stat.Blocks) * statBlockSizeBytes, nil
}
15 changes: 15 additions & 0 deletions pkg/snclient/check_files_helper_osx_bsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,18 @@ func getFileInode(fi fs.FileInfo) (uint64, bool) {

return stat.Ino, true
}

// POSIX st_blocks is always reported in 512 byte units, this is independent of the filesystem block size
const statBlockSizeBytes = 512

func getFileDiskSize(fileInfo fs.FileInfo, _ string) (uint64, error) {
stat, ok := fileInfo.Sys().(*syscall.Stat_t)
if !ok {
return 0, fmt.Errorf("type assertion for fileInfo.Sys() failed")
}
if stat.Blocks < 0 {
return 0, fmt.Errorf("invalid negative block count: %d", stat.Blocks)
}

return uint64(stat.Blocks) * statBlockSizeBytes, nil
}
Loading