Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Test for a messaging_read bug
4 : Copyright (C) Volker Lendecke 2014
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "torture/proto.h"
22 : #include "lib/util/tevent_unix.h"
23 : #include "messages.h"
24 :
25 : struct msg_count_state {
26 : struct tevent_context *ev;
27 : struct messaging_context *msg_ctx;
28 : uint32_t msg_type;
29 : unsigned *count;
30 : };
31 :
32 : static void msg_count_done(struct tevent_req *subreq);
33 :
34 3 : static struct tevent_req *msg_count_send(TALLOC_CTX *mem_ctx,
35 : struct tevent_context *ev,
36 : struct messaging_context *msg_ctx,
37 : uint32_t msg_type,
38 : unsigned *count)
39 : {
40 3 : struct tevent_req *req, *subreq;
41 3 : struct msg_count_state *state;
42 :
43 3 : req = tevent_req_create(mem_ctx, &state, struct msg_count_state);
44 3 : if (req == NULL) {
45 0 : return NULL;
46 : }
47 3 : state->ev = ev;
48 3 : state->msg_ctx = msg_ctx;
49 3 : state->msg_type = msg_type;
50 3 : state->count = count;
51 :
52 3 : subreq = messaging_read_send(state, state->ev, state->msg_ctx,
53 0 : state->msg_type);
54 3 : if (tevent_req_nomem(subreq, req)) {
55 0 : return tevent_req_post(req, ev);
56 : }
57 3 : tevent_req_set_callback(subreq, msg_count_done, req);
58 3 : return req;
59 : }
60 :
61 1 : static void msg_count_done(struct tevent_req *subreq)
62 : {
63 1 : struct tevent_req *req = tevent_req_callback_data(
64 : subreq, struct tevent_req);
65 1 : struct msg_count_state *state = tevent_req_data(
66 : req, struct msg_count_state);
67 1 : int ret;
68 :
69 1 : ret = messaging_read_recv(subreq, NULL, NULL);
70 1 : TALLOC_FREE(subreq);
71 1 : if (tevent_req_error(req, ret)) {
72 0 : return;
73 : }
74 1 : *state->count += 1;
75 :
76 1 : subreq = messaging_read_send(state, state->ev, state->msg_ctx,
77 : state->msg_type);
78 1 : if (tevent_req_nomem(subreq, req)) {
79 0 : return;
80 : }
81 1 : tevent_req_set_callback(subreq, msg_count_done, req);
82 : }
83 :
84 1 : bool run_messaging_read1(int dummy)
85 : {
86 1 : struct tevent_context *ev = NULL;
87 1 : struct messaging_context *msg_ctx = NULL;
88 1 : struct tevent_req *req1 = NULL;
89 1 : unsigned count1 = 0;
90 1 : struct tevent_req *req2 = NULL;
91 1 : unsigned count2 = 0;
92 1 : NTSTATUS status;
93 1 : bool retval = false;
94 1 : int i;
95 :
96 1 : ev = samba_tevent_context_init(talloc_tos());
97 1 : if (ev == NULL) {
98 0 : fprintf(stderr, "tevent_context_init failed\n");
99 0 : goto fail;
100 : }
101 1 : msg_ctx = messaging_init(ev, ev);
102 1 : if (msg_ctx == NULL) {
103 0 : fprintf(stderr, "messaging_init failed\n");
104 0 : goto fail;
105 : }
106 :
107 1 : req1 = msg_count_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &count1);
108 1 : if (req1 == NULL) {
109 0 : fprintf(stderr, "msg_count_send failed\n");
110 0 : goto fail;
111 : }
112 1 : req2 = msg_count_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &count2);
113 1 : if (req1 == NULL) {
114 0 : fprintf(stderr, "msg_count_send failed\n");
115 0 : goto fail;
116 : }
117 1 : status = messaging_send_buf(msg_ctx, messaging_server_id(msg_ctx),
118 : MSG_SMB_NOTIFY, NULL, 0);
119 1 : if (!NT_STATUS_IS_OK(status)) {
120 0 : fprintf(stderr, "messaging_send_buf failed: %s\n",
121 : nt_errstr(status));
122 0 : goto fail;
123 : }
124 :
125 3 : for (i=0; i<2; i++) {
126 2 : if (tevent_loop_once(ev) != 0) {
127 0 : fprintf(stderr, "tevent_loop_once failed\n");
128 0 : goto fail;
129 : }
130 : }
131 :
132 1 : printf("%u/%u\n", count1, count2);
133 :
134 1 : if ((count1 != 1) || (count2 != 0)) {
135 0 : fprintf(stderr, "Got %u/%u msgs, expected 1/0\n",
136 : count1, count2);
137 0 : goto fail;
138 : }
139 :
140 0 : retval = true;
141 1 : fail:
142 1 : TALLOC_FREE(req1);
143 1 : TALLOC_FREE(req2);
144 1 : TALLOC_FREE(msg_ctx);
145 1 : TALLOC_FREE(ev);
146 1 : return retval;
147 : }
148 :
149 : struct msg_free_state {
150 : struct tevent_req **to_free;
151 : };
152 :
153 : static void msg_free_done(struct tevent_req *subreq);
154 :
155 1 : static struct tevent_req *msg_free_send(TALLOC_CTX *mem_ctx,
156 : struct tevent_context *ev,
157 : struct messaging_context *msg_ctx,
158 : uint32_t msg_type,
159 : struct tevent_req **to_free)
160 : {
161 1 : struct tevent_req *req, *subreq;
162 1 : struct msg_free_state *state;
163 :
164 1 : req = tevent_req_create(mem_ctx, &state, struct msg_free_state);
165 1 : if (req == NULL) {
166 0 : return NULL;
167 : }
168 1 : state->to_free = to_free;
169 :
170 1 : subreq = messaging_read_send(state, ev, msg_ctx, msg_type);
171 1 : if (tevent_req_nomem(subreq, req)) {
172 0 : return tevent_req_post(req, ev);
173 : }
174 1 : tevent_req_set_callback(subreq, msg_free_done, req);
175 1 : return req;
176 : }
177 :
178 1 : static void msg_free_done(struct tevent_req *subreq)
179 : {
180 1 : struct tevent_req *req = tevent_req_callback_data(
181 : subreq, struct tevent_req);
182 1 : struct msg_free_state *state = tevent_req_data(
183 : req, struct msg_free_state);
184 1 : int ret;
185 :
186 1 : ret = messaging_read_recv(subreq, NULL, NULL);
187 1 : TALLOC_FREE(subreq);
188 1 : if (tevent_req_error(req, ret)) {
189 0 : return;
190 : }
191 1 : TALLOC_FREE(*state->to_free);
192 1 : tevent_req_done(req);
193 : }
194 :
195 1 : bool run_messaging_read2(int dummy)
196 : {
197 1 : struct tevent_context *ev = NULL;
198 1 : struct messaging_context *msg_ctx = NULL;
199 1 : struct tevent_req *req1 = NULL;
200 1 : struct tevent_req *req2 = NULL;
201 1 : unsigned count = 0;
202 1 : NTSTATUS status;
203 1 : bool retval = false;
204 :
205 1 : ev = samba_tevent_context_init(talloc_tos());
206 1 : if (ev == NULL) {
207 0 : fprintf(stderr, "tevent_context_init failed\n");
208 0 : goto fail;
209 : }
210 1 : msg_ctx = messaging_init(ev, ev);
211 1 : if (msg_ctx == NULL) {
212 0 : fprintf(stderr, "messaging_init failed\n");
213 0 : goto fail;
214 : }
215 :
216 1 : req1 = msg_free_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &req2);
217 1 : if (req1 == NULL) {
218 0 : fprintf(stderr, "msg_count_send failed\n");
219 0 : goto fail;
220 : }
221 1 : req2 = msg_count_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &count);
222 1 : if (req1 == NULL) {
223 0 : fprintf(stderr, "msg_count_send failed\n");
224 0 : goto fail;
225 : }
226 1 : status = messaging_send_buf(msg_ctx, messaging_server_id(msg_ctx),
227 : MSG_SMB_NOTIFY, NULL, 0);
228 1 : if (!NT_STATUS_IS_OK(status)) {
229 0 : fprintf(stderr, "messaging_send_buf failed: %s\n",
230 : nt_errstr(status));
231 0 : goto fail;
232 : }
233 :
234 1 : if (!tevent_req_poll(req1, ev) != 0) {
235 0 : fprintf(stderr, "tevent_req_poll failed\n");
236 0 : goto fail;
237 : }
238 :
239 1 : if (count != 0) {
240 0 : fprintf(stderr, "Got %u msgs, expected none\n", count);
241 0 : goto fail;
242 : }
243 :
244 0 : retval = true;
245 1 : fail:
246 1 : TALLOC_FREE(req1);
247 1 : TALLOC_FREE(msg_ctx);
248 1 : TALLOC_FREE(ev);
249 1 : return retval;
250 : }
251 :
252 : struct msg_pingpong_state {
253 : struct messaging_context *msg_ctx;
254 : };
255 :
256 : static void msg_pingpong_done(struct tevent_req *subreq);
257 :
258 100 : static struct tevent_req *msg_pingpong_send(TALLOC_CTX *mem_ctx,
259 : struct tevent_context *ev,
260 : struct server_id dst)
261 : {
262 100 : struct tevent_req *req, *subreq;
263 100 : struct msg_pingpong_state *state;
264 100 : NTSTATUS status;
265 :
266 100 : req = tevent_req_create(mem_ctx, &state, struct msg_pingpong_state);
267 100 : if (req == NULL) {
268 0 : return NULL;
269 : }
270 :
271 100 : if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(10, 0))) {
272 0 : return tevent_req_post(req, ev);
273 : }
274 :
275 100 : state->msg_ctx = messaging_init(state, ev);
276 100 : if (tevent_req_nomem(state->msg_ctx, req)) {
277 0 : return tevent_req_post(req, ev);
278 : }
279 :
280 100 : status = messaging_send_buf(state->msg_ctx, dst, MSG_PING, NULL, 0);
281 100 : if (!NT_STATUS_IS_OK(status)) {
282 0 : DBG_DEBUG("messaging_send_buf failed: %s\n", nt_errstr(status));
283 0 : tevent_req_error(req, map_errno_from_nt_status(status));
284 0 : return tevent_req_post(req, ev);
285 : }
286 :
287 100 : subreq = messaging_read_send(state, ev, state->msg_ctx, MSG_PONG);
288 100 : if (tevent_req_nomem(subreq, req)) {
289 0 : return tevent_req_post(req, ev);
290 : }
291 100 : tevent_req_set_callback(subreq, msg_pingpong_done, req);
292 100 : return req;
293 : }
294 :
295 100 : static void msg_pingpong_done(struct tevent_req *subreq)
296 : {
297 100 : struct tevent_req *req = tevent_req_callback_data(
298 : subreq, struct tevent_req);
299 100 : int ret;
300 :
301 100 : ret = messaging_read_recv(subreq, NULL, NULL);
302 100 : TALLOC_FREE(subreq);
303 100 : if (ret != 0) {
304 0 : tevent_req_error(req, ret);
305 0 : return;
306 : }
307 100 : tevent_req_done(req);
308 : }
309 :
310 100 : static int msg_pingpong_recv(struct tevent_req *req)
311 : {
312 100 : int err;
313 :
314 100 : if (tevent_req_is_unix_error(req, &err)) {
315 0 : return err;
316 : }
317 0 : return 0;
318 : }
319 :
320 100 : static int msg_pingpong(struct server_id dst)
321 : {
322 100 : struct tevent_context *ev;
323 100 : struct tevent_req *req;
324 100 : int ret = ENOMEM;
325 :
326 100 : ev = tevent_context_init(talloc_tos());
327 100 : if (ev == NULL) {
328 0 : goto fail;
329 : }
330 100 : req = msg_pingpong_send(ev, ev, dst);
331 100 : if (req == NULL) {
332 0 : goto fail;
333 : }
334 100 : if (!tevent_req_poll(req, ev)) {
335 0 : ret = errno;
336 0 : goto fail;
337 : }
338 200 : ret = msg_pingpong_recv(req);
339 100 : fail:
340 100 : TALLOC_FREE(ev);
341 100 : return ret;
342 : }
343 :
344 1 : static void ping_responder_exit(struct tevent_context *ev,
345 : struct tevent_fd *fde,
346 : uint16_t flags,
347 : void *private_data)
348 : {
349 1 : bool *done = private_data;
350 :
351 1 : printf("Child: received write on exit-pipe\n");
352 :
353 1 : *done = true;
354 1 : }
355 :
356 1 : static void ping_responder(int ready_pipe, int exit_pipe)
357 : {
358 1 : struct tevent_context *ev;
359 1 : struct messaging_context *msg_ctx;
360 1 : struct tevent_fd *exit_handler;
361 1 : char c = 0;
362 1 : bool done = false;
363 :
364 1 : ev = samba_tevent_context_init(talloc_tos());
365 1 : if (ev == NULL) {
366 0 : fprintf(stderr, "child tevent_context_init failed\n");
367 0 : exit(1);
368 : }
369 1 : msg_ctx = messaging_init(ev, ev);
370 1 : if (msg_ctx == NULL) {
371 0 : fprintf(stderr, "child messaging_init failed\n");
372 0 : exit(1);
373 : }
374 1 : exit_handler = tevent_add_fd(ev, ev, exit_pipe, TEVENT_FD_READ,
375 : ping_responder_exit, &done);
376 1 : if (exit_handler == NULL) {
377 0 : fprintf(stderr, "child tevent_add_fd failed\n");
378 0 : exit(1);
379 : }
380 :
381 1 : if (write(ready_pipe, &c, 1) != 1) {
382 0 : fprintf(stderr, "child messaging_init failed\n");
383 0 : exit(1);
384 : }
385 :
386 102 : while (!done) {
387 101 : int ret;
388 101 : ret = tevent_loop_once(ev);
389 101 : if (ret != 0) {
390 0 : fprintf(stderr, "child tevent_loop_once failed\n");
391 0 : exit(1);
392 : }
393 : }
394 :
395 1 : printf("Child: done, exiting...\n");
396 :
397 1 : TALLOC_FREE(msg_ctx);
398 1 : TALLOC_FREE(ev);
399 1 : }
400 :
401 1 : bool run_messaging_read3(int dummy)
402 : {
403 1 : struct tevent_context *ev = NULL;
404 1 : struct messaging_context *msg_ctx = NULL;
405 1 : bool retval = false;
406 1 : pid_t child;
407 1 : int ready_pipe[2];
408 1 : int exit_pipe[2];
409 1 : int i, ret;
410 1 : char c;
411 1 : struct server_id dst;
412 1 : ssize_t written;
413 :
414 1 : if ((pipe(ready_pipe) != 0) || (pipe(exit_pipe) != 0)) {
415 0 : perror("pipe failed");
416 0 : return false;
417 : }
418 :
419 1 : child = fork();
420 2 : if (child == -1) {
421 0 : perror("fork failed");
422 0 : return false;
423 : }
424 :
425 2 : if (child == 0) {
426 1 : close(ready_pipe[0]);
427 1 : close(exit_pipe[1]);
428 1 : ping_responder(ready_pipe[1], exit_pipe[0]);
429 1 : exit(0);
430 : }
431 1 : close(ready_pipe[1]);
432 1 : close(exit_pipe[0]);
433 :
434 1 : if (read(ready_pipe[0], &c, 1) != 1) {
435 0 : perror("read failed");
436 0 : return false;
437 : }
438 :
439 1 : ev = samba_tevent_context_init(talloc_tos());
440 1 : if (ev == NULL) {
441 0 : fprintf(stderr, "tevent_context_init failed\n");
442 0 : goto fail;
443 : }
444 :
445 1 : dst = (struct server_id){ .pid = child, .vnn = NONCLUSTER_VNN, };
446 :
447 101 : for (i=0; i<100; i++) {
448 100 : ret = msg_pingpong(dst);
449 100 : if (ret != 0){
450 0 : fprintf(stderr, "msg_pingpong failed\n");
451 0 : goto fail;
452 : }
453 : }
454 :
455 1 : printf("Parent: telling child to exit\n");
456 :
457 1 : written = write(exit_pipe[1], &c, 1);
458 1 : if (written != 1) {
459 0 : perror("write to exit_pipe failed");
460 0 : goto fail;
461 : }
462 :
463 1 : ret = waitpid(child, NULL, 0);
464 1 : if (ret == -1) {
465 0 : perror("waitpid failed");
466 0 : goto fail;
467 : }
468 :
469 1 : printf("Parent: child exited. Done\n");
470 :
471 1 : retval = true;
472 1 : fail:
473 1 : TALLOC_FREE(msg_ctx);
474 1 : TALLOC_FREE(ev);
475 0 : return retval;
476 : }
477 :
478 : /**
479 : * read4:
480 : *
481 : * test transferring a big payload.
482 : */
483 :
484 : #define MSG_TORTURE_READ4 0xF104
485 :
486 1 : static bool read4_child(int ready_fd)
487 : {
488 1 : struct tevent_context *ev = NULL;
489 1 : struct messaging_context *msg_ctx = NULL;
490 1 : TALLOC_CTX *frame = talloc_stackframe();
491 1 : bool retval = false;
492 1 : uint8_t c = 1;
493 1 : struct tevent_req *subreq;
494 1 : int ret;
495 1 : ssize_t bytes;
496 1 : struct messaging_rec *rec;
497 1 : bool ok;
498 :
499 1 : ev = samba_tevent_context_init(frame);
500 1 : if (ev == NULL) {
501 0 : fprintf(stderr, "child: tevent_context_init failed\n");
502 0 : goto done;
503 : }
504 :
505 1 : msg_ctx = messaging_init(ev, ev);
506 1 : if (msg_ctx == NULL) {
507 0 : fprintf(stderr, "child: messaging_init failed\n");
508 0 : goto done;
509 : }
510 :
511 1 : printf("child: telling parent we're ready to receive messages\n");
512 :
513 : /* Tell the parent we are ready to receive messages. */
514 1 : bytes = write(ready_fd, &c, 1);
515 1 : if (bytes != 1) {
516 0 : perror("child: failed to write to ready_fd");
517 0 : goto done;
518 : }
519 :
520 1 : printf("child: waiting for messages\n");
521 :
522 1 : subreq = messaging_read_send(frame, /* TALLOC_CTX */
523 : ev, msg_ctx,
524 : MSG_TORTURE_READ4);
525 1 : if (subreq == NULL) {
526 0 : fprintf(stderr, "child: messaging_read_send failed\n");
527 0 : goto done;
528 : }
529 :
530 1 : ok = tevent_req_poll(subreq, ev);
531 1 : if (!ok) {
532 0 : fprintf(stderr, "child: tevent_req_poll failed\n");
533 0 : goto done;
534 : }
535 :
536 1 : printf("child: receiving message\n");
537 :
538 1 : ret = messaging_read_recv(subreq, frame, &rec);
539 1 : TALLOC_FREE(subreq);
540 1 : if (ret != 0) {
541 0 : fprintf(stderr, "child: messaging_read_recv failed\n");
542 0 : goto done;
543 : }
544 :
545 1 : printf("child: received message\n");
546 :
547 : /* Tell the parent we are done. */
548 1 : bytes = write(ready_fd, &c, 1);
549 1 : if (bytes != 1) {
550 0 : perror("child: failed to write to ready_fd");
551 0 : goto done;
552 : }
553 :
554 1 : printf("child: done\n");
555 :
556 1 : retval = true;
557 :
558 1 : done:
559 1 : TALLOC_FREE(frame);
560 1 : return retval;
561 : }
562 :
563 : struct child_done_state {
564 : int fd;
565 : bool done;
566 : };
567 :
568 1 : static void child_done_cb(struct tevent_context *ev,
569 : struct tevent_fd *fde,
570 : uint16_t flags,
571 : void *private_data)
572 : {
573 1 : struct child_done_state *state =
574 : (struct child_done_state *)private_data;
575 1 : char c = 0;
576 1 : ssize_t bytes;
577 :
578 1 : bytes = read(state->fd, &c, 1);
579 1 : if (bytes != 1) {
580 0 : perror("parent: read from ready_fd failed");
581 : }
582 :
583 1 : state->done = true;
584 1 : }
585 :
586 1 : static bool read4_parent(pid_t child_pid, int ready_fd)
587 : {
588 1 : struct tevent_context *ev = NULL;
589 1 : struct messaging_context *msg_ctx = NULL;
590 1 : bool retval = false;
591 1 : int ret;
592 1 : NTSTATUS status;
593 1 : struct server_id dst;
594 1 : TALLOC_CTX *frame = talloc_stackframe();
595 1 : uint8_t c;
596 1 : ssize_t bytes;
597 1 : struct iovec iov;
598 1 : DATA_BLOB blob;
599 1 : struct tevent_fd *child_done_fde;
600 1 : struct child_done_state child_state;
601 :
602 : /* wait until the child is ready to receive messages */
603 1 : bytes = read(ready_fd, &c, 1);
604 1 : if (bytes != 1) {
605 0 : perror("parent: read from ready_fd failed");
606 0 : goto done;
607 : }
608 :
609 1 : printf("parent: child is ready to receive messages\n");
610 :
611 1 : ev = samba_tevent_context_init(frame);
612 1 : if (ev == NULL) {
613 0 : fprintf(stderr, "parent: tevent_context_init failed\n");
614 0 : goto done;
615 : }
616 :
617 1 : msg_ctx = messaging_init(ev, ev);
618 1 : if (msg_ctx == NULL) {
619 0 : fprintf(stderr, "parent: messaging_init failed\n");
620 0 : goto done;
621 : }
622 :
623 1 : child_state.fd = ready_fd;
624 1 : child_state.done = false;
625 :
626 1 : child_done_fde = tevent_add_fd(ev, ev, ready_fd, TEVENT_FD_READ,
627 : child_done_cb, &child_state);
628 1 : if (child_done_fde == NULL) {
629 0 : fprintf(stderr,
630 : "parent: failed tevent_add_fd for child done\n");
631 0 : goto done;
632 : }
633 :
634 : /*
635 : * Send a 1M payload with the message.
636 : */
637 1 : blob = data_blob_talloc_zero(frame, 1000*1000);
638 1 : iov.iov_base = blob.data;
639 1 : iov.iov_len = blob.length;
640 :
641 1 : dst = messaging_server_id(msg_ctx);
642 1 : dst.pid = child_pid;
643 :
644 1 : printf("parent: sending message to child\n");
645 :
646 1 : status = messaging_send_iov(msg_ctx, dst, MSG_TORTURE_READ4, &iov, 1,
647 : NULL, 0);
648 1 : if (!NT_STATUS_IS_OK(status)) {
649 0 : fprintf(stderr, "parent: messaging_send_iov failed: %s\n",
650 : nt_errstr(status));
651 0 : goto done;
652 : }
653 :
654 1 : printf("parent: waiting for child to confirm\n");
655 :
656 2961 : while (!child_state.done) {
657 2960 : ret = tevent_loop_once(ev);
658 2960 : if (ret != 0) {
659 0 : fprintf(stderr, "parent: tevent_loop_once failed\n");
660 0 : goto done;
661 : }
662 : }
663 :
664 1 : printf("parent: child confirmed\n");
665 :
666 1 : ret = waitpid(child_pid, NULL, 0);
667 1 : if (ret == -1) {
668 0 : perror("parent: waitpid failed");
669 0 : goto done;
670 : }
671 :
672 1 : printf("parent: done\n");
673 :
674 1 : retval = true;
675 :
676 1 : done:
677 1 : TALLOC_FREE(frame);
678 1 : return retval;
679 : }
680 :
681 1 : bool run_messaging_read4(int dummy)
682 : {
683 1 : bool retval = false;
684 1 : pid_t child_pid;
685 1 : int ready_pipe[2];
686 1 : int ret;
687 :
688 1 : ret = pipe(ready_pipe);
689 1 : if (ret != 0) {
690 0 : perror("parent: pipe failed for ready_pipe");
691 0 : return retval;
692 : }
693 :
694 1 : child_pid = fork();
695 2 : if (child_pid == -1) {
696 0 : perror("fork failed");
697 2 : } else if (child_pid == 0) {
698 1 : close(ready_pipe[0]);
699 1 : retval = read4_child(ready_pipe[1]);
700 : } else {
701 1 : close(ready_pipe[1]);
702 1 : retval = read4_parent(child_pid, ready_pipe[0]);
703 : }
704 :
705 0 : return retval;
706 : }
|