blob: 8278883d42ce6da92307124c4ebae05e897f0310 [file] [log] [blame]
Yoshiki Iguchic4ac5f92020-12-01 04:17:45 +09001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "syslog-cat/syslogcat.h"
6
7#include <memory>
8
9#include <sys/socket.h>
10#include <sys/un.h>
11
Qijiang Fan713061e2021-03-08 15:45:12 +090012#include <base/check.h>
13#include <base/check_op.h>
Yoshiki Iguchic4ac5f92020-12-01 04:17:45 +090014#include <base/files/file_path.h>
15#include <base/files/file_util.h>
16#include <base/logging.h>
17#include <base/posix/eintr_wrapper.h>
18#include <base/strings/stringprintf.h>
19#include <gtest/gtest.h>
20
21namespace {
22
23base::ScopedFD CreateDomainSocket(const base::FilePath& path) {
24 base::ScopedFD peer(
25 HANDLE_EINTR(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
26
27 struct sockaddr_un addr {};
28 addr.sun_family = AF_UNIX;
29 CHECK_GT(sizeof(addr.sun_path), path.value().length());
30 strncpy(addr.sun_path, path.value().c_str(), sizeof(addr.sun_path));
31 if (bind(peer.get(), (struct sockaddr*)&addr, sizeof(addr)) == -1) {
32 PLOG(ERROR) << "bind";
33 return base::ScopedFD();
34 }
35
36 if (listen(peer.get(), 1) == -1) {
37 PLOG(ERROR) << "listen";
38 return base::ScopedFD();
39 }
40
41 return peer;
42}
43
44base::Optional<std::string> AcceptAndReadFromSocket(int fd, int size) {
45 struct sockaddr_un sun_client = {};
46 socklen_t socklen = sizeof(sun_client);
47
48 base::ScopedFD fd_client(
49 HANDLE_EINTR(accept(fd, (struct sockaddr*)&sun_client, &socklen)));
50 if (!fd_client.is_valid())
51 return base::nullopt;
52
53 const size_t kBufSize = 1000;
54 char buf[kBufSize];
55 CHECK_GT(kBufSize, size);
56 EXPECT_TRUE(base::ReadFromFD(fd_client.get(), buf, size));
57 return std::string(buf, size);
58}
59
60} // anonymous namespace
61
62class SyslogCatTest : public ::testing::Test {
63 protected:
64 int GetStdOutFd() const {
65 CHECK(sock_stdout_.is_valid());
66 return sock_stdout_.get();
67 }
68 int GetStdErrFd() const {
69 CHECK(sock_stderr_.is_valid());
70 return sock_stderr_.get();
71 }
72
73 pid_t ForkAndExecuteSyslogCat(
74 const std::vector<const char*>& target_command) {
75 const char kIdentifier[] = "IDENT";
76 const int kSeverityStdout = 6;
77 const int kSeverityStderr = 4;
78
79 pid_t child_pid = fork();
80 if (child_pid == 0) {
81 // As a child process.
82 ExecuteCommandWithRedirection(
83 target_command[0], target_command, kIdentifier, kSeverityStdout,
84 kSeverityStderr, sock_path_stdout_, sock_path_stderr_);
85
86 return -1;
87 } else {
88 // As a parent process.
89 // Wait for the child process to terminate.
90 wait(0);
91
92 return child_pid;
93 }
94 }
95
96 private:
97 void SetUp() override {
98 base::FilePath directory;
99 CHECK(base::GetTempDir(&directory));
100 CHECK(base::CreateTemporaryDirInDir(directory, "syslogcat-test",
101 &temp_directory_));
102
103 sock_path_stdout_ = temp_directory_.Append("stdout.sock");
104 sock_path_stderr_ = temp_directory_.Append("stderr.sock");
105
106 sock_stdout_ = CreateDomainSocket(sock_path_stdout_);
107 ASSERT_TRUE(sock_stdout_.is_valid());
108 sock_stderr_ = CreateDomainSocket(sock_path_stderr_);
109 ASSERT_TRUE(sock_stderr_.is_valid());
110 }
111
112 void TearDown() override { base::DeletePathRecursively(temp_directory_); }
113
114 base::FilePath temp_directory_;
115 base::FilePath sock_path_stdout_;
116 base::FilePath sock_path_stderr_;
117 base::ScopedFD sock_stdout_;
118 base::ScopedFD sock_stderr_;
119};
120
121TEST_F(SyslogCatTest, Echo) {
122 pid_t child_pid = ForkAndExecuteSyslogCat(
123 std::vector<const char*>({"/bin/echo", "1234567890", NULL}));
124
125 std::string expected_stdout =
126 base::StringPrintf("TAG=IDENT[%d]\nPRIORITY=6\n\n1234567890", child_pid);
127 base::Optional<std::string> actual_stdout =
128 AcceptAndReadFromSocket(GetStdOutFd(), expected_stdout.length());
129 EXPECT_TRUE(actual_stdout.has_value());
130 EXPECT_EQ(expected_stdout, *actual_stdout);
131
132 std::string expected_stderr =
133 base::StringPrintf("TAG=IDENT[%d]\nPRIORITY=4\n\n", child_pid);
134 base::Optional<std::string> actual_stderr =
135 AcceptAndReadFromSocket(GetStdErrFd(), expected_stderr.length());
136 EXPECT_TRUE(actual_stderr.has_value());
137 EXPECT_EQ(expected_stderr, *actual_stderr);
138}
139
140TEST_F(SyslogCatTest, StdErr) {
141 pid_t child_pid = ForkAndExecuteSyslogCat(std::vector<const char*>(
142 {"/bin/bash", "-c", ">&2 echo 1234567890", NULL}));
143
144 std::string expected_stdout =
145 base::StringPrintf("TAG=IDENT[%d]\nPRIORITY=6\n\n", child_pid);
146 base::Optional<std::string> actual_stdout =
147 AcceptAndReadFromSocket(GetStdOutFd(), expected_stdout.length());
148 EXPECT_TRUE(actual_stdout.has_value());
149 EXPECT_EQ(expected_stdout, *actual_stdout);
150
151 std::string expected_stderr =
152 base::StringPrintf("TAG=IDENT[%d]\nPRIORITY=4\n\n1234567890", child_pid);
153 base::Optional<std::string> actual_stderr =
154 AcceptAndReadFromSocket(GetStdErrFd(), expected_stderr.length());
155 EXPECT_TRUE(actual_stderr.has_value());
156 EXPECT_EQ(expected_stderr, *actual_stderr);
157}
158
159TEST_F(SyslogCatTest, StdOutAndErr) {
160 pid_t child_pid = ForkAndExecuteSyslogCat(std::vector<const char*>(
161 {"/bin/bash", "-c", "echo STDOUT; echo STDERR >&2; echo HELLO.", NULL}));
162
163 std::string expected_stdout = base::StringPrintf(
164 "TAG=IDENT[%d]\nPRIORITY=6\n\nSTDOUT\nHELLO.", child_pid);
165 base::Optional<std::string> actual_stdout =
166 AcceptAndReadFromSocket(GetStdOutFd(), expected_stdout.length());
167 EXPECT_TRUE(actual_stdout.has_value());
168 EXPECT_EQ(expected_stdout, *actual_stdout);
169
170 std::string expected_stderr =
171 base::StringPrintf("TAG=IDENT[%d]\nPRIORITY=4\n\nSTDERR", child_pid);
172 base::Optional<std::string> actual_stderr =
173 AcceptAndReadFromSocket(GetStdErrFd(), expected_stderr.length());
174 EXPECT_TRUE(actual_stderr.has_value());
175 EXPECT_EQ(expected_stderr, *actual_stderr);
176}