github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/tests/inspect/inspect.go (about) 1 // Copyright 2015 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "bufio" 19 "bytes" 20 "flag" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "sort" 26 "strconv" 27 "strings" 28 "syscall" 29 "time" 30 "unsafe" 31 32 "github.com/coreos/rkt/common/cgroup" 33 "github.com/coreos/rkt/tests/testutils" 34 "github.com/syndtr/gocapability/capability" 35 ) 36 37 var ( 38 globalFlagset = flag.NewFlagSet("inspect", flag.ExitOnError) 39 globalFlags = struct { 40 ReadStdin bool 41 CheckTty bool 42 PrintExec bool 43 PrintMsg string 44 PrintEnv string 45 PrintCapsPid int 46 PrintUser bool 47 PrintGroups bool 48 CheckCwd string 49 ExitCode int 50 ReadFile bool 51 WriteFile bool 52 StatFile bool 53 Sleep int 54 PreSleep int 55 PrintMemoryLimit bool 56 PrintCPUQuota bool 57 FileName string 58 Content string 59 CheckCgroupMounts bool 60 PrintNetNS bool 61 PrintIPv4 string 62 PrintIPv6 string 63 PrintDefaultGWv4 bool 64 PrintDefaultGWv6 bool 65 PrintGWv4 string 66 PrintGWv6 string 67 GetHTTP string 68 ServeHTTP string 69 ServeHTTPTimeout int 70 PrintIfaceCount bool 71 PrintAppAnnotation string 72 }{} 73 ) 74 75 func init() { 76 globalFlagset.BoolVar(&globalFlags.ReadStdin, "read-stdin", false, "Read a line from stdin") 77 globalFlagset.BoolVar(&globalFlags.CheckTty, "check-tty", false, "Check if stdin is a terminal") 78 globalFlagset.BoolVar(&globalFlags.PrintExec, "print-exec", false, "Print the command we were execed as (i.e. argv[0])") 79 globalFlagset.StringVar(&globalFlags.PrintMsg, "print-msg", "", "Print the message given as parameter") 80 globalFlagset.StringVar(&globalFlags.CheckCwd, "check-cwd", "", "Check if the current working directory is the one specified") 81 globalFlagset.StringVar(&globalFlags.PrintEnv, "print-env", "", "Print the specified environment variable") 82 globalFlagset.IntVar(&globalFlags.PrintCapsPid, "print-caps-pid", -1, "Print capabilities of the specified pid (or current process if pid=0)") 83 globalFlagset.BoolVar(&globalFlags.PrintUser, "print-user", false, "Print uid and gid") 84 globalFlagset.BoolVar(&globalFlags.PrintGroups, "print-groups", false, "Print all gids") 85 globalFlagset.IntVar(&globalFlags.ExitCode, "exit-code", 0, "Return this exit code") 86 globalFlagset.BoolVar(&globalFlags.ReadFile, "read-file", false, "Print the content of the file $FILE") 87 globalFlagset.BoolVar(&globalFlags.WriteFile, "write-file", false, "Write $CONTENT in the file $FILE") 88 globalFlagset.BoolVar(&globalFlags.StatFile, "stat-file", false, "Print the ownership and mode of the file $FILE") 89 globalFlagset.IntVar(&globalFlags.Sleep, "sleep", -1, "Sleep before exiting (in seconds)") 90 globalFlagset.IntVar(&globalFlags.PreSleep, "pre-sleep", -1, "Sleep before executing (in seconds)") 91 globalFlagset.BoolVar(&globalFlags.PrintMemoryLimit, "print-memorylimit", false, "Print cgroup memory limit") 92 globalFlagset.BoolVar(&globalFlags.PrintCPUQuota, "print-cpuquota", false, "Print cgroup cpu quota in milli-cores") 93 globalFlagset.StringVar(&globalFlags.FileName, "file-name", "", "The file to read/write, $FILE will be ignored if this is specified") 94 globalFlagset.StringVar(&globalFlags.Content, "content", "", "The content to write, $CONTENT will be ignored if this is specified") 95 globalFlagset.BoolVar(&globalFlags.CheckCgroupMounts, "check-cgroups", false, "Try to write to the cgroup filesystem. Everything should be RO except some well-known files") 96 globalFlagset.BoolVar(&globalFlags.PrintNetNS, "print-netns", false, "Print the network namespace") 97 globalFlagset.StringVar(&globalFlags.PrintIPv4, "print-ipv4", "", "Takes an interface name and prints its IPv4") 98 globalFlagset.StringVar(&globalFlags.PrintIPv6, "print-ipv6", "", "Takes an interface name and prints its IPv6") 99 globalFlagset.BoolVar(&globalFlags.PrintDefaultGWv4, "print-defaultgwv4", false, "Print the default IPv4 gateway") 100 globalFlagset.BoolVar(&globalFlags.PrintDefaultGWv6, "print-defaultgwv6", false, "Print the default IPv6 gateway") 101 globalFlagset.StringVar(&globalFlags.PrintGWv4, "print-gwv4", "", "Takes an interface name and prints its gateway's IPv4") 102 globalFlagset.StringVar(&globalFlags.PrintGWv6, "print-gwv6", "", "Takes an interface name and prints its gateway's IPv6") 103 globalFlagset.StringVar(&globalFlags.GetHTTP, "get-http", "", "HTTP-Get from the given address") 104 globalFlagset.StringVar(&globalFlags.ServeHTTP, "serve-http", "", "Serve the hostname via HTTP on the given address:port") 105 globalFlagset.IntVar(&globalFlags.ServeHTTPTimeout, "serve-http-timeout", 30, "HTTP Timeout to wait for a client connection") 106 globalFlagset.BoolVar(&globalFlags.PrintIfaceCount, "print-iface-count", false, "Print the interface count") 107 globalFlagset.StringVar(&globalFlags.PrintAppAnnotation, "print-app-annotation", "", "Take an annotation name of the app, and prints its value") 108 } 109 110 func in(list []int, el int) bool { 111 for _, x := range list { 112 if el == x { 113 return true 114 } 115 } 116 return false 117 } 118 119 func main() { 120 globalFlagset.Parse(os.Args[1:]) 121 args := globalFlagset.Args() 122 if len(args) > 0 { 123 fmt.Fprintln(os.Stderr, "Wrong parameters") 124 os.Exit(1) 125 } 126 127 if globalFlags.PreSleep >= 0 { 128 time.Sleep(time.Duration(globalFlags.PreSleep) * time.Second) 129 } 130 131 if globalFlags.ReadStdin { 132 reader := bufio.NewReader(os.Stdin) 133 fmt.Printf("Enter text:\n") 134 text, _ := reader.ReadString('\n') 135 fmt.Printf("Received text: %s\n", text) 136 } 137 138 if globalFlags.CheckTty { 139 fd := int(os.Stdin.Fd()) 140 var termios syscall.Termios 141 _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TCGETS, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) 142 if err == 0 { 143 fmt.Printf("stdin is a terminal\n") 144 } else { 145 fmt.Printf("stdin is not a terminal\n") 146 } 147 } 148 149 if globalFlags.PrintExec { 150 fmt.Fprintf(os.Stdout, "inspect execed as: %s\n", os.Args[0]) 151 } 152 153 if globalFlags.PrintMsg != "" { 154 fmt.Fprintf(os.Stdout, "%s\n", globalFlags.PrintMsg) 155 messageLoopStr := os.Getenv("MESSAGE_LOOP") 156 messageLoop, err := strconv.Atoi(messageLoopStr) 157 if err == nil { 158 for i := 0; i < messageLoop; i++ { 159 time.Sleep(time.Second) 160 fmt.Fprintf(os.Stdout, "%s\n", globalFlags.PrintMsg) 161 } 162 } 163 } 164 165 if globalFlags.PrintEnv != "" { 166 fmt.Fprintf(os.Stdout, "%s=%s\n", globalFlags.PrintEnv, os.Getenv(globalFlags.PrintEnv)) 167 } 168 169 if globalFlags.PrintCapsPid >= 0 { 170 caps, err := capability.NewPid(globalFlags.PrintCapsPid) 171 if err != nil { 172 fmt.Fprintf(os.Stderr, "Cannot get caps: %v\n", err) 173 os.Exit(1) 174 } 175 fmt.Printf("Capability set: effective: %s\n", caps.StringCap(capability.EFFECTIVE)) 176 fmt.Printf("Capability set: permitted: %s\n", caps.StringCap(capability.PERMITTED)) 177 fmt.Printf("Capability set: inheritable: %s\n", caps.StringCap(capability.INHERITABLE)) 178 fmt.Printf("Capability set: bounding: %s\n", caps.StringCap(capability.BOUNDING)) 179 180 if capStr := os.Getenv("CAPABILITY"); capStr != "" { 181 capInt, err := strconv.Atoi(capStr) 182 if err != nil { 183 fmt.Fprintf(os.Stderr, "Environment variable $CAPABILITY is not a valid capability number: %v\n", err) 184 os.Exit(1) 185 } 186 c := capability.Cap(capInt) 187 if caps.Get(capability.BOUNDING, c) { 188 fmt.Printf("%v=enabled\n", c.String()) 189 } else { 190 fmt.Printf("%v=disabled\n", c.String()) 191 } 192 } 193 } 194 195 if globalFlags.PrintUser { 196 fmt.Printf("User: uid=%d euid=%d gid=%d egid=%d\n", os.Getuid(), os.Geteuid(), os.Getgid(), os.Getegid()) 197 } 198 199 if globalFlags.PrintGroups { 200 gids, err := os.Getgroups() 201 if err != nil { 202 fmt.Fprintf(os.Stderr, "Error getting groups: %v\n", err) 203 os.Exit(1) 204 } 205 // getgroups(2): It is unspecified whether the effective group ID of 206 // the calling process is included in the returned list. (Thus, an 207 // application should also call getegid(2) and add or remove the 208 // resulting value.) 209 egid := os.Getegid() 210 if !in(gids, egid) { 211 gids = append(gids, egid) 212 sort.Ints(gids) 213 } 214 var b bytes.Buffer 215 for _, gid := range gids { 216 b.WriteString(fmt.Sprintf("%d ", gid)) 217 } 218 fmt.Printf("Groups: %s\n", b.String()) 219 } 220 221 if globalFlags.WriteFile { 222 fileName := os.Getenv("FILE") 223 if globalFlags.FileName != "" { 224 fileName = globalFlags.FileName 225 } 226 content := os.Getenv("CONTENT") 227 if globalFlags.Content != "" { 228 content = globalFlags.Content 229 } 230 231 err := ioutil.WriteFile(fileName, []byte(content), 0600) 232 if err != nil { 233 fmt.Fprintf(os.Stderr, "Cannot write to file %q: %v\n", fileName, err) 234 os.Exit(1) 235 } 236 } 237 238 if globalFlags.ReadFile { 239 fileName := os.Getenv("FILE") 240 if globalFlags.FileName != "" { 241 fileName = globalFlags.FileName 242 } 243 244 dat, err := ioutil.ReadFile(fileName) 245 if err != nil { 246 fmt.Fprintf(os.Stderr, "Cannot read file %q: %v\n", fileName, err) 247 os.Exit(1) 248 } 249 fmt.Print("<<<") 250 fmt.Print(string(dat)) 251 fmt.Print(">>>\n") 252 } 253 254 if globalFlags.StatFile { 255 fileName := os.Getenv("FILE") 256 if globalFlags.FileName != "" { 257 fileName = globalFlags.FileName 258 } 259 260 fi, err := os.Stat(fileName) 261 if err != nil { 262 fmt.Fprintf(os.Stderr, "Cannot stat file %q: %v\n", fileName, err) 263 os.Exit(1) 264 } 265 fmt.Printf("%s: mode: %s\n", fileName, fi.Mode().String()) 266 fmt.Printf("%s: user: %v\n", fileName, fi.Sys().(*syscall.Stat_t).Uid) 267 fmt.Printf("%s: group: %v\n", fileName, fi.Sys().(*syscall.Stat_t).Gid) 268 } 269 270 if globalFlags.CheckCwd != "" { 271 wd, err := os.Getwd() 272 if err != nil { 273 fmt.Fprintf(os.Stderr, "Cannot get working directory: %v\n", err) 274 os.Exit(1) 275 } 276 if wd != globalFlags.CheckCwd { 277 fmt.Fprintf(os.Stderr, "Working directory: %q. Expected: %q.\n", wd, globalFlags.CheckCwd) 278 os.Exit(1) 279 } 280 } 281 282 if globalFlags.Sleep >= 0 { 283 time.Sleep(time.Duration(globalFlags.Sleep) * time.Second) 284 } 285 286 if globalFlags.PrintMemoryLimit { 287 memCgroupPath, err := cgroup.GetOwnCgroupPath("memory") 288 if err != nil { 289 fmt.Fprintf(os.Stderr, "Error getting own memory cgroup path: %v\n", err) 290 os.Exit(1) 291 } 292 // we use /proc/1/root to escape the chroot we're in and read our 293 // memory limit 294 limitPath := filepath.Join("/proc/1/root/sys/fs/cgroup/memory", memCgroupPath, "memory.limit_in_bytes") 295 limit, err := ioutil.ReadFile(limitPath) 296 if err != nil { 297 fmt.Fprintf(os.Stderr, "Can't read memory.limit_in_bytes\n") 298 os.Exit(1) 299 } 300 301 fmt.Printf("Memory Limit: %s\n", string(limit)) 302 } 303 304 if globalFlags.PrintCPUQuota { 305 cpuCgroupPath, err := cgroup.GetOwnCgroupPath("cpu") 306 if err != nil { 307 fmt.Fprintf(os.Stderr, "Error getting own cpu cgroup path: %v\n", err) 308 os.Exit(1) 309 } 310 // we use /proc/1/root to escape the chroot we're in and read our 311 // cpu quota 312 periodPath := filepath.Join("/proc/1/root/sys/fs/cgroup/cpu", cpuCgroupPath, "cpu.cfs_period_us") 313 periodBytes, err := ioutil.ReadFile(periodPath) 314 if err != nil { 315 fmt.Fprintf(os.Stderr, "Can't read cpu.cpu_period_us\n") 316 os.Exit(1) 317 } 318 quotaPath := filepath.Join("/proc/1/root/sys/fs/cgroup/cpu", cpuCgroupPath, "cpu.cfs_quota_us") 319 quotaBytes, err := ioutil.ReadFile(quotaPath) 320 if err != nil { 321 fmt.Fprintf(os.Stderr, "Can't read cpu.cpu_quota_us\n") 322 os.Exit(1) 323 } 324 325 period, err := strconv.Atoi(strings.Trim(string(periodBytes), "\n")) 326 if err != nil { 327 fmt.Fprintf(os.Stderr, "%v\n", err) 328 os.Exit(1) 329 } 330 quota, err := strconv.Atoi(strings.Trim(string(quotaBytes), "\n")) 331 if err != nil { 332 fmt.Fprintf(os.Stderr, "%v\n", err) 333 os.Exit(1) 334 } 335 336 quotaMilliCores := quota * 1000 / period 337 fmt.Printf("CPU Quota: %s\n", strconv.Itoa(quotaMilliCores)) 338 } 339 340 if globalFlags.CheckCgroupMounts { 341 rootCgroupPath := "/proc/1/root/sys/fs/cgroup" 342 testPaths := []string{rootCgroupPath} 343 344 // test a couple of controllers if they're available 345 if cgroup.IsIsolatorSupported("memory") { 346 testPaths = append(testPaths, filepath.Join(rootCgroupPath, "memory")) 347 } 348 if cgroup.IsIsolatorSupported("cpu") { 349 testPaths = append(testPaths, filepath.Join(rootCgroupPath, "cpu")) 350 } 351 352 for _, p := range testPaths { 353 if err := syscall.Mkdir(filepath.Join(p, "test"), 0600); err == nil || err != syscall.EROFS { 354 fmt.Println("check-cgroups: FAIL") 355 os.Exit(1) 356 } 357 } 358 359 fmt.Println("check-cgroups: SUCCESS") 360 } 361 362 if globalFlags.PrintNetNS { 363 ns, err := os.Readlink("/proc/self/ns/net") 364 if err != nil { 365 fmt.Fprintf(os.Stderr, "%v\n", err) 366 os.Exit(1) 367 } 368 fmt.Printf("NetNS: %s\n", ns) 369 } 370 371 if globalFlags.PrintIPv4 != "" { 372 iface := globalFlags.PrintIPv4 373 ips, err := testutils.GetIPsv4(iface) 374 if err != nil { 375 fmt.Fprintf(os.Stderr, "%v\n", err) 376 os.Exit(1) 377 } 378 fmt.Printf("%v IPv4: %s\n", iface, ips[0]) 379 } 380 381 if globalFlags.PrintDefaultGWv4 { 382 gw, err := testutils.GetDefaultGWv4() 383 if err != nil { 384 fmt.Fprintf(os.Stderr, "%v\n", err) 385 os.Exit(1) 386 } 387 fmt.Printf("DefaultGWv4: %s\n", gw) 388 } 389 390 if globalFlags.PrintDefaultGWv6 { 391 gw, err := testutils.GetDefaultGWv6() 392 if err != nil { 393 fmt.Fprintf(os.Stderr, "%v\n", err) 394 os.Exit(1) 395 } 396 fmt.Printf("DefaultGWv6: %s\n", gw) 397 } 398 399 if globalFlags.PrintGWv4 != "" { 400 // TODO: GetGW not implemented yet 401 iface := globalFlags.PrintGWv4 402 gw, err := testutils.GetGWv4(iface) 403 if err != nil { 404 fmt.Fprintf(os.Stderr, "%v\n", err) 405 os.Exit(1) 406 } 407 fmt.Printf("%v GWv4: %s\n", iface, gw) 408 } 409 410 if globalFlags.PrintIPv6 != "" { 411 // TODO 412 } 413 414 if globalFlags.PrintGWv6 != "" { 415 // TODO 416 } 417 418 if globalFlags.ServeHTTP != "" { 419 err := testutils.HTTPServe(globalFlags.ServeHTTP, globalFlags.ServeHTTPTimeout) 420 if err != nil { 421 fmt.Fprintf(os.Stderr, "%v\n", err) 422 os.Exit(1) 423 } 424 } 425 426 if globalFlags.GetHTTP != "" { 427 body, err := testutils.HTTPGet(globalFlags.GetHTTP) 428 if err != nil { 429 fmt.Fprintf(os.Stderr, "%v\n", err) 430 os.Exit(1) 431 } 432 fmt.Printf("HTTP-Get received: %s\n", body) 433 } 434 435 if globalFlags.PrintIfaceCount { 436 ifaceCount, err := testutils.GetIfaceCount() 437 if err != nil { 438 fmt.Fprintf(os.Stderr, "%v\n", err) 439 os.Exit(1) 440 } 441 fmt.Printf("Interface count: %d\n", ifaceCount) 442 } 443 444 if globalFlags.PrintAppAnnotation != "" { 445 mdsUrl, appName := os.Getenv("AC_METADATA_URL"), os.Getenv("AC_APP_NAME") 446 body, err := testutils.HTTPGet(fmt.Sprintf("%s/acMetadata/v1/apps/%s/annotations/%s", mdsUrl, appName, globalFlags.PrintAppAnnotation)) 447 if err != nil { 448 fmt.Fprintf(os.Stderr, "%v\n", err) 449 os.Exit(1) 450 } 451 fmt.Printf("Annotation %s=%s\n", globalFlags.PrintAppAnnotation, body) 452 } 453 454 os.Exit(globalFlags.ExitCode) 455 }