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