From 9793a63efe8dea08260ea344444db9e76a139f9a Mon Sep 17 00:00:00 2001 From: Brian McGee Date: Tue, 13 Aug 2024 10:32:16 +0100 Subject: [PATCH] wip: capture filesystems Signed-off-by: Brian McGee --- cmd/root.go | 3 +- go.mod | 1 + go.sum | 2 + nix/packages/nixos-facter/gomod2nix.toml | 3 ++ pkg/ephem/fs.go | 60 ++++++++++++++++++++++++ pkg/facter/report.go | 8 ++++ 6 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 pkg/ephem/fs.go diff --git a/cmd/root.go b/cmd/root.go index a63f3f8..00875fd 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -86,7 +86,8 @@ func init() { // Options for optional ephemeral system properties. f.BoolVarP(&scanner.Swap, "swap", "s", false, "capture swap entries") - f.BoolVarP(&scanner.Ephemeral, "ephemeral", "e", false, "capture all ephemeral properties e.g. swap, filesystems and so on") + f.BoolVarP(&scanner.Mounts, "mounts", "m", false, "capture filesystem mounts") + f.BoolVarP(&scanner.Ephemeral, "ephemeral", "e", false, "capture all ephemeral properties e.g. swap, mounts and so on") // We currently support all probe features at a high level as they share some generic information, // but we do not have mappings for all of their detail sections. diff --git a/go.mod b/go.mod index c1f58cb..e05c46e 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect diff --git a/go.sum b/go.sum index 0553af0..38ba968 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= +github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= diff --git a/nix/packages/nixos-facter/gomod2nix.toml b/nix/packages/nixos-facter/gomod2nix.toml index c985ff5..bf139bb 100644 --- a/nix/packages/nixos-facter/gomod2nix.toml +++ b/nix/packages/nixos-facter/gomod2nix.toml @@ -43,6 +43,9 @@ schema = 3 [mod."github.com/mitchellh/mapstructure"] version = "v1.5.0" hash = "sha256-ztVhGQXs67MF8UadVvG72G3ly0ypQW0IRDdOOkjYwoE=" + [mod."github.com/moby/sys/mountinfo"] + version = "v0.7.2" + hash = "sha256-G0K/wHnhvJ+00gjjtvScvnttQYxPaDru5HniwBM0+vA=" [mod."github.com/muesli/reflow"] version = "v0.3.0" hash = "sha256-Pou2ybE9SFSZG6YfZLVV1Eyfm+X4FuVpDPLxhpn47Cc=" diff --git a/pkg/ephem/fs.go b/pkg/ephem/fs.go new file mode 100644 index 0000000..41783cb --- /dev/null +++ b/pkg/ephem/fs.go @@ -0,0 +1,60 @@ +package ephem + +import ( + "encoding/json" + "regexp" + "strconv" + "strings" + + "github.com/moby/sys/mountinfo" +) + +var specialFsRegex = regexp.MustCompile(`^(/proc|/dev|/sys|/run|/var/lib/docker|/var/lib/nfs/rpc_pipefs).*`) + +// MountInfo represents the information about a mount point. +// It is just a type alias for mountinfo.Info to allow us to add JSON marshalling. +type MountInfo struct { + mountinfo.Info +} + +func (i MountInfo) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]string{ + "id": strconv.Itoa(i.ID), + "parent_id": strconv.Itoa(i.Parent), + "major": strconv.Itoa(i.Major), + "minor": strconv.Itoa(i.Minor), + "root": i.Root, + "mount_point": i.Mountpoint, + "mount_options": i.Options, + "optional": i.Optional, + "filesystem_type": i.FSType, + "mount_source": i.Source, + "super_options": i.VFSOptions, + }) +} + +func Mounts() ([]*MountInfo, error) { + info, err := mountinfo.GetMounts(mountFilter) + if err != nil { + return nil, err + } + var result []*MountInfo + for idx := range info { + result = append(result, &MountInfo{Info: *info[idx]}) + } + return result, nil +} + +func mountFilter(info *mountinfo.Info) (skip, stop bool) { + if skip = specialFsRegex.MatchString(info.Mountpoint); skip { + return true, false + } + + // skip the read-only bind-mount on /nix/store + // https://github.com/NixOS/nixpkgs/blob/dac9cdf8c930c0af98a63cbfe8005546ba0125fb/nixos/modules/installer/tools/nixos-generate-config.pl#L395 + if info.Mountpoint == "/nix/store" && strings.Contains(info.VFSOptions, "rw") && strings.Contains(info.Options, "ro") { + return true, false + } + + return false, false +} diff --git a/pkg/facter/report.go b/pkg/facter/report.go index 97bb04c..8cdbabf 100644 --- a/pkg/facter/report.go +++ b/pkg/facter/report.go @@ -12,6 +12,7 @@ import ( type Report struct { Hardware []*hwinfo.HardwareItem `json:"hardware"` + Mounts []*ephem.MountInfo `json:"mounts,omitempty"` Smbios []hwinfo.Smbios `json:"smbios,omitempty"` Swap []*ephem.SwapEntry `json:"swap,omitempty"` System string `json:"system"` @@ -20,6 +21,7 @@ type Report struct { type Scanner struct { Swap bool + Mounts bool Ephemeral bool Features []hwinfo.ProbeFeature } @@ -48,5 +50,11 @@ func (s *Scanner) Scan() (*Report, error) { } } + if s.Ephemeral || s.Mounts { + if report.Mounts, err = ephem.Mounts(); err != nil { + return nil, fmt.Errorf("failed to detect mounts: %w", err) + } + } + return &report, nil }