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