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