summaryrefslogtreecommitdiff
path: root/dbus/dbus-message-handler.c
blob: f38e51009d6beea646985f049834017f41c0b8a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-message-handler.c Sender/receiver of messages.
 *
 * Copyright (C) 2002, 2003 Red Hat Inc.
 *
 * Licensed under the Academic Free License version 1.2
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "dbus-internals.h"
#include "dbus-message-handler.h"
#include "dbus-list.h"
#include "dbus-threads.h"
#include "dbus-test.h"
#include "dbus-connection-internal.h"

/**
 * @defgroup DBusMessageHandlerInternals DBusMessageHandler implementation details
 * @ingroup DBusInternals
 * @brief DBusMessageHandler private implementation details.
 *
 * The guts of DBusMessageHandler and its methods.
 *
 * @{
 */

_DBUS_DEFINE_GLOBAL_LOCK (message_handler);

/**
 * @brief Internals of DBusMessageHandler
 * 
 * Object that can send and receive messages.
 */
struct DBusMessageHandler
{
  DBusAtomic refcount;                            /**< reference count */

  DBusHandleMessageFunction function;             /**< handler function */
  void                     *user_data;            /**< user data for function */
  DBusFreeFunction          free_user_data;       /**< free the user data */

  DBusList *connections;                          /**< connections we're registered with */
};

/**
 * Add this connection to the list used by this message handler.
 * When the message handler goes away, the connection
 * will be notified.
 *
 * @param handler the message handler
 * @param connection the connection
 * @returns #FALSE if not enough memory
 */
dbus_bool_t
_dbus_message_handler_add_connection (DBusMessageHandler *handler,
                                      DBusConnection     *connection)
{
  dbus_bool_t res;
  
  _DBUS_LOCK (message_handler);
  /* This is a bit wasteful - we just put the connection in the list
   * once per time it's added. :-/
   */
  if (!_dbus_list_prepend (&handler->connections, connection))
    res = FALSE;
  else
    res = TRUE;

  _DBUS_UNLOCK (message_handler);
  
  return res;
}

/**
 * Reverses the effect of _dbus_message_handler_add_connection().
 * @param handler the message handler
 * @param connection the connection
 */
void
_dbus_message_handler_remove_connection (DBusMessageHandler *handler,
                                         DBusConnection     *connection)
{
  _DBUS_LOCK (message_handler);
  if (!_dbus_list_remove (&handler->connections, connection))
    _dbus_warn ("Function _dbus_message_handler_remove_connection() called when the connection hadn't been added\n");
  _DBUS_UNLOCK (message_handler);
}


/**
 * Handles the given message, by dispatching the handler function
 * for this DBusMessageHandler, if any.
 * 
 * @param handler the handler
 * @param connection the connection that received the message
 * @param message the message
 *
 * @returns what to do with the message
 */
DBusHandlerResult
_dbus_message_handler_handle_message (DBusMessageHandler        *handler,
                                      DBusConnection            *connection,
                                      DBusMessage               *message)
{
  DBusHandleMessageFunction function;
  void  *user_data;
  
  _DBUS_LOCK (message_handler);
  function = handler->function;
  user_data = handler->user_data;
  _DBUS_UNLOCK (message_handler);
  
  /* This function doesn't ref handler/connection/message
   * since that's done in dbus_connection_dispatch().
   */
  if (function != NULL)
    return (* function) (handler, connection, message, user_data);
  else
    return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
}

/** @} */

/**
 * @defgroup DBusMessageHandler DBusMessageHandler
 * @ingroup  DBus
 * @brief Message processor  
 *
 * A DBusMessageHandler is an object that can send and receive
 * messages. Typically the handler is registered with one or
 * more DBusConnection objects and processes some types of
 * messages received from the connection.
 *
 * @{
 */

/**
 * @typedef DBusMessageHandler
 *
 * Opaque data type representing a message handler.
 */

/**
 * Creates a new message handler. The handler function
 * may be #NULL for a no-op handler or a handler to
 * be assigned a function later.
 *
 * @param function function to call to handle a message
 * @param user_data data to pass to the function
 * @param free_user_data function to call to free the user data
 * @returns a new DBusMessageHandler or #NULL if no memory.
 */
DBusMessageHandler*
dbus_message_handler_new (DBusHandleMessageFunction function,
                          void                     *user_data,
                          DBusFreeFunction          free_user_data)
{
  DBusMessageHandler *handler;

  handler = dbus_new (DBusMessageHandler, 1);

  if (handler == NULL)
    return NULL;
  
  handler->refcount.value = 1;
  handler->function = function;
  handler->user_data = user_data;
  handler->free_user_data = free_user_data;
  handler->connections = NULL;

  return handler;
}

/**
 * Increments the reference count on a message handler.
 *
 * @param handler the handler
 */
void
dbus_message_handler_ref (DBusMessageHandler *handler)
{
  _dbus_return_if_fail (handler != NULL);

  _dbus_atomic_inc (&handler->refcount);
}

/**
 * Decrements the reference count on a message handler,
 * freeing the handler if the count reaches 0.
 *
 * @param handler the handler
 */
void
dbus_message_handler_unref (DBusMessageHandler *handler)
{
  dbus_bool_t last_unref;

  _dbus_return_if_fail (handler != NULL);

  last_unref = (_dbus_atomic_dec (&handler->refcount) == 1);
  
  if (last_unref)
    {
      DBusList *link;
      
      if (handler->free_user_data)
        (* handler->free_user_data) (handler->user_data);
       
      link = _dbus_list_get_first_link (&handler->connections);
       while (link != NULL)
         {
           DBusConnection *connection = link->data;

           _dbus_connection_handler_destroyed_locked (connection, handler);
           
           link = _dbus_list_get_next_link (&handler->connections, link);
         }

       _dbus_list_clear (&handler->connections);

       dbus_free (handler);
    }
}

/**
 * Gets the user data for the handler (the same user data
 * passed to the handler function.)
 *
 * @param handler the handler
 * @returns the user data
 */
void*
dbus_message_handler_get_data (DBusMessageHandler *handler)
{
  void* user_data;

  _dbus_return_val_if_fail (handler != NULL, NULL);
  
  _DBUS_LOCK (message_handler);
  user_data = handler->user_data;
  _DBUS_UNLOCK (message_handler);
  return user_data;
}

/**
 * Sets the user data for the handler (the same user data
 * to be passed to the handler function). Frees any previously-existing
 * user data with the previous free_user_data function.
 *
 * @param handler the handler
 * @param user_data the user data
 * @param free_user_data free function for the data
 */
void
dbus_message_handler_set_data (DBusMessageHandler *handler,
                               void               *user_data,
                               DBusFreeFunction    free_user_data)
{
  DBusFreeFunction old_free_func;
  void *old_user_data;

  _dbus_return_if_fail (handler != NULL);
  
  _DBUS_LOCK (message_handler);
  old_free_func = handler->free_user_data;
  old_user_data = handler->user_data;

  handler->user_data = user_data;
  handler->free_user_data = free_user_data;
  _DBUS_UNLOCK (message_handler);

  if (old_free_func)
    (* old_free_func) (old_user_data);

}

/**
 * Sets the handler function. Call dbus_message_handler_set_data()
 * to set the user data for the function.
 *
 * @param handler the handler
 * @param function the function
 */
void
dbus_message_handler_set_function (DBusMessageHandler        *handler,
                                   DBusHandleMessageFunction  function)
{
  _dbus_return_if_fail (handler != NULL);
  
  _DBUS_LOCK (message_handler);
  handler->function = function;
  _DBUS_UNLOCK (message_handler);
}

/** @} */

#ifdef DBUS_BUILD_TESTS
static DBusHandlerResult
test_handler (DBusMessageHandler *handler,
              DBusConnection     *connection,
              DBusMessage        *message,
              void               *user_data)
{
  return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
}

static void
free_test_data (void *data)
{
  /* does nothing */
}

/**
 * @ingroup DBusMessageInternals
 * Unit test for DBusMessageHandler.
 *
 * @returns #TRUE on success.
 */
dbus_bool_t
_dbus_message_handler_test (const char *test_data_dir)
{
  DBusMessageHandler *handler;

#define TEST_DATA ((void*) 0xcafebabe)
  
  handler = dbus_message_handler_new (test_handler,
                                      TEST_DATA,
                                      free_test_data);

  _dbus_assert (handler != NULL);
  _dbus_assert (handler->function == test_handler);

  if (dbus_message_handler_get_data (handler) != TEST_DATA)
    _dbus_assert_not_reached ("got wrong data");

  dbus_message_handler_set_data (handler, NULL, NULL);
  if (dbus_message_handler_get_data (handler) != NULL)
    _dbus_assert_not_reached ("got wrong data after set");  
  
  dbus_message_handler_set_function (handler, NULL);
  _dbus_assert (handler->function == NULL);

  dbus_message_handler_ref (handler);
  dbus_message_handler_unref (handler);
  dbus_message_handler_unref (handler);
  
  return TRUE;
}
#endif /* DBUS_BUILD_TESTS */