kdbus: the driver, original and non-working
[platform/kernel/linux-exynos.git] / tools / testing / selftests / kdbus / test-sync.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <time.h>
4 #include <fcntl.h>
5 #include <stdlib.h>
6 #include <stddef.h>
7 #include <unistd.h>
8 #include <stdint.h>
9 #include <errno.h>
10 #include <assert.h>
11 #include <pthread.h>
12 #include <stdbool.h>
13 #include <signal.h>
14 #include <sys/wait.h>
15 #include <sys/eventfd.h>
16
17 #include "kdbus-api.h"
18 #include "kdbus-test.h"
19 #include "kdbus-util.h"
20 #include "kdbus-enum.h"
21
22 static struct kdbus_conn *conn_a, *conn_b;
23 static unsigned int cookie = 0xdeadbeef;
24
25 static void nop_handler(int sig) {}
26
27 static int interrupt_sync(struct kdbus_conn *conn_src,
28                           struct kdbus_conn *conn_dst)
29 {
30         pid_t pid;
31         int ret, status;
32         struct kdbus_msg *msg = NULL;
33         struct sigaction sa = {
34                 .sa_handler = nop_handler,
35                 .sa_flags = SA_NOCLDSTOP|SA_RESTART,
36         };
37
38         cookie++;
39         pid = fork();
40         ASSERT_RETURN_VAL(pid >= 0, pid);
41
42         if (pid == 0) {
43                 ret = sigaction(SIGINT, &sa, NULL);
44                 ASSERT_EXIT(ret == 0);
45
46                 ret = kdbus_msg_send_sync(conn_dst, NULL, cookie,
47                                           KDBUS_MSG_EXPECT_REPLY,
48                                           100000000ULL, 0, conn_src->id, -1);
49                 ASSERT_EXIT(ret == -ETIMEDOUT);
50
51                 _exit(EXIT_SUCCESS);
52         }
53
54         ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL);
55         ASSERT_RETURN(ret == 0 && msg->cookie == cookie);
56
57         kdbus_msg_free(msg);
58
59         ret = kill(pid, SIGINT);
60         ASSERT_RETURN_VAL(ret == 0, ret);
61
62         ret = waitpid(pid, &status, 0);
63         ASSERT_RETURN_VAL(ret >= 0, ret);
64
65         if (WIFSIGNALED(status))
66                 return TEST_ERR;
67
68         ret = kdbus_msg_recv_poll(conn_src, 100, NULL, NULL);
69         ASSERT_RETURN(ret == -ETIMEDOUT);
70
71         return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR;
72 }
73
74 static int close_epipe_sync(const char *bus)
75 {
76         pid_t pid;
77         int ret, status;
78         struct kdbus_conn *conn_src;
79         struct kdbus_conn *conn_dst;
80         struct kdbus_msg *msg = NULL;
81
82         conn_src = kdbus_hello(bus, 0, NULL, 0);
83         ASSERT_RETURN(conn_src);
84
85         ret = kdbus_add_match_empty(conn_src);
86         ASSERT_RETURN(ret == 0);
87
88         conn_dst = kdbus_hello(bus, 0, NULL, 0);
89         ASSERT_RETURN(conn_dst);
90
91         cookie++;
92         pid = fork();
93         ASSERT_RETURN_VAL(pid >= 0, pid);
94
95         if (pid == 0) {
96                 uint64_t dst_id;
97
98                 /* close our reference */
99                 dst_id = conn_dst->id;
100                 kdbus_conn_free(conn_dst);
101
102                 ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL);
103                 ASSERT_EXIT(ret == 0 && msg->cookie == cookie);
104                 ASSERT_EXIT(msg->src_id == dst_id);
105
106                 cookie++;
107                 ret = kdbus_msg_send_sync(conn_src, NULL, cookie,
108                                           KDBUS_MSG_EXPECT_REPLY,
109                                           100000000ULL, 0, dst_id, -1);
110                 ASSERT_EXIT(ret == -EPIPE);
111
112                 _exit(EXIT_SUCCESS);
113         }
114
115         ret = kdbus_msg_send(conn_dst, NULL, cookie, 0, 0, 0,
116                              KDBUS_DST_ID_BROADCAST, 0, NULL);
117         ASSERT_RETURN(ret == 0);
118
119         cookie++;
120         ret = kdbus_msg_recv_poll(conn_dst, 100, &msg, NULL);
121         ASSERT_RETURN(ret == 0 && msg->cookie == cookie);
122
123         kdbus_msg_free(msg);
124
125         /* destroy connection */
126         kdbus_conn_free(conn_dst);
127         kdbus_conn_free(conn_src);
128
129         ret = waitpid(pid, &status, 0);
130         ASSERT_RETURN_VAL(ret >= 0, ret);
131
132         if (!WIFEXITED(status))
133                 return TEST_ERR;
134
135         return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR;
136 }
137
138 static int cancel_fd_sync(struct kdbus_conn *conn_src,
139                           struct kdbus_conn *conn_dst)
140 {
141         pid_t pid;
142         int cancel_fd;
143         int ret, status;
144         uint64_t counter = 1;
145         struct kdbus_msg *msg = NULL;
146
147         cancel_fd = eventfd(0, 0);
148         ASSERT_RETURN_VAL(cancel_fd >= 0, cancel_fd);
149
150         cookie++;
151         pid = fork();
152         ASSERT_RETURN_VAL(pid >= 0, pid);
153
154         if (pid == 0) {
155                 ret = kdbus_msg_send_sync(conn_dst, NULL, cookie,
156                                           KDBUS_MSG_EXPECT_REPLY,
157                                           100000000ULL, 0, conn_src->id,
158                                           cancel_fd);
159                 ASSERT_EXIT(ret == -ECANCELED);
160
161                 _exit(EXIT_SUCCESS);
162         }
163
164         ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL);
165         ASSERT_RETURN(ret == 0 && msg->cookie == cookie);
166
167         kdbus_msg_free(msg);
168
169         ret = write(cancel_fd, &counter, sizeof(counter));
170         ASSERT_RETURN(ret == sizeof(counter));
171
172         ret = waitpid(pid, &status, 0);
173         ASSERT_RETURN_VAL(ret >= 0, ret);
174
175         if (WIFSIGNALED(status))
176                 return TEST_ERR;
177
178         return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR;
179 }
180
181 static int no_cancel_sync(struct kdbus_conn *conn_src,
182                           struct kdbus_conn *conn_dst)
183 {
184         pid_t pid;
185         int cancel_fd;
186         int ret, status;
187         struct kdbus_msg *msg = NULL;
188
189         /* pass eventfd, but never signal it so it shouldn't have any effect */
190
191         cancel_fd = eventfd(0, 0);
192         ASSERT_RETURN_VAL(cancel_fd >= 0, cancel_fd);
193
194         cookie++;
195         pid = fork();
196         ASSERT_RETURN_VAL(pid >= 0, pid);
197
198         if (pid == 0) {
199                 ret = kdbus_msg_send_sync(conn_dst, NULL, cookie,
200                                           KDBUS_MSG_EXPECT_REPLY,
201                                           100000000ULL, 0, conn_src->id,
202                                           cancel_fd);
203                 ASSERT_EXIT(ret == 0);
204
205                 _exit(EXIT_SUCCESS);
206         }
207
208         ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL);
209         ASSERT_RETURN_VAL(ret == 0 && msg->cookie == cookie, -1);
210
211         kdbus_msg_free(msg);
212
213         ret = kdbus_msg_send_reply(conn_src, cookie, conn_dst->id);
214         ASSERT_RETURN_VAL(ret >= 0, ret);
215
216         ret = waitpid(pid, &status, 0);
217         ASSERT_RETURN_VAL(ret >= 0, ret);
218
219         if (WIFSIGNALED(status))
220                 return -1;
221
222         return (status == EXIT_SUCCESS) ? 0 : -1;
223 }
224
225 static void *run_thread_reply(void *data)
226 {
227         int ret;
228         unsigned long status = TEST_OK;
229
230         ret = kdbus_msg_recv_poll(conn_a, 3000, NULL, NULL);
231         if (ret < 0)
232                 goto exit_thread;
233
234         kdbus_printf("Thread received message, sending reply ...\n");
235
236         /* using an unknown cookie must fail */
237         ret = kdbus_msg_send_reply(conn_a, ~cookie, conn_b->id);
238         if (ret != -EPERM) {
239                 status = TEST_ERR;
240                 goto exit_thread;
241         }
242
243         ret = kdbus_msg_send_reply(conn_a, cookie, conn_b->id);
244         if (ret != 0) {
245                 status = TEST_ERR;
246                 goto exit_thread;
247         }
248
249 exit_thread:
250         pthread_exit(NULL);
251         return (void *) status;
252 }
253
254 int kdbus_test_sync_reply(struct kdbus_test_env *env)
255 {
256         unsigned long status;
257         pthread_t thread;
258         int ret;
259
260         conn_a = kdbus_hello(env->buspath, 0, NULL, 0);
261         conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
262         ASSERT_RETURN(conn_a && conn_b);
263
264         pthread_create(&thread, NULL, run_thread_reply, NULL);
265
266         ret = kdbus_msg_send_sync(conn_b, NULL, cookie,
267                                   KDBUS_MSG_EXPECT_REPLY,
268                                   5000000000ULL, 0, conn_a->id, -1);
269
270         pthread_join(thread, (void *) &status);
271         ASSERT_RETURN(status == 0);
272         ASSERT_RETURN(ret == 0);
273
274         ret = interrupt_sync(conn_a, conn_b);
275         ASSERT_RETURN(ret == 0);
276
277         ret = close_epipe_sync(env->buspath);
278         ASSERT_RETURN(ret == 0);
279
280         ret = cancel_fd_sync(conn_a, conn_b);
281         ASSERT_RETURN(ret == 0);
282
283         ret = no_cancel_sync(conn_a, conn_b);
284         ASSERT_RETURN(ret == 0);
285
286         kdbus_printf("-- closing bus connections\n");
287
288         kdbus_conn_free(conn_a);
289         kdbus_conn_free(conn_b);
290
291         return TEST_OK;
292 }
293
294 #define BYEBYE_ME ((void*)0L)
295 #define BYEBYE_THEM ((void*)1L)
296
297 static void *run_thread_byebye(void *data)
298 {
299         struct kdbus_cmd cmd_byebye = { .size = sizeof(cmd_byebye) };
300         int ret;
301
302         ret = kdbus_msg_recv_poll(conn_a, 3000, NULL, NULL);
303         if (ret == 0) {
304                 kdbus_printf("Thread received message, invoking BYEBYE ...\n");
305                 kdbus_msg_recv(conn_a, NULL, NULL);
306                 if (data == BYEBYE_ME)
307                         kdbus_cmd_byebye(conn_b->fd, &cmd_byebye);
308                 else if (data == BYEBYE_THEM)
309                         kdbus_cmd_byebye(conn_a->fd, &cmd_byebye);
310         }
311
312         pthread_exit(NULL);
313         return NULL;
314 }
315
316 int kdbus_test_sync_byebye(struct kdbus_test_env *env)
317 {
318         pthread_t thread;
319         int ret;
320
321         /*
322          * This sends a synchronous message to a thread, which waits until it
323          * received the message and then invokes BYEBYE on the *ORIGINAL*
324          * connection. That is, on the same connection that synchronously waits
325          * for an reply.
326          * This should properly wake the connection up and cause ECONNRESET as
327          * the connection is disconnected now.
328          *
329          * The second time, we do the same but invoke BYEBYE on the *TARGET*
330          * connection. This should also wake up the synchronous sender as the
331          * reply cannot be sent by a disconnected target.
332          */
333
334         conn_a = kdbus_hello(env->buspath, 0, NULL, 0);
335         conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
336         ASSERT_RETURN(conn_a && conn_b);
337
338         pthread_create(&thread, NULL, run_thread_byebye, BYEBYE_ME);
339
340         ret = kdbus_msg_send_sync(conn_b, NULL, cookie,
341                                   KDBUS_MSG_EXPECT_REPLY,
342                                   5000000000ULL, 0, conn_a->id, -1);
343
344         ASSERT_RETURN(ret == -ECONNRESET);
345
346         pthread_join(thread, NULL);
347
348         kdbus_conn_free(conn_a);
349         kdbus_conn_free(conn_b);
350
351         conn_a = kdbus_hello(env->buspath, 0, NULL, 0);
352         conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
353         ASSERT_RETURN(conn_a && conn_b);
354
355         pthread_create(&thread, NULL, run_thread_byebye, BYEBYE_THEM);
356
357         ret = kdbus_msg_send_sync(conn_b, NULL, cookie,
358                                   KDBUS_MSG_EXPECT_REPLY,
359                                   5000000000ULL, 0, conn_a->id, -1);
360
361         ASSERT_RETURN(ret == -EPIPE);
362
363         pthread_join(thread, NULL);
364
365         kdbus_conn_free(conn_a);
366         kdbus_conn_free(conn_b);
367
368         return TEST_OK;
369 }