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