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 }