text: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include <string>
  #include <vector>
  
  #include <gmock/gmock.h>
  
  #include <stout/gtest.hpp>
  #include <stout/none.hpp>
  #include <stout/option.hpp>
  #include <stout/os.hpp>
  #include <stout/try.hpp>
  
  #include <process/gtest.hpp>
  #include <process/reap.hpp>
  #include <process/subprocess.hpp>
  
  #include "slave/containerizer/mesos/launch.hpp"
  
  #include "tests/flags.hpp"
  #include "tests/utils.hpp"
  
  #include "tests/containerizer/rootfs.hpp"
  
  using namespace process;
  
  using std::string;
  using std::vector;
  
  namespace mesos {
  namespace internal {
  namespace tests {
  
  // TODO(jieyu): Move this test to mesos_containerizer_tests.cpp once
  // we have a filesystem isolator that supports changing rootfs.
  
  class MesosContainerizerLaunchTest : public TemporaryDirectoryTest
  {
  public:
    Try<Subprocess> run(
        const string& _command,
        const Option<string>& rootfs = None())
    {
      slave::MesosContainerizerLaunch::Flags launchFlags;
  
      CommandInfo command;
      command.set_value(_command);
  
      launchFlags.command = JSON::Protobuf(command);
      launchFlags.directory = "/tmp";
      launchFlags.pipe_read = open("/dev/zero", O_RDONLY);
      launchFlags.pipe_write = open("/dev/null", O_WRONLY);
      launchFlags.rootfs = rootfs;
  
      vector<string> argv(2);
      argv[0] = "mesos-containerizer";
      argv[1] = slave::MesosContainerizerLaunch::NAME;
  
      Try<Subprocess> s = subprocess(
          path::join(tests::flags.build_dir, "src", "mesos-containerizer"),
          argv,
          Subprocess::PATH("/dev/null"),
          Subprocess::FD(STDOUT_FILENO),
          Subprocess::FD(STDERR_FILENO),
          launchFlags,
          None(),
          None(),
          lambda::bind(&clone, lambda::_1));
  
      close(launchFlags.pipe_read.get());
      close(launchFlags.pipe_write.get());
  
      return s;
    }
  
  private:
    static pid_t clone(const lambda::function<int()>& f)
    {
      static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)];
  
      return ::clone(
          _clone,
          &stack[sizeof(stack)/sizeof(stack[0]) - 1],  // Stack grows down.
          CLONE_NEWNS | SIGCHLD,   // Specify SIGCHLD as child termination signal.
          (void*) &f);
    }
  
    static int _clone(void* f)
    {
      const lambda::function<int()>* _f =
        static_cast<const lambda::function<int()>*> (f);
  
      return (*_f)();
    }
  };
  
  
  TEST_F(MesosContainerizerLaunchTest, ROOT_ChangeRootfs)
  {
    Try<Owned<Rootfs>> rootfs =
      LinuxRootfs::create(path::join(os::getcwd(), "rootfs"));
  
    ASSERT_SOME(rootfs);
  
    // Add /usr/bin/stat into the rootfs.
    ASSERT_SOME(rootfs.get()->add("/usr/bin/stat"));
  
    Clock::pause();
  
    Try<Subprocess> s = run(
        "/usr/bin/stat -c %i / >" + path::join("/", "stat.output"),
        rootfs.get()->root);
  
    ASSERT_SOME(s);
  
    // Advance time until the internal reaper reaps the subprocess.
    while (s.get().status().isPending()) {
      Clock::advance(Seconds(1));
      Clock::settle();
    }
  
    AWAIT_ASSERT_READY(s.get().status());
    ASSERT_SOME(s.get().status().get());
  
    int status = s.get().status().get().get();
    ASSERT_TRUE(WIFEXITED(status));
    ASSERT_EQ(0, WEXITSTATUS(status));
  
    // Check the rootfs has a different root by comparing the inodes.
    Try<ino_t> self = os::stat::inode("/");
    ASSERT_SOME(self);
  
    Try<string> read = os::read(path::join(rootfs.get()->root, "stat.output"));
    ASSERT_SOME(read);
  
    Try<ino_t> other = numify<ino_t>(strings::trim(read.get()));
    ASSERT_SOME(other);
  
    EXPECT_NE(self.get(), other.get());
  }
  
  } // namespace tests {
  } // namespace internal {
  } // namespace mesos {
  