1
/*
2
 * Copyright 2008-2011 Novell, Inc.
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * This library is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with this library; if not, write to the
16
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
 * Boston, MA 02110-1301, USA.
18
 */
19

            
20
#include <glib.h>
21
#include <stdarg.h>
22
#include <stdio.h>
23
#include <string.h>
24
#include <sys/time.h>
25

            
26
#include "atspi/atspi-gmain.h"
27
#include "config.h"
28
#include "dbind/dbind.h"
29

            
30
static int dbind_timeout = -1;
31

            
32
/*
33
 * FIXME: compare types - to ensure they match &
34
 *        do dynamic padding of structures etc.
35
 */
36

            
37
/*---------------------------------------------------------------------------*/
38

            
39
typedef struct _SpiReentrantCallClosure
40
{
41
  DBusMessage *reply;
42
} SpiReentrantCallClosure;
43

            
44
static void
45
340
set_reply (DBusPendingCall *pending, void *user_data)
46
{
47
340
  SpiReentrantCallClosure *closure = (SpiReentrantCallClosure *) user_data;
48

            
49
340
  closure->reply = dbus_pending_call_steal_reply (pending);
50
340
  dbus_pending_call_unref (pending);
51
340
}
52

            
53
static gint
54
1608
time_elapsed (struct timeval *origin)
55
{
56
  struct timeval tv;
57

            
58
1608
  gettimeofday (&tv, NULL);
59
1608
  return (tv.tv_sec - origin->tv_sec) * 1000 + (tv.tv_usec - origin->tv_usec) / 1000;
60
}
61

            
62
DBusMessage *
63
993
dbind_send_and_allow_reentry (DBusConnection *bus, DBusMessage *message, DBusError *error)
64
{
65
  DBusPendingCall *pending;
66
  SpiReentrantCallClosure *closure;
67
993
  const char *unique_name = dbus_bus_get_unique_name (bus);
68
993
  const char *destination = dbus_message_get_destination (message);
69
  struct timeval tv;
70
  DBusMessage *ret;
71
  static gboolean in_dispatch = FALSE;
72

            
73
993
  if (unique_name && destination &&
74
653
      strcmp (destination, unique_name) != 0)
75
    {
76
653
      ret = dbus_connection_send_with_reply_and_block (bus, message,
77
                                                       dbind_timeout, error);
78
653
      if (g_main_depth () == 0 && !in_dispatch)
79
        {
80
159
          in_dispatch = TRUE;
81
163
          while (dbus_connection_dispatch (bus) == DBUS_DISPATCH_DATA_REMAINS)
82
            ;
83
159
          in_dispatch = FALSE;
84
        }
85
653
      return ret;
86
    }
87

            
88
340
  closure = g_new0 (SpiReentrantCallClosure, 1);
89
340
  closure->reply = NULL;
90
340
  if (!dbus_connection_send_with_reply (bus, message, &pending, dbind_timeout) || !pending)
91
    {
92
      g_free (closure);
93
      return NULL;
94
    }
95
340
  dbus_pending_call_set_notify (pending, set_reply, (void *) closure, g_free);
96

            
97
340
  closure->reply = NULL;
98
340
  gettimeofday (&tv, NULL);
99
340
  dbus_pending_call_ref (pending);
100
1948
  while (!closure->reply)
101
    {
102
1608
      if (!dbus_connection_read_write_dispatch (bus, dbind_timeout))
103
        {
104
          // dbus_pending_call_set_notify (pending, NULL, NULL, NULL);
105
          dbus_pending_call_cancel (pending);
106
          dbus_pending_call_unref (pending);
107
          return NULL;
108
        }
109
1608
      if (time_elapsed (&tv) > dbind_timeout)
110
        {
111
          // dbus_pending_call_set_notify (pending, NULL, NULL, NULL);
112
          dbus_pending_call_cancel (pending);
113
          dbus_pending_call_unref (pending);
114
          dbus_set_error_const (error, "org.freedesktop.DBus.Error.NoReply",
115
                                "timeout from dbind");
116
          return NULL;
117
        }
118
    }
119

            
120
340
  ret = closure->reply;
121
340
  dbus_pending_call_unref (pending);
122
340
  return ret;
123
}
124

            
125
dbus_bool_t
126
139
dbind_method_call_reentrant_va (DBusConnection *cnx,
127
                                const char *bus_name,
128
                                const char *path,
129
                                const char *interface,
130
                                const char *method,
131
                                DBusError *opt_error,
132
                                const char *arg_types,
133
                                va_list args)
134
{
135
139
  dbus_bool_t success = FALSE;
136
139
  DBusMessage *msg = NULL, *reply = NULL;
137
  DBusMessageIter iter;
138
  DBusError *err, real_err;
139
  const char *p;
140
  va_list args_demarshal;
141

            
142
139
  dbus_error_init (&real_err);
143

            
144
139
  va_copy (args_demarshal, args);
145
139
  if (opt_error)
146
137
    err = opt_error;
147
  else
148
    {
149
2
      err = &real_err;
150
    }
151

            
152
139
  msg = dbus_message_new_method_call (bus_name, path, interface, method);
153
139
  if (!msg)
154
    goto out;
155

            
156
139
  p = arg_types;
157
139
  dbus_message_iter_init_append (msg, &iter);
158
139
  dbind_any_marshal_va (&iter, &p, args);
159

            
160
139
  reply = dbind_send_and_allow_reentry (cnx, msg, err);
161
139
  if (!reply)
162
    goto out;
163

            
164
139
  if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
165
    {
166
1
      goto out;
167
    }
168
  /* demarshal */
169
138
  if (p[0] == '=' && p[1] == '>')
170
    {
171
      DBusMessageIter iter;
172
135
      dbus_message_iter_init (reply, &iter);
173
135
      if (strcmp (p + 2, dbus_message_get_signature (reply)) != 0)
174
        {
175
          g_warning ("dbind: Call to \"%s\" returned signature %s; expected %s",
176
                     method, dbus_message_get_signature (reply), p + 2);
177
          if (opt_error)
178
            dbus_set_error (opt_error, DBUS_ERROR_INVALID_ARGS,
179
                            "Call to \"%s\" returned signature %s; expected %s",
180
                            method, dbus_message_get_signature (reply),
181
                            p + 2);
182
          goto out;
183
        }
184
135
      p = arg_types;
185
135
      dbind_any_demarshal_va (&iter, &p, args_demarshal);
186
    }
187

            
188
138
  success = TRUE;
189
139
out:
190
139
  if (msg)
191
139
    dbus_message_unref (msg);
192

            
193
139
  if (reply)
194
139
    dbus_message_unref (reply);
195

            
196
139
  if (dbus_error_is_set (&real_err))
197
    dbus_error_free (&real_err);
198

            
199
139
  va_end (args_demarshal);
200
139
  return success;
201
}
202

            
203
/**
204
 * dbind_method_call_reentrant:
205
 *
206
 * @cnx:       A D-Bus Connection used to make the method call.
207
 * @bus_name:  The D-Bus bus name of the program where the method call should
208
 *             be made.
209
 * @path:      The D-Bus object path that should handle the method.
210
 * @interface: The D-Bus interface used to scope the method name.
211
 * @method:    Method to be invoked.
212
 * @opt_error: D-Bus error.
213
 * @arg_types: Variable length arguments interleaving D-Bus argument types
214
 *             and pointers to argument data.
215
 *
216
 * Makes a D-Bus method call using the supplied location data, method name and
217
 * argument data. This function is re-entrant. It continuously reads from the D-Bus
218
 * bus and dispatches messages until a reply has been received.
219
 **/
220
dbus_bool_t
221
2
dbind_method_call_reentrant (DBusConnection *cnx,
222
                             const char *bus_name,
223
                             const char *path,
224
                             const char *interface,
225
                             const char *method,
226
                             DBusError *opt_error,
227
                             const char *arg_types,
228
                             ...)
229
{
230
2
  dbus_bool_t success = FALSE;
231
  va_list args;
232

            
233
2
  va_start (args, arg_types);
234
2
  success = dbind_method_call_reentrant_va (cnx,
235
                                            bus_name,
236
                                            path,
237
                                            interface,
238
                                            method,
239
                                            opt_error,
240
                                            arg_types,
241
                                            args);
242
2
  va_end (args);
243

            
244
2
  return success;
245
}
246

            
247
dbus_bool_t
248
dbind_method_call_va (DBusConnection *cnx,
249
                      const char *bus_name,
250
                      const char *path,
251
                      const char *interface,
252
                      const char *method,
253
                      const char *arg_types,
254
                      va_list args)
255
{
256
  dbus_bool_t success = FALSE;
257
  DBusMessage *msg = NULL;
258
  DBusMessageIter iter;
259
  const char *p;
260
  va_list args_demarshal;
261

            
262
  va_copy (args_demarshal, args);
263

            
264
  msg = dbus_message_new_method_call (bus_name, path, interface, method);
265
  if (!msg)
266
    return FALSE;
267

            
268
  p = arg_types;
269
  dbus_message_iter_init_append (msg, &iter);
270
  dbind_any_marshal_va (&iter, &p, args);
271

            
272
  dbus_connection_send (cnx, msg, NULL);
273
  dbus_message_unref (msg);
274

            
275
  va_end (args_demarshal);
276
  return success;
277
}
278

            
279
/**
280
 * dbind_method_call:
281
 *
282
 * @cnx:       A D-Bus Connection used to make the method call.
283
 * @bus_name:  The D-Bus bus name of the program where the method call should
284
 *             be made.
285
 * @path:      The D-Bus object path that should handle the method.
286
 * @interface: The D-Bus interface used to scope the method name.
287
 * @method:    Method to be invoked.
288
 * @arg_types: Variable length arguments interleaving D-Bus argument types
289
 *             and pointers to argument data.
290
 *
291
 * Sends a D-Bus method call using the supplied location data, method name and
292
 * argument data, without waiting for a re ply.
293
 **/
294
dbus_bool_t
295
dbind_method_call (DBusConnection *cnx,
296
                   const char *bus_name,
297
                   const char *path,
298
                   const char *interface,
299
                   const char *method,
300
                   const char *arg_types,
301
                   ...)
302
{
303
  dbus_bool_t success = FALSE;
304
  va_list args;
305

            
306
  va_start (args, arg_types);
307
  success = dbind_method_call_va (cnx,
308
                                  bus_name,
309
                                  path,
310
                                  interface,
311
                                  method,
312
                                  arg_types,
313
                                  args);
314
  va_end (args);
315

            
316
  return success;
317
}
318

            
319
void
320
991
dbind_set_timeout (int timeout)
321
{
322
991
  dbind_timeout = timeout;
323
991
}
324

            
325
/*END------------------------------------------------------------------------*/