GetConnectionCredentials: add
[platform/upstream/dbus.git] / test / dbus-daemon.c
1 /* Integration tests for the dbus-daemon
2  *
3  * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
4  * Copyright © 2010-2011 Nokia Corporation
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction,
9  * including without limitation the rights to use, copy, modify, merge,
10  * publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26
27 #include <config.h>
28
29 #include <glib.h>
30
31 #include <dbus/dbus.h>
32 #include <dbus/dbus-glib-lowlevel.h>
33
34 #include <string.h>
35
36 #ifdef DBUS_WIN
37 # include <io.h>
38 # include <windows.h>
39 #else
40 # include <signal.h>
41 # include <unistd.h>
42 # include <sys/types.h>
43 #endif
44
45 typedef struct {
46     gboolean skip;
47
48     DBusError e;
49     GError *ge;
50
51     GPid daemon_pid;
52
53     DBusConnection *left_conn;
54
55     DBusConnection *right_conn;
56     gboolean right_conn_echo;
57 } Fixture;
58
59 #define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__)
60 static void
61 _assert_no_error (const DBusError *e,
62     const char *file,
63     int line)
64 {
65   if (G_UNLIKELY (dbus_error_is_set (e)))
66     g_error ("%s:%d: expected success but got error: %s: %s",
67         file, line, e->name, e->message);
68 }
69
70 static gchar *
71 spawn_dbus_daemon (gchar *binary,
72     gchar *configuration,
73     GPid *daemon_pid)
74 {
75   GError *error = NULL;
76   GString *address;
77   gint address_fd;
78   gchar *argv[] = {
79       binary,
80       configuration,
81       "--nofork",
82       "--print-address=1", /* stdout */
83       NULL
84   };
85
86   g_spawn_async_with_pipes (NULL, /* working directory */
87       argv,
88       NULL, /* envp */
89       G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
90       NULL, /* child_setup */
91       NULL, /* user data */
92       daemon_pid,
93       NULL, /* child's stdin = /dev/null */
94       &address_fd,
95       NULL, /* child's stderr = our stderr */
96       &error);
97   g_assert_no_error (error);
98
99   address = g_string_new (NULL);
100
101   /* polling until the dbus-daemon writes out its address is a bit stupid,
102    * but at least it's simple, unlike dbus-launch... in principle we could
103    * use select() here, but life's too short */
104   while (1)
105     {
106       gssize bytes;
107       gchar buf[4096];
108       gchar *newline;
109
110       bytes = read (address_fd, buf, sizeof (buf));
111
112       if (bytes > 0)
113         g_string_append_len (address, buf, bytes);
114
115       newline = strchr (address->str, '\n');
116
117       if (newline != NULL)
118         {
119           g_string_truncate (address, newline - address->str);
120           break;
121         }
122
123       g_usleep (G_USEC_PER_SEC / 10);
124     }
125
126   return g_string_free (address, FALSE);
127 }
128
129 static DBusConnection *
130 connect_to_bus (const gchar *address)
131 {
132   DBusConnection *conn;
133   DBusError error = DBUS_ERROR_INIT;
134   dbus_bool_t ok;
135
136   conn = dbus_connection_open_private (address, &error);
137   assert_no_error (&error);
138   g_assert (conn != NULL);
139
140   ok = dbus_bus_register (conn, &error);
141   assert_no_error (&error);
142   g_assert (ok);
143   g_assert (dbus_bus_get_unique_name (conn) != NULL);
144
145   dbus_connection_setup_with_g_main (conn, NULL);
146   return conn;
147 }
148
149 static DBusHandlerResult
150 echo_filter (DBusConnection *connection,
151     DBusMessage *message,
152     void *user_data)
153 {
154   DBusMessage *reply;
155
156   if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
157     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
158
159   reply = dbus_message_new_method_return (message);
160
161   if (reply == NULL)
162     g_error ("OOM");
163
164   if (!dbus_connection_send (connection, reply, NULL))
165     g_error ("OOM");
166
167   dbus_message_unref (reply);
168
169   return DBUS_HANDLER_RESULT_HANDLED;
170 }
171
172 typedef struct {
173     const char *bug_ref;
174     guint min_messages;
175     const char *config_file;
176 } Config;
177
178 static void
179 setup (Fixture *f,
180     gconstpointer context)
181 {
182   const Config *config = context;
183   gchar *dbus_daemon;
184   gchar *arg;
185   gchar *address;
186
187   f->ge = NULL;
188   dbus_error_init (&f->e);
189
190   if (config != NULL && config->config_file != NULL)
191     {
192       if (g_getenv ("DBUS_TEST_DATA") == NULL)
193         {
194           g_message ("SKIP: set DBUS_TEST_DATA to a directory containing %s",
195               config->config_file);
196           f->skip = TRUE;
197           return;
198         }
199
200       arg = g_strdup_printf (
201           "--config-file=%s/%s",
202           g_getenv ("DBUS_TEST_DATA"), config->config_file);
203     }
204   else if (g_getenv ("DBUS_TEST_SYSCONFDIR") != NULL)
205     {
206       arg = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
207           g_getenv ("DBUS_TEST_SYSCONFDIR"));
208     }
209   else if (g_getenv ("DBUS_TEST_DATA") != NULL)
210     {
211       arg = g_strdup_printf (
212           "--config-file=%s/valid-config-files/session.conf",
213           g_getenv ("DBUS_TEST_DATA"));
214     }
215   else
216     {
217       arg = g_strdup ("--session");
218     }
219
220   dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
221
222   if (dbus_daemon == NULL)
223     dbus_daemon = g_strdup ("dbus-daemon");
224
225   address = spawn_dbus_daemon (dbus_daemon, arg, &f->daemon_pid);
226
227   g_free (dbus_daemon);
228   g_free (arg);
229
230   f->left_conn = connect_to_bus (address);
231   f->right_conn = connect_to_bus (address);
232   g_free (address);
233 }
234
235 static void
236 add_echo_filter (Fixture *f)
237 {
238   if (!dbus_connection_add_filter (f->right_conn, echo_filter, NULL, NULL))
239     g_error ("OOM");
240
241   f->right_conn_echo = TRUE;
242 }
243
244 static void
245 pc_count (DBusPendingCall *pc,
246     void *data)
247 {
248   guint *received_p = data;
249
250   (*received_p)++;
251 }
252
253 static void
254 test_echo (Fixture *f,
255     gconstpointer context)
256 {
257   const Config *config = context;
258   guint count = 2000;
259   guint sent;
260   guint received = 0;
261   double elapsed;
262
263   if (f->skip)
264     return;
265
266   if (config != NULL && config->bug_ref != NULL)
267     g_test_bug (config->bug_ref);
268
269   if (g_test_perf ())
270     count = 100000;
271
272   if (config != NULL)
273     count = MAX (config->min_messages, count);
274
275   add_echo_filter (f);
276
277   g_test_timer_start ();
278
279   for (sent = 0; sent < count; sent++)
280     {
281       DBusMessage *m = dbus_message_new_method_call (
282           dbus_bus_get_unique_name (f->right_conn), "/",
283           "com.example", "Spam");
284       DBusPendingCall *pc;
285
286       if (m == NULL)
287         g_error ("OOM");
288
289       if (!dbus_connection_send_with_reply (f->left_conn, m, &pc,
290                                             DBUS_TIMEOUT_INFINITE) ||
291           pc == NULL)
292         g_error ("OOM");
293
294       if (dbus_pending_call_get_completed (pc))
295         pc_count (pc, &received);
296       else if (!dbus_pending_call_set_notify (pc, pc_count, &received,
297             NULL))
298         g_error ("OOM");
299
300       dbus_pending_call_unref (pc);
301       dbus_message_unref (m);
302     }
303
304   while (received < count)
305     g_main_context_iteration (NULL, TRUE);
306
307   elapsed = g_test_timer_elapsed ();
308
309   g_test_maximized_result (count / elapsed, "%u messages / %f seconds",
310       count, elapsed);
311 }
312
313 static void
314 pending_call_store_reply (DBusPendingCall *pc,
315     void *data)
316 {
317   DBusMessage **message_p = data;
318
319   *message_p = dbus_pending_call_steal_reply (pc);
320   g_assert (*message_p != NULL);
321 }
322
323 static void
324 test_creds (Fixture *f,
325     gconstpointer context)
326 {
327   const char *unique = dbus_bus_get_unique_name (f->left_conn);
328   DBusMessage *m = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
329       DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetConnectionCredentials");
330   DBusPendingCall *pc;
331   DBusMessageIter args_iter;
332   DBusMessageIter arr_iter;
333   DBusMessageIter pair_iter;
334   DBusMessageIter var_iter;
335   enum {
336       SEEN_UNIX_USER = 1,
337       SEEN_PID = 2,
338       SEEN_WINDOWS_SID = 4
339   } seen = 0;
340
341   if (m == NULL)
342     g_error ("OOM");
343
344   if (!dbus_message_append_args (m,
345         DBUS_TYPE_STRING, &unique,
346         DBUS_TYPE_INVALID))
347     g_error ("OOM");
348
349   if (!dbus_connection_send_with_reply (f->left_conn, m, &pc,
350                                         DBUS_TIMEOUT_USE_DEFAULT) ||
351       pc == NULL)
352     g_error ("OOM");
353
354   dbus_message_unref (m);
355   m = NULL;
356
357   if (dbus_pending_call_get_completed (pc))
358     pending_call_store_reply (pc, &m);
359   else if (!dbus_pending_call_set_notify (pc, pending_call_store_reply,
360                                           &m, NULL))
361     g_error ("OOM");
362
363   while (m == NULL)
364     g_main_context_iteration (NULL, TRUE);
365
366   g_assert_cmpstr (dbus_message_get_signature (m), ==, "a{sv}");
367
368   dbus_message_iter_init (m, &args_iter);
369   g_assert_cmpuint (dbus_message_iter_get_arg_type (&args_iter), ==,
370       DBUS_TYPE_ARRAY);
371   g_assert_cmpuint (dbus_message_iter_get_element_type (&args_iter), ==,
372       DBUS_TYPE_DICT_ENTRY);
373   dbus_message_iter_recurse (&args_iter, &arr_iter);
374
375   while (dbus_message_iter_get_arg_type (&arr_iter) != DBUS_TYPE_INVALID)
376     {
377       const char *name;
378
379       dbus_message_iter_recurse (&arr_iter, &pair_iter);
380       g_assert_cmpuint (dbus_message_iter_get_arg_type (&pair_iter), ==,
381           DBUS_TYPE_STRING);
382       dbus_message_iter_get_basic (&pair_iter, &name);
383       dbus_message_iter_next (&pair_iter);
384       g_assert_cmpuint (dbus_message_iter_get_arg_type (&pair_iter), ==,
385           DBUS_TYPE_VARIANT);
386       dbus_message_iter_recurse (&pair_iter, &var_iter);
387
388       if (g_strcmp0 (name, "UnixUserID") == 0)
389         {
390 #ifdef G_OS_UNIX
391           guint32 u32;
392
393           g_assert (!(seen & SEEN_UNIX_USER));
394           g_assert_cmpuint (dbus_message_iter_get_arg_type (&var_iter), ==,
395               DBUS_TYPE_UINT32);
396           dbus_message_iter_get_basic (&var_iter, &u32);
397           g_message ("%s of this process is %u", name, u32);
398           g_assert_cmpuint (u32, ==, geteuid ());
399           seen |= SEEN_UNIX_USER;
400 #else
401           g_assert_not_reached ();
402 #endif
403         }
404       else if (g_strcmp0 (name, "ProcessID") == 0)
405         {
406           guint32 u32;
407
408           g_assert (!(seen & SEEN_PID));
409           g_assert_cmpuint (dbus_message_iter_get_arg_type (&var_iter), ==,
410               DBUS_TYPE_UINT32);
411           dbus_message_iter_get_basic (&var_iter, &u32);
412           g_message ("%s of this process is %u", name, u32);
413 #ifdef G_OS_UNIX
414           g_assert_cmpuint (u32, ==, getpid ());
415 #elif defined(G_OS_WIN32)
416           g_assert_cmpuint (u32, ==, GetCurrentProcessId ());
417 #else
418           g_assert_not_reached ();
419 #endif
420           seen |= SEEN_PID;
421         }
422
423       dbus_message_iter_next (&arr_iter);
424     }
425
426 #ifdef G_OS_UNIX
427   g_assert (seen & SEEN_UNIX_USER);
428   g_assert (seen & SEEN_PID);
429 #endif
430
431 #ifdef G_OS_WIN32
432   /* FIXME: when implemented:
433   g_assert (seen & SEEN_WINDOWS_SID);
434    */
435 #endif
436 }
437
438 static void
439 teardown (Fixture *f,
440     gconstpointer context G_GNUC_UNUSED)
441 {
442   dbus_error_free (&f->e);
443   g_clear_error (&f->ge);
444
445   if (f->left_conn != NULL)
446     {
447       dbus_connection_close (f->left_conn);
448       dbus_connection_unref (f->left_conn);
449       f->left_conn = NULL;
450     }
451
452   if (f->right_conn != NULL)
453     {
454       if (f->right_conn_echo)
455         {
456           dbus_connection_remove_filter (f->right_conn, echo_filter, NULL);
457           f->right_conn_echo = FALSE;
458         }
459
460       dbus_connection_close (f->right_conn);
461       dbus_connection_unref (f->right_conn);
462       f->right_conn = NULL;
463     }
464
465   if (f->daemon_pid != 0)
466     {
467 #ifdef DBUS_WIN
468       TerminateProcess (f->daemon_pid, 1);
469 #else
470       kill (f->daemon_pid, SIGTERM);
471 #endif
472
473       g_spawn_close_pid (f->daemon_pid);
474       f->daemon_pid = 0;
475     }
476 }
477
478 static Config limited_config = {
479     "34393", 10000, "valid-config-files/incoming-limit.conf"
480 };
481
482 int
483 main (int argc,
484     char **argv)
485 {
486   g_test_init (&argc, &argv, NULL);
487   g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
488
489   g_test_add ("/echo/session", Fixture, NULL, setup, test_echo, teardown);
490   g_test_add ("/echo/limited", Fixture, &limited_config,
491       setup, test_echo, teardown);
492   g_test_add ("/creds", Fixture, NULL, setup, test_creds, teardown);
493
494   return g_test_run ();
495 }