1 // Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <algorithm>
6
7 #include <sys/select.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10
11 #include "dbus_test.h"
12
13 namespace {
14
15 const char kServerAddress[] = "unix:abstract=/org/chromium/DBusTest";
16
17 } // namespace
18
DBusMatch()19 DBusMatch::DBusMatch()
20 : message_type_(DBUS_MESSAGE_TYPE_INVALID),
21 as_property_dictionary_(false),
22 send_reply_(false),
23 send_error_(false),
24 expect_serial_(false),
25 matched_(false) {
26
27 }
28
WithString(std::string value)29 DBusMatch& DBusMatch::WithString(std::string value) {
30 Arg arg;
31 arg.type = DBUS_TYPE_STRING;
32 arg.array = false;
33 arg.string_value = value;
34
35 if (send_reply_)
36 reply_args_.push_back(arg);
37 else
38 args_.push_back(arg);
39 return *this;
40 }
41
WithUnixFd(int value)42 DBusMatch& DBusMatch::WithUnixFd(int value) {
43 Arg arg;
44 arg.type = DBUS_TYPE_UNIX_FD;
45 arg.array = false;
46 arg.int_value = value;
47
48 if (send_reply_)
49 reply_args_.push_back(arg);
50 else
51 args_.push_back(arg);
52 return *this;
53 }
54
WithObjectPath(std::string value)55 DBusMatch& DBusMatch::WithObjectPath(std::string value) {
56 Arg arg;
57 arg.type = DBUS_TYPE_OBJECT_PATH;
58 arg.array = false;
59 arg.string_value = value;
60
61 if (send_reply_)
62 reply_args_.push_back(arg);
63 else
64 args_.push_back(arg);
65 return *this;
66 }
67
WithArrayOfStrings(std::vector<std::string> values)68 DBusMatch& DBusMatch::WithArrayOfStrings(std::vector<std::string> values) {
69 Arg arg;
70 arg.type = DBUS_TYPE_STRING;
71 arg.array = true;
72 arg.string_values = values;
73
74 if (send_reply_)
75 reply_args_.push_back(arg);
76 else
77 args_.push_back(arg);
78 return *this;
79 }
80
WithArrayOfObjectPaths(std::vector<std::string> values)81 DBusMatch& DBusMatch::WithArrayOfObjectPaths(std::vector<std::string> values) {
82 Arg arg;
83 arg.type = DBUS_TYPE_OBJECT_PATH;
84 arg.array = true;
85 arg.string_values = values;
86
87 if (send_reply_)
88 reply_args_.push_back(arg);
89 else
90 args_.push_back(arg);
91 return *this;
92 }
93
WithNoMoreArgs()94 DBusMatch& DBusMatch::WithNoMoreArgs() {
95 Arg arg;
96 arg.type = DBUS_TYPE_INVALID;
97
98 args_.push_back(arg);
99 return *this;
100 }
101
AsPropertyDictionary()102 DBusMatch& DBusMatch::AsPropertyDictionary() {
103 as_property_dictionary_ = true;
104 return *this;
105 }
106
107
SendReply()108 DBusMatch& DBusMatch::SendReply() {
109 send_reply_ = true;
110 expect_serial_ = true;
111 return *this;
112 }
113
SendError(std::string error_name,std::string error_message)114 DBusMatch& DBusMatch::SendError(std::string error_name,
115 std::string error_message) {
116 send_error_ = true;
117 error_name_ = error_name;
118 error_message_ = error_message;
119 expect_serial_ = true;
120 return *this;
121 }
122
SendReplyNoWait()123 DBusMatch& DBusMatch::SendReplyNoWait() {
124 send_reply_ = true;
125 expect_serial_ = false;
126 return *this;
127 }
128
129
Send()130 DBusMatch& DBusMatch::Send() {
131 DBusMessage *message;
132 if (message_type_ == DBUS_MESSAGE_TYPE_SIGNAL)
133 message = dbus_message_new_signal(path_.c_str(),
134 interface_.c_str(),
135 member_.c_str());
136 else if (message_type_ == DBUS_MESSAGE_TYPE_METHOD_CALL)
137 message = dbus_message_new_method_call(NULL,
138 path_.c_str(),
139 interface_.c_str(),
140 member_.c_str());
141 else
142 return *this;
143
144 AppendArgsToMessage(message, &args_);
145 SendMessage(conn_, message);
146
147 dbus_message_unref(message);
148
149 return *this;
150 }
151
152
ExpectMethodCall(std::string path,std::string interface,std::string method)153 void DBusMatch::ExpectMethodCall(std::string path,
154 std::string interface,
155 std::string method) {
156 message_type_ = DBUS_MESSAGE_TYPE_METHOD_CALL;
157 path_ = path;
158 interface_ = interface;
159 member_ = method;
160 }
161
162
CreateSignal(DBusConnection * conn,std::string path,std::string interface,std::string signal_name)163 void DBusMatch::CreateSignal(DBusConnection *conn,
164 std::string path,
165 std::string interface,
166 std::string signal_name) {
167 message_type_ = DBUS_MESSAGE_TYPE_SIGNAL;
168 path_ = path;
169 interface_ = interface;
170 member_ = signal_name;
171
172 conn_ = conn;
173 expect_serial_ = true;
174 matched_ = true;
175 }
176
CreateMessageCall(DBusConnection * conn,std::string path,std::string interface,std::string method_name)177 void DBusMatch::CreateMessageCall(DBusConnection *conn,
178 std::string path,
179 std::string interface,
180 std::string method_name) {
181 message_type_ = DBUS_MESSAGE_TYPE_METHOD_CALL;
182 path_ = path;
183 interface_ = interface;
184 member_ = method_name;
185
186 conn_ = conn;
187 expect_serial_ = true;
188 matched_ = true;
189 }
190
191
MatchMessageArgs(DBusMessage * message,std::vector<Arg> * args)192 bool DBusMatch::MatchMessageArgs(DBusMessage *message,
193 std::vector<Arg> *args)
194 {
195 DBusMessageIter iter;
196 dbus_message_iter_init(message, &iter);
197 for (std::vector<Arg>::iterator it = args->begin(); it != args->end(); ++it) {
198 Arg &arg = *it;
199
200 int type = dbus_message_iter_get_arg_type(&iter);
201 if (type != arg.type)
202 return false;
203
204 if (arg.type == DBUS_TYPE_STRING
205 || arg.type == DBUS_TYPE_OBJECT_PATH) {
206 const char *str_value;
207 dbus_message_iter_get_basic(&iter, &str_value);
208 if (strcmp(str_value, arg.string_value.c_str()) != 0)
209 return false;
210 }
211 // TODO(keybuk): additional argument types
212
213 dbus_message_iter_next(&iter);
214 }
215
216 return true;
217 }
218
AppendArgsToMessage(DBusMessage * message,std::vector<Arg> * args)219 void DBusMatch::AppendArgsToMessage(DBusMessage *message,
220 std::vector<Arg> *args)
221 {
222 DBusMessageIter message_iter;
223 DBusMessageIter dict_array_iter;
224 DBusMessageIter struct_iter;
225 DBusMessageIter iter;
226
227 if (as_property_dictionary_) {
228 dbus_message_iter_init_append(message, &message_iter);
229 dbus_message_iter_open_container(&message_iter,
230 DBUS_TYPE_ARRAY, "{sv}",
231 &dict_array_iter);
232 } else {
233 dbus_message_iter_init_append(message, &iter);
234 }
235
236 for (std::vector<Arg>::iterator it = args->begin(); it != args->end(); ++it) {
237 Arg &arg = *it;
238
239 if (as_property_dictionary_) {
240 dbus_message_iter_open_container(&dict_array_iter,
241 DBUS_TYPE_DICT_ENTRY, NULL,
242 &struct_iter);
243
244 const char *str_value = arg.string_value.c_str();
245 dbus_message_iter_append_basic(&struct_iter, arg.type, &str_value);
246
247 arg = *(++it);
248 }
249
250 const char *array_type, *element_type;
251 switch (arg.type) {
252 case DBUS_TYPE_STRING:
253 array_type = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
254 element_type = DBUS_TYPE_STRING_AS_STRING;
255 break;
256 case DBUS_TYPE_OBJECT_PATH:
257 array_type = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING;
258 element_type = DBUS_TYPE_OBJECT_PATH_AS_STRING;
259 break;
260 case DBUS_TYPE_UNIX_FD:
261 array_type = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UNIX_FD_AS_STRING;
262 element_type = DBUS_TYPE_UNIX_FD_AS_STRING;
263 break;
264 default:
265 abort();
266 // TODO(keybuk): additional argument types
267 }
268
269 if (as_property_dictionary_) {
270 dbus_message_iter_open_container(&struct_iter,
271 DBUS_TYPE_VARIANT,
272 arg.array ? array_type : element_type,
273 &iter);
274 }
275
276 DBusMessageIter array_iter;
277 if (arg.array) {
278 dbus_message_iter_open_container(&iter,
279 DBUS_TYPE_ARRAY, element_type,
280 &array_iter);
281
282 if (arg.type == DBUS_TYPE_STRING
283 || arg.type == DBUS_TYPE_OBJECT_PATH) {
284 for (std::vector<std::string>::const_iterator vit =
285 arg.string_values.begin(); vit != arg.string_values.end();
286 ++vit) {
287 const char *str_value = vit->c_str();
288 dbus_message_iter_append_basic(&array_iter, arg.type, &str_value);
289 }
290 }
291 // TODO(keybuk): additional element types
292
293 dbus_message_iter_close_container(&iter, &array_iter);
294 } else {
295 if (arg.type == DBUS_TYPE_STRING
296 || arg.type == DBUS_TYPE_OBJECT_PATH) {
297 const char *str_value = arg.string_value.c_str();
298 dbus_message_iter_append_basic(&iter, arg.type, &str_value);
299 } else if (arg.type == DBUS_TYPE_UNIX_FD) {
300 dbus_message_iter_append_basic(&iter, arg.type, &arg.int_value);
301 }
302 // TODO(keybuk): additional argument types
303 }
304
305 if (as_property_dictionary_) {
306 dbus_message_iter_close_container(&struct_iter, &iter);
307 dbus_message_iter_close_container(&dict_array_iter, &struct_iter);
308 }
309 }
310
311 if (as_property_dictionary_)
312 dbus_message_iter_close_container(&message_iter, &dict_array_iter);
313 }
314
SendMessage(DBusConnection * conn,DBusMessage * message)315 void DBusMatch::SendMessage(DBusConnection *conn, DBusMessage *message) {
316 dbus_bool_t success;
317 dbus_uint32_t serial;
318 success = dbus_connection_send(conn, message, &serial);
319
320 if (success && expect_serial_)
321 expected_serials_.push_back(serial);
322 }
323
324
HandleServerMessage(DBusConnection * conn,DBusMessage * message)325 bool DBusMatch::HandleServerMessage(DBusConnection *conn,
326 DBusMessage *message) {
327 // Make sure we're expecting a method call or signal of this name
328 if (message_type_ == DBUS_MESSAGE_TYPE_METHOD_CALL &&
329 !dbus_message_is_method_call(message,
330 interface_.c_str(), member_.c_str()))
331 return false;
332 else if (message_type_ == DBUS_MESSAGE_TYPE_SIGNAL &&
333 !dbus_message_is_signal(message,
334 interface_.c_str(), member_.c_str()))
335 return false;
336
337 // Make sure the path is what we expected.
338 if (path_.length() &&
339 strcmp(path_.c_str(), dbus_message_get_path(message)) != 0)
340 return false;
341
342 // And the arguments.
343 if (!MatchMessageArgs(message, &args_))
344 return false;
345
346 // Handle any actions.
347 matched_ = true;
348 if (send_reply_ || send_error_) {
349 // Send out the reply
350 DBusMessage *reply = NULL;
351 if (send_reply_)
352 reply = dbus_message_new_method_return(message);
353 else if (send_error_)
354 reply = dbus_message_new_error(message,
355 error_name_.c_str(),
356 error_message_.c_str());
357
358 AppendArgsToMessage(reply, &reply_args_);
359 SendMessage(conn, reply);
360
361 dbus_message_unref(reply);
362 }
363
364 return true;
365 }
366
HandleClientMessage(DBusConnection * conn,DBusMessage * message)367 bool DBusMatch::HandleClientMessage(DBusConnection *conn,
368 DBusMessage *message) {
369 // From the client side we check whether the message has a serial number
370 // we generated on our server side, and if so, remove it from the list of
371 // those we're expecting to see.
372 for (std::vector<dbus_uint32_t>::iterator it = expected_serials_.begin();
373 it != expected_serials_.end(); ++it) {
374 if (*it == dbus_message_get_serial(message)) {
375 expected_serials_.erase(it);
376 return true;
377 }
378 }
379
380 return false;
381 }
382
Complete()383 bool DBusMatch::Complete() {
384 return matched_ && expected_serials_.size() == 0;
385 }
386
387
DBusTest()388 DBusTest::DBusTest()
389 : conn_(NULL),
390 server_(NULL),
391 server_conn_(NULL),
392 dispatch_(false) {
393 }
394
~DBusTest()395 DBusTest::~DBusTest() {
396 }
397
398
ExpectMethodCall(std::string path,std::string interface,std::string method)399 DBusMatch& DBusTest::ExpectMethodCall(std::string path,
400 std::string interface,
401 std::string method) {
402 DBusMatch match;
403 match.ExpectMethodCall(path, interface, method);
404 pthread_mutex_lock(&mutex_);
405 matches_.push_back(match);
406 DBusMatch &ref = matches_.back();
407 pthread_mutex_unlock(&mutex_);
408 return ref;
409 }
410
411
CreateSignal(std::string path,std::string interface,std::string signal_name)412 DBusMatch& DBusTest::CreateSignal(std::string path,
413 std::string interface,
414 std::string signal_name) {
415 DBusMatch match;
416 match.CreateSignal(server_conn_, path, interface, signal_name);
417 pthread_mutex_lock(&mutex_);
418 matches_.push_back(match);
419 DBusMatch &ref = matches_.back();
420 pthread_mutex_unlock(&mutex_);
421 return ref;
422 }
423
CreateMessageCall(std::string path,std::string interface,std::string signal_name)424 DBusMatch& DBusTest::CreateMessageCall(std::string path,
425 std::string interface,
426 std::string signal_name) {
427 DBusMatch match;
428 match.CreateMessageCall(server_conn_, path, interface, signal_name);
429 pthread_mutex_lock(&mutex_);
430 matches_.push_back(match);
431 DBusMatch &ref = matches_.back();
432 pthread_mutex_unlock(&mutex_);
433 return ref;
434 }
435
436
WaitForMatches()437 void DBusTest::WaitForMatches() {
438 for (;;) {
439 pthread_mutex_lock(&mutex_);
440 size_t incomplete_matches = 0;
441 for (std::vector<DBusMatch>::iterator it = matches_.begin();
442 it != matches_.end(); ++it) {
443 DBusMatch &match = *it;
444 if (!match.Complete())
445 ++incomplete_matches;
446 }
447 pthread_mutex_unlock(&mutex_);
448
449 if (!incomplete_matches)
450 break;
451
452 // Fish a message from the queue.
453 DBusMessage *message;
454 while ((message = dbus_connection_borrow_message(conn_)) == NULL)
455 dbus_connection_read_write(conn_, -1);
456
457 // Allow matches to verify the serial of the message.
458 pthread_mutex_lock(&mutex_);
459 for (std::vector<DBusMatch>::iterator it = matches_.begin();
460 it != matches_.end(); ++it) {
461 DBusMatch &match = *it;
462
463 if (match.HandleClientMessage(conn_, message))
464 break;
465 }
466 pthread_mutex_unlock(&mutex_);
467
468 // Throw it back and dispatch.
469 dbus_connection_return_message(conn_, message);
470 dbus_connection_dispatch(conn_);
471 }
472
473 pthread_mutex_lock(&mutex_);
474 matches_.erase(matches_.begin(), matches_.end());
475 pthread_mutex_unlock(&mutex_);
476 }
477
478
SetUp()479 void DBusTest::SetUp() {
480 dbus_threads_init_default();
481
482 // Create the D-Bus server that will accept a connection for us, since
483 // there's no "just give me a socketpair" option in libdbus.
484 server_ = dbus_server_listen(kServerAddress, NULL);
485 ASSERT_TRUE(server_ != NULL);
486
487 dbus_server_set_new_connection_function(server_, NewConnectionThunk,
488 this, NULL);
489
490 dbus_bool_t success;
491 success = dbus_server_set_watch_functions(server_,
492 AddWatchThunk,
493 RemoveWatchThunk,
494 WatchToggledThunk,
495 this, NULL);
496 ASSERT_TRUE(success);
497
498 success = dbus_server_set_timeout_functions(server_,
499 AddTimeoutThunk,
500 RemoveTimeoutThunk,
501 TimeoutToggledThunk,
502 this, NULL);
503 ASSERT_TRUE(success);
504
505 // Open a connection to our server, this returns the "client" side of the
506 // connection.
507 conn_ = dbus_connection_open_private(kServerAddress, NULL);
508 ASSERT_TRUE(conn_ != NULL);
509
510 // The "server" side of the connection comes from the NewConnection method
511 // we set above. Dispatch until we have it.
512 while (!server_conn_)
513 DispatchOnce();
514
515 // Now we set off "main loop" in the background to dispatch until the
516 // client is disconnected by the TearDown method.
517 int r;
518 r = pthread_mutex_init(&mutex_, NULL);
519 ASSERT_EQ(0, r);
520
521 dispatch_ = true;
522 r = pthread_create(&thread_id_, NULL, DispatchLoopThunk, this);
523 ASSERT_EQ(0, r);
524 }
525
TearDown()526 void DBusTest::TearDown() {
527 WaitForMatches();
528
529 // Close the client end of the connection, this will result in a signal
530 // within the dispatch loop of the server.
531 if (conn_) {
532 dbus_connection_flush(conn_);
533 dbus_connection_close(conn_);
534 dbus_connection_unref(conn_);
535 conn_ = NULL;
536 }
537
538 // Join the thread and wait for it to finish dispatch.
539 if (dispatch_)
540 pthread_join(thread_id_, NULL);
541 pthread_mutex_destroy(&mutex_);
542
543 // Clean up the server end of the connection and the server itself.
544 if (server_conn_) {
545 dbus_connection_flush(server_conn_);
546 dbus_connection_close(server_conn_);
547 dbus_connection_unref(server_conn_);
548 server_conn_ = NULL;
549 }
550
551 dbus_server_disconnect(server_);
552 dbus_server_unref(server_);
553 server_ = NULL;
554
555 dbus_shutdown();
556 }
557
NewConnectionThunk(DBusServer * server,DBusConnection * conn,void * data)558 void DBusTest::NewConnectionThunk(DBusServer *server,
559 DBusConnection *conn,
560 void *data) {
561 DBusTest *test = static_cast<DBusTest *>(data);
562 test->NewConnection(server, conn);
563 }
564
NewConnection(DBusServer * server,DBusConnection * conn)565 void DBusTest::NewConnection(DBusServer *server, DBusConnection *conn) {
566 ASSERT_TRUE(server_conn_ == NULL);
567
568 dbus_bool_t success;
569 success = dbus_connection_set_watch_functions(conn,
570 AddWatchThunk,
571 RemoveWatchThunk,
572 WatchToggledThunk,
573 this, NULL);
574 ASSERT_TRUE(success);
575
576 success = dbus_connection_set_timeout_functions(conn,
577 AddTimeoutThunk,
578 RemoveTimeoutThunk,
579 TimeoutToggledThunk,
580 this, NULL);
581 ASSERT_TRUE(success);
582
583 success = dbus_connection_add_filter(conn,
584 HandleMessageThunk,
585 this, NULL);
586 ASSERT_TRUE(success);
587
588 server_conn_ = conn;
589 dbus_connection_ref(server_conn_);
590 }
591
AddWatchThunk(DBusWatch * watch,void * data)592 dbus_bool_t DBusTest::AddWatchThunk(DBusWatch *watch, void *data) {
593 DBusTest *test = static_cast<DBusTest *>(data);
594 return test->AddWatch(watch);
595 }
596
AddWatch(DBusWatch * watch)597 dbus_bool_t DBusTest::AddWatch(DBusWatch *watch) {
598 watches_.push_back(watch);
599 return TRUE;
600 }
601
RemoveWatchThunk(DBusWatch * watch,void * data)602 void DBusTest::RemoveWatchThunk(DBusWatch *watch, void *data) {
603 DBusTest *test = static_cast<DBusTest *>(data);
604 test->RemoveWatch(watch);
605 }
606
RemoveWatch(DBusWatch * watch)607 void DBusTest::RemoveWatch(DBusWatch *watch) {
608 std::vector<DBusWatch *>::iterator it =
609 find(watches_.begin(), watches_.end(), watch);
610 if (it != watches_.end())
611 watches_.erase(it);
612 }
613
WatchToggledThunk(DBusWatch * watch,void * data)614 void DBusTest::WatchToggledThunk(DBusWatch *watch, void *data) {
615 DBusTest *test = static_cast<DBusTest *>(data);
616 test->WatchToggled(watch);
617 }
618
WatchToggled(DBusWatch * watch)619 void DBusTest::WatchToggled(DBusWatch *watch) {
620 }
621
AddTimeoutThunk(DBusTimeout * timeout,void * data)622 dbus_bool_t DBusTest::AddTimeoutThunk(DBusTimeout *timeout, void *data) {
623 DBusTest *test = static_cast<DBusTest *>(data);
624 return test->AddTimeout(timeout);
625 }
626
AddTimeout(DBusTimeout * timeout)627 dbus_bool_t DBusTest::AddTimeout(DBusTimeout *timeout) {
628 timeouts_.push_back(timeout);
629 return TRUE;
630 }
631
RemoveTimeoutThunk(DBusTimeout * timeout,void * data)632 void DBusTest::RemoveTimeoutThunk(DBusTimeout *timeout, void *data) {
633 DBusTest *test = static_cast<DBusTest *>(data);
634 test->RemoveTimeout(timeout);
635 }
636
RemoveTimeout(DBusTimeout * timeout)637 void DBusTest::RemoveTimeout(DBusTimeout *timeout) {
638 std::vector<DBusTimeout *>::iterator it =
639 find(timeouts_.begin(), timeouts_.end(), timeout);
640 if (it != timeouts_.end())
641 timeouts_.erase(it);
642 }
643
TimeoutToggledThunk(DBusTimeout * timeout,void * data)644 void DBusTest::TimeoutToggledThunk(DBusTimeout *timeout, void *data) {
645 DBusTest *test = static_cast<DBusTest *>(data);
646 test->TimeoutToggled(timeout);
647 }
648
TimeoutToggled(DBusTimeout * timeout)649 void DBusTest::TimeoutToggled(DBusTimeout *timeout) {
650 }
651
HandleMessageThunk(DBusConnection * conn,DBusMessage * message,void * data)652 DBusHandlerResult DBusTest::HandleMessageThunk(DBusConnection *conn,
653 DBusMessage *message,
654 void *data) {
655 DBusTest *test = static_cast<DBusTest *>(data);
656 return test->HandleMessage(conn, message);
657 }
658
HandleMessage(DBusConnection * conn,DBusMessage * message)659 DBusHandlerResult DBusTest::HandleMessage(DBusConnection *conn,
660 DBusMessage *message) {
661 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
662 dispatch_ = false;
663 return DBUS_HANDLER_RESULT_HANDLED;
664 }
665
666 pthread_mutex_lock(&mutex_);
667 for (std::vector<DBusMatch>::iterator it = matches_.begin();
668 it != matches_.end(); ++it) {
669 DBusMatch &match = *it;
670
671 if (match.HandleServerMessage(conn, message)) {
672 pthread_mutex_unlock(&mutex_);
673 return DBUS_HANDLER_RESULT_HANDLED;
674 }
675 }
676 pthread_mutex_unlock(&mutex_);
677
678 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
679 }
680
681
DispatchLoopThunk(void * ptr)682 void *DBusTest::DispatchLoopThunk(void *ptr) {
683 DBusTest *test = static_cast<DBusTest *>(ptr);
684 return test->DispatchLoop();
685 }
686
DispatchLoop()687 void *DBusTest::DispatchLoop() {
688 while (dispatch_)
689 DispatchOnce();
690
691 return NULL;
692 }
693
DispatchOnce()694 void DBusTest::DispatchOnce() {
695 fd_set readfds, writefds;
696 int nfds = 0;
697 int r;
698
699 // Ideally we'd just use dbus_connection_read_write_dispatch() here, but
700 // we have to deal with both the server itself and its connection, so we
701 // have to do it all by hand.
702 FD_ZERO(&readfds);
703 FD_ZERO(&writefds);
704
705 for (std::vector<DBusWatch *>::iterator it = watches_.begin();
706 it != watches_.end(); ++it) {
707 DBusWatch *watch = *it;
708
709 if (!dbus_watch_get_enabled(watch))
710 continue;
711
712 int fd = dbus_watch_get_unix_fd(watch);
713 if (fd + 1 > nfds)
714 nfds = fd + 1;
715
716 unsigned int flags = dbus_watch_get_flags(watch);
717 if (flags & DBUS_WATCH_READABLE)
718 FD_SET(fd, &readfds);
719 if (flags & DBUS_WATCH_WRITABLE)
720 FD_SET(fd, &writefds);
721 }
722
723 // Only block in select for the interval of the smallest timeout; this
724 // isn't quite right according to the D-Bus spec, since the interval is
725 // supposed to be since the time the timeout was added or toggled, but
726 // it's good enough for the purposes of testing.
727 DBusTimeout *earliest_timeout = NULL;
728 struct timeval timeval;
729
730 for (std::vector<DBusTimeout *>::iterator it = timeouts_.begin();
731 it != timeouts_.end(); ++it) {
732 DBusTimeout *timeout = *it;
733
734 if (!dbus_timeout_get_enabled(timeout))
735 continue;
736
737 if (!earliest_timeout || (dbus_timeout_get_interval(timeout) <
738 dbus_timeout_get_interval(earliest_timeout)))
739 earliest_timeout = timeout;
740 }
741
742 if (earliest_timeout) {
743 int interval = dbus_timeout_get_interval(earliest_timeout);
744 timeval.tv_sec = interval / 1000;
745 timeval.tv_usec = (interval % 1000) * 1000;
746
747 r = select(nfds, &readfds, &writefds, NULL, &timeval);
748 } else {
749 r = select(nfds, &readfds, &writefds, NULL, NULL);
750 }
751
752 ASSERT_LE(0, r);
753
754 // Handle the timeout if we didn't poll for anything else.
755 if (r == 0 && earliest_timeout)
756 dbus_timeout_handle(earliest_timeout);
757
758 // Handle the watches, use a copy of the vector since a watch handler
759 // might remove other watches in the vector.
760 std::vector<DBusWatch *> immutable_watches = watches_;
761 for (std::vector<DBusWatch *>::iterator it = immutable_watches.begin();
762 it != immutable_watches.end(); ++it) {
763 DBusWatch *watch = *it;
764
765 int fd = dbus_watch_get_unix_fd(watch);
766 unsigned int flags = 0;
767
768 if (FD_ISSET(fd, &readfds))
769 flags |= DBUS_WATCH_READABLE;
770 if (FD_ISSET(fd, &writefds))
771 flags |= DBUS_WATCH_WRITABLE;
772
773 if (flags)
774 dbus_watch_handle(watch, flags);
775 }
776
777 // Dispatch data on the server-side of the connection.
778 while (server_conn_ &&
779 dbus_connection_dispatch(server_conn_) == DBUS_DISPATCH_DATA_REMAINS)
780 ;
781 }
782