github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/libcontainer/integration/exec_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"reflect"
    11  	"strconv"
    12  	"strings"
    13  	"syscall"
    14  	"testing"
    15  
    16  	"github.com/opencontainers/runc/libcontainer"
    17  	"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
    18  	"github.com/opencontainers/runc/libcontainer/configs"
    19  )
    20  
    21  func TestExecPS(t *testing.T) {
    22  	testExecPS(t, false)
    23  }
    24  
    25  func TestUsernsExecPS(t *testing.T) {
    26  	if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
    27  		t.Skip("userns is unsupported")
    28  	}
    29  	testExecPS(t, true)
    30  }
    31  
    32  func testExecPS(t *testing.T, userns bool) {
    33  	if testing.Short() {
    34  		return
    35  	}
    36  	rootfs, err := newRootfs()
    37  	ok(t, err)
    38  	defer remove(rootfs)
    39  	config := newTemplateConfig(rootfs)
    40  	if userns {
    41  		config.UidMappings = []configs.IDMap{{0, 0, 1000}}
    42  		config.GidMappings = []configs.IDMap{{0, 0, 1000}}
    43  		config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER})
    44  	}
    45  
    46  	buffers, exitCode, err := runContainer(config, "", "ps")
    47  	if err != nil {
    48  		t.Fatalf("%s: %s", buffers, err)
    49  	}
    50  	if exitCode != 0 {
    51  		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
    52  	}
    53  	lines := strings.Split(buffers.Stdout.String(), "\n")
    54  	if len(lines) < 2 {
    55  		t.Fatalf("more than one process running for output %q", buffers.Stdout.String())
    56  	}
    57  	expected := `1 root     ps`
    58  	actual := strings.Trim(lines[1], "\n ")
    59  	if actual != expected {
    60  		t.Fatalf("expected output %q but received %q", expected, actual)
    61  	}
    62  }
    63  
    64  func TestIPCPrivate(t *testing.T) {
    65  	if testing.Short() {
    66  		return
    67  	}
    68  
    69  	rootfs, err := newRootfs()
    70  	ok(t, err)
    71  	defer remove(rootfs)
    72  
    73  	l, err := os.Readlink("/proc/1/ns/ipc")
    74  	ok(t, err)
    75  
    76  	config := newTemplateConfig(rootfs)
    77  	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
    78  	ok(t, err)
    79  
    80  	if exitCode != 0 {
    81  		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
    82  	}
    83  
    84  	if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual == l {
    85  		t.Fatalf("ipc link should be private to the container but equals host %q %q", actual, l)
    86  	}
    87  }
    88  
    89  func TestIPCHost(t *testing.T) {
    90  	if testing.Short() {
    91  		return
    92  	}
    93  
    94  	rootfs, err := newRootfs()
    95  	ok(t, err)
    96  	defer remove(rootfs)
    97  
    98  	l, err := os.Readlink("/proc/1/ns/ipc")
    99  	ok(t, err)
   100  
   101  	config := newTemplateConfig(rootfs)
   102  	config.Namespaces.Remove(configs.NEWIPC)
   103  	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
   104  	ok(t, err)
   105  
   106  	if exitCode != 0 {
   107  		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
   108  	}
   109  
   110  	if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual != l {
   111  		t.Fatalf("ipc link not equal to host link %q %q", actual, l)
   112  	}
   113  }
   114  
   115  func TestIPCJoinPath(t *testing.T) {
   116  	if testing.Short() {
   117  		return
   118  	}
   119  
   120  	rootfs, err := newRootfs()
   121  	ok(t, err)
   122  	defer remove(rootfs)
   123  
   124  	l, err := os.Readlink("/proc/1/ns/ipc")
   125  	ok(t, err)
   126  
   127  	config := newTemplateConfig(rootfs)
   128  	config.Namespaces.Add(configs.NEWIPC, "/proc/1/ns/ipc")
   129  
   130  	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
   131  	ok(t, err)
   132  
   133  	if exitCode != 0 {
   134  		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
   135  	}
   136  
   137  	if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual != l {
   138  		t.Fatalf("ipc link not equal to host link %q %q", actual, l)
   139  	}
   140  }
   141  
   142  func TestIPCBadPath(t *testing.T) {
   143  	if testing.Short() {
   144  		return
   145  	}
   146  
   147  	rootfs, err := newRootfs()
   148  	ok(t, err)
   149  	defer remove(rootfs)
   150  
   151  	config := newTemplateConfig(rootfs)
   152  	config.Namespaces.Add(configs.NEWIPC, "/proc/1/ns/ipcc")
   153  
   154  	_, _, err = runContainer(config, "", "true")
   155  	if err == nil {
   156  		t.Fatal("container succeeded with bad ipc path")
   157  	}
   158  }
   159  
   160  func TestRlimit(t *testing.T) {
   161  	testRlimit(t, false)
   162  }
   163  
   164  func TestUsernsRlimit(t *testing.T) {
   165  	if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
   166  		t.Skip("userns is unsupported")
   167  	}
   168  
   169  	testRlimit(t, true)
   170  }
   171  
   172  func testRlimit(t *testing.T, userns bool) {
   173  	if testing.Short() {
   174  		return
   175  	}
   176  
   177  	rootfs, err := newRootfs()
   178  	ok(t, err)
   179  	defer remove(rootfs)
   180  
   181  	config := newTemplateConfig(rootfs)
   182  	if userns {
   183  		config.UidMappings = []configs.IDMap{{0, 0, 1000}}
   184  		config.GidMappings = []configs.IDMap{{0, 0, 1000}}
   185  		config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER})
   186  	}
   187  
   188  	// ensure limit is lower than what the config requests to test that in a user namespace
   189  	// the Setrlimit call happens early enough that we still have permissions to raise the limit.
   190  	ok(t, syscall.Setrlimit(syscall.RLIMIT_NOFILE, &syscall.Rlimit{
   191  		Max: 1024,
   192  		Cur: 1024,
   193  	}))
   194  
   195  	out, _, err := runContainer(config, "", "/bin/sh", "-c", "ulimit -n")
   196  	ok(t, err)
   197  	if limit := strings.TrimSpace(out.Stdout.String()); limit != "1025" {
   198  		t.Fatalf("expected rlimit to be 1025, got %s", limit)
   199  	}
   200  }
   201  
   202  func TestEnter(t *testing.T) {
   203  	if testing.Short() {
   204  		return
   205  	}
   206  	root, err := newTestRoot()
   207  	ok(t, err)
   208  	defer os.RemoveAll(root)
   209  
   210  	rootfs, err := newRootfs()
   211  	ok(t, err)
   212  	defer remove(rootfs)
   213  
   214  	config := newTemplateConfig(rootfs)
   215  
   216  	container, err := factory.Create("test", config)
   217  	ok(t, err)
   218  	defer container.Destroy()
   219  
   220  	// Execute a first process in the container
   221  	stdinR, stdinW, err := os.Pipe()
   222  	ok(t, err)
   223  
   224  	var stdout, stdout2 bytes.Buffer
   225  
   226  	pconfig := libcontainer.Process{
   227  		Cwd:    "/",
   228  		Args:   []string{"sh", "-c", "cat && readlink /proc/self/ns/pid"},
   229  		Env:    standardEnvironment,
   230  		Stdin:  stdinR,
   231  		Stdout: &stdout,
   232  	}
   233  	err = container.Run(&pconfig)
   234  	stdinR.Close()
   235  	defer stdinW.Close()
   236  	ok(t, err)
   237  	pid, err := pconfig.Pid()
   238  	ok(t, err)
   239  
   240  	// Execute another process in the container
   241  	stdinR2, stdinW2, err := os.Pipe()
   242  	ok(t, err)
   243  	pconfig2 := libcontainer.Process{
   244  		Cwd: "/",
   245  		Env: standardEnvironment,
   246  	}
   247  	pconfig2.Args = []string{"sh", "-c", "cat && readlink /proc/self/ns/pid"}
   248  	pconfig2.Stdin = stdinR2
   249  	pconfig2.Stdout = &stdout2
   250  
   251  	err = container.Run(&pconfig2)
   252  	stdinR2.Close()
   253  	defer stdinW2.Close()
   254  	ok(t, err)
   255  
   256  	pid2, err := pconfig2.Pid()
   257  	ok(t, err)
   258  
   259  	processes, err := container.Processes()
   260  	ok(t, err)
   261  
   262  	n := 0
   263  	for i := range processes {
   264  		if processes[i] == pid || processes[i] == pid2 {
   265  			n++
   266  		}
   267  	}
   268  	if n != 2 {
   269  		t.Fatal("unexpected number of processes", processes, pid, pid2)
   270  	}
   271  
   272  	// Wait processes
   273  	stdinW2.Close()
   274  	waitProcess(&pconfig2, t)
   275  
   276  	stdinW.Close()
   277  	waitProcess(&pconfig, t)
   278  
   279  	// Check that both processes live in the same pidns
   280  	pidns := string(stdout.Bytes())
   281  	ok(t, err)
   282  
   283  	pidns2 := string(stdout2.Bytes())
   284  	ok(t, err)
   285  
   286  	if pidns != pidns2 {
   287  		t.Fatal("The second process isn't in the required pid namespace", pidns, pidns2)
   288  	}
   289  }
   290  
   291  func TestProcessEnv(t *testing.T) {
   292  	if testing.Short() {
   293  		return
   294  	}
   295  	root, err := newTestRoot()
   296  	ok(t, err)
   297  	defer os.RemoveAll(root)
   298  
   299  	rootfs, err := newRootfs()
   300  	ok(t, err)
   301  	defer remove(rootfs)
   302  
   303  	config := newTemplateConfig(rootfs)
   304  
   305  	container, err := factory.Create("test", config)
   306  	ok(t, err)
   307  	defer container.Destroy()
   308  
   309  	var stdout bytes.Buffer
   310  	pconfig := libcontainer.Process{
   311  		Cwd:  "/",
   312  		Args: []string{"sh", "-c", "env"},
   313  		Env: []string{
   314  			"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
   315  			"HOSTNAME=integration",
   316  			"TERM=xterm",
   317  			"FOO=BAR",
   318  		},
   319  		Stdin:  nil,
   320  		Stdout: &stdout,
   321  	}
   322  	err = container.Run(&pconfig)
   323  	ok(t, err)
   324  
   325  	// Wait for process
   326  	waitProcess(&pconfig, t)
   327  
   328  	outputEnv := string(stdout.Bytes())
   329  
   330  	// Check that the environment has the key/value pair we added
   331  	if !strings.Contains(outputEnv, "FOO=BAR") {
   332  		t.Fatal("Environment doesn't have the expected FOO=BAR key/value pair: ", outputEnv)
   333  	}
   334  
   335  	// Make sure that HOME is set
   336  	if !strings.Contains(outputEnv, "HOME=/root") {
   337  		t.Fatal("Environment doesn't have HOME set: ", outputEnv)
   338  	}
   339  }
   340  
   341  func TestProcessCaps(t *testing.T) {
   342  	if testing.Short() {
   343  		return
   344  	}
   345  	root, err := newTestRoot()
   346  	ok(t, err)
   347  	defer os.RemoveAll(root)
   348  
   349  	rootfs, err := newRootfs()
   350  	ok(t, err)
   351  	defer remove(rootfs)
   352  
   353  	config := newTemplateConfig(rootfs)
   354  
   355  	container, err := factory.Create("test", config)
   356  	ok(t, err)
   357  	defer container.Destroy()
   358  
   359  	processCaps := append(config.Capabilities, "CAP_NET_ADMIN")
   360  
   361  	var stdout bytes.Buffer
   362  	pconfig := libcontainer.Process{
   363  		Cwd:          "/",
   364  		Args:         []string{"sh", "-c", "cat /proc/self/status"},
   365  		Env:          standardEnvironment,
   366  		Capabilities: processCaps,
   367  		Stdin:        nil,
   368  		Stdout:       &stdout,
   369  	}
   370  	err = container.Run(&pconfig)
   371  	ok(t, err)
   372  
   373  	// Wait for process
   374  	waitProcess(&pconfig, t)
   375  
   376  	outputStatus := string(stdout.Bytes())
   377  
   378  	lines := strings.Split(outputStatus, "\n")
   379  
   380  	effectiveCapsLine := ""
   381  	for _, l := range lines {
   382  		line := strings.TrimSpace(l)
   383  		if strings.Contains(line, "CapEff:") {
   384  			effectiveCapsLine = line
   385  			break
   386  		}
   387  	}
   388  
   389  	if effectiveCapsLine == "" {
   390  		t.Fatal("Couldn't find effective caps: ", outputStatus)
   391  	}
   392  
   393  	parts := strings.Split(effectiveCapsLine, ":")
   394  	effectiveCapsStr := strings.TrimSpace(parts[1])
   395  
   396  	effectiveCaps, err := strconv.ParseUint(effectiveCapsStr, 16, 64)
   397  	if err != nil {
   398  		t.Fatal("Could not parse effective caps", err)
   399  	}
   400  
   401  	var netAdminMask uint64
   402  	var netAdminBit uint
   403  	netAdminBit = 12 // from capability.h
   404  	netAdminMask = 1 << netAdminBit
   405  	if effectiveCaps&netAdminMask != netAdminMask {
   406  		t.Fatal("CAP_NET_ADMIN is not set as expected")
   407  	}
   408  }
   409  
   410  func TestAdditionalGroups(t *testing.T) {
   411  	if testing.Short() {
   412  		return
   413  	}
   414  	root, err := newTestRoot()
   415  	ok(t, err)
   416  	defer os.RemoveAll(root)
   417  
   418  	rootfs, err := newRootfs()
   419  	ok(t, err)
   420  	defer remove(rootfs)
   421  
   422  	config := newTemplateConfig(rootfs)
   423  
   424  	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
   425  	ok(t, err)
   426  
   427  	container, err := factory.Create("test", config)
   428  	ok(t, err)
   429  	defer container.Destroy()
   430  
   431  	var stdout bytes.Buffer
   432  	pconfig := libcontainer.Process{
   433  		Cwd:              "/",
   434  		Args:             []string{"sh", "-c", "id", "-Gn"},
   435  		Env:              standardEnvironment,
   436  		Stdin:            nil,
   437  		Stdout:           &stdout,
   438  		AdditionalGroups: []string{"plugdev", "audio"},
   439  	}
   440  	err = container.Run(&pconfig)
   441  	ok(t, err)
   442  
   443  	// Wait for process
   444  	waitProcess(&pconfig, t)
   445  
   446  	outputGroups := string(stdout.Bytes())
   447  
   448  	// Check that the groups output has the groups that we specified
   449  	if !strings.Contains(outputGroups, "audio") {
   450  		t.Fatalf("Listed groups do not contain the audio group as expected: %v", outputGroups)
   451  	}
   452  
   453  	if !strings.Contains(outputGroups, "plugdev") {
   454  		t.Fatalf("Listed groups do not contain the plugdev group as expected: %v", outputGroups)
   455  	}
   456  }
   457  
   458  func TestFreeze(t *testing.T) {
   459  	testFreeze(t, false)
   460  }
   461  
   462  func TestSystemdFreeze(t *testing.T) {
   463  	if !systemd.UseSystemd() {
   464  		t.Skip("Systemd is unsupported")
   465  	}
   466  	testFreeze(t, true)
   467  }
   468  
   469  func testFreeze(t *testing.T, systemd bool) {
   470  	if testing.Short() {
   471  		return
   472  	}
   473  	root, err := newTestRoot()
   474  	ok(t, err)
   475  	defer os.RemoveAll(root)
   476  
   477  	rootfs, err := newRootfs()
   478  	ok(t, err)
   479  	defer remove(rootfs)
   480  
   481  	config := newTemplateConfig(rootfs)
   482  	f := factory
   483  	if systemd {
   484  		f = systemdFactory
   485  	}
   486  
   487  	container, err := f.Create("test", config)
   488  	ok(t, err)
   489  	defer container.Destroy()
   490  
   491  	stdinR, stdinW, err := os.Pipe()
   492  	ok(t, err)
   493  
   494  	pconfig := &libcontainer.Process{
   495  		Cwd:   "/",
   496  		Args:  []string{"cat"},
   497  		Env:   standardEnvironment,
   498  		Stdin: stdinR,
   499  	}
   500  	err = container.Run(pconfig)
   501  	stdinR.Close()
   502  	defer stdinW.Close()
   503  	ok(t, err)
   504  
   505  	err = container.Pause()
   506  	ok(t, err)
   507  	state, err := container.Status()
   508  	ok(t, err)
   509  	err = container.Resume()
   510  	ok(t, err)
   511  	if state != libcontainer.Paused {
   512  		t.Fatal("Unexpected state: ", state)
   513  	}
   514  
   515  	stdinW.Close()
   516  	waitProcess(pconfig, t)
   517  }
   518  
   519  func TestCpuShares(t *testing.T) {
   520  	testCpuShares(t, false)
   521  }
   522  
   523  func TestCpuSharesSystemd(t *testing.T) {
   524  	if !systemd.UseSystemd() {
   525  		t.Skip("Systemd is unsupported")
   526  	}
   527  	testCpuShares(t, true)
   528  }
   529  
   530  func testCpuShares(t *testing.T, systemd bool) {
   531  	if testing.Short() {
   532  		return
   533  	}
   534  	rootfs, err := newRootfs()
   535  	ok(t, err)
   536  	defer remove(rootfs)
   537  
   538  	config := newTemplateConfig(rootfs)
   539  	if systemd {
   540  		config.Cgroups.Parent = "system.slice"
   541  	}
   542  	config.Cgroups.Resources.CpuShares = 1
   543  
   544  	_, _, err = runContainer(config, "", "ps")
   545  	if err == nil {
   546  		t.Fatalf("runContainer should failed with invalid CpuShares")
   547  	}
   548  }
   549  
   550  func TestPids(t *testing.T) {
   551  	testPids(t, false)
   552  }
   553  
   554  func TestPidsSystemd(t *testing.T) {
   555  	if !systemd.UseSystemd() {
   556  		t.Skip("Systemd is unsupported")
   557  	}
   558  	testPids(t, true)
   559  }
   560  
   561  func testPids(t *testing.T, systemd bool) {
   562  	if testing.Short() {
   563  		return
   564  	}
   565  
   566  	rootfs, err := newRootfs()
   567  	ok(t, err)
   568  	defer remove(rootfs)
   569  
   570  	config := newTemplateConfig(rootfs)
   571  	if systemd {
   572  		config.Cgroups.Parent = "system.slice"
   573  	}
   574  	config.Cgroups.Resources.PidsLimit = -1
   575  
   576  	// Running multiple processes.
   577  	_, ret, err := runContainer(config, "", "/bin/sh", "-c", "/bin/true | /bin/true | /bin/true | /bin/true")
   578  	if err != nil && strings.Contains(err.Error(), "no such directory for pids.max") {
   579  		t.Skip("PIDs cgroup is unsupported")
   580  	}
   581  	ok(t, err)
   582  
   583  	if ret != 0 {
   584  		t.Fatalf("expected fork() to succeed with no pids limit")
   585  	}
   586  
   587  	// Enforce a permissive limit. This needs to be fairly hand-wavey due to the
   588  	// issues with running Go binaries with pids restrictions (see below).
   589  	config.Cgroups.Resources.PidsLimit = 64
   590  	_, ret, err = runContainer(config, "", "/bin/sh", "-c", `
   591  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true |
   592  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true |
   593  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true |
   594  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true`)
   595  	if err != nil && strings.Contains(err.Error(), "no such directory for pids.max") {
   596  		t.Skip("PIDs cgroup is unsupported")
   597  	}
   598  	ok(t, err)
   599  
   600  	if ret != 0 {
   601  		t.Fatalf("expected fork() to succeed with permissive pids limit")
   602  	}
   603  
   604  	// Enforce a restrictive limit. 64 * /bin/true + 1 * shell should cause this
   605  	// to fail reliability.
   606  	config.Cgroups.Resources.PidsLimit = 64
   607  	out, _, err := runContainer(config, "", "/bin/sh", "-c", `
   608  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true |
   609  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true |
   610  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true |
   611  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true |
   612  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true |
   613  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true |
   614  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true |
   615  	/bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true`)
   616  	if err != nil && strings.Contains(err.Error(), "no such directory for pids.max") {
   617  		t.Skip("PIDs cgroup is unsupported")
   618  	}
   619  	if err != nil && !strings.Contains(out.String(), "sh: can't fork") {
   620  		ok(t, err)
   621  	}
   622  
   623  	if err == nil {
   624  		t.Fatalf("expected fork() to fail with restrictive pids limit")
   625  	}
   626  
   627  	// Minimal restrictions are not really supported, due to quirks in using Go
   628  	// due to the fact that it spawns random processes. While we do our best with
   629  	// late setting cgroup values, it's just too unreliable with very small pids.max.
   630  	// As such, we don't test that case. YMMV.
   631  }
   632  
   633  func TestRunWithKernelMemory(t *testing.T) {
   634  	testRunWithKernelMemory(t, false)
   635  }
   636  
   637  func TestRunWithKernelMemorySystemd(t *testing.T) {
   638  	if !systemd.UseSystemd() {
   639  		t.Skip("Systemd is unsupported")
   640  	}
   641  	testRunWithKernelMemory(t, true)
   642  }
   643  
   644  func testRunWithKernelMemory(t *testing.T, systemd bool) {
   645  	if testing.Short() {
   646  		return
   647  	}
   648  	rootfs, err := newRootfs()
   649  	ok(t, err)
   650  	defer remove(rootfs)
   651  
   652  	config := newTemplateConfig(rootfs)
   653  	if systemd {
   654  		config.Cgroups.Parent = "system.slice"
   655  	}
   656  	config.Cgroups.Resources.KernelMemory = 52428800
   657  
   658  	_, _, err = runContainer(config, "", "ps")
   659  	if err != nil {
   660  		t.Fatalf("runContainer failed with kernel memory limit: %v", err)
   661  	}
   662  }
   663  
   664  func TestContainerState(t *testing.T) {
   665  	if testing.Short() {
   666  		return
   667  	}
   668  	root, err := newTestRoot()
   669  	if err != nil {
   670  		t.Fatal(err)
   671  	}
   672  	defer os.RemoveAll(root)
   673  
   674  	rootfs, err := newRootfs()
   675  	if err != nil {
   676  		t.Fatal(err)
   677  	}
   678  	defer remove(rootfs)
   679  
   680  	l, err := os.Readlink("/proc/1/ns/ipc")
   681  	if err != nil {
   682  		t.Fatal(err)
   683  	}
   684  
   685  	config := newTemplateConfig(rootfs)
   686  	config.Namespaces = configs.Namespaces([]configs.Namespace{
   687  		{Type: configs.NEWNS},
   688  		{Type: configs.NEWUTS},
   689  		// host for IPC
   690  		//{Type: configs.NEWIPC},
   691  		{Type: configs.NEWPID},
   692  		{Type: configs.NEWNET},
   693  	})
   694  
   695  	container, err := factory.Create("test", config)
   696  	if err != nil {
   697  		t.Fatal(err)
   698  	}
   699  	defer container.Destroy()
   700  
   701  	stdinR, stdinW, err := os.Pipe()
   702  	if err != nil {
   703  		t.Fatal(err)
   704  	}
   705  	p := &libcontainer.Process{
   706  		Cwd:   "/",
   707  		Args:  []string{"cat"},
   708  		Env:   standardEnvironment,
   709  		Stdin: stdinR,
   710  	}
   711  	err = container.Run(p)
   712  	if err != nil {
   713  		t.Fatal(err)
   714  	}
   715  	stdinR.Close()
   716  	defer stdinW.Close()
   717  
   718  	st, err := container.State()
   719  	if err != nil {
   720  		t.Fatal(err)
   721  	}
   722  
   723  	l1, err := os.Readlink(st.NamespacePaths[configs.NEWIPC])
   724  	if err != nil {
   725  		t.Fatal(err)
   726  	}
   727  	if l1 != l {
   728  		t.Fatal("Container using non-host ipc namespace")
   729  	}
   730  	stdinW.Close()
   731  	waitProcess(p, t)
   732  }
   733  
   734  func TestPassExtraFiles(t *testing.T) {
   735  	if testing.Short() {
   736  		return
   737  	}
   738  
   739  	rootfs, err := newRootfs()
   740  	if err != nil {
   741  		t.Fatal(err)
   742  	}
   743  	defer remove(rootfs)
   744  
   745  	config := newTemplateConfig(rootfs)
   746  
   747  	container, err := factory.Create("test", config)
   748  	if err != nil {
   749  		t.Fatal(err)
   750  	}
   751  	defer container.Destroy()
   752  
   753  	var stdout bytes.Buffer
   754  	pipeout1, pipein1, err := os.Pipe()
   755  	pipeout2, pipein2, err := os.Pipe()
   756  	process := libcontainer.Process{
   757  		Cwd:        "/",
   758  		Args:       []string{"sh", "-c", "cd /proc/$$/fd; echo -n *; echo -n 1 >3; echo -n 2 >4"},
   759  		Env:        []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
   760  		ExtraFiles: []*os.File{pipein1, pipein2},
   761  		Stdin:      nil,
   762  		Stdout:     &stdout,
   763  	}
   764  	err = container.Run(&process)
   765  	if err != nil {
   766  		t.Fatal(err)
   767  	}
   768  
   769  	waitProcess(&process, t)
   770  
   771  	out := string(stdout.Bytes())
   772  	// fd 5 is the directory handle for /proc/$$/fd
   773  	if out != "0 1 2 3 4 5" {
   774  		t.Fatalf("expected to have the file descriptors '0 1 2 3 4 5' passed to init, got '%s'", out)
   775  	}
   776  	var buf = []byte{0}
   777  	_, err = pipeout1.Read(buf)
   778  	if err != nil {
   779  		t.Fatal(err)
   780  	}
   781  	out1 := string(buf)
   782  	if out1 != "1" {
   783  		t.Fatalf("expected first pipe to receive '1', got '%s'", out1)
   784  	}
   785  
   786  	_, err = pipeout2.Read(buf)
   787  	if err != nil {
   788  		t.Fatal(err)
   789  	}
   790  	out2 := string(buf)
   791  	if out2 != "2" {
   792  		t.Fatalf("expected second pipe to receive '2', got '%s'", out2)
   793  	}
   794  }
   795  
   796  func TestMountCmds(t *testing.T) {
   797  	if testing.Short() {
   798  		return
   799  	}
   800  	root, err := newTestRoot()
   801  	if err != nil {
   802  		t.Fatal(err)
   803  	}
   804  	defer os.RemoveAll(root)
   805  
   806  	rootfs, err := newRootfs()
   807  	if err != nil {
   808  		t.Fatal(err)
   809  	}
   810  	defer remove(rootfs)
   811  
   812  	tmpDir, err := ioutil.TempDir("", "tmpdir")
   813  	if err != nil {
   814  		t.Fatal(err)
   815  	}
   816  	defer os.RemoveAll(tmpDir)
   817  
   818  	config := newTemplateConfig(rootfs)
   819  	config.Mounts = append(config.Mounts, &configs.Mount{
   820  		Source:      tmpDir,
   821  		Destination: "/tmp",
   822  		Device:      "bind",
   823  		Flags:       syscall.MS_BIND | syscall.MS_REC,
   824  		PremountCmds: []configs.Command{
   825  			{Path: "touch", Args: []string{filepath.Join(tmpDir, "hello")}},
   826  			{Path: "touch", Args: []string{filepath.Join(tmpDir, "world")}},
   827  		},
   828  		PostmountCmds: []configs.Command{
   829  			{Path: "cp", Args: []string{filepath.Join(rootfs, "tmp", "hello"), filepath.Join(rootfs, "tmp", "hello-backup")}},
   830  			{Path: "cp", Args: []string{filepath.Join(rootfs, "tmp", "world"), filepath.Join(rootfs, "tmp", "world-backup")}},
   831  		},
   832  	})
   833  
   834  	container, err := factory.Create("test", config)
   835  	if err != nil {
   836  		t.Fatal(err)
   837  	}
   838  	defer container.Destroy()
   839  
   840  	pconfig := libcontainer.Process{
   841  		Cwd:  "/",
   842  		Args: []string{"sh", "-c", "env"},
   843  		Env:  standardEnvironment,
   844  	}
   845  	err = container.Run(&pconfig)
   846  	if err != nil {
   847  		t.Fatal(err)
   848  	}
   849  
   850  	// Wait for process
   851  	waitProcess(&pconfig, t)
   852  
   853  	entries, err := ioutil.ReadDir(tmpDir)
   854  	if err != nil {
   855  		t.Fatal(err)
   856  	}
   857  	expected := []string{"hello", "hello-backup", "world", "world-backup"}
   858  	for i, e := range entries {
   859  		if e.Name() != expected[i] {
   860  			t.Errorf("Got(%s), expect %s", e.Name(), expected[i])
   861  		}
   862  	}
   863  }
   864  
   865  func TestSysctl(t *testing.T) {
   866  	if testing.Short() {
   867  		return
   868  	}
   869  	root, err := newTestRoot()
   870  	ok(t, err)
   871  	defer os.RemoveAll(root)
   872  
   873  	rootfs, err := newRootfs()
   874  	ok(t, err)
   875  	defer remove(rootfs)
   876  
   877  	config := newTemplateConfig(rootfs)
   878  	config.Sysctl = map[string]string{
   879  		"kernel.shmmni": "8192",
   880  	}
   881  
   882  	container, err := factory.Create("test", config)
   883  	ok(t, err)
   884  	defer container.Destroy()
   885  
   886  	var stdout bytes.Buffer
   887  	pconfig := libcontainer.Process{
   888  		Cwd:    "/",
   889  		Args:   []string{"sh", "-c", "cat /proc/sys/kernel/shmmni"},
   890  		Env:    standardEnvironment,
   891  		Stdin:  nil,
   892  		Stdout: &stdout,
   893  	}
   894  	err = container.Run(&pconfig)
   895  	ok(t, err)
   896  
   897  	// Wait for process
   898  	waitProcess(&pconfig, t)
   899  
   900  	shmmniOutput := strings.TrimSpace(string(stdout.Bytes()))
   901  	if shmmniOutput != "8192" {
   902  		t.Fatalf("kernel.shmmni property expected to be 8192, but is %s", shmmniOutput)
   903  	}
   904  }
   905  
   906  func TestMountCgroupRO(t *testing.T) {
   907  	if testing.Short() {
   908  		return
   909  	}
   910  	rootfs, err := newRootfs()
   911  	ok(t, err)
   912  	defer remove(rootfs)
   913  	config := newTemplateConfig(rootfs)
   914  
   915  	config.Mounts = append(config.Mounts, &configs.Mount{
   916  		Destination: "/sys/fs/cgroup",
   917  		Device:      "cgroup",
   918  		Flags:       defaultMountFlags | syscall.MS_RDONLY,
   919  	})
   920  
   921  	buffers, exitCode, err := runContainer(config, "", "mount")
   922  	if err != nil {
   923  		t.Fatalf("%s: %s", buffers, err)
   924  	}
   925  	if exitCode != 0 {
   926  		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
   927  	}
   928  	mountInfo := buffers.Stdout.String()
   929  	lines := strings.Split(mountInfo, "\n")
   930  	for _, l := range lines {
   931  		if strings.HasPrefix(l, "tmpfs on /sys/fs/cgroup") {
   932  			if !strings.Contains(l, "ro") ||
   933  				!strings.Contains(l, "nosuid") ||
   934  				!strings.Contains(l, "nodev") ||
   935  				!strings.Contains(l, "noexec") {
   936  				t.Fatalf("Mode expected to contain 'ro,nosuid,nodev,noexec': %s", l)
   937  			}
   938  			if !strings.Contains(l, "mode=755") {
   939  				t.Fatalf("Mode expected to contain 'mode=755': %s", l)
   940  			}
   941  			continue
   942  		}
   943  		if !strings.HasPrefix(l, "cgroup") {
   944  			continue
   945  		}
   946  		if !strings.Contains(l, "ro") ||
   947  			!strings.Contains(l, "nosuid") ||
   948  			!strings.Contains(l, "nodev") ||
   949  			!strings.Contains(l, "noexec") {
   950  			t.Fatalf("Mode expected to contain 'ro,nosuid,nodev,noexec': %s", l)
   951  		}
   952  	}
   953  }
   954  
   955  func TestMountCgroupRW(t *testing.T) {
   956  	if testing.Short() {
   957  		return
   958  	}
   959  	rootfs, err := newRootfs()
   960  	ok(t, err)
   961  	defer remove(rootfs)
   962  	config := newTemplateConfig(rootfs)
   963  
   964  	config.Mounts = append(config.Mounts, &configs.Mount{
   965  		Destination: "/sys/fs/cgroup",
   966  		Device:      "cgroup",
   967  		Flags:       defaultMountFlags,
   968  	})
   969  
   970  	buffers, exitCode, err := runContainer(config, "", "mount")
   971  	if err != nil {
   972  		t.Fatalf("%s: %s", buffers, err)
   973  	}
   974  	if exitCode != 0 {
   975  		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
   976  	}
   977  	mountInfo := buffers.Stdout.String()
   978  	lines := strings.Split(mountInfo, "\n")
   979  	for _, l := range lines {
   980  		if strings.HasPrefix(l, "tmpfs on /sys/fs/cgroup") {
   981  			if !strings.Contains(l, "rw") ||
   982  				!strings.Contains(l, "nosuid") ||
   983  				!strings.Contains(l, "nodev") ||
   984  				!strings.Contains(l, "noexec") {
   985  				t.Fatalf("Mode expected to contain 'rw,nosuid,nodev,noexec': %s", l)
   986  			}
   987  			if !strings.Contains(l, "mode=755") {
   988  				t.Fatalf("Mode expected to contain 'mode=755': %s", l)
   989  			}
   990  			continue
   991  		}
   992  		if !strings.HasPrefix(l, "cgroup") {
   993  			continue
   994  		}
   995  		if !strings.Contains(l, "rw") ||
   996  			!strings.Contains(l, "nosuid") ||
   997  			!strings.Contains(l, "nodev") ||
   998  			!strings.Contains(l, "noexec") {
   999  			t.Fatalf("Mode expected to contain 'rw,nosuid,nodev,noexec': %s", l)
  1000  		}
  1001  	}
  1002  }
  1003  
  1004  func TestOomScoreAdj(t *testing.T) {
  1005  	if testing.Short() {
  1006  		return
  1007  	}
  1008  	root, err := newTestRoot()
  1009  	ok(t, err)
  1010  	defer os.RemoveAll(root)
  1011  
  1012  	rootfs, err := newRootfs()
  1013  	ok(t, err)
  1014  	defer remove(rootfs)
  1015  
  1016  	config := newTemplateConfig(rootfs)
  1017  	config.OomScoreAdj = 200
  1018  
  1019  	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
  1020  	ok(t, err)
  1021  
  1022  	container, err := factory.Create("test", config)
  1023  	ok(t, err)
  1024  	defer container.Destroy()
  1025  
  1026  	var stdout bytes.Buffer
  1027  	pconfig := libcontainer.Process{
  1028  		Cwd:    "/",
  1029  		Args:   []string{"sh", "-c", "cat /proc/self/oom_score_adj"},
  1030  		Env:    standardEnvironment,
  1031  		Stdin:  nil,
  1032  		Stdout: &stdout,
  1033  	}
  1034  	err = container.Run(&pconfig)
  1035  	ok(t, err)
  1036  
  1037  	// Wait for process
  1038  	waitProcess(&pconfig, t)
  1039  	outputOomScoreAdj := strings.TrimSpace(string(stdout.Bytes()))
  1040  
  1041  	// Check that the oom_score_adj matches the value that was set as part of config.
  1042  	if outputOomScoreAdj != strconv.Itoa(config.OomScoreAdj) {
  1043  		t.Fatalf("Expected oom_score_adj %d; got %q", config.OomScoreAdj, outputOomScoreAdj)
  1044  	}
  1045  }
  1046  
  1047  func TestHook(t *testing.T) {
  1048  	if testing.Short() {
  1049  		return
  1050  	}
  1051  	root, err := newTestRoot()
  1052  	ok(t, err)
  1053  	defer os.RemoveAll(root)
  1054  
  1055  	rootfs, err := newRootfs()
  1056  	ok(t, err)
  1057  	defer remove(rootfs)
  1058  
  1059  	config := newTemplateConfig(rootfs)
  1060  	expectedBundlePath := "/path/to/bundle/path"
  1061  	config.Labels = append(config.Labels, fmt.Sprintf("bundle=%s", expectedBundlePath))
  1062  	config.Hooks = &configs.Hooks{
  1063  		Prestart: []configs.Hook{
  1064  			configs.NewFunctionHook(func(s configs.HookState) error {
  1065  				if s.BundlePath != expectedBundlePath {
  1066  					t.Fatalf("Expected prestart hook bundlePath '%s'; got '%s'", expectedBundlePath, s.BundlePath)
  1067  				}
  1068  
  1069  				f, err := os.Create(filepath.Join(s.Root, "test"))
  1070  				if err != nil {
  1071  					return err
  1072  				}
  1073  				return f.Close()
  1074  			}),
  1075  		},
  1076  		Poststart: []configs.Hook{
  1077  			configs.NewFunctionHook(func(s configs.HookState) error {
  1078  				if s.BundlePath != expectedBundlePath {
  1079  					t.Fatalf("Expected poststart hook bundlePath '%s'; got '%s'", expectedBundlePath, s.BundlePath)
  1080  				}
  1081  
  1082  				return ioutil.WriteFile(filepath.Join(s.Root, "test"), []byte("hello world"), 0755)
  1083  			}),
  1084  		},
  1085  		Poststop: []configs.Hook{
  1086  			configs.NewFunctionHook(func(s configs.HookState) error {
  1087  				if s.BundlePath != expectedBundlePath {
  1088  					t.Fatalf("Expected poststop hook bundlePath '%s'; got '%s'", expectedBundlePath, s.BundlePath)
  1089  				}
  1090  
  1091  				return os.RemoveAll(filepath.Join(s.Root, "test"))
  1092  			}),
  1093  		},
  1094  	}
  1095  	container, err := factory.Create("test", config)
  1096  	ok(t, err)
  1097  
  1098  	var stdout bytes.Buffer
  1099  	pconfig := libcontainer.Process{
  1100  		Cwd:    "/",
  1101  		Args:   []string{"sh", "-c", "ls /test"},
  1102  		Env:    standardEnvironment,
  1103  		Stdin:  nil,
  1104  		Stdout: &stdout,
  1105  	}
  1106  	err = container.Run(&pconfig)
  1107  	ok(t, err)
  1108  
  1109  	// Wait for process
  1110  	waitProcess(&pconfig, t)
  1111  
  1112  	outputLs := string(stdout.Bytes())
  1113  
  1114  	// Check that the ls output has the expected file touched by the prestart hook
  1115  	if !strings.Contains(outputLs, "/test") {
  1116  		container.Destroy()
  1117  		t.Fatalf("ls output doesn't have the expected file: %s", outputLs)
  1118  	}
  1119  
  1120  	// Check that the file is written by the poststart hook
  1121  	testFilePath := filepath.Join(rootfs, "test")
  1122  	contents, err := ioutil.ReadFile(testFilePath)
  1123  	if err != nil {
  1124  		t.Fatalf("cannot read file '%s': %s", testFilePath, err)
  1125  	}
  1126  	if string(contents) != "hello world" {
  1127  		t.Fatalf("Expected test file to contain 'hello world'; got '%s'", string(contents))
  1128  	}
  1129  
  1130  	if err := container.Destroy(); err != nil {
  1131  		t.Fatalf("container destroy %s", err)
  1132  	}
  1133  	fi, err := os.Stat(filepath.Join(rootfs, "test"))
  1134  	if err == nil || !os.IsNotExist(err) {
  1135  		t.Fatalf("expected file to not exist, got %s", fi.Name())
  1136  	}
  1137  }
  1138  
  1139  func TestSTDIOPermissions(t *testing.T) {
  1140  	if testing.Short() {
  1141  		return
  1142  	}
  1143  
  1144  	rootfs, err := newRootfs()
  1145  	ok(t, err)
  1146  	defer remove(rootfs)
  1147  	config := newTemplateConfig(rootfs)
  1148  	buffers, exitCode, err := runContainer(config, "", "sh", "-c", "echo hi > /dev/stderr")
  1149  	ok(t, err)
  1150  	if exitCode != 0 {
  1151  		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
  1152  	}
  1153  
  1154  	if actual := strings.Trim(buffers.Stderr.String(), "\n"); actual != "hi" {
  1155  		t.Fatalf("stderr should equal be equal %q %q", actual, "hi")
  1156  	}
  1157  }
  1158  
  1159  func unmountOp(path string) error {
  1160  	if err := syscall.Unmount(path, syscall.MNT_DETACH); err != nil {
  1161  		return err
  1162  	}
  1163  	return nil
  1164  }
  1165  
  1166  // Launch container with rootfsPropagation in rslave mode. Also
  1167  // bind mount a volume /mnt1host at /mnt1cont at the time of launch. Now do
  1168  // another mount on host (/mnt1host/mnt2host) and this new mount should
  1169  // propagate to container (/mnt1cont/mnt2host)
  1170  func TestRootfsPropagationSlaveMount(t *testing.T) {
  1171  	var mountPropagated bool
  1172  	var dir1cont string
  1173  	var dir2cont string
  1174  
  1175  	dir1cont = "/root/mnt1cont"
  1176  
  1177  	if testing.Short() {
  1178  		return
  1179  	}
  1180  	rootfs, err := newRootfs()
  1181  	ok(t, err)
  1182  	defer remove(rootfs)
  1183  	config := newTemplateConfig(rootfs)
  1184  
  1185  	config.RootPropagation = syscall.MS_SLAVE | syscall.MS_REC
  1186  
  1187  	// Bind mount a volume
  1188  	dir1host, err := ioutil.TempDir("", "mnt1host")
  1189  	ok(t, err)
  1190  	defer os.RemoveAll(dir1host)
  1191  
  1192  	// Make this dir a "shared" mount point. This will make sure a
  1193  	// slave relationship can be established in container.
  1194  	err = syscall.Mount(dir1host, dir1host, "bind", syscall.MS_BIND|syscall.MS_REC, "")
  1195  	ok(t, err)
  1196  	err = syscall.Mount("", dir1host, "", syscall.MS_SHARED|syscall.MS_REC, "")
  1197  	ok(t, err)
  1198  	defer unmountOp(dir1host)
  1199  
  1200  	config.Mounts = append(config.Mounts, &configs.Mount{
  1201  		Source:      dir1host,
  1202  		Destination: dir1cont,
  1203  		Device:      "bind",
  1204  		Flags:       syscall.MS_BIND | syscall.MS_REC})
  1205  
  1206  	// TODO: systemd specific processing
  1207  	f := factory
  1208  
  1209  	container, err := f.Create("testSlaveMount", config)
  1210  	ok(t, err)
  1211  	defer container.Destroy()
  1212  
  1213  	stdinR, stdinW, err := os.Pipe()
  1214  	ok(t, err)
  1215  
  1216  	pconfig := &libcontainer.Process{
  1217  		Cwd:   "/",
  1218  		Args:  []string{"cat"},
  1219  		Env:   standardEnvironment,
  1220  		Stdin: stdinR,
  1221  	}
  1222  
  1223  	err = container.Run(pconfig)
  1224  	stdinR.Close()
  1225  	defer stdinW.Close()
  1226  	ok(t, err)
  1227  
  1228  	// Create mnt1host/mnt2host and bind mount itself on top of it. This
  1229  	// should be visible in container.
  1230  	dir2host, err := ioutil.TempDir(dir1host, "mnt2host")
  1231  	ok(t, err)
  1232  	defer os.RemoveAll(dir2host)
  1233  
  1234  	err = syscall.Mount(dir2host, dir2host, "bind", syscall.MS_BIND, "")
  1235  	defer unmountOp(dir2host)
  1236  	ok(t, err)
  1237  
  1238  	// Run "cat /proc/self/mountinfo" in container and look at mount points.
  1239  	var stdout2 bytes.Buffer
  1240  
  1241  	stdinR2, stdinW2, err := os.Pipe()
  1242  	ok(t, err)
  1243  
  1244  	pconfig2 := &libcontainer.Process{
  1245  		Cwd:    "/",
  1246  		Args:   []string{"cat", "/proc/self/mountinfo"},
  1247  		Env:    standardEnvironment,
  1248  		Stdin:  stdinR2,
  1249  		Stdout: &stdout2,
  1250  	}
  1251  
  1252  	err = container.Run(pconfig2)
  1253  	stdinR2.Close()
  1254  	defer stdinW2.Close()
  1255  	ok(t, err)
  1256  
  1257  	stdinW2.Close()
  1258  	waitProcess(pconfig2, t)
  1259  	stdinW.Close()
  1260  	waitProcess(pconfig, t)
  1261  
  1262  	mountPropagated = false
  1263  	dir2cont = filepath.Join(dir1cont, filepath.Base(dir2host))
  1264  
  1265  	propagationInfo := string(stdout2.Bytes())
  1266  	lines := strings.Split(propagationInfo, "\n")
  1267  	for _, l := range lines {
  1268  		linefields := strings.Split(l, " ")
  1269  		if len(linefields) < 5 {
  1270  			continue
  1271  		}
  1272  
  1273  		if linefields[4] == dir2cont {
  1274  			mountPropagated = true
  1275  			break
  1276  		}
  1277  	}
  1278  
  1279  	if mountPropagated != true {
  1280  		t.Fatalf("Mount on host %s did not propagate in container at %s\n", dir2host, dir2cont)
  1281  	}
  1282  }
  1283  
  1284  // Launch container with rootfsPropagation 0 so no propagation flags are
  1285  // applied. Also bind mount a volume /mnt1host at /mnt1cont at the time of
  1286  // launch. Now do a mount in container (/mnt1cont/mnt2cont) and this new
  1287  // mount should propagate to host (/mnt1host/mnt2cont)
  1288  
  1289  func TestRootfsPropagationSharedMount(t *testing.T) {
  1290  	var dir1cont string
  1291  	var dir2cont string
  1292  
  1293  	dir1cont = "/root/mnt1cont"
  1294  
  1295  	if testing.Short() {
  1296  		return
  1297  	}
  1298  	rootfs, err := newRootfs()
  1299  	ok(t, err)
  1300  	defer remove(rootfs)
  1301  	config := newTemplateConfig(rootfs)
  1302  	config.RootPropagation = syscall.MS_PRIVATE
  1303  
  1304  	// Bind mount a volume
  1305  	dir1host, err := ioutil.TempDir("", "mnt1host")
  1306  	ok(t, err)
  1307  	defer os.RemoveAll(dir1host)
  1308  
  1309  	// Make this dir a "shared" mount point. This will make sure a
  1310  	// shared relationship can be established in container.
  1311  	err = syscall.Mount(dir1host, dir1host, "bind", syscall.MS_BIND|syscall.MS_REC, "")
  1312  	ok(t, err)
  1313  	err = syscall.Mount("", dir1host, "", syscall.MS_SHARED|syscall.MS_REC, "")
  1314  	ok(t, err)
  1315  	defer unmountOp(dir1host)
  1316  
  1317  	config.Mounts = append(config.Mounts, &configs.Mount{
  1318  		Source:      dir1host,
  1319  		Destination: dir1cont,
  1320  		Device:      "bind",
  1321  		Flags:       syscall.MS_BIND | syscall.MS_REC})
  1322  
  1323  	// TODO: systemd specific processing
  1324  	f := factory
  1325  
  1326  	container, err := f.Create("testSharedMount", config)
  1327  	ok(t, err)
  1328  	defer container.Destroy()
  1329  
  1330  	stdinR, stdinW, err := os.Pipe()
  1331  	ok(t, err)
  1332  
  1333  	pconfig := &libcontainer.Process{
  1334  		Cwd:   "/",
  1335  		Args:  []string{"cat"},
  1336  		Env:   standardEnvironment,
  1337  		Stdin: stdinR,
  1338  	}
  1339  
  1340  	err = container.Run(pconfig)
  1341  	stdinR.Close()
  1342  	defer stdinW.Close()
  1343  	ok(t, err)
  1344  
  1345  	// Create mnt1host/mnt2cont.  This will become visible inside container
  1346  	// at mnt1cont/mnt2cont. Bind mount itself on top of it. This
  1347  	// should be visible on host now.
  1348  	dir2host, err := ioutil.TempDir(dir1host, "mnt2cont")
  1349  	ok(t, err)
  1350  	defer os.RemoveAll(dir2host)
  1351  
  1352  	dir2cont = filepath.Join(dir1cont, filepath.Base(dir2host))
  1353  
  1354  	// Mount something in container and see if it is visible on host.
  1355  	var stdout2 bytes.Buffer
  1356  
  1357  	stdinR2, stdinW2, err := os.Pipe()
  1358  	ok(t, err)
  1359  
  1360  	// Provide CAP_SYS_ADMIN
  1361  	processCaps := append(config.Capabilities, "CAP_SYS_ADMIN")
  1362  
  1363  	pconfig2 := &libcontainer.Process{
  1364  		Cwd:          "/",
  1365  		Args:         []string{"mount", "--bind", dir2cont, dir2cont},
  1366  		Env:          standardEnvironment,
  1367  		Stdin:        stdinR2,
  1368  		Stdout:       &stdout2,
  1369  		Capabilities: processCaps,
  1370  	}
  1371  
  1372  	err = container.Run(pconfig2)
  1373  	stdinR2.Close()
  1374  	defer stdinW2.Close()
  1375  	ok(t, err)
  1376  
  1377  	// Wait for process
  1378  	stdinW2.Close()
  1379  	waitProcess(pconfig2, t)
  1380  	stdinW.Close()
  1381  	waitProcess(pconfig, t)
  1382  
  1383  	defer unmountOp(dir2host)
  1384  
  1385  	// Check if mount is visible on host or not.
  1386  	out, err := exec.Command("findmnt", "-n", "-f", "-oTARGET", dir2host).CombinedOutput()
  1387  	outtrim := strings.TrimSpace(string(out))
  1388  	if err != nil {
  1389  		t.Logf("findmnt error %q: %q", err, outtrim)
  1390  	}
  1391  
  1392  	if string(outtrim) != dir2host {
  1393  		t.Fatalf("Mount in container on %s did not propagate to host on %s. finmnt output=%s", dir2cont, dir2host, outtrim)
  1394  	}
  1395  }
  1396  
  1397  func TestPIDHost(t *testing.T) {
  1398  	if testing.Short() {
  1399  		return
  1400  	}
  1401  
  1402  	rootfs, err := newRootfs()
  1403  	ok(t, err)
  1404  	defer remove(rootfs)
  1405  
  1406  	l, err := os.Readlink("/proc/1/ns/pid")
  1407  	ok(t, err)
  1408  
  1409  	config := newTemplateConfig(rootfs)
  1410  	config.Namespaces.Remove(configs.NEWPID)
  1411  	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/pid")
  1412  	ok(t, err)
  1413  
  1414  	if exitCode != 0 {
  1415  		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
  1416  	}
  1417  
  1418  	if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual != l {
  1419  		t.Fatalf("ipc link not equal to host link %q %q", actual, l)
  1420  	}
  1421  }
  1422  
  1423  func TestInitJoinPID(t *testing.T) {
  1424  	if testing.Short() {
  1425  		return
  1426  	}
  1427  	rootfs, err := newRootfs()
  1428  	ok(t, err)
  1429  	defer remove(rootfs)
  1430  
  1431  	// Execute a long-running container
  1432  	container1, err := newContainer(newTemplateConfig(rootfs))
  1433  	ok(t, err)
  1434  	defer container1.Destroy()
  1435  
  1436  	stdinR1, stdinW1, err := os.Pipe()
  1437  	ok(t, err)
  1438  	init1 := &libcontainer.Process{
  1439  		Cwd:   "/",
  1440  		Args:  []string{"cat"},
  1441  		Env:   standardEnvironment,
  1442  		Stdin: stdinR1,
  1443  	}
  1444  	err = container1.Run(init1)
  1445  	stdinR1.Close()
  1446  	defer stdinW1.Close()
  1447  	ok(t, err)
  1448  
  1449  	// get the state of the first container
  1450  	state1, err := container1.State()
  1451  	ok(t, err)
  1452  	pidns1 := state1.NamespacePaths[configs.NEWPID]
  1453  
  1454  	// Run a container inside the existing pidns but with different cgroups
  1455  	config2 := newTemplateConfig(rootfs)
  1456  	config2.Namespaces.Add(configs.NEWPID, pidns1)
  1457  	config2.Cgroups.Path = "integration/test2"
  1458  	container2, err := newContainerWithName("testCT2", config2)
  1459  	ok(t, err)
  1460  	defer container2.Destroy()
  1461  
  1462  	stdinR2, stdinW2, err := os.Pipe()
  1463  	ok(t, err)
  1464  	init2 := &libcontainer.Process{
  1465  		Cwd:   "/",
  1466  		Args:  []string{"cat"},
  1467  		Env:   standardEnvironment,
  1468  		Stdin: stdinR2,
  1469  	}
  1470  	err = container2.Run(init2)
  1471  	stdinR2.Close()
  1472  	defer stdinW2.Close()
  1473  	ok(t, err)
  1474  	// get the state of the second container
  1475  	state2, err := container2.State()
  1476  	ok(t, err)
  1477  
  1478  	ns1, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", state1.InitProcessPid))
  1479  	ok(t, err)
  1480  	ns2, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", state2.InitProcessPid))
  1481  	ok(t, err)
  1482  	if ns1 != ns2 {
  1483  		t.Errorf("pidns(%s), wanted %s", ns2, ns1)
  1484  	}
  1485  
  1486  	// check that namespaces are not the same
  1487  	if reflect.DeepEqual(state2.NamespacePaths, state1.NamespacePaths) {
  1488  		t.Errorf("Namespaces(%v), original %v", state2.NamespacePaths,
  1489  			state1.NamespacePaths)
  1490  	}
  1491  	// check that pidns is joined correctly. The initial container process list
  1492  	// should contain the second container's init process
  1493  	buffers := newStdBuffers()
  1494  	ps := &libcontainer.Process{
  1495  		Cwd:    "/",
  1496  		Args:   []string{"ps"},
  1497  		Env:    standardEnvironment,
  1498  		Stdout: buffers.Stdout,
  1499  	}
  1500  	err = container1.Run(ps)
  1501  	ok(t, err)
  1502  	waitProcess(ps, t)
  1503  
  1504  	// Stop init processes one by one. Stop the second container should
  1505  	// not stop the first.
  1506  	stdinW2.Close()
  1507  	waitProcess(init2, t)
  1508  	stdinW1.Close()
  1509  	waitProcess(init1, t)
  1510  
  1511  	out := strings.TrimSpace(buffers.Stdout.String())
  1512  	// output of ps inside the initial PID namespace should have
  1513  	// 1 line of header,
  1514  	// 2 lines of init processes,
  1515  	// 1 line of ps process
  1516  	if len(strings.Split(out, "\n")) != 4 {
  1517  		t.Errorf("unexpected running process, output %q", out)
  1518  	}
  1519  }
  1520  
  1521  func TestInitJoinNetworkAndUser(t *testing.T) {
  1522  	if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
  1523  		t.Skip("userns is unsupported")
  1524  	}
  1525  	if testing.Short() {
  1526  		return
  1527  	}
  1528  	rootfs, err := newRootfs()
  1529  	ok(t, err)
  1530  	defer remove(rootfs)
  1531  
  1532  	// Execute a long-running container
  1533  	config1 := newTemplateConfig(rootfs)
  1534  	config1.UidMappings = []configs.IDMap{{0, 0, 1000}}
  1535  	config1.GidMappings = []configs.IDMap{{0, 0, 1000}}
  1536  	config1.Namespaces = append(config1.Namespaces, configs.Namespace{Type: configs.NEWUSER})
  1537  	container1, err := newContainer(config1)
  1538  	ok(t, err)
  1539  	defer container1.Destroy()
  1540  
  1541  	stdinR1, stdinW1, err := os.Pipe()
  1542  	ok(t, err)
  1543  	init1 := &libcontainer.Process{
  1544  		Cwd:   "/",
  1545  		Args:  []string{"cat"},
  1546  		Env:   standardEnvironment,
  1547  		Stdin: stdinR1,
  1548  	}
  1549  	err = container1.Run(init1)
  1550  	stdinR1.Close()
  1551  	defer stdinW1.Close()
  1552  	ok(t, err)
  1553  
  1554  	// get the state of the first container
  1555  	state1, err := container1.State()
  1556  	ok(t, err)
  1557  	netns1 := state1.NamespacePaths[configs.NEWNET]
  1558  	userns1 := state1.NamespacePaths[configs.NEWUSER]
  1559  
  1560  	// Run a container inside the existing pidns but with different cgroups
  1561  	rootfs2, err := newRootfs()
  1562  	ok(t, err)
  1563  	defer remove(rootfs2)
  1564  
  1565  	config2 := newTemplateConfig(rootfs2)
  1566  	config2.UidMappings = []configs.IDMap{{0, 0, 1000}}
  1567  	config2.GidMappings = []configs.IDMap{{0, 0, 1000}}
  1568  	config2.Namespaces.Add(configs.NEWNET, netns1)
  1569  	config2.Namespaces.Add(configs.NEWUSER, userns1)
  1570  	config2.Cgroups.Path = "integration/test2"
  1571  	container2, err := newContainerWithName("testCT2", config2)
  1572  	ok(t, err)
  1573  	defer container2.Destroy()
  1574  
  1575  	stdinR2, stdinW2, err := os.Pipe()
  1576  	ok(t, err)
  1577  	init2 := &libcontainer.Process{
  1578  		Cwd:   "/",
  1579  		Args:  []string{"cat"},
  1580  		Env:   standardEnvironment,
  1581  		Stdin: stdinR2,
  1582  	}
  1583  	err = container2.Run(init2)
  1584  	stdinR2.Close()
  1585  	defer stdinW2.Close()
  1586  	ok(t, err)
  1587  
  1588  	// get the state of the second container
  1589  	state2, err := container2.State()
  1590  	ok(t, err)
  1591  
  1592  	for _, ns := range []string{"net", "user"} {
  1593  		ns1, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/%s", state1.InitProcessPid, ns))
  1594  		ok(t, err)
  1595  		ns2, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/%s", state2.InitProcessPid, ns))
  1596  		ok(t, err)
  1597  		if ns1 != ns2 {
  1598  			t.Errorf("%s(%s), wanted %s", ns, ns2, ns1)
  1599  		}
  1600  	}
  1601  
  1602  	// check that namespaces are not the same
  1603  	if reflect.DeepEqual(state2.NamespacePaths, state1.NamespacePaths) {
  1604  		t.Errorf("Namespaces(%v), original %v", state2.NamespacePaths,
  1605  			state1.NamespacePaths)
  1606  	}
  1607  	// Stop init processes one by one. Stop the second container should
  1608  	// not stop the first.
  1609  	stdinW2.Close()
  1610  	waitProcess(init2, t)
  1611  	stdinW1.Close()
  1612  	waitProcess(init1, t)
  1613  }
  1614  
  1615  func TestTmpfsCopyUp(t *testing.T) {
  1616  	if testing.Short() {
  1617  		return
  1618  	}
  1619  	root, err := newTestRoot()
  1620  	ok(t, err)
  1621  	defer os.RemoveAll(root)
  1622  
  1623  	rootfs, err := newRootfs()
  1624  	ok(t, err)
  1625  	defer remove(rootfs)
  1626  
  1627  	config := newTemplateConfig(rootfs)
  1628  
  1629  	config.Mounts = append(config.Mounts, &configs.Mount{
  1630  		Source:      "tmpfs",
  1631  		Destination: "/etc",
  1632  		Device:      "tmpfs",
  1633  		Extensions:  configs.EXT_COPYUP,
  1634  	})
  1635  
  1636  	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
  1637  	ok(t, err)
  1638  
  1639  	container, err := factory.Create("test", config)
  1640  	ok(t, err)
  1641  	defer container.Destroy()
  1642  
  1643  	var stdout bytes.Buffer
  1644  	pconfig := libcontainer.Process{
  1645  		Args:   []string{"ls", "/etc/passwd"},
  1646  		Env:    standardEnvironment,
  1647  		Stdin:  nil,
  1648  		Stdout: &stdout,
  1649  	}
  1650  	err = container.Run(&pconfig)
  1651  	ok(t, err)
  1652  
  1653  	// Wait for process
  1654  	waitProcess(&pconfig, t)
  1655  
  1656  	outputLs := string(stdout.Bytes())
  1657  
  1658  	// Check that the ls output has /etc/passwd
  1659  	if !strings.Contains(outputLs, "/etc/passwd") {
  1660  		t.Fatalf("/etc/passwd not copied up as expected: %v", outputLs)
  1661  	}
  1662  }