Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/Vector.h>
28#include <LibCore/ArgsParser.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <signal.h>
32#include <stdio.h>
33#include <unistd.h>
34
35static Vector<int> collect_fds(Vector<const char*> paths, bool append, bool* err)
36{
37 int oflag;
38 mode_t mode;
39 if (append) {
40 oflag = O_APPEND;
41 mode = 0;
42 } else {
43 oflag = O_CREAT | O_WRONLY | O_TRUNC;
44 mode = S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP | S_IRUSR | S_IWUSR;
45 }
46
47 Vector<int> fds;
48 for (const char* path : paths) {
49 int fd = open(path, oflag, mode);
50 if (fd < 0) {
51 perror("failed to open file for writing");
52 *err = true;
53 } else {
54 fds.append(fd);
55 }
56 }
57 fds.append(STDOUT_FILENO);
58 return fds;
59}
60
61static void copy_stdin(Vector<int>& fds, bool* err)
62{
63 for (;;) {
64 char buf[4096];
65 ssize_t nread = read(STDIN_FILENO, buf, sizeof(buf));
66 if (nread == 0)
67 break;
68 if (nread < 0) {
69 perror("read() error");
70 *err = true;
71 // a failure to read from stdin should lead to an early exit
72 return;
73 }
74
75 Vector<int> broken_fds;
76 for (size_t i = 0; i < fds.size(); ++i) {
77 auto fd = fds.at(i);
78 int twrite = 0;
79 while (twrite != nread) {
80 ssize_t nwrite = write(fd, buf + twrite, nread - twrite);
81 if (nwrite < 0) {
82 if (errno == EINTR) {
83 continue;
84 } else {
85 perror("write() failed");
86 *err = true;
87 broken_fds.append(fd);
88 // write failures to a successfully opened fd shall
89 // prevent further writes, but shall not block writes
90 // to the other open fds
91 break;
92 }
93 } else {
94 twrite += nwrite;
95 }
96 }
97 }
98
99 // remove any fds which we can no longer write to for subsequent copies
100 for (auto to_remove : broken_fds)
101 fds.remove_first_matching([&](int fd) { return to_remove == fd; });
102 }
103}
104
105static void close_fds(Vector<int>& fds)
106{
107 for (int fd : fds) {
108 int closed = close(fd);
109 if (closed < 0) {
110 perror("failed to close output file");
111 }
112 }
113}
114
115static void int_handler(int)
116{
117 // pass
118}
119
120int main(int argc, char** argv)
121{
122 bool append = false;
123 bool ignore_interrupts = false;
124 Vector<const char*> paths;
125
126 Core::ArgsParser args_parser;
127 args_parser.add_option(append, "Append, don't overwrite", "append", 'a');
128 args_parser.add_option(ignore_interrupts, "Ignore SIGINT", "ignore-interrupts", 'i');
129 args_parser.add_positional_argument(paths, "Files to copy stdin to", "file", Core::ArgsParser::Required::No);
130 args_parser.parse(argc, argv);
131
132 if (ignore_interrupts) {
133 if (signal(SIGINT, int_handler) == SIG_ERR)
134 perror("failed to install SIGINT handler");
135 }
136
137 bool err_open = false;
138 bool err_write = false;
139 auto fds = collect_fds(paths, append, &err_open);
140 copy_stdin(fds, &err_write);
141 close_fds(fds);
142
143 return (err_open || err_write) ? 1 : 0;
144}