chiark / gitweb /
fd-util: add new call rearrange_stdio()
[elogind.git] / src / test / test-fd-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <fcntl.h>
22 #include <unistd.h>
23
24 #include "alloc-util.h"
25 #include "fd-util.h"
26 #include "fileio.h"
27 #include "macro.h"
28 //#include "path-util.h"
29 //#include "process-util.h"
30 #include "random-util.h"
31 #include "string-util.h"
32 #include "util.h"
33
34 static void test_close_many(void) {
35         int fds[3];
36         char name0[] = "/tmp/test-close-many.XXXXXX";
37         char name1[] = "/tmp/test-close-many.XXXXXX";
38         char name2[] = "/tmp/test-close-many.XXXXXX";
39
40         fds[0] = mkostemp_safe(name0);
41         fds[1] = mkostemp_safe(name1);
42         fds[2] = mkostemp_safe(name2);
43
44         close_many(fds, 2);
45
46         assert_se(fcntl(fds[0], F_GETFD) == -1);
47         assert_se(fcntl(fds[1], F_GETFD) == -1);
48         assert_se(fcntl(fds[2], F_GETFD) >= 0);
49
50         safe_close(fds[2]);
51
52         unlink(name0);
53         unlink(name1);
54         unlink(name2);
55 }
56
57 static void test_close_nointr(void) {
58         char name[] = "/tmp/test-test-close_nointr.XXXXXX";
59         int fd;
60
61         fd = mkostemp_safe(name);
62         assert_se(fd >= 0);
63         assert_se(close_nointr(fd) >= 0);
64         assert_se(close_nointr(fd) < 0);
65
66         unlink(name);
67 }
68
69 #if 0 /// UNNEEDED by elogind
70 static void test_same_fd(void) {
71         _cleanup_close_pair_ int p[2] = { -1, -1 };
72         _cleanup_close_ int a = -1, b = -1, c = -1;
73
74         assert_se(pipe2(p, O_CLOEXEC) >= 0);
75         assert_se((a = dup(p[0])) >= 0);
76         assert_se((b = open("/dev/null", O_RDONLY|O_CLOEXEC)) >= 0);
77         assert_se((c = dup(a)) >= 0);
78
79         assert_se(same_fd(p[0], p[0]) > 0);
80         assert_se(same_fd(p[1], p[1]) > 0);
81         assert_se(same_fd(a, a) > 0);
82         assert_se(same_fd(b, b) > 0);
83
84         assert_se(same_fd(a, p[0]) > 0);
85         assert_se(same_fd(p[0], a) > 0);
86         assert_se(same_fd(c, p[0]) > 0);
87         assert_se(same_fd(p[0], c) > 0);
88         assert_se(same_fd(a, c) > 0);
89         assert_se(same_fd(c, a) > 0);
90
91         assert_se(same_fd(p[0], p[1]) == 0);
92         assert_se(same_fd(p[1], p[0]) == 0);
93         assert_se(same_fd(p[0], b) == 0);
94         assert_se(same_fd(b, p[0]) == 0);
95         assert_se(same_fd(p[1], a) == 0);
96         assert_se(same_fd(a, p[1]) == 0);
97         assert_se(same_fd(p[1], b) == 0);
98         assert_se(same_fd(b, p[1]) == 0);
99
100         assert_se(same_fd(a, b) == 0);
101         assert_se(same_fd(b, a) == 0);
102 }
103 #endif // 0
104
105 static void test_open_serialization_fd(void) {
106         _cleanup_close_ int fd = -1;
107
108         fd = open_serialization_fd("test");
109         assert_se(fd >= 0);
110
111         assert_se(write(fd, "test\n", 5) == 5);
112 }
113
114 static void test_acquire_data_fd_one(unsigned flags) {
115         char wbuffer[196*1024 - 7];
116         char rbuffer[sizeof(wbuffer)];
117         int fd;
118
119         fd = acquire_data_fd("foo", 3, flags);
120         assert_se(fd >= 0);
121
122         zero(rbuffer);
123         assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 3);
124         assert_se(streq(rbuffer, "foo"));
125
126         fd = safe_close(fd);
127
128         fd = acquire_data_fd("", 0, flags);
129         assert_se(fd >= 0);
130
131         zero(rbuffer);
132         assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 0);
133         assert_se(streq(rbuffer, ""));
134
135         fd = safe_close(fd);
136
137         random_bytes(wbuffer, sizeof(wbuffer));
138
139         fd = acquire_data_fd(wbuffer, sizeof(wbuffer), flags);
140         assert_se(fd >= 0);
141
142         zero(rbuffer);
143         assert_se(read(fd, rbuffer, sizeof(rbuffer)) == sizeof(rbuffer));
144         assert_se(memcmp(rbuffer, wbuffer, sizeof(rbuffer)) == 0);
145
146         fd = safe_close(fd);
147 }
148
149 static void test_acquire_data_fd(void) {
150
151         test_acquire_data_fd_one(0);
152         test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL);
153         test_acquire_data_fd_one(ACQUIRE_NO_MEMFD);
154         test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD);
155         test_acquire_data_fd_one(ACQUIRE_NO_PIPE);
156         test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_PIPE);
157         test_acquire_data_fd_one(ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE);
158         test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE);
159         test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE|ACQUIRE_NO_TMPFILE);
160 }
161
162 static void test_fd_move_above_stdio(void) {
163         int original_stdin, new_fd;
164
165         original_stdin = fcntl(0, F_DUPFD, 3);
166         assert_se(original_stdin >= 3);
167         assert_se(close_nointr(0) != EBADF);
168
169         new_fd = open("/dev/null", O_RDONLY);
170         assert_se(new_fd == 0);
171
172         new_fd = fd_move_above_stdio(new_fd);
173         assert_se(new_fd >= 3);
174
175         assert_se(dup(original_stdin) == 0);
176         assert_se(close_nointr(original_stdin) != EBADF);
177         assert_se(close_nointr(new_fd) != EBADF);
178 }
179
180 static void test_rearrange_stdio(void) {
181         pid_t pid;
182         int r;
183
184         r = safe_fork("rearrange", FORK_WAIT|FORK_LOG, &pid);
185         assert_se(r >= 0);
186
187         if (r == 0) {
188                 _cleanup_free_ char *path = NULL;
189                 char buffer[10];
190
191                 /* Child */
192
193                 safe_close(STDERR_FILENO); /* Let's close an fd < 2, to make it more interesting */
194
195                 assert_se(rearrange_stdio(-1, -1, -1) >= 0);
196
197                 assert_se(fd_get_path(STDIN_FILENO, &path) >= 0);
198                 assert_se(path_equal(path, "/dev/null"));
199                 path = mfree(path);
200
201                 assert_se(fd_get_path(STDOUT_FILENO, &path) >= 0);
202                 assert_se(path_equal(path, "/dev/null"));
203                 path = mfree(path);
204
205                 assert_se(fd_get_path(STDOUT_FILENO, &path) >= 0);
206                 assert_se(path_equal(path, "/dev/null"));
207                 path = mfree(path);
208
209                 safe_close(STDIN_FILENO);
210                 safe_close(STDOUT_FILENO);
211                 safe_close(STDERR_FILENO);
212
213                 {
214                         int pair[2];
215                         assert_se(pipe(pair) >= 0);
216                         assert_se(pair[0] == 0);
217                         assert_se(pair[1] == 1);
218                         assert_se(fd_move_above_stdio(0) == 3);
219                 }
220                 assert_se(open("/dev/full", O_WRONLY|O_CLOEXEC) == 0);
221                 assert_se(acquire_data_fd("foobar", 6, 0) == 2);
222
223                 assert_se(rearrange_stdio(2, 0, 1) >= 0);
224
225                 assert_se(write(1, "x", 1) < 0 && errno == ENOSPC);
226                 assert_se(write(2, "z", 1) == 1);
227                 assert_se(read(3, buffer, sizeof(buffer)) == 1);
228                 assert_se(buffer[0] == 'z');
229                 assert_se(read(0, buffer, sizeof(buffer)) == 6);
230                 assert_se(memcmp(buffer, "foobar", 6) == 0);
231
232                 assert_se(rearrange_stdio(-1, 1, 2) >= 0);
233                 assert_se(write(1, "a", 1) < 0 && errno == ENOSPC);
234                 assert_se(write(2, "y", 1) == 1);
235                 assert_se(read(3, buffer, sizeof(buffer)) == 1);
236                 assert_se(buffer[0] == 'y');
237
238                 assert_se(fd_get_path(0, &path) >= 0);
239                 assert_se(path_equal(path, "/dev/null"));
240                 path = mfree(path);
241
242                 _exit(EXIT_SUCCESS);
243         }
244 }
245
246 int main(int argc, char *argv[]) {
247         test_close_many();
248         test_close_nointr();
249 #if 0 /// UNNEEDED by elogind
250         test_same_fd();
251 #endif // 0
252         test_open_serialization_fd();
253         test_acquire_data_fd();
254         test_fd_move_above_stdio();
255         test_rearrange_stdio();
256
257         return 0;
258 }