2004-08-28 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-userdb.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-userdb.c User database abstraction
3  * 
4  * Copyright (C) 2003, 2004  Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include "dbus-userdb.h"
24 #include "dbus-hash.h"
25 #include "dbus-test.h"
26 #include "dbus-internals.h"
27 #include "dbus-protocol.h"
28 #include <string.h>
29
30 /**
31  * Internals of DBusUserDatabase
32  */
33 struct DBusUserDatabase
34 {
35   int refcount; /**< Reference count */
36
37   DBusHashTable *users; /**< Users in the database by UID */
38   DBusHashTable *groups; /**< Groups in the database by GID */
39   DBusHashTable *users_by_name; /**< Users in the database by name */
40   DBusHashTable *groups_by_name; /**< Groups in the database by name */
41
42 };
43
44 static void
45 free_user_info (void *data)
46 {
47   DBusUserInfo *info = data;
48
49   if (info == NULL) /* hash table will pass NULL */
50     return;
51
52   _dbus_user_info_free (info);
53   dbus_free (info);
54 }
55
56 static void
57 free_group_info (void *data)
58 {
59   DBusGroupInfo *info = data;
60
61   if (info == NULL) /* hash table will pass NULL */
62     return;
63
64   _dbus_group_info_free (info);
65   dbus_free (info);
66 }
67
68 static DBusUserInfo*
69 _dbus_user_database_lookup (DBusUserDatabase *db,
70                             dbus_uid_t        uid,
71                             const DBusString *username,
72                             DBusError        *error)
73 {
74   DBusUserInfo *info;
75
76   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
77   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
78   
79   if (uid != DBUS_UID_UNSET)
80     info = _dbus_hash_table_lookup_ulong (db->users, uid);
81   else
82     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
83   
84   if (info)
85     {
86       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
87                      uid);
88       return info;
89     }
90   else
91     {
92       _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
93                      uid);
94       
95       info = dbus_new0 (DBusUserInfo, 1);
96       if (info == NULL)
97         {
98           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
99           return NULL;
100         }
101
102       if (uid != DBUS_UID_UNSET)
103         {
104           if (!_dbus_user_info_fill_uid (info, uid, error))
105             {
106               _DBUS_ASSERT_ERROR_IS_SET (error);
107               free_user_info (info);
108               return NULL;
109             }
110         }
111       else
112         {
113           if (!_dbus_user_info_fill (info, username, error))
114             {
115               _DBUS_ASSERT_ERROR_IS_SET (error);
116               free_user_info (info);
117               return NULL;
118             }
119         }
120
121       /* be sure we don't use these after here */
122       uid = DBUS_UID_UNSET;
123       username = NULL;
124
125       /* insert into hash */
126       if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
127         {
128           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
129           free_user_info (info);
130           return NULL;
131         }
132
133       if (!_dbus_hash_table_insert_string (db->users_by_name,
134                                            info->username,
135                                            info))
136         {
137           _dbus_hash_table_remove_ulong (db->users, info->uid);
138           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
139           return NULL;
140         }
141       
142       return info;
143     }
144 }
145
146 static DBusGroupInfo*
147 _dbus_user_database_lookup_group (DBusUserDatabase *db,
148                                   dbus_gid_t        gid,
149                                   const DBusString *groupname,
150                                   DBusError        *error)
151 {
152   DBusGroupInfo *info;
153
154   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
155
156   if (gid != DBUS_GID_UNSET)
157     info = _dbus_hash_table_lookup_ulong (db->groups, gid);
158   else
159     info = _dbus_hash_table_lookup_string (db->groups_by_name,
160                                            _dbus_string_get_const_data (groupname));
161   if (info)
162     {
163       _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
164                      gid);
165       return info;
166     }
167   else
168     {
169       _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
170                      gid);
171       
172       info = dbus_new0 (DBusGroupInfo, 1);
173       if (info == NULL)
174         {
175           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
176           return NULL;
177         }
178
179       if (!_dbus_group_info_fill_gid (info, gid, error))
180         {
181           _DBUS_ASSERT_ERROR_IS_SET (error);
182           free_group_info (info);
183           return NULL;
184         }
185
186       if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
187         {
188           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
189           free_group_info (info);
190           return NULL;
191         }
192
193
194       if (!_dbus_hash_table_insert_string (db->groups_by_name,
195                                            info->groupname,
196                                            info))
197         {
198           _dbus_hash_table_remove_ulong (db->groups, info->gid);
199           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
200           return NULL;
201         }
202       
203       return info;
204     }
205 }
206
207 _DBUS_DEFINE_GLOBAL_LOCK(system_users);
208 static dbus_bool_t database_locked = FALSE;
209 static DBusUserDatabase *system_db = NULL;
210 static DBusString process_username;
211 static DBusString process_homedir;
212       
213 static void
214 shutdown_system_db (void *data)
215 {
216   _dbus_user_database_unref (system_db);
217   system_db = NULL;
218   _dbus_string_free (&process_username);
219   _dbus_string_free (&process_homedir);
220 }
221
222 static dbus_bool_t
223 init_system_db (void)
224 {
225   _dbus_assert (database_locked);
226     
227   if (system_db == NULL)
228     {
229       DBusError error;
230       const DBusUserInfo *info;
231       
232       system_db = _dbus_user_database_new ();
233       if (system_db == NULL)
234         return FALSE;
235
236       dbus_error_init (&error);
237
238       if (!_dbus_user_database_get_uid (system_db,
239                                         _dbus_getuid (),
240                                         &info,
241                                         &error))
242         {
243           _dbus_user_database_unref (system_db);
244           system_db = NULL;
245           
246           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
247             {
248               dbus_error_free (&error);
249               return FALSE;
250             }
251           else
252             {
253               /* This really should not happen. */
254               _dbus_warn ("Could not get password database information for UID of current process: %s\n",
255                           error.message);
256               dbus_error_free (&error);
257               return FALSE;
258             }
259         }
260
261       if (!_dbus_string_init (&process_username))
262         {
263           _dbus_user_database_unref (system_db);
264           system_db = NULL;
265           return FALSE;
266         }
267
268       if (!_dbus_string_init (&process_homedir))
269         {
270           _dbus_string_free (&process_username);
271           _dbus_user_database_unref (system_db);
272           system_db = NULL;
273           return FALSE;
274         }
275
276       if (!_dbus_string_append (&process_username,
277                                 info->username) ||
278           !_dbus_string_append (&process_homedir,
279                                 info->homedir) ||
280           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
281         {
282           _dbus_string_free (&process_username);
283           _dbus_string_free (&process_homedir);
284           _dbus_user_database_unref (system_db);
285           system_db = NULL;
286           return FALSE;
287         }
288     }
289
290   return TRUE;
291 }
292
293 /**
294  * @addtogroup DBusInternalsUtils
295  * @{
296  */
297
298 /**
299  * Locks global system user database.
300  */
301 void
302 _dbus_user_database_lock_system (void)
303 {
304   _DBUS_LOCK (system_users);
305   database_locked = TRUE;
306 }
307
308 /**
309  * Unlocks global system user database.
310  */
311 void
312 _dbus_user_database_unlock_system (void)
313 {
314   database_locked = FALSE;
315   _DBUS_UNLOCK (system_users);
316 }
317
318 /**
319  * Gets the system global user database;
320  * must be called with lock held (_dbus_user_database_lock_system()).
321  *
322  * @returns the database or #NULL if no memory
323  */
324 DBusUserDatabase*
325 _dbus_user_database_get_system (void)
326 {
327   _dbus_assert (database_locked);
328
329   init_system_db ();
330   
331   return system_db;
332 }
333
334 /**
335  * Gets username of user owning current process.  The returned string
336  * is valid until dbus_shutdown() is called.
337  *
338  * @param username place to store pointer to username
339  * @returns #FALSE if no memory
340  */
341 dbus_bool_t
342 _dbus_username_from_current_process (const DBusString **username)
343 {
344   _dbus_user_database_lock_system ();
345   if (!init_system_db ())
346     {
347       _dbus_user_database_unlock_system ();
348       return FALSE;
349     }
350   *username = &process_username;
351   _dbus_user_database_unlock_system ();  
352
353   return TRUE;
354 }
355
356 /**
357  * Gets homedir of user owning current process.  The returned string
358  * is valid until dbus_shutdown() is called.
359  *
360  * @param homedir place to store pointer to homedir
361  * @returns #FALSE if no memory
362  */
363 dbus_bool_t
364 _dbus_homedir_from_current_process (const DBusString  **homedir)
365 {
366   _dbus_user_database_lock_system ();
367   if (!init_system_db ())
368     {
369       _dbus_user_database_unlock_system ();
370       return FALSE;
371     }
372   *homedir = &process_homedir;
373   _dbus_user_database_unlock_system ();
374
375   return TRUE;
376 }
377
378 /**
379  * Gets user ID given username
380  *
381  * @param username the username
382  * @param uid return location for UID
383  * @returns #TRUE if username existed and we got the UID
384  */
385 dbus_bool_t
386 _dbus_get_user_id (const DBusString  *username,
387                    dbus_uid_t        *uid)
388 {
389   DBusCredentials creds;
390
391   if (!_dbus_credentials_from_username (username, &creds))
392     return FALSE;
393
394   if (creds.uid == DBUS_UID_UNSET)
395     return FALSE;
396
397   *uid = creds.uid;
398
399   return TRUE;
400 }
401
402 /**
403  * Checks to see if the UID sent in is the console user
404  *
405  * @param uid UID of person to check 
406  * @param error return location for errors
407  * @returns #TRUE if the UID is the same as the console user and there are no errors
408  */
409 dbus_bool_t
410 _dbus_is_console_user (dbus_uid_t uid,
411                        DBusError *error)
412 {
413
414   DBusUserDatabase *db;
415   const DBusUserInfo *info;
416   dbus_bool_t result = FALSE; 
417
418   _dbus_user_database_lock_system ();
419
420   db = _dbus_user_database_get_system ();
421   if (db == NULL)
422     {
423       dbus_set_error (error, DBUS_ERROR_FAILED, "Could not get system database.");
424       _dbus_user_database_unlock_system ();
425       return FALSE;
426     }
427
428   info = _dbus_user_database_lookup (db, uid, NULL, error);
429
430   if (info == NULL)
431     {
432       _dbus_user_database_unlock_system ();
433        return FALSE;
434     }
435
436   result = _dbus_user_at_console (info->username, error);
437
438   _dbus_user_database_unlock_system ();
439
440   return result;
441 }
442
443 /**
444  * Gets group ID given groupname
445  *
446  * @param groupname the groupname
447  * @param gid return location for GID
448  * @returns #TRUE if group name existed and we got the GID
449  */
450 dbus_bool_t
451 _dbus_get_group_id (const DBusString  *groupname,
452                     dbus_gid_t        *gid)
453 {
454   DBusUserDatabase *db;
455   const DBusGroupInfo *info;
456   _dbus_user_database_lock_system ();
457
458   db = _dbus_user_database_get_system ();
459   if (db == NULL)
460     {
461       _dbus_user_database_unlock_system ();
462       return FALSE;
463     }
464
465   if (!_dbus_user_database_get_groupname (db, groupname,
466                                           &info, NULL))
467     {
468       _dbus_user_database_unlock_system ();
469       return FALSE;
470     }
471
472   *gid = info->gid;
473   
474   _dbus_user_database_unlock_system ();
475   return TRUE;
476 }
477
478 /**
479  * Gets the home directory for the given user.
480  *
481  * @param username the username
482  * @param homedir string to append home directory to
483  * @returns #TRUE if user existed and we appended their homedir
484  */
485 dbus_bool_t
486 _dbus_homedir_from_username (const DBusString *username,
487                              DBusString       *homedir)
488 {
489   DBusUserDatabase *db;
490   const DBusUserInfo *info;
491   _dbus_user_database_lock_system ();
492
493   db = _dbus_user_database_get_system ();
494   if (db == NULL)
495     {
496       _dbus_user_database_unlock_system ();
497       return FALSE;
498     }
499
500   if (!_dbus_user_database_get_username (db, username,
501                                          &info, NULL))
502     {
503       _dbus_user_database_unlock_system ();
504       return FALSE;
505     }
506
507   if (!_dbus_string_append (homedir, info->homedir))
508     {
509       _dbus_user_database_unlock_system ();
510       return FALSE;
511     }
512   
513   _dbus_user_database_unlock_system ();
514   return TRUE;
515 }
516
517 /**
518  * Gets a UID from a UID string.
519  *
520  * @param uid_str the UID in string form
521  * @param uid UID to fill in
522  * @returns #TRUE if successfully filled in UID
523  */
524 dbus_bool_t
525 _dbus_uid_from_string (const DBusString      *uid_str,
526                        dbus_uid_t            *uid)
527 {
528   int end;
529   long val;
530   
531   if (_dbus_string_get_length (uid_str) == 0)
532     {
533       _dbus_verbose ("UID string was zero length\n");
534       return FALSE;
535     }
536
537   val = -1;
538   end = 0;
539   if (!_dbus_string_parse_int (uid_str, 0, &val,
540                                &end))
541     {
542       _dbus_verbose ("could not parse string as a UID\n");
543       return FALSE;
544     }
545   
546   if (end != _dbus_string_get_length (uid_str))
547     {
548       _dbus_verbose ("string contained trailing stuff after UID\n");
549       return FALSE;
550     }
551
552   *uid = val;
553
554   return TRUE;
555 }
556
557 /**
558  * Gets the credentials corresponding to the given username.
559  *
560  * @param username the username
561  * @param credentials credentials to fill in
562  * @returns #TRUE if the username existed and we got some credentials
563  */
564 dbus_bool_t
565 _dbus_credentials_from_username (const DBusString *username,
566                                  DBusCredentials  *credentials)
567 {
568   DBusUserDatabase *db;
569   const DBusUserInfo *info;
570   _dbus_user_database_lock_system ();
571
572   db = _dbus_user_database_get_system ();
573   if (db == NULL)
574     {
575       _dbus_user_database_unlock_system ();
576       return FALSE;
577     }
578
579   if (!_dbus_user_database_get_username (db, username,
580                                          &info, NULL))
581     {
582       _dbus_user_database_unlock_system ();
583       return FALSE;
584     }
585
586   credentials->pid = DBUS_PID_UNSET;
587   credentials->uid = info->uid;
588   credentials->gid = info->primary_gid;
589   
590   _dbus_user_database_unlock_system ();
591   return TRUE;
592 }
593
594 /**
595  * Gets the credentials corresponding to the given UID.
596  *
597  * @param uid the UID
598  * @param credentials credentials to fill in
599  * @returns #TRUE if the UID existed and we got some credentials
600  */
601 dbus_bool_t
602 _dbus_credentials_from_uid (dbus_uid_t        uid,
603                             DBusCredentials  *credentials)
604 {
605   DBusUserDatabase *db;
606   const DBusUserInfo *info;
607   _dbus_user_database_lock_system ();
608
609   db = _dbus_user_database_get_system ();
610   if (db == NULL)
611     {
612       _dbus_user_database_unlock_system ();
613       return FALSE;
614     }
615
616   if (!_dbus_user_database_get_uid (db, uid,
617                                     &info, NULL))
618     {
619       _dbus_user_database_unlock_system ();
620       return FALSE;
621     }
622
623   _dbus_assert (info->uid == uid);
624   
625   credentials->pid = DBUS_PID_UNSET;
626   credentials->uid = info->uid;
627   credentials->gid = info->primary_gid;
628   
629   _dbus_user_database_unlock_system ();
630   return TRUE;
631 }
632
633 /**
634  * Creates a new user database object used to look up and
635  * cache user information.
636  * @returns new database, or #NULL on out of memory
637  */
638 DBusUserDatabase*
639 _dbus_user_database_new (void)
640 {
641   DBusUserDatabase *db;
642   
643   db = dbus_new0 (DBusUserDatabase, 1);
644   if (db == NULL)
645     return NULL;
646
647   db->refcount = 1;
648
649   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
650                                     NULL, free_user_info);
651   
652   if (db->users == NULL)
653     goto failed;
654
655   db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
656                                      NULL, free_group_info);
657   
658   if (db->groups == NULL)
659     goto failed;
660
661   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
662                                             NULL, NULL);
663   if (db->users_by_name == NULL)
664     goto failed;
665   
666   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
667                                              NULL, NULL);
668   if (db->groups_by_name == NULL)
669     goto failed;
670   
671   return db;
672   
673  failed:
674   _dbus_user_database_unref (db);
675   return NULL;
676 }
677
678 /**
679  * Increments refcount of user database.
680  * @param db the database
681  * @returns the database
682  */
683 DBusUserDatabase *
684 _dbus_user_database_ref (DBusUserDatabase  *db)
685 {
686   _dbus_assert (db->refcount > 0);
687
688   db->refcount += 1;
689
690   return db;
691 }
692
693 /**
694  * Decrements refcount of user database.
695  * @param db the database
696  */
697 void
698 _dbus_user_database_unref (DBusUserDatabase  *db)
699 {
700   _dbus_assert (db->refcount > 0);
701
702   db->refcount -= 1;
703   if (db->refcount == 0)
704     {
705       if (db->users)
706         _dbus_hash_table_unref (db->users);
707
708       if (db->groups)
709         _dbus_hash_table_unref (db->groups);
710
711       if (db->users_by_name)
712         _dbus_hash_table_unref (db->users_by_name);
713
714       if (db->groups_by_name)
715         _dbus_hash_table_unref (db->groups_by_name);
716       
717       dbus_free (db);
718     }
719 }
720
721 /**
722  * Gets all groups for a particular user. Returns #FALSE
723  * if no memory, or user isn't known, but always initializes
724  * group_ids to a NULL array. Sets error to the reason
725  * for returning #FALSE.
726  *
727  * @param db the user database object
728  * @param uid the user ID
729  * @param group_ids return location for array of group IDs
730  * @param n_group_ids return location for length of returned array
731  * @param error return location for error
732  * @returns #TRUE on success
733  */
734 dbus_bool_t
735 _dbus_user_database_get_groups (DBusUserDatabase  *db,
736                                 dbus_uid_t         uid,
737                                 dbus_gid_t       **group_ids,
738                                 int               *n_group_ids,
739                                 DBusError         *error)
740 {
741   DBusUserInfo *info;
742   
743   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
744
745   *group_ids = NULL;
746   *n_group_ids = 0;
747   
748   info = _dbus_user_database_lookup (db, uid, NULL, error);
749   if (info == NULL)
750     {
751       _DBUS_ASSERT_ERROR_IS_SET (error);
752       return FALSE;
753     }
754
755   if (info->n_group_ids > 0)
756     {
757       *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
758       if (*group_ids == NULL)
759         {
760           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
761           return FALSE;
762         }
763
764       *n_group_ids = info->n_group_ids;
765
766       memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
767     }
768
769   return TRUE;
770 }
771
772 /**
773  * Gets the user information for the given UID,
774  * returned user info should not be freed. 
775  *
776  * @param db user database
777  * @param uid the user ID
778  * @param info return location for const ref to user info
779  * @param error error location
780  * @returns #FALSE if error is set
781  */
782 dbus_bool_t
783 _dbus_user_database_get_uid (DBusUserDatabase    *db,
784                              dbus_uid_t           uid,
785                              const DBusUserInfo **info,
786                              DBusError           *error)
787 {
788   *info = _dbus_user_database_lookup (db, uid, NULL, error);
789   return *info != NULL;
790 }
791
792 /**
793  * Gets the user information for the given GID,
794  * returned group info should not be freed. 
795  *
796  * @param db user database
797  * @param gid the group ID
798  * @param info return location for const ref to group info
799  * @param error error location
800  * @returns #FALSE if error is set
801  */
802 dbus_bool_t
803 _dbus_user_database_get_gid (DBusUserDatabase     *db,
804                              dbus_gid_t            gid,
805                              const DBusGroupInfo **info,
806                              DBusError            *error)
807 {
808   *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
809   return *info != NULL;
810 }
811
812 /**
813  * Gets the user information for the given username.
814  *
815  * @param db user database
816  * @param username the user name
817  * @param info return location for const ref to user info
818  * @param error error location
819  * @returns #FALSE if error is set
820  */
821 dbus_bool_t
822 _dbus_user_database_get_username  (DBusUserDatabase     *db,
823                                    const DBusString     *username,
824                                    const DBusUserInfo  **info,
825                                    DBusError            *error)
826 {
827   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
828   return *info != NULL;
829 }
830
831 /**
832  * Gets the user information for the given group name,
833  * returned group info should not be freed. 
834  *
835  * @param db user database
836  * @param groupname the group name
837  * @param info return location for const ref to group info
838  * @param error error location
839  * @returns #FALSE if error is set
840  */
841 dbus_bool_t
842 _dbus_user_database_get_groupname (DBusUserDatabase     *db,
843                                    const DBusString     *groupname,
844                                    const DBusGroupInfo **info,
845                                    DBusError            *error)
846 {
847   *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
848   return *info != NULL;
849 }
850
851 /** @} */
852
853 #ifdef DBUS_BUILD_TESTS
854 #include <stdio.h>
855
856 /**
857  * Unit test for dbus-userdb.c.
858  * 
859  * @returns #TRUE on success.
860  */
861 dbus_bool_t
862 _dbus_userdb_test (const char *test_data_dir)
863 {
864   const DBusString *username;
865   const DBusString *homedir;
866
867   if (!_dbus_username_from_current_process (&username))
868     _dbus_assert_not_reached ("didn't get username");
869
870   if (!_dbus_homedir_from_current_process (&homedir))
871     _dbus_assert_not_reached ("didn't get homedir");  
872
873   printf ("    Current user: %s homedir: %s\n",
874           _dbus_string_get_const_data (username),
875           _dbus_string_get_const_data (homedir));
876   
877   return TRUE;
878 }
879 #endif /* DBUS_BUILD_TESTS */