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  }