12 #include "kdbus-api.h"
13 #include "kdbus-util.h"
14 #include "kdbus-enum.h"
15 #include "kdbus-test.h"
17 static wur int add_id_match_(struct kdbus_conn *conn, uint64_t flags, uint64_t notification_type, uint64_t id)
20 struct kdbus_cmd_match cmd;
24 struct kdbus_notify_id_change chg;
27 memset(&buf, 0, sizeof(buf));
28 buf.cmd.size = sizeof(buf);
29 buf.cmd.cookie = 0xdeafbeefdeaddead;
30 buf.cmd.flags = flags;
31 buf.item.size = sizeof(buf.item);
32 buf.item.type = notification_type;
34 ASSERT_ZERO(kdbus_cmd_match_add(conn->fd, &buf.cmd));
37 #define ADD_ID_MATCH(CONN,FLAGS,NOTIFICATION_TYPE,ID)\
38 ASSERT_ZERO(add_id_match_((CONN),(FLAGS),KDBUS_ITEM_ID_##NOTIFICATION_TYPE,(ID)))
40 wur int kdbus_test_match_id_add(struct kdbus_test_env *env)
42 struct kdbus_conn *conn;
43 struct kdbus_msg *msg;
46 ADD_ID_MATCH(env->conn, 0, ADD, KDBUS_MATCH_ID_ANY);
48 /* create 2nd connection */
49 ASSERT_NONZERO(conn = kdbus_hello(env->buspath, 0, NULL, 0));
51 /* 1st connection should have received a notification */
52 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
53 ASSERT_NO_PENDING(env->conn);
55 ASSERT_RETURN(msg->items[0].type,==,(typeof(msg->items[0].type))KDBUS_ITEM_ID_ADD);
56 ASSERT_RETURN(msg->items[0].id_change.id,==,conn->id);
58 kdbus_conn_free(conn);
63 wur int kdbus_test_match_id_remove(struct kdbus_test_env *env)
65 struct kdbus_conn *conn;
66 struct kdbus_msg *msg;
69 /* create 2nd connection */
70 ASSERT_NONZERO(conn = kdbus_hello(env->buspath, 0, NULL, 0));
73 /* register match on 1st connection */
74 ADD_ID_MATCH(env->conn, 0, REMOVE, id);
76 /* remove 2nd connection again */
77 kdbus_conn_free(conn);
79 /* 1st connection should have received a notification */
80 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
82 ASSERT_RETURN(msg->items[0].type,==,(typeof(msg->items[0].type))KDBUS_ITEM_ID_REMOVE);
83 ASSERT_RETURN(msg->items[0].id_change.id,==,id);
88 wur int kdbus_test_match_replace(struct kdbus_test_env *env)
90 struct kdbus_conn *conn;
91 struct kdbus_msg *msg;
94 /* add a match to id_add */
95 ASSERT_RETURN(kdbus_test_match_id_add(env),==,TEST_OK);
97 /* do a replace of the match from id_add to id_remove */
98 ADD_ID_MATCH(env->conn, KDBUS_MATCH_REPLACE, REMOVE, KDBUS_MATCH_ID_ANY);
100 /* create 2nd connection */
101 ASSERT_NONZERO(conn = kdbus_hello(env->buspath, 0, NULL, 0));
104 /* 1st connection should _not_ have received a notification */
105 ASSERT_NONZERO(kdbus_msg_recv(env->conn, &msg, NULL));
107 /* remove 2nd connection */
108 kdbus_conn_free(conn);
110 /* 1st connection should _now_ have received a notification */
111 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
113 ASSERT_RETURN(msg->items[0].type,==,(typeof(msg->items[0].type))KDBUS_ITEM_ID_REMOVE);
114 ASSERT_RETURN(msg->items[0].id_change.id,==,id);
119 #define UNMATCHED_NAME "this.will.never.match"
121 static wur int add_name_match_(struct kdbus_conn *conn, uint64_t flags, uint64_t old_id, uint64_t new_id, uint64_t notification_type, char const *name)
124 struct kdbus_cmd_match cmd;
128 struct kdbus_notify_name_change chg;
132 memset(&buf, 0, sizeof(buf));
133 buf.cmd.flags = flags;
134 buf.cmd.cookie = 0xdeaddeaddeafbeef;
135 buf.item.type = notification_type;
136 buf.item.chg.old_id.id = old_id;
137 buf.item.chg.new_id.id = new_id;
138 buf.item.size = sizeof(buf.item);
140 __auto_type name_size = strlen(name)+1;
141 memcpy(buf.name, name, name_size);
142 buf.item.size += name_size;
144 buf.cmd.size = sizeof(buf.cmd) + buf.item.size;
145 ASSERT_ZERO(kdbus_cmd_match_add(conn->fd, &buf.cmd));
148 #define ADD_NAME_MATCH(CONN,FLAGS,OLD_ID,NEW_ID,NOTIFICATION_TYPE,NAME)\
149 ASSERT_ZERO(add_name_match_((CONN),(FLAGS),(OLD_ID),(NEW_ID),KDBUS_ITEM_NAME_##NOTIFICATION_TYPE,(NAME)))
151 static wur int assert_single_match_(struct kdbus_conn *conn, uint64_t old_id, uint64_t new_id, uint64_t notification_type, char const *name)
153 struct kdbus_msg *msg;
154 ASSERT_ZERO(kdbus_msg_recv(conn, &msg, NULL));
155 ASSERT_RETURN(msg->items[0].type,==,notification_type);
156 ASSERT_RETURN(msg->items[0].name_change.old_id.id,==,old_id);
157 ASSERT_RETURN(msg->items[0].name_change.new_id.id,==,new_id);
158 ASSERT_ZERO(strcmp(msg->items[0].name_change.name, name));
159 ASSERT_ZERO(kdbus_free_msg(conn, msg));
160 ASSERT_NO_PENDING(conn);
163 #define ASSERT_SINGLE_MATCH(CONN,OLD_ID,NEW_ID,NOTIFICATION_TYPE,NAME)\
164 ASSERT_ZERO(assert_single_match_((CONN),(OLD_ID),(NEW_ID),KDBUS_ITEM_NAME_##NOTIFICATION_TYPE,(NAME)))
166 static wur int kdbus_test_match_name_add_(struct kdbus_test_env *env, char const *name, struct kdbus_conn *listener)
168 /* acquire the name */
169 ASSERT_ZERO(kdbus_name_acquire(env->conn, name, NULL));
171 #define CHECK(CONN) ASSERT_SINGLE_MATCH((CONN), 0, env->conn->id, ADD, name)
177 ASSERT_ZERO(kdbus_name_release(env->conn, name));
182 wur int kdbus_test_match_name_add(struct kdbus_test_env *env)
184 struct kdbus_conn *listener;
185 char const *name = "foo.bla.blaz";
187 ASSERT_NONZERO(listener = kdbus_hello(env->buspath, 0, NULL, 0));
189 /* install the match rule */
190 ADD_NAME_MATCH(env->conn, 0, KDBUS_MATCH_ID_ANY, KDBUS_MATCH_ID_ANY, ADD, name);
192 /* no accidental notifications when no match set up */
193 ASSERT_ZERO(kdbus_test_match_name_add_(env, name, NULL));
194 ASSERT_NO_PENDING(listener);
196 #define CHECK(ID,NAME,CONN_IF_WILL_MATCH) do {\
197 ADD_NAME_MATCH(listener, KDBUS_MATCH_REPLACE, KDBUS_MATCH_ID_ANY, (ID), ADD, NAME);\
198 ASSERT_ZERO(kdbus_test_match_name_add_(env, name, (CONN_IF_WILL_MATCH)));\
199 ASSERT_NO_PENDING(listener);\
201 CHECK(KDBUS_MATCH_ID_ANY, UNMATCHED_NAME, NULL); /* wrong name */
202 CHECK(KDBUS_MATCH_ID_ANY, NULL, listener); /* wildcard name */
203 CHECK(env->conn->id, NULL, listener); /* wildcard name + good id */
204 CHECK(env->conn->id + 1, NULL, NULL); /* wildcard name + bad id */
206 kdbus_conn_free(listener);
211 static wur int kdbus_test_match_name_remove_(struct kdbus_test_env *env, char const *name, struct kdbus_conn *listener)
213 /* acquire the name */
214 ASSERT_ZERO(kdbus_name_acquire(env->conn, name, NULL));
216 /* release the name again */
217 ASSERT_ZERO(kdbus_name_release(env->conn, name));
219 /* we should have received a notification */
220 #define CHECK(CONN) ASSERT_SINGLE_MATCH((CONN), env->conn->id, 0, REMOVE, name)
229 wur int kdbus_test_match_name_remove(struct kdbus_test_env *env)
231 struct kdbus_conn *listener;
232 char const *name = "foo.bla.blazz";
234 ASSERT_NONZERO(listener = kdbus_hello(env->buspath, 0, NULL, 0));
236 /* install the match rule */
237 ADD_NAME_MATCH(env->conn, 0, KDBUS_MATCH_ID_ANY, KDBUS_MATCH_ID_ANY, REMOVE, name);
239 /* no accidental notifications when no match set up */
240 ASSERT_ZERO(kdbus_test_match_name_remove_(env, name, NULL));
241 ASSERT_NO_PENDING(listener);
243 #define CHECK(ID,NAME,CONN_IF_WILL_MATCH) do {\
244 ADD_NAME_MATCH(listener, KDBUS_MATCH_REPLACE, (ID), KDBUS_MATCH_ID_ANY, REMOVE, NAME);\
245 ASSERT_ZERO(kdbus_test_match_name_remove_(env, name, (CONN_IF_WILL_MATCH)));\
246 ASSERT_NO_PENDING(listener);\
248 CHECK(KDBUS_MATCH_ID_ANY, UNMATCHED_NAME, NULL); /* wrong name */
249 CHECK(KDBUS_MATCH_ID_ANY, NULL, listener); /* wildcard name */
250 CHECK(env->conn->id, NULL, listener); /* wildcard name + good id */
251 CHECK(env->conn->id + 1, NULL, NULL); /* wildcard name + bad id */
253 kdbus_conn_free(listener);
258 static wur int kdbus_test_match_name_change_(struct kdbus_test_env *env, struct kdbus_conn *conn, char const *name, struct kdbus_conn *listener)
262 /* acquire the name */
263 ASSERT_ZERO(kdbus_name_acquire(env->conn, name, NULL));
265 /* allow the new connection to own the same name */
266 /* queue the 2nd connection as waiting owner */
267 flags = KDBUS_NAME_QUEUE;
268 ASSERT_ZERO(kdbus_name_acquire(conn, name, &flags));
269 ASSERT_NONZERO(flags & KDBUS_NAME_IN_QUEUE);
271 /* release name from 1st connection */
272 ASSERT_ZERO(kdbus_name_release(env->conn, name));
274 /* we should have received a notification */
275 #define CHECK(CONN) ASSERT_SINGLE_MATCH((CONN), env->conn->id, conn->id, CHANGE, name)
281 ASSERT_ZERO(kdbus_name_release(conn, name));
286 wur int kdbus_test_match_name_change(struct kdbus_test_env *env)
288 struct kdbus_conn *listener, *conn;
289 char const *name = "foo.bla.baz";
291 /* create a 2nd connection */
292 ASSERT_NONZERO(conn = kdbus_hello(env->buspath, 0, NULL, 0));
294 ASSERT_NONZERO(listener = kdbus_hello(env->buspath, 0, NULL, 0));
296 ADD_NAME_MATCH(env->conn, 0, KDBUS_MATCH_ID_ANY, KDBUS_MATCH_ID_ANY, CHANGE, name);
298 /* no accidental notifications when no match set up */
299 ASSERT_ZERO(kdbus_test_match_name_change_(env, conn, name, NULL));
300 ASSERT_NO_PENDING(listener);
302 #define CHECK(OLD_ID,NEW_ID,NAME,CONN_IF_WILL_MATCH) do {\
303 ADD_NAME_MATCH(listener, KDBUS_MATCH_REPLACE, (OLD_ID), (NEW_ID), CHANGE, NAME);\
304 ASSERT_ZERO(kdbus_test_match_name_change_(env, conn, name, (CONN_IF_WILL_MATCH)));\
305 ASSERT_NO_PENDING(listener);\
307 CHECK(KDBUS_MATCH_ID_ANY, KDBUS_MATCH_ID_ANY, UNMATCHED_NAME, NULL); /* wrong name */
308 CHECK(KDBUS_MATCH_ID_ANY, KDBUS_MATCH_ID_ANY, NULL, listener); /* wildcard name */
310 CHECK(env->conn->id, KDBUS_MATCH_ID_ANY, NULL, listener); /* wildcard name + good id */
311 CHECK(env->conn->id + 1, KDBUS_MATCH_ID_ANY, NULL, NULL); /* wildcard name + bad id */
313 CHECK(KDBUS_MATCH_ID_ANY, conn->id, NULL, listener); /* good id */
314 CHECK(KDBUS_MATCH_ID_ANY, conn->id + 1, NULL, NULL); /* bad id */
316 CHECK(env->conn->id, conn->id, NULL, listener); /* wildcard name + good id */
318 CHECK(env->conn->id + 1, conn->id, NULL, NULL); /* wildcard name + bad id */
319 CHECK(env->conn->id, conn->id + 1, NULL, NULL); /* wildcard name + bad id */
321 kdbus_conn_free(listener);
322 ASSERT_NO_PENDING(conn);
323 kdbus_conn_free(conn);
328 static wur int send_bloom_filter(const struct kdbus_conn *conn,
330 const uint8_t *filter,
332 uint64_t filter_generation)
334 struct kdbus_cmd_send cmd = {};
335 struct kdbus_msg *msg;
336 struct kdbus_item *item;
340 size = sizeof(struct kdbus_msg);
341 size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + filter_size;
345 memset(msg, 0, size);
347 msg->src_id = conn->id;
348 msg->dst_id = KDBUS_DST_ID_BROADCAST;
349 msg->flags = KDBUS_MSG_SIGNAL;
350 msg->payload_type = KDBUS_PAYLOAD_DBUS;
351 msg->cookie = cookie;
354 item->type = KDBUS_ITEM_BLOOM_FILTER;
355 item->size = KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) +
358 item->bloom_filter.generation = filter_generation;
359 memcpy(item->bloom_filter.data, filter, filter_size);
361 cmd.size = sizeof(cmd);
362 cmd.msg_address = (uintptr_t)msg;
364 ret = kdbus_cmd_send(conn->fd, &cmd);
366 kdbus_printf("error sending message: %d (%m)\n", ret);
373 wur int kdbus_test_match_bloom(struct kdbus_test_env *env)
376 struct kdbus_cmd_match cmd;
380 uint8_t data_gen0[64];
381 uint8_t data_gen1[64];
384 struct kdbus_conn *conn;
385 struct kdbus_msg *msg;
386 uint64_t cookie = 0xf000f00f;
389 /* install the match rule */
390 memset(&buf, 0, sizeof(buf));
391 buf.cmd.size = sizeof(buf);
393 buf.item.size = sizeof(buf.item);
394 buf.item.type = KDBUS_ITEM_BLOOM_MASK;
395 buf.item.data_gen0[0] = 0x55;
396 buf.item.data_gen0[63] = 0x80;
398 buf.item.data_gen1[1] = 0xaa;
399 buf.item.data_gen1[9] = 0x02;
401 ASSERT_ZERO(kdbus_cmd_match_add(env->conn->fd, &buf.cmd));
403 /* create a 2nd connection */
404 ASSERT_NONZERO(conn = kdbus_hello(env->buspath, 0, NULL, 0));
406 /* a message with a 0'ed out filter must not reach the other peer */
407 memset(filter, 0, sizeof(filter));
408 ASSERT_ZERO(send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 0));
410 ASSERT_RETURN(-EAGAIN,==,kdbus_msg_recv(env->conn, &msg, NULL));
412 /* now set the filter to the connection's mask and expect success */
415 ASSERT_ZERO(send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 0));
417 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
418 ASSERT_RETURN(msg->cookie,==,cookie);
420 /* broaden the filter and try again. this should also succeed. */
424 ASSERT_ZERO(send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 0));
426 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
427 ASSERT_RETURN(msg->cookie,==,cookie);
429 /* the same filter must not match against bloom generation 1 */
430 ASSERT_ZERO(send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 1));
432 ASSERT_RETURN(-EAGAIN,==,kdbus_msg_recv(env->conn, &msg, NULL));
434 /* set a different filter and try again */
437 ASSERT_ZERO(send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 1));
439 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
440 ASSERT_RETURN(msg->cookie,==,cookie);
442 kdbus_conn_free(conn);
447 static wur int has_all_names(struct kdbus_item *item, unsigned size)
450 int left = sizeof(seen);
451 memset(seen, 0, sizeof(seen));
453 while (size && left) {
454 uint64_t item_size = KDBUS_ALIGN8(item->size);
455 if (item_size > size)
457 if (KDBUS_ITEM_OWNED_NAME == item->type) {
459 ASSERT_RETURN(255u,==,strlen(item->name.name));
460 ASSERT_RETURN(0,==,memcmp(item->name.name, "big.n", 5));
462 ASSERT_RETURN('0',==,(int)item->name.name[i]);
463 ASSERT_RETURN('0',<=,(int)item->name.name[252]);
464 ASSERT_RETURN((int)item->name.name[252],<=,'9');
465 ASSERT_RETURN('0',<=,(int)item->name.name[253]);
466 ASSERT_RETURN((int)item->name.name[253],<=,'9');
467 ASSERT_RETURN('0',<=,(int)item->name.name[254]);
468 ASSERT_RETURN((int)item->name.name[254],<=,'9');
469 i = item->name.name[254]-'0' + 10*(item->name.name[253]-'0') + 100*(item->name.name[252]-'0');
470 ASSERT_RETURN(0,<=,i);
471 ASSERT_RETURN(i,<,256);
472 ASSERT_ZERO((int)seen[i]);
476 size -= (unsigned)item_size;
477 item = (typeof(item))((uintptr_t)item + (unsigned)item_size);
483 wur int kdbus_test_big_metadata(struct kdbus_test_env *env)
486 struct kdbus_conn *conn;
487 struct kdbus_msg *msg;
488 struct kdbus_info *info;
492 ASSERT_NONZERO(conn = kdbus_hello(env->buspath, 0, NULL, 0));
493 for (i=256; --i>=0;) {
494 sprintf(buf, "big.n%0250d", i);
495 /*print("\nreg (%s)\n", buf);*/
496 ASSERT_ZERO(kdbus_name_acquire(conn, buf, NULL));
499 /*print("\nreg done\n");*/
501 ASSERT_ZERO(kdbus_msg_send(conn, NULL, 1, 0, 0, 0, env->conn->id));
502 /*print("\nsend done\n");*/
503 /*print("\nfree done\n");*/
505 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
506 /*print("\nrecv done\n");*/
508 ASSERT_ZERO(has_all_names(msg->items, msg->size - offsetof(typeof(*msg), items)));
512 ASSERT_ZERO(kdbus_conn_info(env->conn, conn->id, NULL, _KDBUS_ATTACH_ALL, &offset));
513 info = (struct kdbus_info *)(env->conn->buf + offset);
514 ASSERT_RETURN(info->id,==,conn->id);
515 ASSERT_ZERO(has_all_names(info->items, info->size - offsetof(typeof(*info), items)));
517 kdbus_conn_free(conn);
519 /*print("\nfree done\n");*/
524 wur int kdbus_test_match_itemless(struct kdbus_test_env *env)
526 struct kdbus_cmd_match cmd;
527 struct kdbus_conn *conn;
528 struct kdbus_msg *msg;
529 uint64_t cookie = 0xf000f00f;
532 /* install the match rule */
533 memset(&cmd, 0, sizeof(cmd));
534 cmd.size = sizeof(cmd);
536 ASSERT_ZERO(kdbus_cmd_match_add(env->conn->fd, &cmd));
538 /* create a 2nd connection */
539 ASSERT_NONZERO(conn = kdbus_hello(env->buspath, 0, NULL, 0));
541 /* 1st connection should have received a notification */
542 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
543 ASSERT_NO_PENDING(env->conn);
544 ASSERT_RETURN(msg->items[0].type,==,(typeof(msg->items[0].type))KDBUS_ITEM_ID_ADD);
545 ASSERT_RETURN(msg->items[0].id_change.id,==,conn->id);
547 /* even a message with a 0'ed out filter must match */
548 memset(filter, 0, sizeof(filter));
549 ASSERT_ZERO(send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 0));
551 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
552 ASSERT_NO_PENDING(env->conn);
553 ASSERT_RETURN(msg->cookie,==,cookie);
555 ASSERT_ZERO(kdbus_name_acquire(conn, "mein.volk", NULL));
556 /* 1st connection should have received a notification */
557 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
558 ASSERT_NO_PENDING(env->conn);
559 ASSERT_RETURN(msg->items[0].type,==,(typeof(msg->items[0].type))KDBUS_ITEM_NAME_ADD);
560 ASSERT_RETURN(msg->items[0].name_change.new_id.id,==,conn->id);
562 ASSERT_ZERO(kdbus_name_release(conn, "mein.volk"));
563 /* 1st connection should have received a notification */
564 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
565 ASSERT_NO_PENDING(env->conn);
566 ASSERT_RETURN(msg->items[0].type,==,(typeof(msg->items[0].type))KDBUS_ITEM_NAME_REMOVE);
567 ASSERT_RETURN(msg->items[0].name_change.old_id.id,==,conn->id);
569 kdbus_conn_free(conn);
571 /* 1st connection should have received a notification */
572 ASSERT_ZERO(kdbus_msg_recv(env->conn, &msg, NULL));
573 ASSERT_NO_PENDING(env->conn);
574 ASSERT_RETURN(msg->items[0].type,==,(typeof(msg->items[0].type))KDBUS_ITEM_ID_REMOVE);
575 ASSERT_RETURN(msg->items[0].id_change.id,==,conn->id);
577 ASSERT_NO_PENDING(env->conn);