2003-03-31 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-transport-debug.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-transport-debug.c In-proc debug subclass of DBusTransport
3  *
4  * Copyright (C) 2003  CodeFactory AB
5  * Copyright (C) 2003  Red Hat, Inc.
6  *
7  * Licensed under the Academic Free License version 1.2
8  * 
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #include "dbus-internals.h"
26 #include "dbus-connection-internal.h"
27 #include "dbus-transport-protected.h"
28 #include "dbus-transport-debug.h"
29 #include "dbus-server-debug.h"
30 #include "dbus-list.h"
31
32 #ifdef DBUS_BUILD_TESTS
33
34 /**
35  * @defgroup DBusTransportDebug DBusTransportDebug
36  * @ingroup  DBusInternals
37  * @brief In-process debug transport used in unit tests.
38  *
39  * Types and functions related to DBusTransportDebug.
40  * This is used for unit testing.
41  *
42  * @{
43  */
44
45 /**
46  * Default timeout interval when reading or writing.
47  */
48 #define DEFAULT_INTERVAL 1
49
50 /**
51  * Hack due to lack of OOM handling in a couple places.
52  * Need to alloc timeout permanently and enabled/disable so
53  * that check_timeout won't fail in messages_pending
54  */
55 #define WAIT_FOR_MEMORY() _dbus_sleep_milliseconds (250)
56
57 static dbus_bool_t check_timeout (DBusTransport *transport);
58
59 /**
60  * Opaque object representing a debug transport.
61  *
62  */
63 typedef struct DBusTransportDebug DBusTransportDebug;
64
65 /**
66  * Implementation details of DBusTransportDebug. All members are private.
67  */
68 struct DBusTransportDebug
69 {
70   DBusTransport base;                   /**< Parent instance */
71
72   DBusTimeout *timeout;                 /**< Timeout for moving messages. */
73   
74   DBusTransport *other_end;             /**< The transport that this transport is connected to. */
75
76   unsigned int timeout_added : 1;       /**< Whether timeout has been added */
77 };
78
79 /* move messages in both directions */
80 static dbus_bool_t
81 move_messages (DBusTransport *transport)
82 {
83   DBusTransportDebug *debug_transport = (DBusTransportDebug*) transport;
84   
85   if (transport->disconnected)
86     return TRUE;
87
88   while (!transport->disconnected &&
89          _dbus_connection_have_messages_to_send (transport->connection))
90     {
91       DBusMessage *message, *copy;
92       
93       message = _dbus_connection_get_message_to_send (transport->connection);
94       _dbus_assert (message != NULL);
95       
96       copy = dbus_message_copy (message);
97       if (copy == NULL)
98         return FALSE;
99         
100       _dbus_message_lock (message);
101       
102       _dbus_connection_message_sent (transport->connection,
103                                      message);
104
105       _dbus_verbose ("   -->transporting message %s from %s %p to %s %p\n",
106                      dbus_message_get_name (copy),
107                      transport->is_server ? "server" : "client",
108                      transport->connection,
109                      debug_transport->other_end->is_server ? "server" : "client",
110                      debug_transport->other_end->connection);
111       
112       _dbus_connection_queue_received_message (debug_transport->other_end->connection,
113                                                copy);
114       dbus_message_unref (copy);
115     }
116
117   if (debug_transport->other_end &&
118       !debug_transport->other_end->disconnected &&
119       _dbus_connection_have_messages_to_send (debug_transport->other_end->connection))
120     {
121       if (!move_messages (debug_transport->other_end))
122         return FALSE;
123     }
124   
125   return TRUE;
126 }
127
128 static dbus_bool_t
129 timeout_handler (void *data)
130 {
131   DBusTransport *transport = data;
132   
133   if (!move_messages (transport))
134     return FALSE;
135
136   if (!check_timeout (transport))
137     return FALSE;
138
139   return TRUE;
140 }
141
142 static dbus_bool_t
143 check_timeout (DBusTransport *transport)
144 {
145   DBusTransportDebug *debug_transport = (DBusTransportDebug*) transport;
146   
147   if (transport->connection &&
148       transport->authenticated &&
149       (transport->messages_need_sending ||
150        (debug_transport->other_end &&
151         debug_transport->other_end->messages_need_sending)))
152     {
153       if (!debug_transport->timeout_added)
154         {
155           /* FIXME this can be fixed now, by enabling/disabling
156            * the timeout instead of adding it here
157            */
158           if (!_dbus_connection_add_timeout (transport->connection,
159                                              debug_transport->timeout))
160             return FALSE;
161           debug_transport->timeout_added = TRUE;
162         }
163     }
164   else
165     {
166       if (debug_transport->timeout_added)
167         {
168           _dbus_connection_remove_timeout (transport->connection,
169                                            debug_transport->timeout);
170           debug_transport->timeout_added = FALSE;
171         }
172     }
173
174   return TRUE;
175 }
176
177 static void
178 debug_finalize (DBusTransport *transport)
179 {
180   DBusTransportDebug *debug_transport = (DBusTransportDebug*) transport;
181   
182   if (debug_transport->timeout_added)
183     _dbus_connection_remove_timeout (transport->connection,
184                                      debug_transport->timeout);
185   
186   if (debug_transport->other_end)
187     {
188       _dbus_transport_disconnect (debug_transport->other_end);
189       debug_transport->other_end = NULL;
190     }
191   
192   _dbus_transport_finalize_base (transport);  
193
194   _dbus_timeout_unref (debug_transport->timeout);
195   
196   dbus_free (transport);
197 }
198
199 static dbus_bool_t
200 debug_handle_watch (DBusTransport *transport,
201                     DBusWatch     *watch,
202                     unsigned int   flags)
203 {
204   return TRUE;
205 }
206
207 static void
208 debug_disconnect (DBusTransport *transport)
209 {
210 }
211
212 static dbus_bool_t
213 debug_connection_set (DBusTransport *transport)
214 {
215   if (!check_timeout (transport))
216     return FALSE;
217   return TRUE;
218 }
219
220 static void
221 debug_messages_pending (DBusTransport *transport,
222                         int            messages_pending)
223 {
224   while (!check_timeout (transport))
225     WAIT_FOR_MEMORY ();
226 }
227
228 static void
229 debug_do_iteration (DBusTransport *transport,
230                     unsigned int   flags,
231                     int            timeout_milliseconds)
232 {
233   move_messages (transport);
234 }
235
236 static void
237 debug_live_messages_changed (DBusTransport *transport)
238 {
239 }
240
241 static DBusTransportVTable debug_vtable = {
242   debug_finalize,
243   debug_handle_watch,
244   debug_disconnect,
245   debug_connection_set,
246   debug_messages_pending,
247   debug_do_iteration,
248   debug_live_messages_changed
249 };
250
251 static dbus_bool_t
252 create_timeout_object (DBusTransportDebug *debug_transport)
253 {
254   debug_transport->timeout = _dbus_timeout_new (DEFAULT_INTERVAL,
255                                                 timeout_handler,
256                                                 debug_transport, NULL);
257   
258   return debug_transport->timeout != NULL;
259 }
260
261 /**
262  * Creates a new debug server transport.
263  *
264  * @param client the client transport that the server transport
265  * should use.
266  * @returns a new debug transport
267  */
268 DBusTransport*
269 _dbus_transport_debug_server_new (DBusTransport *client)
270 {
271   DBusTransportDebug *debug_transport;
272
273   debug_transport = dbus_new0 (DBusTransportDebug, 1);
274   
275   if (debug_transport == NULL)
276     return NULL;
277
278   if (!_dbus_transport_init_base (&debug_transport->base,
279                                   &debug_vtable,
280                                   TRUE, NULL))
281     {
282       dbus_free (debug_transport);
283       return NULL;
284     }
285
286   if (!create_timeout_object (debug_transport))
287     {
288       _dbus_transport_finalize_base (&debug_transport->base);      
289       dbus_free (debug_transport);
290       return NULL;
291     }
292   
293   debug_transport->base.authenticated = TRUE;
294
295   /* Connect the two transports */
296   debug_transport->other_end = client;
297   ((DBusTransportDebug *)client)->other_end = (DBusTransport *)debug_transport;
298
299   _dbus_verbose ("  new debug server transport %p created, other end %p\n",
300                  debug_transport, debug_transport->other_end);
301   
302   return (DBusTransport *)debug_transport;
303 }
304
305 /**
306  * Creates a new debug client transport.
307  *
308  * @param server_name name of the server transport that
309  * the client should try to connect to.
310  * @param error address where an error can be returned.
311  * @returns a new transport, or #NULL on failure. 
312  */
313 DBusTransport*
314 _dbus_transport_debug_client_new (const char     *server_name,
315                                   DBusError      *error)
316 {
317   DBusServer *debug_server;
318   DBusTransportDebug *debug_transport;
319   DBusString address;
320   
321   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
322   
323   debug_server = _dbus_server_debug_lookup (server_name);
324
325   if (!debug_server)
326     {
327       dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL);
328       return NULL;
329     }
330
331   if (!_dbus_string_init (&address))
332     {
333       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
334       return NULL;
335     }
336
337   if (!_dbus_string_append (&address, "debug-pipe:name=") ||
338       !_dbus_string_append (&address, server_name))
339     {
340       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
341       _dbus_string_free (&address);
342       return NULL;
343     }
344   
345   debug_transport = dbus_new0 (DBusTransportDebug, 1);
346   if (debug_transport == NULL)
347     {
348       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
349       _dbus_string_free (&address);
350       return NULL;
351     }
352
353   if (!_dbus_transport_init_base (&debug_transport->base,
354                                   &debug_vtable,
355                                   FALSE, &address))
356     {
357       dbus_free (debug_transport);
358       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
359       _dbus_string_free (&address);
360       return NULL;
361     }
362
363   _dbus_string_free (&address);
364   
365   if (!create_timeout_object (debug_transport))
366     {
367       _dbus_transport_finalize_base (&debug_transport->base);
368       dbus_free (debug_transport);
369       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
370       return NULL;
371     }
372   
373   if (!_dbus_server_debug_accept_transport (debug_server,
374                                             (DBusTransport *)debug_transport))
375     {
376       _dbus_timeout_unref (debug_transport->timeout);
377       _dbus_transport_finalize_base (&debug_transport->base);
378       dbus_free (debug_transport);
379       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
380       return NULL;
381     }
382
383   /* FIXME: Prolly wrong to do this. */
384   debug_transport->base.authenticated = TRUE;
385
386   _dbus_verbose ("  new debug client transport %p created, other end %p\n",
387                  debug_transport, debug_transport->other_end);
388   
389   return (DBusTransport *)debug_transport;
390 }
391
392 /** @} */
393
394 #endif /* DBUS_BUILD_TESTS */