• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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