You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
404 lines
12 KiB
404 lines
12 KiB
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "brillo/process.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <base/files/file_path.h>
|
|
#include <base/files/file_util.h>
|
|
#include <base/files/scoped_temp_dir.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "brillo/process_mock.h"
|
|
#include "brillo/test_helpers.h"
|
|
#include "brillo/unittest_utils.h"
|
|
|
|
using base::FilePath;
|
|
|
|
// This test assumes the following standard binaries are installed.
|
|
#if defined(__ANDROID__)
|
|
# define SYSTEM_PREFIX "/system"
|
|
static const char kBinStat[] = SYSTEM_PREFIX "/bin/stat";
|
|
#else
|
|
# define SYSTEM_PREFIX ""
|
|
static const char kBinStat[] = "/usr/bin/stat";
|
|
#endif
|
|
|
|
static const char kBinSh[] = SYSTEM_PREFIX "/bin/sh";
|
|
static const char kBinCat[] = SYSTEM_PREFIX "/bin/cat";
|
|
static const char kBinEcho[] = SYSTEM_PREFIX "/bin/echo";
|
|
static const char kBinFalse[] = SYSTEM_PREFIX "/bin/false";
|
|
static const char kBinSleep[] = SYSTEM_PREFIX "/bin/sleep";
|
|
static const char kBinTrue[] = SYSTEM_PREFIX "/bin/true";
|
|
|
|
namespace brillo {
|
|
|
|
// Test that the mock has all the functions of the interface by
|
|
// instantiating it. This variable is not used elsewhere.
|
|
struct CompileMocks {
|
|
ProcessMock process_mock;
|
|
};
|
|
|
|
TEST(SimpleProcess, Basic) {
|
|
// Log must be cleared before running this test, just as ProcessTest::SetUp.
|
|
ClearLog();
|
|
ProcessImpl process;
|
|
process.AddArg(kBinEcho);
|
|
EXPECT_EQ(0, process.Run());
|
|
EXPECT_EQ("", GetLog());
|
|
}
|
|
|
|
TEST(SimpleProcess, NoSearchPath) {
|
|
ProcessImpl process;
|
|
process.AddArg("echo");
|
|
EXPECT_EQ(127, process.Run());
|
|
}
|
|
|
|
TEST(SimpleProcess, SearchPath) {
|
|
ProcessImpl process;
|
|
process.AddArg("echo");
|
|
process.SetSearchPath(true);
|
|
EXPECT_EQ(EXIT_SUCCESS, process.Run());
|
|
}
|
|
|
|
TEST(SimpleProcess, BindFd) {
|
|
int fds[2];
|
|
char buf[16];
|
|
static const char* kMsg = "hello, world!";
|
|
ProcessImpl process;
|
|
EXPECT_EQ(0, pipe(fds));
|
|
process.AddArg(kBinEcho);
|
|
process.AddArg(kMsg);
|
|
process.BindFd(fds[1], 1);
|
|
process.Run();
|
|
memset(buf, 0, sizeof(buf));
|
|
EXPECT_EQ(read(fds[0], buf, sizeof(buf) - 1), strlen(kMsg) + 1);
|
|
EXPECT_EQ(std::string(kMsg) + "\n", std::string(buf));
|
|
}
|
|
|
|
// The test framework uses the device's dash shell as "sh", which doesn't
|
|
// support redirecting stdout to arbitrary large file descriptor numbers
|
|
// directly, nor has /proc mounted to open /proc/self/fd/NN. This test would
|
|
// fail if pipe.writer is big enough.
|
|
// TODO(deymo): Write a helper program that writes "hello_world" to the passed
|
|
// file descriptor and re-enabled this test.
|
|
TEST(DISABLED_SimpleProcess, BindFdToSameFd) {
|
|
static const char* kMsg = "hello_world";
|
|
ScopedPipe pipe;
|
|
ProcessImpl process;
|
|
process.AddArg(kBinSh);
|
|
process.AddArg("-c");
|
|
process.AddArg(base::StringPrintf("echo %s >&%d", kMsg, pipe.writer));
|
|
process.BindFd(pipe.writer, pipe.writer);
|
|
process.Run();
|
|
close(pipe.writer);
|
|
pipe.writer = -1;
|
|
|
|
char buf[16];
|
|
memset(buf, 0, sizeof(buf));
|
|
EXPECT_EQ(read(pipe.reader, buf, sizeof(buf) - 1), strlen(kMsg) + 1);
|
|
EXPECT_EQ(std::string(kMsg) + "\n", std::string(buf));
|
|
}
|
|
|
|
class ProcessTest : public ::testing::Test {
|
|
public:
|
|
void SetUp() {
|
|
CHECK(temp_dir_.CreateUniqueTempDir());
|
|
output_file_ = temp_dir_.GetPath().Append("fork_out").value();
|
|
process_.RedirectOutput(output_file_);
|
|
ClearLog();
|
|
}
|
|
|
|
static void SetUpTestCase() {
|
|
base::CommandLine::Init(0, nullptr);
|
|
::brillo::InitLog(brillo::kLogToStderr);
|
|
::brillo::LogToString(true);
|
|
}
|
|
|
|
protected:
|
|
void CheckStderrCaptured();
|
|
FilePath GetFdPath(int fd);
|
|
|
|
ProcessImpl process_;
|
|
std::vector<const char*> args_;
|
|
std::string output_file_;
|
|
base::ScopedTempDir temp_dir_;
|
|
};
|
|
|
|
TEST_F(ProcessTest, Basic) {
|
|
process_.AddArg(kBinEcho);
|
|
process_.AddArg("hello world");
|
|
EXPECT_EQ(0, process_.Run());
|
|
ExpectFileEquals("hello world\n", output_file_.c_str());
|
|
EXPECT_EQ("", GetLog());
|
|
}
|
|
|
|
TEST_F(ProcessTest, AddStringOption) {
|
|
process_.AddArg(kBinEcho);
|
|
process_.AddStringOption("--hello", "world");
|
|
EXPECT_EQ(0, process_.Run());
|
|
ExpectFileEquals("--hello world\n", output_file_.c_str());
|
|
}
|
|
|
|
TEST_F(ProcessTest, AddIntValue) {
|
|
process_.AddArg(kBinEcho);
|
|
process_.AddIntOption("--answer", 42);
|
|
EXPECT_EQ(0, process_.Run());
|
|
ExpectFileEquals("--answer 42\n", output_file_.c_str());
|
|
}
|
|
|
|
TEST_F(ProcessTest, NonZeroReturnValue) {
|
|
process_.AddArg(kBinFalse);
|
|
EXPECT_EQ(1, process_.Run());
|
|
ExpectFileEquals("", output_file_.c_str());
|
|
EXPECT_EQ("", GetLog());
|
|
}
|
|
|
|
TEST_F(ProcessTest, RedirectInputDevNull) {
|
|
process_.AddArg(kBinCat);
|
|
process_.RedirectInput("/dev/null");
|
|
EXPECT_EQ(0, process_.Run());
|
|
}
|
|
|
|
TEST_F(ProcessTest, BadInputFile) {
|
|
process_.AddArg(kBinCat);
|
|
process_.RedirectInput("/bad/path");
|
|
EXPECT_EQ(static_cast<pid_t>(Process::kErrorExitStatus), process_.Run());
|
|
}
|
|
|
|
TEST_F(ProcessTest, BadOutputFile) {
|
|
process_.AddArg(kBinEcho);
|
|
process_.RedirectOutput("/bad/path");
|
|
EXPECT_EQ(static_cast<pid_t>(Process::kErrorExitStatus), process_.Run());
|
|
}
|
|
|
|
TEST_F(ProcessTest, BadExecutable) {
|
|
process_.AddArg("false");
|
|
EXPECT_EQ(static_cast<pid_t>(Process::kErrorExitStatus), process_.Run());
|
|
}
|
|
|
|
void ProcessTest::CheckStderrCaptured() {
|
|
process_.AddArg(kBinSh);
|
|
process_.AddArg("-c");
|
|
process_.AddArg("echo errormessage 1>&2 && exit 1");
|
|
EXPECT_EQ(1, process_.Run());
|
|
std::string contents;
|
|
EXPECT_TRUE(base::ReadFileToString(FilePath(output_file_), &contents));
|
|
EXPECT_NE(std::string::npos, contents.find("errormessage"));
|
|
EXPECT_EQ("", GetLog());
|
|
}
|
|
|
|
TEST_F(ProcessTest, StderrCaptured) {
|
|
CheckStderrCaptured();
|
|
}
|
|
|
|
TEST_F(ProcessTest, StderrCapturedWhenPreviouslyClosed) {
|
|
int saved_stderr = dup(STDERR_FILENO);
|
|
close(STDERR_FILENO);
|
|
CheckStderrCaptured();
|
|
dup2(saved_stderr, STDERR_FILENO);
|
|
}
|
|
|
|
FilePath ProcessTest::GetFdPath(int fd) {
|
|
return FilePath(base::StringPrintf("/proc/self/fd/%d", fd));
|
|
}
|
|
|
|
TEST_F(ProcessTest, RedirectStderrUsingPipe) {
|
|
process_.RedirectOutput("");
|
|
process_.AddArg(kBinSh);
|
|
process_.AddArg("-c");
|
|
process_.AddArg("echo errormessage >&2 && exit 1");
|
|
process_.RedirectUsingPipe(STDERR_FILENO, false);
|
|
EXPECT_EQ(-1, process_.GetPipe(STDERR_FILENO));
|
|
EXPECT_EQ(1, process_.Run());
|
|
int pipe_fd = process_.GetPipe(STDERR_FILENO);
|
|
EXPECT_GE(pipe_fd, 0);
|
|
EXPECT_EQ(-1, process_.GetPipe(STDOUT_FILENO));
|
|
EXPECT_EQ(-1, process_.GetPipe(STDIN_FILENO));
|
|
std::string contents;
|
|
EXPECT_TRUE(base::ReadFileToString(GetFdPath(pipe_fd), &contents));
|
|
EXPECT_NE(std::string::npos, contents.find("errormessage"));
|
|
EXPECT_EQ("", GetLog());
|
|
}
|
|
|
|
TEST_F(ProcessTest, RedirectStderrUsingPipeWhenPreviouslyClosed) {
|
|
int saved_stderr = dup(STDERR_FILENO);
|
|
close(STDERR_FILENO);
|
|
process_.RedirectOutput("");
|
|
process_.AddArg(kBinSh);
|
|
process_.AddArg("-c");
|
|
process_.AddArg("echo errormessage >&2 && exit 1");
|
|
process_.RedirectUsingPipe(STDERR_FILENO, false);
|
|
EXPECT_EQ(1, process_.Run());
|
|
int pipe_fd = process_.GetPipe(STDERR_FILENO);
|
|
EXPECT_GE(pipe_fd, 0);
|
|
EXPECT_EQ(-1, process_.GetPipe(STDOUT_FILENO));
|
|
EXPECT_EQ(-1, process_.GetPipe(STDIN_FILENO));
|
|
std::string contents;
|
|
EXPECT_TRUE(base::ReadFileToString(GetFdPath(pipe_fd), &contents));
|
|
EXPECT_NE(std::string::npos, contents.find("errormessage"));
|
|
EXPECT_EQ("", GetLog());
|
|
dup2(saved_stderr, STDERR_FILENO);
|
|
}
|
|
|
|
TEST_F(ProcessTest, RedirectStdoutUsingPipe) {
|
|
process_.RedirectOutput("");
|
|
process_.AddArg(kBinEcho);
|
|
process_.AddArg("hello world\n");
|
|
process_.RedirectUsingPipe(STDOUT_FILENO, false);
|
|
EXPECT_EQ(-1, process_.GetPipe(STDOUT_FILENO));
|
|
EXPECT_EQ(0, process_.Run());
|
|
int pipe_fd = process_.GetPipe(STDOUT_FILENO);
|
|
EXPECT_GE(pipe_fd, 0);
|
|
EXPECT_EQ(-1, process_.GetPipe(STDERR_FILENO));
|
|
EXPECT_EQ(-1, process_.GetPipe(STDIN_FILENO));
|
|
std::string contents;
|
|
EXPECT_TRUE(base::ReadFileToString(GetFdPath(pipe_fd), &contents));
|
|
EXPECT_NE(std::string::npos, contents.find("hello world\n"));
|
|
EXPECT_EQ("", GetLog());
|
|
}
|
|
|
|
TEST_F(ProcessTest, RedirectStdinUsingPipe) {
|
|
const char kMessage[] = "made it!\n";
|
|
process_.AddArg(kBinCat);
|
|
process_.RedirectUsingPipe(STDIN_FILENO, true);
|
|
process_.RedirectOutput(output_file_);
|
|
EXPECT_TRUE(process_.Start());
|
|
int write_fd = process_.GetPipe(STDIN_FILENO);
|
|
EXPECT_EQ(-1, process_.GetPipe(STDERR_FILENO));
|
|
EXPECT_TRUE(base::WriteFile(GetFdPath(write_fd), kMessage, strlen(kMessage)));
|
|
close(write_fd);
|
|
EXPECT_EQ(0, process_.Wait());
|
|
ExpectFileEquals(kMessage, output_file_.c_str());
|
|
}
|
|
|
|
TEST_F(ProcessTest, WithSameUid) {
|
|
gid_t uid = geteuid();
|
|
process_.AddArg(kBinEcho);
|
|
process_.SetUid(uid);
|
|
EXPECT_EQ(0, process_.Run());
|
|
}
|
|
|
|
TEST_F(ProcessTest, WithSameGid) {
|
|
gid_t gid = getegid();
|
|
process_.AddArg(kBinEcho);
|
|
process_.SetGid(gid);
|
|
EXPECT_EQ(0, process_.Run());
|
|
}
|
|
|
|
TEST_F(ProcessTest, WithIllegalUid) {
|
|
ASSERT_NE(0, geteuid());
|
|
process_.AddArg(kBinEcho);
|
|
process_.SetUid(0);
|
|
EXPECT_EQ(static_cast<pid_t>(Process::kErrorExitStatus), process_.Run());
|
|
std::string contents;
|
|
EXPECT_TRUE(base::ReadFileToString(FilePath(output_file_), &contents));
|
|
EXPECT_NE(std::string::npos, contents.find("Unable to set UID to 0: 1\n"));
|
|
}
|
|
|
|
TEST_F(ProcessTest, WithIllegalGid) {
|
|
ASSERT_NE(0, getegid());
|
|
process_.AddArg(kBinEcho);
|
|
process_.SetGid(0);
|
|
EXPECT_EQ(static_cast<pid_t>(Process::kErrorExitStatus), process_.Run());
|
|
std::string contents;
|
|
EXPECT_TRUE(base::ReadFileToString(FilePath(output_file_), &contents));
|
|
EXPECT_NE(std::string::npos, contents.find("Unable to set GID to 0: 1\n"));
|
|
}
|
|
|
|
TEST_F(ProcessTest, NoParams) {
|
|
EXPECT_EQ(-1, process_.Run());
|
|
}
|
|
|
|
#if !defined(__BIONIC__) // Bionic intercepts the segfault on Android.
|
|
TEST_F(ProcessTest, SegFaultHandling) {
|
|
process_.AddArg(kBinSh);
|
|
process_.AddArg("-c");
|
|
process_.AddArg("kill -SEGV $$");
|
|
EXPECT_EQ(-1, process_.Run());
|
|
EXPECT_TRUE(FindLog("did not exit normally: 11"));
|
|
}
|
|
#endif
|
|
|
|
TEST_F(ProcessTest, KillHandling) {
|
|
process_.AddArg(kBinSh);
|
|
process_.AddArg("-c");
|
|
process_.AddArg("kill -KILL $$");
|
|
EXPECT_EQ(-1, process_.Run());
|
|
EXPECT_TRUE(FindLog("did not exit normally: 9"));
|
|
}
|
|
|
|
|
|
TEST_F(ProcessTest, KillNoPid) {
|
|
process_.Kill(SIGTERM, 0);
|
|
EXPECT_TRUE(FindLog("Process not running"));
|
|
}
|
|
|
|
TEST_F(ProcessTest, ProcessExists) {
|
|
EXPECT_FALSE(Process::ProcessExists(0));
|
|
EXPECT_TRUE(Process::ProcessExists(1));
|
|
EXPECT_TRUE(Process::ProcessExists(getpid()));
|
|
}
|
|
|
|
TEST_F(ProcessTest, ResetPidByFile) {
|
|
FilePath pid_path = temp_dir_.GetPath().Append("pid");
|
|
EXPECT_FALSE(process_.ResetPidByFile(pid_path.value()));
|
|
EXPECT_TRUE(base::WriteFile(pid_path, "456\n", 4));
|
|
EXPECT_TRUE(process_.ResetPidByFile(pid_path.value()));
|
|
EXPECT_EQ(456, process_.pid());
|
|
// The purpose of this unit test is to check if Process::ResetPidByFile() can
|
|
// properly read a pid from a file. We don't really want to kill the process
|
|
// with pid 456, so update the pid to 0 to prevent the Process destructor from
|
|
// killing any innocent process.
|
|
process_.UpdatePid(0);
|
|
}
|
|
|
|
TEST_F(ProcessTest, KillSleeper) {
|
|
process_.AddArg(kBinSleep);
|
|
process_.AddArg("10000");
|
|
ASSERT_TRUE(process_.Start());
|
|
pid_t pid = process_.pid();
|
|
ASSERT_GT(pid, 1);
|
|
EXPECT_TRUE(process_.Kill(SIGTERM, 1));
|
|
EXPECT_EQ(0, process_.pid());
|
|
}
|
|
|
|
TEST_F(ProcessTest, Reset) {
|
|
process_.AddArg(kBinFalse);
|
|
process_.Reset(0);
|
|
process_.AddArg(kBinEcho);
|
|
EXPECT_EQ(0, process_.Run());
|
|
}
|
|
|
|
bool ReturnFalse() { return false; }
|
|
|
|
TEST_F(ProcessTest, PreExecCallback) {
|
|
process_.AddArg(kBinTrue);
|
|
process_.SetPreExecCallback(base::Bind(&ReturnFalse));
|
|
ASSERT_NE(0, process_.Run());
|
|
}
|
|
|
|
TEST_F(ProcessTest, LeakUnusedFileDescriptors) {
|
|
ScopedPipe pipe;
|
|
process_.AddArg(kBinStat);
|
|
process_.AddArg(GetFdPath(pipe.reader).value());
|
|
process_.AddArg(GetFdPath(pipe.writer).value());
|
|
process_.SetCloseUnusedFileDescriptors(false);
|
|
EXPECT_EQ(0, process_.Run());
|
|
}
|
|
|
|
TEST_F(ProcessTest, CloseUnusedFileDescriptors) {
|
|
ScopedPipe pipe;
|
|
process_.AddArg(kBinStat);
|
|
process_.AddArg(GetFdPath(pipe.reader).value());
|
|
process_.AddArg(GetFdPath(pipe.writer).value());
|
|
process_.SetCloseUnusedFileDescriptors(true);
|
|
// Stat should fail when running on these file descriptor because the files
|
|
// should not be there.
|
|
EXPECT_EQ(1, process_.Run());
|
|
}
|
|
|
|
} // namespace brillo
|