github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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  	"net"
    26  	"os"
    27  	"os/exec"
    28  	"regexp"
    29  	"strconv"
    30  	sys "syscall"
    31  	"time"
    32  
    33  	"github.com/google/subcommands"
    34  	"github.com/kr/pty"
    35  	"github.com/SagerNet/gvisor/pkg/test/testutil"
    36  	"github.com/SagerNet/gvisor/runsc/flag"
    37  )
    38  
    39  func main() {
    40  	subcommands.Register(subcommands.HelpCommand(), "")
    41  	subcommands.Register(subcommands.FlagsCommand(), "")
    42  	subcommands.Register(new(capability), "")
    43  	subcommands.Register(new(fdReceiver), "")
    44  	subcommands.Register(new(fdSender), "")
    45  	subcommands.Register(new(forkBomb), "")
    46  	subcommands.Register(new(ptyRunner), "")
    47  	subcommands.Register(new(reaper), "")
    48  	subcommands.Register(new(syscall), "")
    49  	subcommands.Register(new(taskTree), "")
    50  	subcommands.Register(new(uds), "")
    51  
    52  	flag.Parse()
    53  
    54  	exitCode := subcommands.Execute(context.Background())
    55  	os.Exit(int(exitCode))
    56  }
    57  
    58  type uds struct {
    59  	fileName   string
    60  	socketPath string
    61  }
    62  
    63  // Name implements subcommands.Command.Name.
    64  func (*uds) Name() string {
    65  	return "uds"
    66  }
    67  
    68  // Synopsis implements subcommands.Command.Synopsys.
    69  func (*uds) Synopsis() string {
    70  	return "creates unix domain socket client and server. Client sends a contant flow of sequential numbers. Server prints them to --file"
    71  }
    72  
    73  // Usage implements subcommands.Command.Usage.
    74  func (*uds) Usage() string {
    75  	return "uds <flags>"
    76  }
    77  
    78  // SetFlags implements subcommands.Command.SetFlags.
    79  func (c *uds) SetFlags(f *flag.FlagSet) {
    80  	f.StringVar(&c.fileName, "file", "", "name of output file")
    81  	f.StringVar(&c.socketPath, "socket", "", "path to socket")
    82  }
    83  
    84  // Execute implements subcommands.Command.Execute.
    85  func (c *uds) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
    86  	if c.fileName == "" || c.socketPath == "" {
    87  		log.Fatalf("Flags cannot be empty, given: fileName: %q, socketPath: %q", c.fileName, c.socketPath)
    88  		return subcommands.ExitFailure
    89  	}
    90  	outputFile, err := os.OpenFile(c.fileName, os.O_WRONLY|os.O_CREATE, 0666)
    91  	if err != nil {
    92  		log.Fatal("error opening output file:", err)
    93  	}
    94  
    95  	defer os.Remove(c.socketPath)
    96  
    97  	listener, err := net.Listen("unix", c.socketPath)
    98  	if err != nil {
    99  		log.Fatalf("error listening on socket %q: %v", c.socketPath, err)
   100  	}
   101  
   102  	go server(listener, outputFile)
   103  	for i := 0; ; i++ {
   104  		conn, err := net.Dial("unix", c.socketPath)
   105  		if err != nil {
   106  			log.Fatal("error dialing:", err)
   107  		}
   108  		if _, err := conn.Write([]byte(strconv.Itoa(i))); err != nil {
   109  			log.Fatal("error writing:", err)
   110  		}
   111  		conn.Close()
   112  		time.Sleep(100 * time.Millisecond)
   113  	}
   114  }
   115  
   116  func server(listener net.Listener, out *os.File) {
   117  	buf := make([]byte, 16)
   118  
   119  	for {
   120  		c, err := listener.Accept()
   121  		if err != nil {
   122  			log.Fatal("error accepting connection:", err)
   123  		}
   124  		nr, err := c.Read(buf)
   125  		if err != nil {
   126  			log.Fatal("error reading from buf:", err)
   127  		}
   128  		data := buf[0:nr]
   129  		fmt.Fprint(out, string(data)+"\n")
   130  	}
   131  }
   132  
   133  type taskTree struct {
   134  	depth int
   135  	width int
   136  	pause bool
   137  }
   138  
   139  // Name implements subcommands.Command.
   140  func (*taskTree) Name() string {
   141  	return "task-tree"
   142  }
   143  
   144  // Synopsis implements subcommands.Command.
   145  func (*taskTree) Synopsis() string {
   146  	return "creates a tree of tasks"
   147  }
   148  
   149  // Usage implements subcommands.Command.
   150  func (*taskTree) Usage() string {
   151  	return "task-tree <flags>"
   152  }
   153  
   154  // SetFlags implements subcommands.Command.
   155  func (c *taskTree) SetFlags(f *flag.FlagSet) {
   156  	f.IntVar(&c.depth, "depth", 1, "number of levels to create")
   157  	f.IntVar(&c.width, "width", 1, "number of tasks at each level")
   158  	f.BoolVar(&c.pause, "pause", false, "whether the tasks should pause perpetually")
   159  }
   160  
   161  // Execute implements subcommands.Command.
   162  func (c *taskTree) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
   163  	if c.depth == 0 {
   164  		log.Printf("Child sleeping, PID: %d\n", os.Getpid())
   165  		for {
   166  			time.Sleep(time.Hour)
   167  		}
   168  	}
   169  
   170  	log.Printf("Parent %d creating %d children, PID: %d\n", c.depth, c.width, os.Getpid())
   171  
   172  	stop := testutil.StartReaper()
   173  	defer stop()
   174  
   175  	var cmds []*exec.Cmd
   176  	for i := 0; i < c.width; i++ {
   177  		cmd := exec.Command(
   178  			"/proc/self/exe", c.Name(),
   179  			"--depth", strconv.Itoa(c.depth-1),
   180  			"--width", strconv.Itoa(c.width),
   181  			fmt.Sprintf("--pause=%t", c.pause))
   182  		cmd.Stdout = os.Stdout
   183  		cmd.Stderr = os.Stderr
   184  
   185  		if err := cmd.Start(); err != nil {
   186  			log.Fatal("failed to call self:", err)
   187  		}
   188  		cmds = append(cmds, cmd)
   189  	}
   190  
   191  	for _, c := range cmds {
   192  		c.Wait()
   193  	}
   194  
   195  	if c.pause {
   196  		log.Printf("Parent %d sleeping, PID: %d\n", c.depth, os.Getpid())
   197  		for {
   198  			time.Sleep(time.Hour)
   199  		}
   200  	}
   201  
   202  	return subcommands.ExitSuccess
   203  }
   204  
   205  type forkBomb struct {
   206  	delay time.Duration
   207  }
   208  
   209  // Name implements subcommands.Command.
   210  func (*forkBomb) Name() string {
   211  	return "fork-bomb"
   212  }
   213  
   214  // Synopsis implements subcommands.Command.
   215  func (*forkBomb) Synopsis() string {
   216  	return "creates child process until the end of times"
   217  }
   218  
   219  // Usage implements subcommands.Command.
   220  func (*forkBomb) Usage() string {
   221  	return "fork-bomb <flags>"
   222  }
   223  
   224  // SetFlags implements subcommands.Command.
   225  func (c *forkBomb) SetFlags(f *flag.FlagSet) {
   226  	f.DurationVar(&c.delay, "delay", 100*time.Millisecond, "amount of time to delay creation of child")
   227  }
   228  
   229  // Execute implements subcommands.Command.
   230  func (c *forkBomb) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
   231  	time.Sleep(c.delay)
   232  
   233  	cmd := exec.Command("/proc/self/exe", c.Name())
   234  	cmd.Stdout = os.Stdout
   235  	cmd.Stderr = os.Stderr
   236  	if err := cmd.Run(); err != nil {
   237  		log.Fatal("failed to call self:", err)
   238  	}
   239  	return subcommands.ExitSuccess
   240  }
   241  
   242  type reaper struct{}
   243  
   244  // Name implements subcommands.Command.
   245  func (*reaper) Name() string {
   246  	return "reaper"
   247  }
   248  
   249  // Synopsis implements subcommands.Command.
   250  func (*reaper) Synopsis() string {
   251  	return "reaps all children in a loop"
   252  }
   253  
   254  // Usage implements subcommands.Command.
   255  func (*reaper) Usage() string {
   256  	return "reaper <flags>"
   257  }
   258  
   259  // SetFlags implements subcommands.Command.
   260  func (*reaper) SetFlags(*flag.FlagSet) {}
   261  
   262  // Execute implements subcommands.Command.
   263  func (c *reaper) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
   264  	stop := testutil.StartReaper()
   265  	defer stop()
   266  	select {}
   267  }
   268  
   269  type syscall struct {
   270  	sysno uint64
   271  }
   272  
   273  // Name implements subcommands.Command.
   274  func (*syscall) Name() string {
   275  	return "syscall"
   276  }
   277  
   278  // Synopsis implements subcommands.Command.
   279  func (*syscall) Synopsis() string {
   280  	return "syscall makes a syscall"
   281  }
   282  
   283  // Usage implements subcommands.Command.
   284  func (*syscall) Usage() string {
   285  	return "syscall <flags>"
   286  }
   287  
   288  // SetFlags implements subcommands.Command.
   289  func (s *syscall) SetFlags(f *flag.FlagSet) {
   290  	f.Uint64Var(&s.sysno, "syscall", 0, "syscall to call")
   291  }
   292  
   293  // Execute implements subcommands.Command.
   294  func (s *syscall) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
   295  	if _, _, errno := sys.Syscall(uintptr(s.sysno), 0, 0, 0); errno != 0 {
   296  		fmt.Printf("syscall(%d, 0, 0...) failed: %v\n", s.sysno, errno)
   297  	} else {
   298  		fmt.Printf("syscall(%d, 0, 0...) success\n", s.sysno)
   299  	}
   300  	return subcommands.ExitSuccess
   301  }
   302  
   303  type capability struct {
   304  	enabled  uint64
   305  	disabled uint64
   306  }
   307  
   308  // Name implements subcommands.Command.
   309  func (*capability) Name() string {
   310  	return "capability"
   311  }
   312  
   313  // Synopsis implements subcommands.Command.
   314  func (*capability) Synopsis() string {
   315  	return "checks if effective capabilities are set/unset"
   316  }
   317  
   318  // Usage implements subcommands.Command.
   319  func (*capability) Usage() string {
   320  	return "capability [--enabled=number] [--disabled=number]"
   321  }
   322  
   323  // SetFlags implements subcommands.Command.
   324  func (c *capability) SetFlags(f *flag.FlagSet) {
   325  	f.Uint64Var(&c.enabled, "enabled", 0, "")
   326  	f.Uint64Var(&c.disabled, "disabled", 0, "")
   327  }
   328  
   329  // Execute implements subcommands.Command.
   330  func (c *capability) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
   331  	if c.enabled == 0 && c.disabled == 0 {
   332  		fmt.Println("One of the flags must be set")
   333  		return subcommands.ExitUsageError
   334  	}
   335  
   336  	status, err := ioutil.ReadFile("/proc/self/status")
   337  	if err != nil {
   338  		fmt.Printf("Error reading %q: %v\n", "proc/self/status", err)
   339  		return subcommands.ExitFailure
   340  	}
   341  	re := regexp.MustCompile("CapEff:\t([0-9a-f]+)\n")
   342  	matches := re.FindStringSubmatch(string(status))
   343  	if matches == nil || len(matches) != 2 {
   344  		fmt.Printf("Effective capabilities not found in\n%s\n", status)
   345  		return subcommands.ExitFailure
   346  	}
   347  	caps, err := strconv.ParseUint(matches[1], 16, 64)
   348  	if err != nil {
   349  		fmt.Printf("failed to convert capabilities %q: %v\n", matches[1], err)
   350  		return subcommands.ExitFailure
   351  	}
   352  
   353  	if c.enabled != 0 && (caps&c.enabled) != c.enabled {
   354  		fmt.Printf("Missing capabilities, want: %#x: got: %#x\n", c.enabled, caps)
   355  		return subcommands.ExitFailure
   356  	}
   357  	if c.disabled != 0 && (caps&c.disabled) != 0 {
   358  		fmt.Printf("Extra capabilities found, dont_want: %#x: got: %#x\n", c.disabled, caps)
   359  		return subcommands.ExitFailure
   360  	}
   361  
   362  	return subcommands.ExitSuccess
   363  }
   364  
   365  type ptyRunner struct{}
   366  
   367  // Name implements subcommands.Command.
   368  func (*ptyRunner) Name() string {
   369  	return "pty-runner"
   370  }
   371  
   372  // Synopsis implements subcommands.Command.
   373  func (*ptyRunner) Synopsis() string {
   374  	return "runs the given command with an open pty terminal"
   375  }
   376  
   377  // Usage implements subcommands.Command.
   378  func (*ptyRunner) Usage() string {
   379  	return "pty-runner [command]"
   380  }
   381  
   382  // SetFlags implements subcommands.Command.SetFlags.
   383  func (*ptyRunner) SetFlags(f *flag.FlagSet) {}
   384  
   385  // Execute implements subcommands.Command.
   386  func (*ptyRunner) Execute(_ context.Context, fs *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
   387  	c := exec.Command(fs.Args()[0], fs.Args()[1:]...)
   388  	f, err := pty.Start(c)
   389  	if err != nil {
   390  		fmt.Printf("pty.Start failed: %v", err)
   391  		return subcommands.ExitFailure
   392  	}
   393  	defer f.Close()
   394  
   395  	// Copy stdout from the command to keep this process alive until the
   396  	// subprocess exits.
   397  	io.Copy(os.Stdout, f)
   398  
   399  	return subcommands.ExitSuccess
   400  }