2 Unix SMB/CIFS implementation.
3 Test for fd passing with messaging
5 Copyright (C) Michael Adam 2014
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "torture/proto.h"
23 #include "lib/util/tevent_unix.h"
29 * Try to pass an fd to the sending process - fails.
31 bool run_messaging_fdpass1(int dummy
)
33 struct tevent_context
*ev
= NULL
;
34 struct messaging_context
*msg_ctx
= NULL
;
37 int pass_fds
[1] = { 0 };
41 TALLOC_CTX
*frame
= talloc_stackframe();
43 ev
= samba_tevent_context_init(frame
);
45 fprintf(stderr
, "tevent_context_init failed\n");
48 msg_ctx
= messaging_init(ev
, ev
);
49 if (msg_ctx
== NULL
) {
50 fprintf(stderr
, "messaging_init failed\n");
54 dst
= messaging_server_id(msg_ctx
);
58 perror("pipe failed");
62 pass_fds
[0] = pipe_fds
[0];
64 status
= messaging_send_iov(msg_ctx
, dst
, MSG_PING
, NULL
, 0,
66 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OK
)) {
68 "messaging_send_iov gave: %s\n", nt_errstr(status
));
82 * - parent: create a child
83 * - parent: create a two pipes in the parent: up and down
84 * - parent: pass the up pipe's reading end and the down pipe's writing
85 * end to the child and close them
86 * - parent: write a number into the up pipe's writing end
87 * - child: read number from the passed reading fd (up)
88 * - child: write the read number to the passed writing fd (down)
89 * - parent: read number from the down pipe's reading end and compare with
93 #define MSG_TORTURE_FDPASS2 0xF002
95 static bool fdpass2_filter(struct messaging_rec
*rec
, void *private_data
)
97 if (rec
->msg_type
!= MSG_TORTURE_FDPASS2
) {
101 if (rec
->num_fds
!= 2) {
108 static bool fdpass2_child(int ready_fd
)
110 struct tevent_context
*ev
= NULL
;
111 struct messaging_context
*msg_ctx
= NULL
;
112 TALLOC_CTX
*frame
= talloc_stackframe();
115 struct tevent_req
*subreq
;
119 struct messaging_rec
*rec
;
122 ev
= samba_tevent_context_init(frame
);
124 fprintf(stderr
, "child: tevent_context_init failed\n");
128 msg_ctx
= messaging_init(ev
, ev
);
129 if (msg_ctx
== NULL
) {
130 fprintf(stderr
, "child: messaging_init failed\n");
134 /* Tell the parent we are ready to receive mesages. */
135 bytes
= write(ready_fd
, &c
, 1);
137 perror("child: failed to write to ready_fd");
141 subreq
= messaging_filtered_read_send(frame
, /* TALLOC_CTX */
143 fdpass2_filter
, NULL
);
144 if (subreq
== NULL
) {
145 fprintf(stderr
, "child: messaging_filtered_read_send failed\n");
149 ok
= tevent_req_poll(subreq
, ev
);
151 fprintf(stderr
, "child: tevent_req_poll failed\n");
155 ret
= messaging_filtered_read_recv(subreq
, frame
, &rec
);
158 fprintf(stderr
, "child: messaging_filtered_read_recv failed\n");
162 SMB_ASSERT(rec
->num_fds
== 2);
164 /* Tell the parent we are done. */
165 bytes
= write(ready_fd
, &c
, 1);
167 perror("child: failed to write to ready_fd");
172 down_fd
= rec
->fds
[1];
174 bytes
= read(up_fd
, &c
, 1);
176 perror("child: read from up_fd failed");
180 bytes
= write(down_fd
, &c
, 1);
182 perror("child: write to down_fd failed");
185 printf("child: done\n");
194 struct child_done_state
{
199 static void child_done_cb(struct tevent_context
*ev
,
200 struct tevent_fd
*fde
,
204 struct child_done_state
*state
=
205 (struct child_done_state
*)private_data
;
209 bytes
= read(state
->fd
, &c
, 1);
211 perror("parent: read from ready_fd failed");
217 static bool fdpass2_parent(pid_t child_pid
, int ready_fd
, size_t payload_size
)
219 struct tevent_context
*ev
= NULL
;
220 struct messaging_context
*msg_ctx
= NULL
;
224 int pass_fds
[2] = { 0 };
227 struct server_id dst
;
228 TALLOC_CTX
*frame
= talloc_stackframe();
229 uint8_t c1
= 1, c2
, c
;
233 struct tevent_fd
*child_done_fde
;
234 struct child_done_state child_state
;
236 ev
= samba_tevent_context_init(frame
);
238 fprintf(stderr
, "parent: tevent_context_init failed\n");
242 msg_ctx
= messaging_init(ev
, ev
);
243 if (msg_ctx
== NULL
) {
244 fprintf(stderr
, "parent: messaging_init failed\n");
248 /* wait util the child is ready to receive messages */
249 bytes
= read(ready_fd
, &c
, 1);
251 perror("parent: read from ready_fd failed");
257 perror("parent: pipe failed for up_pipe");
261 ret
= pipe(down_pipe
);
263 perror("parent: pipe failed for down_pipe");
267 child_state
.fd
= ready_fd
;
268 child_state
.done
= false;
270 child_done_fde
= tevent_add_fd(ev
, ev
, ready_fd
, TEVENT_FD_READ
,
271 child_done_cb
, &child_state
);
272 if (child_done_fde
== NULL
) {
274 "parent: failed tevent_add_fd for child done\n");
278 pass_fds
[0] = up_pipe
[0];
279 pass_fds
[1] = down_pipe
[1];
281 dst
= messaging_server_id(msg_ctx
);
285 * Send a certain payload with the fds, to test to test
286 * that fd-passing works when we have fragmentation and
287 * re-assembly of the datagrams.
289 * Fragmentation/queuing is triggered by a certain payload
290 * size. Payloads below that size use the fast path.
292 blob
= data_blob_talloc_zero(frame
, payload_size
);
293 iov
.iov_base
= blob
.data
;
294 iov
.iov_len
= blob
.length
;
296 status
= messaging_send_iov(msg_ctx
, dst
, MSG_TORTURE_FDPASS2
, &iov
, 1,
298 if (!NT_STATUS_IS_OK(status
)) {
299 fprintf(stderr
, "parent: messaging_send_iov failed: %s\n",
304 printf("parent: waiting for child to confirm\n");
306 while (!child_state
.done
) {
307 ret
= tevent_loop_once(ev
);
309 fprintf(stderr
, "parent: tevent_loop_once failed\n");
314 printf("parent: child confirmed\n");
319 bytes
= write(up_pipe
[1], &c1
, 1);
321 perror("parent: write to up pipe failed");
325 bytes
= read(down_pipe
[0], &c2
, 1);
327 perror("parent: read from down pipe failed");
332 fprintf(stderr
, "parent: c1[%d] != c2[%d]\n", c1
, c2
);
336 ret
= waitpid(child_pid
, NULL
, 0);
338 perror("parent: waitpid failed");
349 static bool run_messaging_fdpass2_int(int dummy
, size_t payload_size
)
356 ret
= pipe(ready_pipe
);
358 perror("parent: pipe failed for ready_pipe");
363 if (child_pid
== -1) {
364 perror("fork failed");
365 } else if (child_pid
== 0) {
366 close(ready_pipe
[0]);
367 retval
= fdpass2_child(ready_pipe
[1]);
369 close(ready_pipe
[1]);
370 retval
= fdpass2_parent(child_pid
, ready_pipe
[0], payload_size
);
376 bool run_messaging_fdpass2(int dummy
)
378 return run_messaging_fdpass2_int(dummy
, 1000*1000);
382 * Variant of the FDPASS2 test that tests the non-queuing fast path
383 * with a small payload.
385 bool run_messaging_fdpass2a(int dummy
)
387 return run_messaging_fdpass2_int(dummy
, 1);
391 * Variant of the FDPASS2 test that tests the non-queuing fast path
394 bool run_messaging_fdpass2b(int dummy
)
396 return run_messaging_fdpass2_int(dummy
, 0);