gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/cmd/test_app/main.go (about)

     1  // Copyright 2018 The gVisor 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  // Binary test_app is like a swiss knife for tests that need to run anything
    16  // inside the sandbox. New functionality can be added with new commands.
    17  package main
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"log"
    25  	"math/rand"
    26  	"net"
    27  	"os"
    28  	"os/exec"
    29  	"path/filepath"
    30  	"regexp"
    31  	"strconv"
    32  	"strings"
    33  	sys "syscall"
    34  	"time"
    35  
    36  	"github.com/google/subcommands"
    37  	"github.com/kr/pty"
    38  	"gvisor.dev/gvisor/pkg/test/testutil"
    39  	"gvisor.dev/gvisor/runsc/flag"
    40  )
    41  
    42  func main() {
    43  	subcommands.Register(subcommands.HelpCommand(), "")
    44  	subcommands.Register(subcommands.FlagsCommand(), "")
    45  	subcommands.Register(new(capability), "")
    46  	subcommands.Register(new(fdReceiver), "")
    47  	subcommands.Register(new(fdSender), "")
    48  	subcommands.Register(new(forkBomb), "")
    49  	subcommands.Register(new(fsTreeCreator), "")
    50  	subcommands.Register(new(ptyRunner), "")
    51  	subcommands.Register(new(reaper), "")
    52  	subcommands.Register(new(syscall), "")
    53  	subcommands.Register(new(taskTree), "")
    54  	subcommands.Register(new(uds), "")
    55  	subcommands.Register(new(zombieTest), "")
    56  
    57  	flag.Parse()
    58  
    59  	exitCode := subcommands.Execute(context.Background())
    60  	os.Exit(int(exitCode))
    61  }
    62  
    63  type fsTreeCreator struct {
    64  	depth            uint
    65  	numFilesPerLevel uint
    66  	fileSize         uint
    67  	targetDir        string
    68  	createSymlink    bool
    69  }
    70  
    71  // Name implements subcommands.Command.Name.
    72  func (*fsTreeCreator) Name() string {
    73  	return "fsTreeCreate"
    74  }
    75  
    76  // Synopsis implements subcommands.Command.Synopsys.
    77  func (*fsTreeCreator) Synopsis() string {
    78  	return "creates a filesystem tree of a certain depth, with a certain number of files on each level and each file with a certain size and type, under a certain directory. Some randomization is added on top of this"
    79  }
    80  
    81  // Usage implements subcommands.Command.Usage.
    82  func (*fsTreeCreator) Usage() string {
    83  	return "fsTreeCreate <flags>"
    84  }
    85  
    86  // SetFlags implements subcommands.Command.SetFlags.
    87  func (c *fsTreeCreator) SetFlags(f *flag.FlagSet) {
    88  	f.UintVar(&c.depth, "depth", 10, "number of levels to create")
    89  	f.UintVar(&c.numFilesPerLevel, "file-per-level", 10, "number of files to create per level")
    90  	f.UintVar(&c.fileSize, "file-size", 4096, "size of each file")
    91  	f.StringVar(&c.targetDir, "target-dir", "/", "directory under which to create the filesystem tree")
    92  	f.BoolVar(&c.createSymlink, "create-symlink", false, "create symlinks other than the first file per level")
    93  }
    94  
    95  // Execute implements subcommands.Command.Execute.
    96  func (c *fsTreeCreator) Execute(ctx context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
    97  	depth := c.depth + uint(rand.Uint32())%c.depth
    98  	numFilesPerLevel := c.numFilesPerLevel + uint(rand.Uint32())%c.numFilesPerLevel
    99  	fileSize := c.fileSize + uint(rand.Uint32())%c.fileSize
   100  	curDir := c.targetDir
   101  	if _, err := os.Stat(curDir); os.IsNotExist(err) {
   102  		if err := os.MkdirAll(curDir, 0777); err != nil {
   103  			log.Fatalf("error creating directory %q: %v", curDir, err)
   104  		}
   105  	}
   106  
   107  	data := make([]byte, fileSize)
   108  	rand.Read(data)
   109  	for i := uint(0); i < depth; i++ {
   110  		for j := uint(0); j < numFilesPerLevel; j++ {
   111  			filePath := filepath.Join(curDir, fmt.Sprintf("file%d", j))
   112  			if c.createSymlink && j > 0 {
   113  				if err := os.Symlink("file0", filePath); err != nil {
   114  					log.Fatalf("error creating symlink %q: %v", filePath, err)
   115  				}
   116  			} else {
   117  				if err := os.WriteFile(filePath, data, 0666); err != nil {
   118  					log.Fatalf("error writing file %q: %v", filePath, err)
   119  				}
   120  			}
   121  		}
   122  		nextDir := filepath.Join(curDir, "dir")
   123  		if err := os.Mkdir(nextDir, 0777); err != nil {
   124  			log.Fatalf("error creating directory %q: %v", nextDir, err)
   125  		}
   126  		curDir = nextDir
   127  	}
   128  	return subcommands.ExitSuccess
   129  }
   130  
   131  type uds struct {
   132  	fileName   string
   133  	socketPath string
   134  }
   135  
   136  // Name implements subcommands.Command.Name.
   137  func (*uds) Name() string {
   138  	return "uds"
   139  }
   140  
   141  // Synopsis implements subcommands.Command.Synopsys.
   142  func (*uds) Synopsis() string {
   143  	return "creates unix domain socket client and server. Client sends a contant flow of sequential numbers. Server prints them to --file"
   144  }
   145  
   146  // Usage implements subcommands.Command.Usage.
   147  func (*uds) Usage() string {
   148  	return "uds <flags>"
   149  }
   150  
   151  // SetFlags implements subcommands.Command.SetFlags.
   152  func (c *uds) SetFlags(f *flag.FlagSet) {
   153  	f.StringVar(&c.fileName, "file", "", "name of output file")
   154  	f.StringVar(&c.socketPath, "socket", "", "path to socket")
   155  }
   156  
   157  // Execute implements subcommands.Command.Execute.
   158  func (c *uds) Execute(ctx context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
   159  	if c.fileName == "" || c.socketPath == "" {
   160  		log.Fatalf("Flags cannot be empty, given: fileName: %q, socketPath: %q", c.fileName, c.socketPath)
   161  		return subcommands.ExitFailure
   162  	}
   163  	outputFile, err := os.OpenFile(c.fileName, os.O_WRONLY|os.O_CREATE, 0666)
   164  	if err != nil {
   165  		log.Fatal("error opening output file:", err)
   166  	}
   167  
   168  	defer os.Remove(c.socketPath)
   169  
   170  	listener, err := net.Listen("unix", c.socketPath)
   171  	if err != nil {
   172  		log.Fatalf("error listening on socket %q: %v", c.socketPath, err)
   173  	}
   174  
   175  	go server(listener, outputFile)
   176  	for i := 0; ; i++ {
   177  		conn, err := net.Dial("unix", c.socketPath)
   178  		if err != nil {
   179  			log.Fatal("error dialing:", err)
   180  		}
   181  		if _, err := conn.Write([]byte(strconv.Itoa(i))); err != nil {
   182  			log.Fatal("error writing:", err)
   183  		}
   184  		conn.Close()
   185  		time.Sleep(100 * time.Millisecond)
   186  	}
   187  }
   188  
   189  func server(listener net.Listener, out *os.File) {
   190  	buf := make([]byte, 16)
   191  
   192  	for {
   193  		c, err := listener.Accept()
   194  		if err != nil {
   195  			log.Fatal("error accepting connection:", err)
   196  		}
   197  		nr, err := c.Read(buf)
   198  		if err != nil {
   199  			log.Fatal("error reading from buf:", err)
   200  		}
   201  		data := buf[0:nr]
   202  		fmt.Fprint(out, string(data)+"\n")
   203  	}
   204  }
   205  
   206  type taskTree struct {
   207  	depth int
   208  	width int
   209  	pause bool
   210  }
   211  
   212  // Name implements subcommands.Command.
   213  func (*taskTree) Name() string {
   214  	return "task-tree"
   215  }
   216  
   217  // Synopsis implements subcommands.Command.
   218  func (*taskTree) Synopsis() string {
   219  	return "creates a tree of tasks"
   220  }
   221  
   222  // Usage implements subcommands.Command.
   223  func (*taskTree) Usage() string {
   224  	return "task-tree <flags>"
   225  }
   226  
   227  // SetFlags implements subcommands.Command.
   228  func (c *taskTree) SetFlags(f *flag.FlagSet) {
   229  	f.IntVar(&c.depth, "depth", 1, "number of levels to create")
   230  	f.IntVar(&c.width, "width", 1, "number of tasks at each level")
   231  	f.BoolVar(&c.pause, "pause", false, "whether the tasks should pause perpetually")
   232  }
   233  
   234  // Execute implements subcommands.Command.
   235  func (c *taskTree) Execute(ctx context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
   236  	if c.depth == 0 {
   237  		log.Printf("Child sleeping, PID: %d\n", os.Getpid())
   238  		for {
   239  			time.Sleep(time.Hour)
   240  		}
   241  	}
   242  
   243  	log.Printf("Parent %d creating %d children, PID: %d\n", c.depth, c.width, os.Getpid())
   244  
   245  	stop := testutil.StartReaper()
   246  	defer stop()
   247  
   248  	var cmds []*exec.Cmd
   249  	for i := 0; i < c.width; i++ {
   250  		cmd := exec.Command(
   251  			"/proc/self/exe", c.Name(),
   252  			"--depth", strconv.Itoa(c.depth-1),
   253  			"--width", strconv.Itoa(c.width),
   254  			fmt.Sprintf("--pause=%t", c.pause))
   255  		cmd.Stdout = os.Stdout
   256  		cmd.Stderr = os.Stderr
   257  
   258  		if err := cmd.Start(); err != nil {
   259  			log.Fatal("failed to call self:", err)
   260  		}
   261  		cmds = append(cmds, cmd)
   262  	}
   263  
   264  	for _, c := range cmds {
   265  		c.Wait()
   266  	}
   267  
   268  	if c.pause {
   269  		log.Printf("Parent %d sleeping, PID: %d\n", c.depth, os.Getpid())
   270  		for {
   271  			time.Sleep(time.Hour)
   272  		}
   273  	}
   274  
   275  	return subcommands.ExitSuccess
   276  }
   277  
   278  type forkBomb struct {
   279  	delay time.Duration
   280  }
   281  
   282  // Name implements subcommands.Command.
   283  func (*forkBomb) Name() string {
   284  	return "fork-bomb"
   285  }
   286  
   287  // Synopsis implements subcommands.Command.
   288  func (*forkBomb) Synopsis() string {
   289  	return "creates child process until the end of times"
   290  }
   291  
   292  // Usage implements subcommands.Command.
   293  func (*forkBomb) Usage() string {
   294  	return "fork-bomb <flags>"
   295  }
   296  
   297  // SetFlags implements subcommands.Command.
   298  func (c *forkBomb) SetFlags(f *flag.FlagSet) {
   299  	f.DurationVar(&c.delay, "delay", 100*time.Millisecond, "amount of time to delay creation of child")
   300  }
   301  
   302  // Execute implements subcommands.Command.
   303  func (c *forkBomb) Execute(ctx context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
   304  	time.Sleep(c.delay)
   305  
   306  	cmd := exec.Command("/proc/self/exe", c.Name())
   307  	cmd.Stdout = os.Stdout
   308  	cmd.Stderr = os.Stderr
   309  	if err := cmd.Run(); err != nil {
   310  		log.Fatal("failed to call self:", err)
   311  	}
   312  	return subcommands.ExitSuccess
   313  }
   314  
   315  type reaper struct{}
   316  
   317  // Name implements subcommands.Command.
   318  func (*reaper) Name() string {
   319  	return "reaper"
   320  }
   321  
   322  // Synopsis implements subcommands.Command.
   323  func (*reaper) Synopsis() string {
   324  	return "reaps all children in a loop"
   325  }
   326  
   327  // Usage implements subcommands.Command.
   328  func (*reaper) Usage() string {
   329  	return "reaper <flags>"
   330  }
   331  
   332  // SetFlags implements subcommands.Command.
   333  func (*reaper) SetFlags(*flag.FlagSet) {}
   334  
   335  // Execute implements subcommands.Command.
   336  func (c *reaper) Execute(ctx context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
   337  	stop := testutil.StartReaper()
   338  	defer stop()
   339  	select {}
   340  }
   341  
   342  type syscall struct {
   343  	sysno uint64
   344  }
   345  
   346  // Name implements subcommands.Command.
   347  func (*syscall) Name() string {
   348  	return "syscall"
   349  }
   350  
   351  // Synopsis implements subcommands.Command.
   352  func (*syscall) Synopsis() string {
   353  	return "syscall makes a syscall"
   354  }
   355  
   356  // Usage implements subcommands.Command.
   357  func (*syscall) Usage() string {
   358  	return "syscall <flags> [arg1 arg2...]"
   359  }
   360  
   361  // SetFlags implements subcommands.Command.
   362  func (s *syscall) SetFlags(f *flag.FlagSet) {
   363  	f.Uint64Var(&s.sysno, "syscall", 0, "syscall to call")
   364  }
   365  
   366  // Execute implements subcommands.Command.
   367  func (s *syscall) Execute(ctx context.Context, f *flag.FlagSet, _ ...any) subcommands.ExitStatus {
   368  	const maxSyscallArgs = 6
   369  	numArgs := f.NArg()
   370  	if numArgs > maxSyscallArgs {
   371  		fmt.Printf("number of sycall arguments not supported: %d (max is %d)\n", numArgs, maxSyscallArgs)
   372  		return subcommands.ExitUsageError
   373  	}
   374  	var syscallArgs [maxSyscallArgs]uintptr
   375  	for i := 0; i < numArgs; i++ {
   376  		uintArg, err := strconv.ParseUint(f.Arg(i), 10, 64)
   377  		if err != nil {
   378  			fmt.Printf("not an integer: %q\n", f.Arg(i))
   379  			return subcommands.ExitUsageError
   380  		}
   381  		syscallArgs[i] = uintptr(uintArg)
   382  	}
   383  	var errno sys.Errno
   384  	switch numArgs {
   385  	case 0:
   386  		_, _, errno = sys.Syscall(uintptr(s.sysno), 0, 0, 0)
   387  	case 3:
   388  		_, _, errno = sys.Syscall(uintptr(s.sysno), syscallArgs[0], syscallArgs[1], syscallArgs[2])
   389  	case 6:
   390  		_, _, errno = sys.Syscall6(uintptr(s.sysno), syscallArgs[0], syscallArgs[1], syscallArgs[2], syscallArgs[3], syscallArgs[4], syscallArgs[5])
   391  	default:
   392  		fmt.Printf("number of sycall arguments not supported: %d\n", numArgs)
   393  		return subcommands.ExitUsageError
   394  	}
   395  	if errno != 0 {
   396  		fmt.Printf("syscall(%d, %s) failed: %v\n", s.sysno, strings.Join(f.Args(), ", "), errno)
   397  	} else {
   398  		fmt.Printf("syscall(%d, %s) success\n", s.sysno, strings.Join(f.Args(), ", "))
   399  	}
   400  	return subcommands.ExitSuccess
   401  }
   402  
   403  type capability struct {
   404  	enabled  uint64
   405  	disabled uint64
   406  }
   407  
   408  // Name implements subcommands.Command.
   409  func (*capability) Name() string {
   410  	return "capability"
   411  }
   412  
   413  // Synopsis implements subcommands.Command.
   414  func (*capability) Synopsis() string {
   415  	return "checks if effective capabilities are set/unset"
   416  }
   417  
   418  // Usage implements subcommands.Command.
   419  func (*capability) Usage() string {
   420  	return "capability [--enabled=number] [--disabled=number]"
   421  }
   422  
   423  // SetFlags implements subcommands.Command.
   424  func (c *capability) SetFlags(f *flag.FlagSet) {
   425  	f.Uint64Var(&c.enabled, "enabled", 0, "")
   426  	f.Uint64Var(&c.disabled, "disabled", 0, "")
   427  }
   428  
   429  // Execute implements subcommands.Command.
   430  func (c *capability) Execute(ctx context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
   431  	if c.enabled == 0 && c.disabled == 0 {
   432  		fmt.Println("One of the flags must be set")
   433  		return subcommands.ExitUsageError
   434  	}
   435  
   436  	status, err := ioutil.ReadFile("/proc/self/status")
   437  	if err != nil {
   438  		fmt.Printf("Error reading %q: %v\n", "proc/self/status", err)
   439  		return subcommands.ExitFailure
   440  	}
   441  	re := regexp.MustCompile("CapEff:\t([0-9a-f]+)\n")
   442  	matches := re.FindStringSubmatch(string(status))
   443  	if matches == nil || len(matches) != 2 {
   444  		fmt.Printf("Effective capabilities not found in\n%s\n", status)
   445  		return subcommands.ExitFailure
   446  	}
   447  	caps, err := strconv.ParseUint(matches[1], 16, 64)
   448  	if err != nil {
   449  		fmt.Printf("failed to convert capabilities %q: %v\n", matches[1], err)
   450  		return subcommands.ExitFailure
   451  	}
   452  
   453  	if c.enabled != 0 && (caps&c.enabled) != c.enabled {
   454  		fmt.Printf("Missing capabilities, want: %#x: got: %#x\n", c.enabled, caps)
   455  		return subcommands.ExitFailure
   456  	}
   457  	if c.disabled != 0 && (caps&c.disabled) != 0 {
   458  		fmt.Printf("Extra capabilities found, dont_want: %#x: got: %#x\n", c.disabled, caps)
   459  		return subcommands.ExitFailure
   460  	}
   461  
   462  	return subcommands.ExitSuccess
   463  }
   464  
   465  type ptyRunner struct{}
   466  
   467  // Name implements subcommands.Command.
   468  func (*ptyRunner) Name() string {
   469  	return "pty-runner"
   470  }
   471  
   472  // Synopsis implements subcommands.Command.
   473  func (*ptyRunner) Synopsis() string {
   474  	return "runs the given command with an open pty terminal"
   475  }
   476  
   477  // Usage implements subcommands.Command.
   478  func (*ptyRunner) Usage() string {
   479  	return "pty-runner [command]"
   480  }
   481  
   482  // SetFlags implements subcommands.Command.SetFlags.
   483  func (*ptyRunner) SetFlags(f *flag.FlagSet) {}
   484  
   485  // Execute implements subcommands.Command.
   486  func (*ptyRunner) Execute(_ context.Context, fs *flag.FlagSet, _ ...any) subcommands.ExitStatus {
   487  	c := exec.Command(fs.Args()[0], fs.Args()[1:]...)
   488  	f, err := pty.Start(c)
   489  	if err != nil {
   490  		fmt.Printf("pty.Start failed: %v", err)
   491  		return subcommands.ExitFailure
   492  	}
   493  	defer f.Close()
   494  
   495  	// Copy stdout from the command to keep this process alive until the
   496  	// subprocess exits.
   497  	io.Copy(os.Stdout, f)
   498  
   499  	return subcommands.ExitSuccess
   500  }