blob: 9c730d0d2ebda1701cc749240d6262ba1441f74b [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 <sys/socket.h>
8#include <sys/un.h>
9
Qijiang Fan713061e2021-03-08 15:45:12 +090010#include <base/check.h>
11#include <base/check_op.h>
Yoshiki Iguchic4ac5f92020-12-01 04:17:45 +090012#include <base/files/file_path.h>
13#include <base/files/file_util.h>
14#include <base/files/scoped_file.h>
15#include <base/logging.h>
16#include <base/posix/eintr_wrapper.h>
17#include <base/strings/stringprintf.h>
18
19namespace {
20
21base::ScopedFD PrepareSocket(const std::string& identifier,
22 int severity,
23 int pid,
24 const base::FilePath& socket_path) {
25 DCHECK(!identifier.empty());
26 DCHECK_GE(severity, 0);
27 DCHECK_LE(severity, 7);
28
29 // Open the unix socket to write logs.
30 base::ScopedFD sock(
31 socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_CLOEXEC, 0));
32 if (!sock.is_valid()) {
33 PLOG(ERROR) << "opening stream socket";
34 return base::ScopedFD();
35 }
36
37 // Connect the syslog unix socket file.
38 struct sockaddr_un server = {};
39 server.sun_family = AF_UNIX;
40 CHECK_GT(sizeof(server.sun_path), socket_path.value().length());
41 strncpy(server.sun_path, socket_path.value().c_str(),
42 sizeof(server.sun_path));
43 if (HANDLE_EINTR(connect(sock.get(), (struct sockaddr*)&server,
44 sizeof(struct sockaddr_un))) < 0) {
45 PLOG(ERROR) << "connecting stream socket";
46 return base::ScopedFD();
47 }
48
49 // Construct the header string to send.
50 std::string header = base::StringPrintf("TAG=%s[%d]\nPRIORITY=%d\n\n",
51 identifier.c_str(), pid, severity);
52
53 // Send headers (tag and severity).
54 if (!base::WriteFileDescriptor(sock.get(), header.c_str(), header.size())) {
55 PLOG(ERROR) << "writing headers on stream socket";
56 return base::ScopedFD();
57 }
58
59 return sock;
60}
61
62bool CreateSocketAndBindToFD(const std::string& identifier,
63 int severity,
64 int pid,
65 int target_fd,
66 const base::FilePath& socket_path) {
67 base::ScopedFD sock = PrepareSocket(identifier, severity, pid, socket_path);
68 if (!sock.is_valid()) {
69 LOG(ERROR) << "Failed to open the rsyslog socket for stderr.";
70 return false;
71 }
72
73 // Connect the socket to stderr.
74 if (HANDLE_EINTR(dup2(sock.get(), target_fd)) == -1) {
75 PLOG(ERROR) << "duping the stderr";
76 return false;
77 }
78
79 return true;
80}
81
82} // namespace
83
84void ExecuteCommandWithRedirection(
85 const std::string& target_command,
86 const std::vector<const char*>& target_command_argv,
87 const std::string& identifier,
88 int severity_stdout,
89 int severity_stderr,
90 const base::FilePath& socket_path_stdout,
91 const base::FilePath& socket_path_stderr) {
92 // Prepare a pid.
93 pid_t pid = getpid();
94
95 // Open the unix socket to redirect logs from stdout (and stderr).
96 bool ret_stdout = CreateSocketAndBindToFD(identifier, severity_stdout, pid,
97 STDOUT_FILENO, socket_path_stdout);
98 CHECK(ret_stdout) << "Failed to bind stdout.";
99
100 // Open the unix socket to redirect logs from stderr.
101 // We prepare a separate socket for stderr even if the severities are same,
102 // in order to prevent interleave of simultaneous lines.
103 bool ret_stderr = CreateSocketAndBindToFD(identifier, severity_stderr, pid,
104 STDERR_FILENO, socket_path_stderr);
105 CHECK(ret_stderr) << "Failed to bind stderr.";
106
107 // Execute the target process.
108 execvp(const_cast<char*>(target_command.c_str()),
109 const_cast<char**>(target_command_argv.data()));
110
111 /////////////////////////////////////////////////////////////////////////////
112 // The code below is executed only when the execvp() above failed.
113 // (eg. the executable doesn't exist, or is not executable)
114
115 PLOG(ERROR) << "execvp '" << target_command << "'";
116}