Line data Source code
1 : /*
2 : * Unix SMB/CIFS implementation.
3 : * Test for a messaging_send_all bug
4 : * Copyright (C) Volker Lendecke 2017
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 : #include "lib/async_req/async_sock.h"
25 : #include "lib/util/sys_rw.h"
26 :
27 5 : static pid_t fork_responder(struct messaging_context *msg_ctx,
28 : int exit_pipe[2])
29 : {
30 5 : struct tevent_context *ev = messaging_tevent_context(msg_ctx);
31 5 : struct tevent_req *req;
32 5 : pid_t child_pid;
33 5 : int ready_pipe[2];
34 5 : char c = 0;
35 5 : bool ok;
36 5 : int ret, err;
37 5 : NTSTATUS status;
38 5 : ssize_t nwritten;
39 :
40 5 : ret = pipe(ready_pipe);
41 5 : if (ret == -1) {
42 0 : perror("pipe failed");
43 0 : return -1;
44 : }
45 :
46 5 : child_pid = fork();
47 10 : if (child_pid == -1) {
48 0 : perror("fork failed");
49 0 : close(ready_pipe[0]);
50 0 : close(ready_pipe[1]);
51 0 : return -1;
52 : }
53 :
54 10 : if (child_pid != 0) {
55 5 : ssize_t nread;
56 5 : close(ready_pipe[1]);
57 5 : nread = read(ready_pipe[0], &c, 1);
58 5 : close(ready_pipe[0]);
59 5 : if (nread != 1) {
60 0 : perror("read failed");
61 0 : return -1;
62 : }
63 0 : return child_pid;
64 : }
65 :
66 5 : close(ready_pipe[0]);
67 5 : close(exit_pipe[1]);
68 :
69 5 : status = messaging_reinit(msg_ctx);
70 5 : if (!NT_STATUS_IS_OK(status)) {
71 0 : fprintf(stderr, "messaging_reinit failed: %s\n",
72 : nt_errstr(status));
73 0 : close(ready_pipe[1]);
74 0 : exit(1);
75 : }
76 :
77 5 : nwritten = sys_write(ready_pipe[1], &c, 1);
78 5 : if (nwritten != 1) {
79 0 : fprintf(stderr, "write failed: %s\n", strerror(errno));
80 0 : exit(1);
81 : }
82 :
83 5 : close(ready_pipe[1]);
84 :
85 5 : req = wait_for_read_send(ev, ev, exit_pipe[0], false);
86 5 : if (req == NULL) {
87 0 : fprintf(stderr, "wait_for_read_send failed\n");
88 0 : exit(1);
89 : }
90 :
91 5 : ok = tevent_req_poll_unix(req, ev, &err);
92 5 : if (!ok) {
93 0 : fprintf(stderr, "tevent_req_poll_unix failed: %s\n",
94 : strerror(err));
95 0 : exit(1);
96 : }
97 :
98 5 : exit(0);
99 : }
100 :
101 : struct messaging_send_all_state {
102 : struct tevent_context *ev;
103 : struct messaging_context *msg;
104 : pid_t *senders;
105 : size_t num_received;
106 : };
107 :
108 : static void collect_pong_received(struct tevent_req *subreq);
109 :
110 1 : static struct tevent_req *collect_pong_send(TALLOC_CTX *mem_ctx,
111 : struct tevent_context *ev,
112 : struct messaging_context *msg,
113 : const pid_t *senders,
114 : size_t num_senders)
115 : {
116 1 : struct tevent_req *req, *subreq;
117 1 : struct messaging_send_all_state *state;
118 :
119 1 : req = tevent_req_create(mem_ctx, &state,
120 : struct messaging_send_all_state);
121 1 : if (req == NULL) {
122 0 : return NULL;
123 : }
124 1 : state->senders = talloc_memdup(
125 : state, senders, num_senders * sizeof(pid_t));
126 1 : if (tevent_req_nomem(state->senders, req)) {
127 0 : return tevent_req_post(req, ev);
128 : }
129 1 : state->ev = ev;
130 1 : state->msg = msg;
131 :
132 1 : subreq = messaging_read_send(state, state->ev, state->msg, MSG_PONG);
133 1 : if (tevent_req_nomem(subreq, req)) {
134 0 : return tevent_req_post(req, ev);
135 : }
136 1 : tevent_req_set_callback(subreq, collect_pong_received, req);
137 1 : return req;
138 : }
139 :
140 5 : static void collect_pong_received(struct tevent_req *subreq)
141 : {
142 5 : struct tevent_req *req = tevent_req_callback_data(
143 : subreq, struct tevent_req);
144 5 : struct messaging_send_all_state *state = tevent_req_data(
145 : req, struct messaging_send_all_state);
146 5 : size_t num_senders = talloc_array_length(state->senders);
147 5 : size_t i;
148 5 : struct messaging_rec *rec;
149 5 : int ret;
150 :
151 5 : ret = messaging_read_recv(subreq, state, &rec);
152 5 : TALLOC_FREE(subreq);
153 5 : if (tevent_req_error(req, ret)) {
154 1 : return;
155 : }
156 :
157 : /*
158 : * We need to make sure we don't receive our own broadcast!
159 : */
160 :
161 5 : if (rec->src.pid == (uint64_t)getpid()) {
162 0 : fprintf(stderr, "Received my own broadcast!\n");
163 0 : tevent_req_error(req, EMULTIHOP);
164 0 : return;
165 : }
166 :
167 15 : for (i=0; i<num_senders; i++) {
168 15 : if (state->senders[i] == (pid_t)rec->src.pid) {
169 5 : printf("got message from %"PRIu64"\n", rec->src.pid);
170 5 : state->senders[i] = 0;
171 5 : state->num_received += 1;
172 5 : break;
173 : }
174 : }
175 :
176 5 : if (state->num_received == num_senders) {
177 1 : printf("done\n");
178 1 : tevent_req_done(req);
179 1 : return;
180 : }
181 :
182 4 : subreq = messaging_read_send(state, state->ev, state->msg, MSG_PONG);
183 4 : if (tevent_req_nomem(subreq, req)) {
184 0 : return;
185 : }
186 4 : tevent_req_set_callback(subreq, collect_pong_received, req);
187 : }
188 :
189 1 : static int collect_pong_recv(struct tevent_req *req)
190 : {
191 1 : return tevent_req_simple_recv_unix(req);
192 : }
193 :
194 : extern int torture_nprocs;
195 :
196 1 : bool run_messaging_send_all(int dummy)
197 1 : {
198 1 : struct tevent_context *ev = NULL;
199 1 : struct messaging_context *msg_ctx = NULL;
200 1 : int exit_pipe[2];
201 1 : pid_t children[MAX(5, torture_nprocs)];
202 1 : struct tevent_req *req;
203 1 : size_t i;
204 1 : bool ok;
205 1 : int ret, err;
206 :
207 1 : ev = samba_tevent_context_init(talloc_tos());
208 1 : if (ev == NULL) {
209 0 : fprintf(stderr, "tevent_context_init failed\n");
210 0 : return false;
211 : }
212 1 : msg_ctx = messaging_init(ev, ev);
213 1 : if (msg_ctx == NULL) {
214 0 : fprintf(stderr, "messaging_init failed\n");
215 0 : return false;
216 : }
217 1 : ret = pipe(exit_pipe);
218 1 : if (ret != 0) {
219 0 : perror("parent: pipe failed for exit_pipe");
220 0 : return false;
221 : }
222 :
223 6 : for (i=0; i<ARRAY_SIZE(children); i++) {
224 5 : children[i] = fork_responder(msg_ctx, exit_pipe);
225 5 : if (children[i] == -1) {
226 0 : fprintf(stderr, "fork_responder(%zu) failed\n", i);
227 0 : return false;
228 : }
229 : }
230 :
231 1 : req = collect_pong_send(ev, ev, msg_ctx, children,
232 0 : ARRAY_SIZE(children));
233 1 : if (req == NULL) {
234 0 : perror("collect_pong failed");
235 0 : return false;
236 : }
237 :
238 1 : ok = tevent_req_set_endtime(req, ev,
239 : tevent_timeval_current_ofs(10, 0));
240 1 : if (!ok) {
241 0 : perror("tevent_req_set_endtime failed");
242 0 : return false;
243 : }
244 :
245 1 : messaging_send_all(msg_ctx, MSG_PING, NULL, 0);
246 :
247 1 : ok = tevent_req_poll_unix(req, ev, &err);
248 1 : if (!ok) {
249 0 : perror("tevent_req_poll_unix failed");
250 0 : return false;
251 : }
252 :
253 1 : ret = collect_pong_recv(req);
254 1 : TALLOC_FREE(req);
255 :
256 1 : if (ret != 0) {
257 0 : fprintf(stderr, "collect_pong_send returned %s\n",
258 : strerror(ret));
259 0 : return false;
260 : }
261 :
262 1 : close(exit_pipe[1]);
263 :
264 7 : for (i=0; i<ARRAY_SIZE(children); i++) {
265 5 : pid_t child;
266 5 : int status;
267 :
268 5 : do {
269 5 : child = waitpid(children[i], &status, 0);
270 5 : } while ((child == -1) && (errno == EINTR));
271 :
272 5 : if (child != children[i]) {
273 0 : printf("waitpid(%d) failed\n", children[i]);
274 0 : return false;
275 : }
276 : }
277 :
278 0 : return true;
279 : }
|