fs.go
  1  // Copyright 2017 The Prometheus Authors
  2  // Licensed under the Apache License, Version 2.0 (the "License");
  3  // you may not use this file except in compliance with the License.
  4  // You may obtain a copy of the License at
  5  //
  6  // http://www.apache.org/licenses/LICENSE-2.0
  7  //
  8  // Unless required by applicable law or agreed to in writing, software
  9  // distributed under the License is distributed on an "AS IS" BASIS,
 10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 11  // See the License for the specific language governing permissions and
 12  // limitations under the License.
 13  
 14  package sysfs
 15  
 16  import (
 17  	"fmt"
 18  	"os"
 19  	"path/filepath"
 20  
 21  	"github.com/prometheus/procfs/bcache"
 22  	"github.com/prometheus/procfs/xfs"
 23  )
 24  
 25  // FS represents the pseudo-filesystem sys, which provides an interface to
 26  // kernel data structures.
 27  type FS string
 28  
 29  // DefaultMountPoint is the common mount point of the sys filesystem.
 30  const DefaultMountPoint = "/sys"
 31  
 32  // NewFS returns a new FS mounted under the given mountPoint. It will error
 33  // if the mount point can't be read.
 34  func NewFS(mountPoint string) (FS, error) {
 35  	info, err := os.Stat(mountPoint)
 36  	if err != nil {
 37  		return "", fmt.Errorf("could not read %s: %s", mountPoint, err)
 38  	}
 39  	if !info.IsDir() {
 40  		return "", fmt.Errorf("mount point %s is not a directory", mountPoint)
 41  	}
 42  
 43  	return FS(mountPoint), nil
 44  }
 45  
 46  // Path returns the path of the given subsystem relative to the sys root.
 47  func (fs FS) Path(p ...string) string {
 48  	return filepath.Join(append([]string{string(fs)}, p...)...)
 49  }
 50  
 51  // XFSStats retrieves XFS filesystem runtime statistics for each mounted XFS
 52  // filesystem.  Only available on kernel 4.4+.  On older kernels, an empty
 53  // slice of *xfs.Stats will be returned.
 54  func (fs FS) XFSStats() ([]*xfs.Stats, error) {
 55  	matches, err := filepath.Glob(fs.Path("fs/xfs/*/stats/stats"))
 56  	if err != nil {
 57  		return nil, err
 58  	}
 59  
 60  	stats := make([]*xfs.Stats, 0, len(matches))
 61  	for _, m := range matches {
 62  		f, err := os.Open(m)
 63  		if err != nil {
 64  			return nil, err
 65  		}
 66  
 67  		// "*" used in glob above indicates the name of the filesystem.
 68  		name := filepath.Base(filepath.Dir(filepath.Dir(m)))
 69  
 70  		// File must be closed after parsing, regardless of success or
 71  		// failure.  Defer is not used because of the loop.
 72  		s, err := xfs.ParseStats(f)
 73  		_ = f.Close()
 74  		if err != nil {
 75  			return nil, err
 76  		}
 77  
 78  		s.Name = name
 79  		stats = append(stats, s)
 80  	}
 81  
 82  	return stats, nil
 83  }
 84  
 85  // BcacheStats retrieves bcache runtime statistics for each bcache.
 86  func (fs FS) BcacheStats() ([]*bcache.Stats, error) {
 87  	matches, err := filepath.Glob(fs.Path("fs/bcache/*-*"))
 88  	if err != nil {
 89  		return nil, err
 90  	}
 91  
 92  	stats := make([]*bcache.Stats, 0, len(matches))
 93  	for _, uuidPath := range matches {
 94  		// "*-*" in glob above indicates the name of the bcache.
 95  		name := filepath.Base(uuidPath)
 96  
 97  		// stats
 98  		s, err := bcache.GetStats(uuidPath)
 99  		if err != nil {
100  			return nil, err
101  		}
102  
103  		s.Name = name
104  		stats = append(stats, s)
105  	}
106  
107  	return stats, nil
108  }