Ejemplo n.º 1
0
// This test checks that the we can reap a child process and obtain
// the correct exit status.
TEST(Reap, ChildProcess)
{
  ASSERT_TRUE(GTEST_IS_THREADSAFE);

  // The child process sleeps and will be killed by the parent.
  Try<ProcessTree> tree = Fork(None(),
                               Exec("sleep 10"))();

  ASSERT_SOME(tree);
  pid_t child = tree.get();

  // Reap the child process.
  Future<Option<int> > status = process::reap(child);

  // Now kill the child.
  EXPECT_EQ(0, kill(child, SIGKILL));

  Clock::pause();

  // Now advance time until the reaper reaps the child.
  while (status.isPending()) {
    Clock::advance(Seconds(1));
    Clock::settle();
  }

  AWAIT_READY(status);

  // Check if the status is correct.
  ASSERT_SOME(status.get());
  int status_ = status.get().get();
  ASSERT_TRUE(WIFSIGNALED(status_));
  ASSERT_EQ(SIGKILL, WTERMSIG(status_));

  Clock::resume();
}
Ejemplo n.º 2
0
// This test checks that we can reap a non-child process, in terms
// of receiving the termination notification.
TEST(Reap, NonChildProcess)
{
  // The child process creates a grandchild and then exits. The
  // grandchild sleeps for 10 seconds. The process tree looks like:
  //  -+- child exit 0
  //   \-+- grandchild sleep 10

  // After the child exits, the grandchild is going to be re-parented
  // by 'init', like this:
  //  -+- child (exit 0)
  //  -+- grandchild sleep 10
  Try<ProcessTree> tree = Fork(None(),
                               Fork(Exec("sleep 10")),
                               Exec("exit 0"))();
  ASSERT_SOME(tree);
  ASSERT_EQ(1u, tree.get().children.size());
  pid_t grandchild = tree.get().children.front();

  // Reap the grandchild process.
  Future<Option<int> > status = process::reap(grandchild);

  EXPECT_TRUE(status.isPending());

  // Now kill the grandchild.
  // NOTE: We send a SIGKILL here because sometimes the grandchild
  // process seems to be in a hung state and not responding to
  // SIGTERM/SIGINT.
  EXPECT_EQ(0, kill(grandchild, SIGKILL));

  Clock::pause();

  // Now advance time until the Reaper reaps the grandchild.
  while (status.isPending()) {
    Clock::advance(Seconds(1));
    Clock::settle();
  }

  AWAIT_READY(status);

  // The status is None because pid is not an immediate child.
  ASSERT_NONE(status.get()) << status.get().get();

  // Reap the child as well to clean up after ourselves.
  status = process::reap(tree.get().process.pid);

  // Now advance time until the child is reaped.
  while (status.isPending()) {
    Clock::advance(Seconds(1));
    Clock::settle();
  }

  // Check if the status is correct.
  ASSERT_SOME(status.get());
  int status_ = status.get().get();
  ASSERT_TRUE(WIFEXITED(status_));
  ASSERT_EQ(0, WEXITSTATUS(status_));

  Clock::resume();
}
Ejemplo n.º 3
0
TEST_F(OsTest, Children)
{
  Try<set<pid_t> > children = os::children(getpid());

  ASSERT_SOME(children);
  EXPECT_EQ(0u, children.get().size());

  Try<ProcessTree> tree =
    Fork(None(),                   // Child.
         Fork(Exec("sleep 10")),   // Grandchild.
         Exec("sleep 10"))();

  ASSERT_SOME(tree);
  ASSERT_EQ(1u, tree.get().children.size());

  pid_t child = tree.get().process.pid;
  pid_t grandchild = tree.get().children.front().process.pid;

  // Ensure the non-recursive children does not include the
  // grandchild.
  children = os::children(getpid(), false);

  ASSERT_SOME(children);
  EXPECT_EQ(1u, children.get().size());
  EXPECT_EQ(1u, children.get().count(child));

  children = os::children(getpid());

  ASSERT_SOME(children);

  // Depending on whether or not the shell has fork/exec'ed in each
  // above 'Exec', we could have 2 or 4 children. That is, some shells
  // might simply for exec the command above (i.e., 'sleep 10') while
  // others might fork/exec the command, keeping around a 'sh -c'
  // process as well.
  EXPECT_LE(2u, children.get().size());
  EXPECT_GE(4u, children.get().size());

  EXPECT_EQ(1u, children.get().count(child));
  EXPECT_EQ(1u, children.get().count(grandchild));

  // Cleanup by killing the descendant processes.
  EXPECT_EQ(0, kill(grandchild, SIGKILL));
  EXPECT_EQ(0, kill(child, SIGKILL));

  // We have to reap the child for running the tests in repetition.
  ASSERT_EQ(child, waitpid(child, NULL, 0));
}
Ejemplo n.º 4
0
// Check that we can reap a child process that is already exited.
TEST(Reap, TerminatedChildProcess)
{
  ASSERT_TRUE(GTEST_IS_THREADSAFE);

  // The child process immediately exits.
  Try<ProcessTree> tree = Fork(None(),
                               Exec("exit 0"))();

  ASSERT_SOME(tree);
  pid_t child = tree.get();

  ASSERT_SOME(os::process(child));

  // Make sure the process is transitioned into the zombie
  // state before we reap it.
  while (true) {
    const Result<os::Process>& process = os::process(child);
    ASSERT_SOME(process) << "Process " << child << " reaped unexpectedly";

    if (process.get().zombie) {
      break;
    }

    os::sleep(Milliseconds(1));
  }

  // Now that it's terminated, attempt to reap it.
  Future<Option<int> > status = process::reap(child);

  // Advance time until the reaper sends the notification.
  Clock::pause();
  while (status.isPending()) {
    Clock::advance(Seconds(1));
    Clock::settle();
  }

  AWAIT_READY(status);

  // Expect to get the correct status.
  ASSERT_SOME(status.get());

  int status_ = status.get().get();
  ASSERT_TRUE(WIFEXITED(status_));
  ASSERT_EQ(0, WEXITSTATUS(status_));

  Clock::resume();
}
Ejemplo n.º 5
0
TEST_F(OsTest, Pstree)
{
  Try<ProcessTree> tree = os::pstree(getpid());

  ASSERT_SOME(tree);
  EXPECT_EQ(0u, tree.get().children.size()) << stringify(tree.get());

  tree =
    Fork(None(),                   // Child.
         Fork(Exec("sleep 10")),   // Grandchild.
         Exec("sleep 10"))();

  ASSERT_SOME(tree);

  // Depending on whether or not the shell has fork/exec'ed,
  // we could have 1 or 2 direct children. That is, some shells
  // might simply exec the command above (i.e., 'sleep 10') while
  // others might fork/exec the command, keeping around a 'sh -c'
  // process as well.
  ASSERT_LE(1u, tree.get().children.size());
  ASSERT_GE(2u, tree.get().children.size());

  pid_t child = tree.get().process.pid;
  pid_t grandchild = tree.get().children.front().process.pid;

  // Now check pstree again.
  tree = os::pstree(child);

  ASSERT_SOME(tree);
  EXPECT_EQ(child, tree.get().process.pid);

  ASSERT_LE(1u, tree.get().children.size());
  ASSERT_GE(2u, tree.get().children.size());

  // Cleanup by killing the descendant processes.
  EXPECT_EQ(0, kill(grandchild, SIGKILL));
  EXPECT_EQ(0, kill(child, SIGKILL));

  // We have to reap the child for running the tests in repetition.
  ASSERT_EQ(child, waitpid(child, NULL, 0));
}
Ejemplo n.º 6
0
TEST_F(OsTest, KilltreeNoRoot)
{
  Try<ProcessTree> tree =
    Fork(&dosetsid,       // Child.
         Fork(None(),     // Grandchild.
              Fork(None(),
                   Exec("sleep 100")),
              Exec("sleep 100")),
         Exec("exit 0"))();
  ASSERT_SOME(tree);

  // The process tree we instantiate initially looks like this:
  //
  // -+- child exit 0             [new session and process group leader]
  //  \-+- grandchild sleep 100
  //   \-+- great grandchild sleep 100
  //
  // But becomes the following tree after the child exits:
  //
  // -+- child (exited 0)
  //  \-+- grandchild sleep 100
  //   \-+- great grandchild sleep 100
  //
  // And gets reparented when we reap the child:
  //
  // -+- new parent
  //  \-+- grandchild sleep 100
  //   \-+- great grandchild sleep 100

  // Grab the pids from the instantiated process tree.
  ASSERT_EQ(1u, tree.get().children.size());
  ASSERT_EQ(1u, tree.get().children.front().children.size());

  pid_t child = tree.get();
  pid_t grandchild = tree.get().children.front();
  pid_t greatGrandchild = tree.get().children.front().children.front();

  // Wait for the child to exit.
  Duration elapsed = Duration::zero();
  while (true) {
    Result<os::Process> process = os::process(child);
    ASSERT_FALSE(process.isError());

    if (process.get().zombie) {
      break;
    }

    if (elapsed > Seconds(1)) {
      FAIL() << "Child process " << stringify(child) << " did not terminate";
    }

    os::sleep(Milliseconds(5));
    elapsed += Milliseconds(5);
  }

  // Ensure we reap our child now.
  EXPECT_SOME(os::process(child));
  EXPECT_TRUE(os::process(child).get().zombie);
  ASSERT_EQ(child, waitpid(child, NULL, 0));

  // Check the grandchild and great grandchild are still running.
  ASSERT_TRUE(os::exists(grandchild));
  ASSERT_TRUE(os::exists(greatGrandchild));

  // Check the subtree has been reparented: the parent is no longer
  // child (the root of the tree), and that the process is not a
  // zombie. This is done because some systems run a secondary init
  // process for the user (init --user) that does not have pid 1,
  // meaning we can't just check that the parent pid == 1.
  Result<os::Process> _grandchild = os::process(grandchild);
  ASSERT_SOME(_grandchild);
  ASSERT_NE(child, _grandchild.get().parent);
  ASSERT_FALSE(_grandchild.get().zombie);

  // Check to see if we're in a jail on FreeBSD in case we've been
  // reparented to pid 1
#if __FreeBSD__
  if (!isJailed()) {
#endif
  // Check that grandchild's parent is also not a zombie.
  Result<os::Process> currentParent = os::process(_grandchild.get().parent);
  ASSERT_SOME(currentParent);
  ASSERT_FALSE(currentParent.get().zombie);
#ifdef __FreeBSD__
  }
#endif


  // Kill the process tree. Even though the root process has exited,
  // we specify to follow sessions and groups which should kill the
  // grandchild and greatgrandchild.
  Try<list<ProcessTree>> trees = os::killtree(child, SIGKILL, true, true);

  ASSERT_SOME(trees);
  EXPECT_FALSE(trees.get().empty());

  // All processes should be reparented and reaped by init.
  elapsed = Duration::zero();
  while (true) {
    if (os::process(grandchild).isNone() &&
        os::process(greatGrandchild).isNone()) {
      break;
    }

    if (elapsed > Seconds(10)) {
      FAIL() << "Processes were not reaped after killtree invocation";
    }

    os::sleep(Milliseconds(5));
    elapsed += Milliseconds(5);
  }

  EXPECT_NONE(os::process(grandchild));
  EXPECT_NONE(os::process(greatGrandchild));
}
Ejemplo n.º 7
0
TEST_F(OsTest, Killtree)
{
  Try<ProcessTree> tree =
    Fork(&dosetsid,                        // Child.
         Fork(None(),                      // Grandchild.
              Fork(None(),                 // Great-grandchild.
                   Fork(&dosetsid,         // Great-great-granchild.
                        Exec("sleep 10")),
                   Exec("sleep 10")),
              Exec("exit 0")),
         Exec("sleep 10"))();

  ASSERT_SOME(tree);

  // The process tree we instantiate initially looks like this:
  //
  //  -+- child sleep 10
  //   \-+- grandchild exit 0
  //     \-+- greatGrandchild sleep 10
  //       \--- greatGreatGrandchild sleep 10
  //
  // But becomes two process trees after the grandchild exits:
  //
  //  -+- child sleep 10
  //   \--- grandchild (exit 0)
  //
  //  -+- greatGrandchild sleep 10
  //   \--- greatGreatGrandchild sleep 10

  // Grab the pids from the instantiated process tree.
  ASSERT_EQ(1u, tree.get().children.size());
  ASSERT_EQ(1u, tree.get().children.front().children.size());
  ASSERT_EQ(1u, tree.get().children.front().children.front().children.size());

  pid_t child = tree.get();
  pid_t grandchild = tree.get().children.front();
  pid_t greatGrandchild = tree.get().children.front().children.front();
  pid_t greatGreatGrandchild =
    tree.get().children.front().children.front().children.front();

  // Now wait for the grandchild to exit splitting the process tree.
  Duration elapsed = Duration::zero();
  while (true) {
    Result<os::Process> process = os::process(grandchild);

    ASSERT_FALSE(process.isError());

    if (process.isNone() || process.get().zombie) {
      break;
    }

    if (elapsed > Seconds(10)) {
      FAIL() << "Granchild process '" << process.get().pid << "' "
             << "(" << process.get().command << ") did not terminate";
    }

    os::sleep(Milliseconds(5));
    elapsed += Milliseconds(5);
  }

  // Kill the process tree and follow sessions and groups to make sure
  // we cross the broken link due to the grandchild.
  Try<list<ProcessTree> > trees =
    os::killtree(child, SIGKILL, true, true);

  ASSERT_SOME(trees);

  EXPECT_EQ(2u, trees.get().size()) << stringify(trees.get());

  foreach (const ProcessTree& tree, trees.get()) {
    if (tree.process.pid == child) {
      // The 'grandchild' _might_ still be in the tree, just zombied,
      // unless the 'child' reaps the 'grandchild', which may happen
      // if the shell "sticks around" (i.e., some invocations of 'sh
      // -c' will 'exec' the command which will likely not do any
      // reaping, but in other cases an invocation of 'sh -c' will not
      // 'exec' the command, for example when the command is a
      // sequence of commands separated by ';').
      EXPECT_FALSE(tree.contains(greatGrandchild)) << tree;
      EXPECT_FALSE(tree.contains(greatGreatGrandchild)) << tree;
    } else if (tree.process.pid == greatGrandchild) {
      EXPECT_TRUE(tree.contains(greatGreatGrandchild)) << tree;
    } else {
      FAIL()
        << "Not expecting a process tree rooted at "
        << tree.process.pid << "\n" << tree;
    }
  }

  // All processes should be reaped since we've killed everything.
  // The direct child must be reaped by us below.
  elapsed = Duration::zero();
  while (true) {
    Result<os::Process> _child = os::process(child);
    ASSERT_SOME(_child);

    if (os::process(greatGreatGrandchild).isNone() &&
        os::process(greatGrandchild).isNone() &&
        os::process(grandchild).isNone() &&
        _child.get().zombie) {
      break;
    }

    if (elapsed > Seconds(10)) {
      FAIL() << "Processes were not reaped after killtree invocation";
    }

    os::sleep(Milliseconds(5));
    elapsed += Milliseconds(5);
  }

  // Expect the pids to be wiped!
  EXPECT_NONE(os::process(greatGreatGrandchild));
  EXPECT_NONE(os::process(greatGrandchild));
  EXPECT_NONE(os::process(grandchild));
  EXPECT_SOME(os::process(child));
  EXPECT_TRUE(os::process(child).get().zombie);

  // We have to reap the child for running the tests in repetition.
  ASSERT_EQ(child, waitpid(child, NULL, 0));
}
Ejemplo n.º 8
0
bool LyXComm::pipeServer()
{
	DWORD i;
	DWORD error;

	for (i = 0; i < MAX_PIPES; ++i) {
		bool const is_outpipe = i >= MAX_CLIENTS;
		DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
						   : PIPE_ACCESS_INBOUND;
		string const pipename = external_path(pipeName(i));

		// Manual-reset event, initial state = signaled
		event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
		if (!event_[i]) {
			error = GetLastError();
			lyxerr << "LyXComm: Could not create event for pipe "
			       << pipename << "\nLyXComm: "
			       << errormsg(error) << endl;
			return false;
		}

		pipe_[i].overlap.hEvent = event_[i];
		pipe_[i].iobuf.erase();
		pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
				open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
				MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
				PIPE_TIMEOUT, NULL);

		if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
			error = GetLastError();
			lyxerr << "LyXComm: Could not create pipe "
			       << pipename << "\nLyXComm: "
			       << errormsg(error) << endl;
			return false;
		}

		if (!startPipe(i))
			return false;
		pipe_[i].state = pipe_[i].pending_io ?
			CONNECTING_STATE : (is_outpipe ? WRITING_STATE
						       : READING_STATE);
	}

	// Add the stopserver_ event
	event_[MAX_PIPES] = stopserver_;

	// We made it!
	LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
	ready_ = true;
	outbuf_.erase();
	DWORD status;
	bool success;

	while (!checkStopServer()) {
		// Indefinitely wait for the completion of an overlapped
		// read, write, or connect operation.
		DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_,
						    FALSE, INFINITE);

		// Determine which pipe instance completed the operation.
		i = wait - WAIT_OBJECT_0;
		LASSERT(i >= 0 && i <= MAX_PIPES, /**/);

		// Check whether we were waked up for stopping the pipe server.
		if (i == MAX_PIPES)
			break;

		bool const is_outpipe = i >= MAX_CLIENTS;

		// Get the result if the operation was pending.
		if (pipe_[i].pending_io) {
			success = GetOverlappedResult(pipe_[i].handle,
					&pipe_[i].overlap, &status, FALSE);

			switch (pipe_[i].state) {
			case CONNECTING_STATE:
				// Pending connect operation
				if (!success) {
					error = GetLastError();
					lyxerr << "LyXComm: "
					       << errormsg(error) << endl;
					if (!resetPipe(i, true))
						return false;
					continue;
				}
				pipe_[i].state = is_outpipe ? WRITING_STATE
							    : READING_STATE;
				break;

			case READING_STATE:
				// Pending read operation
				LASSERT(!is_outpipe, /**/);
				if (!success || status == 0) {
					if (!resetPipe(i, !success))
						return false;
					continue;
				}
				pipe_[i].nbytes = status;
				pipe_[i].state = WRITING_STATE;
				break;

			case WRITING_STATE:
				// Pending write operation
				LASSERT(is_outpipe, /**/);
				// Let's see whether we have a reply
				if (!outbuf_.empty()) {
					// Yep. Deliver it to all pipe
					// instances if we get ownership
					// of the mutex, otherwise we'll
					// try again the next round.
					DWORD result = WaitForSingleObject(
							outbuf_mutex_, 200);
					if (result == WAIT_OBJECT_0) {
						DWORD j = MAX_CLIENTS;
						while (j < MAX_PIPES) {
							pipe_[j].iobuf = outbuf_;
							++j;
						}
						outbuf_.erase();
					}
					ReleaseMutex(outbuf_mutex_);
				}
				if (pipe_[i].iobuf.empty())
					pipe_[i].pending_io = false;
				break;
			}
		}

		// Operate according to the pipe state
		switch (pipe_[i].state) {
		case READING_STATE:
			// The pipe instance is connected to a client
			// and is ready to read a request.
			LASSERT(!is_outpipe, /**/);
			success = ReadFile(pipe_[i].handle,
					pipe_[i].readbuf, PIPE_BUFSIZE - 1,
					&pipe_[i].nbytes, &pipe_[i].overlap);

			if (success && pipe_[i].nbytes != 0) {
				// The read operation completed successfully.
				pipe_[i].pending_io = false;
				pipe_[i].state = WRITING_STATE;
				continue;
			}

			error = GetLastError();

			if (!success && error == ERROR_IO_PENDING) {
				// The read operation is still pending.
				pipe_[i].pending_io = true;
				continue;
			}

			success = error == ERROR_BROKEN_PIPE;

			// Client closed connection (ERROR_BROKEN_PIPE) or
			// an error occurred; in either case, reset the pipe.
			if (!success) {
				lyxerr << "LyXComm: " << errormsg(error) << endl;
				if (!pipe_[i].iobuf.empty()) {
					lyxerr << "LyXComm: truncated command: "
					       << pipe_[i].iobuf << endl;
					pipe_[i].iobuf.erase();
				}
			}
			if (!resetPipe(i, !success))
				return false;
			break;

		case WRITING_STATE:
			if (!is_outpipe) {
				// The request was successfully read
				// from the client; commit it.
				ReadReadyEvent * event = new ReadReadyEvent(i);
				QCoreApplication::postEvent(this,
						static_cast<QEvent *>(event));
				// Wait for completion
				while (pipe_[i].nbytes && !checkStopServer(100))
					;
				pipe_[i].pending_io = false;
				pipe_[i].state = READING_STATE;
				continue;
			}

			// This is an output pipe instance. Initiate the
			// overlapped write operation or monitor its progress.

			if (pipe_[i].pending_io) {
				success = WriteFile(pipe_[i].handle,
						pipe_[i].iobuf.c_str(),
						pipe_[i].iobuf.length(),
						&status,
						&pipe_[i].overlap);
			}

			if (success && !pipe_[i].iobuf.empty()
			    && status == pipe_[i].iobuf.length()) {
				// The write operation completed successfully.
				pipe_[i].iobuf.erase();
				pipe_[i].pending_io = false;
				if (!resetPipe(i))
					return false;
				continue;
			}

			error = GetLastError();

			if (success && error == ERROR_IO_PENDING) {
				// The write operation is still pending.
				// We get here when a reader is started
				// well before a reply is ready, so delay
				// a bit in order to not burden the cpu.
				checkStopServer(100);
				pipe_[i].pending_io = true;
				continue;
			}

			success = error == ERROR_NO_DATA;

			// Client closed connection (ERROR_NO_DATA) or
			// an error occurred; in either case, reset the pipe.
			if (!success) {
				lyxerr << "LyXComm: Error sending message: "
				       << pipe_[i].iobuf << "\nLyXComm: "
				       << errormsg(error) << endl;
			}
			if (!resetPipe(i, !success))
				return false;
			break;
		}
	}