blob: 1bbb9c41727e92e639e2fa070a8024f9ee073b6a [file] [log] [blame]
// Copyright 2022 The Fuchsia 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 <fcntl.h>
#include <getopt.h>
#include <lib/stdcompat/span.h>
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <string_view>
#include <thread>
#include <vector>
#include <fbl/unique_fd.h>
#ifdef __Fuchsia__
#include <zircon/syscalls.h>
#endif
namespace {
constexpr std::string_view kStdinoutFilename = "-";
constexpr char kOptString[] = "c:e:o:t:x:";
constexpr option kLongOpts[] = {
{"cat-from", required_argument, nullptr, 'c'}, //
{"cat-to", required_argument, nullptr, 'o'}, //
{"echo", required_argument, nullptr, 'e'}, //
{"threads", required_argument, nullptr, 't'}, //
{"exit", required_argument, nullptr, 'x'}, //
};
int Usage() {
fprintf(stderr, "Usage: test-child [--echo=STRING] [--cat=FILE] [--threads=N]\n");
return 1;
}
[[noreturn]] void Hang() {
while (true) {
#ifdef __Fuchsia__
zx_thread_legacy_yield(0);
#else
pause();
#endif
}
}
void Cat(fbl::unique_fd from, int to) {
char buf[BUFSIZ];
ssize_t nread;
while ((nread = read(from.get(), buf, sizeof(buf))) > 0) {
cpp20::span<const char> chunk(buf, static_cast<size_t>(nread));
while (!chunk.empty()) {
ssize_t nwrote = write(to, chunk.data(), chunk.size());
if (nwrote < 0) {
perror("write");
exit(2);
}
chunk = chunk.subspan(static_cast<size_t>(nwrote));
}
}
if (nread < 0) {
perror("read");
exit(2);
}
}
fbl::unique_fd CatOpen(const char* filename, int stdfd, int oflags) {
fbl::unique_fd fd{filename == kStdinoutFilename ? stdfd : open(filename, oflags, 0666)};
if (!fd) {
perror(filename);
exit(2);
}
return fd;
}
void CatFrom(const char* filename) {
Cat(CatOpen(filename, STDIN_FILENO, O_RDONLY), STDOUT_FILENO);
}
void CatTo(const char* filename) {
Cat(fbl::unique_fd{STDIN_FILENO},
CatOpen(filename, STDOUT_FILENO, O_WRONLY | O_CREAT | O_EXCL).get());
}
} // namespace
int main(int argc, char** argv) {
size_t thread_count = 0;
while (true) {
switch (getopt_long(argc, argv, kOptString, kLongOpts, nullptr)) {
case -1:
// This ends the loop. All other cases continue (or return).
break;
case 'c':
CatFrom(optarg);
continue;
case 'o':
CatTo(optarg);
continue;
case 'e':
puts(optarg);
continue;
case 't':
thread_count = atoi(optarg);
continue;
case 'x':
return atoi(optarg);
default:
return Usage();
}
break;
}
if (optind != argc) {
return Usage();
}
std::vector<std::thread> threads(thread_count);
for (std::thread& thread : threads) {
thread = std::thread(Hang);
}
if (thread_count > 0) {
printf("started %zu additional threads\n", thread_count);
}
Hang();
return 0;
}