gitlab.com/apertussolutions/u-root@v7.0.0+incompatible/cmds/core/free/free.go (about)

     1  // Copyright 2012-2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // free reports usage information for physical memory and swap space.
     6  //
     7  // Synopsis:
     8  //     free [-k] [-m] [-g] [-t] [-h] [-json]
     9  //
    10  // Description:
    11  //     Read memory information from /proc/meminfo and display a summary for
    12  //     physical memory and swap space. The unit options use powers of 1024.
    13  //
    14  // Options:
    15  //     -k: display the values in kibibytes
    16  //     -m: display the values in mebibytes
    17  //     -g: display the values in gibibytes
    18  //     -t: display the values in tebibytes
    19  //     -h: display the values in human-readable form
    20  //     -json: use JSON output
    21  package main
    22  
    23  import (
    24  	"encoding/json"
    25  	"flag"
    26  	"fmt"
    27  	"log"
    28  )
    29  
    30  var (
    31  	humanOutput = flag.Bool("h", false, "Human output: show automatically the shortest three-digits unit")
    32  	inBytes     = flag.Bool("b", false, "Express the values in bytes")
    33  	inKB        = flag.Bool("k", false, "Express the values in kibibytes (default)")
    34  	inMB        = flag.Bool("m", false, "Express the values in mebibytes")
    35  	inGB        = flag.Bool("g", false, "Express the values in gibibytes")
    36  	inTB        = flag.Bool("t", false, "Express the values in tebibytes")
    37  	toJSON      = flag.Bool("json", false, "Use JSON for output")
    38  )
    39  
    40  type unit uint
    41  
    42  const (
    43  	// B is bytes
    44  	B unit = 0
    45  	// KB is kibibytes
    46  	KB = 10
    47  	// MB is mebibytes
    48  	MB = 20
    49  	// GB is gibibytes
    50  	GB = 30
    51  	// TB is tebibytes
    52  	TB = 40
    53  )
    54  
    55  var units = [...]string{"B", "K", "M", "G", "T"}
    56  
    57  // FreeConfig is a structure used to configure the behaviour of Free()
    58  type FreeConfig struct {
    59  	Unit        unit
    60  	HumanOutput bool
    61  	ToJSON      bool
    62  }
    63  
    64  // the following types are used for JSON serialization
    65  type mainMemInfo struct {
    66  	Total     uint64 `json:"total"`
    67  	Used      uint64 `json:"used"`
    68  	Free      uint64 `json:"free"`
    69  	Shared    uint64 `json:"shared"`
    70  	Cached    uint64 `json:"cached"`
    71  	Buffers   uint64 `json:"buffers"`
    72  	Available uint64 `json:"available"`
    73  }
    74  
    75  type swapInfo struct {
    76  	Total uint64 `json:"total"`
    77  	Used  uint64 `json:"used"`
    78  	Free  uint64 `json:"free"`
    79  }
    80  
    81  // MemInfo represents the main memory and swap space information in a structured
    82  // manner, suitable for JSON encoding.
    83  type MemInfo struct {
    84  	Mem  mainMemInfo `json:"mem"`
    85  	Swap swapInfo    `json:"swap"`
    86  }
    87  
    88  type meminfomap map[string]uint64
    89  
    90  // missingRequiredFields checks if any of the specified fields are present in
    91  // the input map.
    92  func missingRequiredFields(m meminfomap, fields []string) bool {
    93  	for _, f := range fields {
    94  		if _, ok := m[f]; !ok {
    95  			log.Printf("Missing field '%v'", f)
    96  			return true
    97  		}
    98  	}
    99  	return false
   100  }
   101  
   102  // humanReadableValue returns a string representing the input value, treated as
   103  // a size in bytes, interpreted in a human readable form. E.g. the number 10240
   104  // woud return the string "10 kB". Note that the decimal part is truncated, not
   105  // rounded, so the values are guaranteed to be "at least X"
   106  func humanReadableValue(value uint64) string {
   107  	v := value
   108  	// bits to shift. 0 means bytes, 10 means kB, and so on. 40 is the highest
   109  	// and it means tB
   110  	var shift uint
   111  	for {
   112  		if shift >= uint(len(units)*10) {
   113  			// 4 means tebibyte, we don't go further
   114  			break
   115  		}
   116  		if v/1024 < 1 {
   117  			break
   118  		}
   119  		v /= 1024
   120  		shift += 10
   121  	}
   122  	var decimal uint64
   123  	if shift > 0 {
   124  		// no rounding. Is there a better way to do this?
   125  		decimal = ((value - (value >> shift << shift)) >> (shift - 10)) * 1000 / 1024 / 100
   126  	}
   127  	return fmt.Sprintf("%v.%v%v",
   128  		value>>shift,
   129  		decimal,
   130  		units[shift/10],
   131  	)
   132  }
   133  
   134  // formatValueByConfig formats a size in bytes in the appropriate unit,
   135  // depending on whether FreeConfig specifies a human-readable format or a
   136  // specific unit
   137  func formatValueByConfig(value uint64, config *FreeConfig) string {
   138  	if config.HumanOutput {
   139  		return humanReadableValue(value)
   140  	}
   141  	// units and decimal part are not printed when a unit is explicitly specified
   142  	return fmt.Sprintf("%v", value>>config.Unit)
   143  }
   144  
   145  // Free prints physical memory and swap space information. The fields will be
   146  // expressed with the specified unit (e.g. KB, MB)
   147  func Free(config *FreeConfig) error {
   148  	m, err := meminfo()
   149  	if err != nil {
   150  		log.Fatal(err)
   151  	}
   152  	mmi, err := getMainMemInfo(m, config)
   153  	if err != nil {
   154  		return err
   155  	}
   156  	si, err := getSwapInfo(m, config)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	mi := MemInfo{Mem: *mmi, Swap: *si}
   161  	if config.ToJSON {
   162  		jsonData, err := json.Marshal(mi)
   163  		if err != nil {
   164  			return err
   165  		}
   166  		fmt.Println(string(jsonData))
   167  	} else {
   168  		fmt.Printf("              total        used        free      shared  buff/cache   available\n")
   169  		fmt.Printf("%-7s %11v %11v %11v %11v %11v %11v\n",
   170  			"Mem:",
   171  			formatValueByConfig(mmi.Total, config),
   172  			formatValueByConfig(mmi.Used, config),
   173  			formatValueByConfig(mmi.Free, config),
   174  			formatValueByConfig(mmi.Shared, config),
   175  			formatValueByConfig(mmi.Buffers+mmi.Cached, config),
   176  			formatValueByConfig(mmi.Available, config),
   177  		)
   178  		fmt.Printf("%-7s %11v %11v %11v\n",
   179  			"Swap:",
   180  			formatValueByConfig(si.Total, config),
   181  			formatValueByConfig(si.Used, config),
   182  			formatValueByConfig(si.Free, config),
   183  		)
   184  	}
   185  	return nil
   186  }
   187  
   188  // validateUnits checks that only one option of -b, -k, -m, -g, -t or -h has been
   189  // specified on the command line
   190  func validateUnits() bool {
   191  	count := 0
   192  	if *inBytes {
   193  		count++
   194  	}
   195  	if *inKB {
   196  		count++
   197  	}
   198  	if *inMB {
   199  		count++
   200  	}
   201  	if *inGB {
   202  		count++
   203  	}
   204  	if *inTB {
   205  		count++
   206  	}
   207  	if *humanOutput {
   208  		count++
   209  	}
   210  	if count > 1 {
   211  		return false
   212  	}
   213  	return true
   214  }
   215  
   216  func main() {
   217  	flag.Parse()
   218  	if !validateUnits() {
   219  		log.Fatal("Options -k, -m, -g, -t and -h are mutually exclusive")
   220  	}
   221  	config := FreeConfig{ToJSON: *toJSON}
   222  	if *humanOutput {
   223  		config.HumanOutput = true
   224  	} else {
   225  		switch {
   226  		case *inBytes:
   227  			config.Unit = B
   228  		case *inKB:
   229  			config.Unit = KB
   230  		case *inMB:
   231  			config.Unit = MB
   232  		case *inGB:
   233  			config.Unit = GB
   234  		case *inTB:
   235  			config.Unit = TB
   236  		}
   237  	}
   238  
   239  	if err := Free(&config); err != nil {
   240  		log.Fatal(err)
   241  	}
   242  }