// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/cast_core/child_log_process.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/strings/string_split.h"
namespace chromecast {
constexpr char kChildLogProcess[] = "child_log_process";
constexpr char kChildLogProcessArgs[] = "child_log_process_args";
void ForkAndRunLogProcess(std::string log_process_path,
std::string log_process_args) {
if (log_process_args.size() > 2 &&
(log_process_args[0] == '\"' && log_process_args.back() == '\"')) {
log_process_args = log_process_args.substr(1, log_process_args.size() - 2);
}
std::vector<std::string> raw_args = base::SplitString(
log_process_args, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
std::vector<const char*> argv = {log_process_path.c_str()};
for (const auto& arg : raw_args) {
argv.push_back(arg.c_str());
}
argv.push_back(nullptr);
int pipefds[2];
pid_t cpid;
if (pipe(pipefds) == -1) {
fprintf(stderr, "Could not create pipes for logging process: %s\n",
strerror(errno));
fflush(stderr);
return;
}
cpid = fork();
if (cpid == -1) {
fprintf(stderr, "Could not fork new process for logging process: %s\n",
strerror(errno));
fflush(stderr);
close(pipefds[0]);
close(pipefds[1]);
return;
}
if (cpid == 0) {
// child
close(pipefds[1]); // writable end
dup2(pipefds[0], fileno(stdin));
close(pipefds[0]);
int ret =
execv(log_process_path.c_str(), const_cast<char* const*>(&argv[0]));
if (ret == -1) {
fprintf(stderr, "Could not start process: %s error: %s\n",
log_process_path.c_str(), strerror(errno));
_exit(1);
}
} else {
// parent
close(pipefds[0]); // readable end
int original_stderr = dup(fileno(stderr));
int original_stdout = dup(fileno(stdout));
if (dup2(pipefds[1], fileno(stdout)) == -1 ||
dup2(pipefds[1], fileno(stderr)) == -1) {
dup2(original_stderr, fileno(stderr));
dup2(original_stdout, fileno(stdout));
fprintf(stderr,
"Failed to map stderr and stdout to writable pipe for logging\n");
fflush(stderr);
} else {
printf("Log client initialized.\n");
close(original_stderr);
close(original_stdout);
}
close(pipefds[1]); // stderr now references the pipe, or dup2 failed.
// regardless we can close our old fd.
}
}
void ForkAndRunLogProcessIfSpecified(const int argc, const char* const* argv) {
base::CommandLine::Init(argc, argv);
auto* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(kChildLogProcess)) {
ForkAndRunLogProcess(
command_line->GetSwitchValueASCII(kChildLogProcess),
command_line->GetSwitchValueASCII(kChildLogProcessArgs));
}
}
} // namespace chromecast