bdb18306fe9818ec9b23448465208376b212e07f
[platform/upstream/dbus.git] / test / watch.c
1 #include "watch.h"
2 #include <stdio.h>
3
4 #define DBUS_COMPILATION /* cheat and use DBusList */
5 #include <dbus/dbus-list.h>
6 #undef DBUS_COMPILATION
7
8 #include <sys/types.h>
9 #include <unistd.h>
10
11 /* Cheesy main loop used in test programs.  Any real app would use the
12  * GLib or Qt or other non-sucky main loops.
13  */ 
14
15 #undef  MAX
16 #define MAX(a, b)  (((a) > (b)) ? (a) : (b))
17
18 static int watch_list_serial = 0;
19 static DBusList *watches = NULL;
20 static dbus_bool_t exited = FALSE;
21 static DBusList *connections = NULL;
22
23 typedef enum
24 {
25   WATCH_CONNECTION,
26   WATCH_SERVER
27 } WatchType;
28
29 typedef struct
30 {
31   WatchType type;
32   void *data;
33 } WatchData;
34
35 static void
36 free_watch_data (void *data)
37 {
38   WatchData *wd = data;
39
40   if (wd->type == WATCH_CONNECTION)
41     dbus_connection_unref (wd->data);
42   else if (wd->type == WATCH_SERVER)
43     dbus_server_unref (wd->data);
44
45   dbus_free (wd);
46 }
47
48 static dbus_bool_t
49 add_connection_watch (DBusWatch      *watch,
50                       DBusConnection *connection)
51 {
52   WatchData *wd;
53
54   if (!_dbus_list_append (&watches, watch))
55     return FALSE;
56   
57   wd = dbus_new0 (WatchData, 1);
58   if (wd == NULL)
59     {
60       _dbus_list_remove_last (&watches, watch);
61       return FALSE;
62     }  
63   wd->type = WATCH_CONNECTION;
64   wd->data = connection;
65
66   dbus_connection_ref (connection);  
67
68   dbus_watch_set_data (watch, wd, free_watch_data);
69
70   watch_list_serial += 1;
71
72 #if 0
73   printf ("Added connection %swatch for fd %d\n",
74           dbus_watch_get_flags (watch) & DBUS_WATCH_WRITABLE ? "write " : "",
75           dbus_watch_get_fd (watch));
76 #endif
77
78   return TRUE;
79  }
80
81 static void
82 remove_connection_watch (DBusWatch      *watch,
83                          DBusConnection *connection)
84 {
85   if (!_dbus_list_remove (&watches, watch))
86     _dbus_assert_not_reached ("removed nonexistent watch");
87
88   dbus_watch_set_data (watch, NULL, NULL);
89
90   watch_list_serial += 1;
91
92 #if 0
93   printf ("Removed connection watch for fd %d\n",
94           dbus_watch_get_fd (watch));
95 #endif
96 }
97
98 static dbus_bool_t
99 add_server_watch (DBusWatch      *watch,
100                   DBusServer     *server)
101 {
102   WatchData *wd;
103   
104   if (!_dbus_list_append (&watches, watch))
105     return FALSE;
106   
107   wd = dbus_new0 (WatchData, 1);
108   if (wd == NULL)
109     {
110       _dbus_list_remove_last (&watches, watch);
111       return FALSE;
112     }
113   
114   wd->type = WATCH_SERVER;
115   wd->data = server;
116
117   dbus_server_ref (server);
118
119   dbus_watch_set_data (watch, wd, free_watch_data);
120
121   watch_list_serial += 1;
122
123 #if 0
124   printf ("Added server %swatch for fd %d\n",
125           dbus_watch_get_flags (watch) & DBUS_WATCH_WRITABLE ? "write " : "",
126           dbus_watch_get_fd (watch));
127 #endif
128
129   return TRUE;
130 }
131
132 static void
133 remove_server_watch (DBusWatch      *watch,
134                      DBusServer     *server)
135 {
136   if (!_dbus_list_remove (&watches, watch))
137     _dbus_assert_not_reached ("removed nonexistent server watch");
138   
139   dbus_watch_set_data (watch, NULL, NULL);
140
141   watch_list_serial += 1;
142
143 #if 0
144   printf ("Removed server watch for fd %d\n",
145           dbus_watch_get_fd (watch));
146 #endif
147 }
148
149 static int count = 0;
150
151 static void
152 disconnect (DBusConnection *connection)
153 {
154   fprintf (stderr, "Disconnected\n");
155   
156   _dbus_list_remove (&connections, connection);
157   dbus_connection_unref (connection);
158   quit_mainloop ();
159 }
160
161
162 static void
163 check_messages (void)
164 {
165   DBusList *link;
166   
167   link = _dbus_list_get_first_link (&connections);
168   while (link != NULL)
169     {
170       DBusList *next = _dbus_list_get_next_link (&connections, link);
171       DBusConnection *connection = link->data;
172       DBusMessage *message;
173       const char *name;
174       
175       while ((message = dbus_connection_pop_message (connection)))
176         {
177           DBusMessage *reply;
178
179           name = dbus_message_get_name (message);
180           if (name && strcmp (name, DBUS_MESSAGE_LOCAL_DISCONNECT) == 0)
181             {
182               disconnect (connection);
183             }
184           else
185             {
186               fprintf (stderr, "Received message %d, sending reply\n", count);
187               
188               reply = dbus_message_new ("org.freedesktop.DBus.Test", "org.freedesktop.DBus.Test");
189               if (!dbus_connection_send (connection,
190                                          reply,
191                                          NULL))
192                 fprintf (stderr, "No memory to send reply\n");
193               dbus_message_unref (reply);
194               
195               dbus_message_unref (message);
196               
197               count += 1;
198               if (count > 100)
199                 {
200                   printf ("Saw %d messages, exiting\n", count);
201                   quit_mainloop ();
202                 }
203             }
204         }
205       
206       link = next;
207     }
208 }
209
210 void
211 do_mainloop (void)
212 {
213   /* Of course with any real app you'd use GMainLoop or
214    * QSocketNotifier and not have to see all this crap.
215    */
216   while (!exited && watches != NULL)
217     {
218       fd_set read_set;
219       fd_set write_set;
220       fd_set err_set;
221       int max_fd;
222       DBusList *link;
223       int initial_watch_serial;
224       
225       check_messages ();
226
227       if (exited)
228         break;
229       
230       FD_ZERO (&read_set);
231       FD_ZERO (&write_set);
232       FD_ZERO (&err_set);
233
234       max_fd = -1;
235
236       link = _dbus_list_get_first_link (&watches);
237       while (link != NULL)
238         {
239           DBusList *next = _dbus_list_get_next_link (&watches, link);
240           DBusWatch *watch;
241           
242           watch = link->data;
243
244           if (dbus_watch_get_enabled (watch))
245             {
246               int fd;
247               unsigned int flags;
248
249               fd = dbus_watch_get_fd (watch);
250               flags = dbus_watch_get_flags (watch);
251               
252               max_fd = MAX (max_fd, fd);
253               
254               if (flags & DBUS_WATCH_READABLE)
255                 FD_SET (fd, &read_set);
256               
257               if (flags & DBUS_WATCH_WRITABLE)
258                 FD_SET (fd, &write_set);
259               
260               FD_SET (fd, &err_set);
261             }
262           
263           link = next;
264         }
265
266       select (max_fd + 1, &read_set, &write_set, &err_set, NULL);
267
268       initial_watch_serial = watch_list_serial;
269       link = _dbus_list_get_first_link (&watches);
270       while (link != NULL)
271         {
272           DBusList *next = _dbus_list_get_next_link (&watches, link);
273           DBusWatch *watch;
274
275           if (initial_watch_serial != watch_list_serial)
276             {
277               /* Watches were added/removed,
278                * hosing our list; break out of here
279                */
280               /* A more elegant solution might be to ref
281                * all watches, then check which have fd >= 0
282                * as we iterate over them, since removed
283                * watches have their fd invalidated.
284                */
285               printf ("Aborting watch iteration due to serial increment\n");
286               break;
287             }
288           
289           watch = link->data;
290
291           if (dbus_watch_get_enabled (watch))
292             {
293               int fd;
294               unsigned int flags;
295               unsigned int condition;
296
297           
298               fd = dbus_watch_get_fd (watch);
299               flags = dbus_watch_get_flags (watch);
300
301               condition = 0;
302           
303               if ((flags & DBUS_WATCH_READABLE) &&
304                   FD_ISSET (fd, &read_set))
305                 condition |= DBUS_WATCH_READABLE;
306
307               if ((flags & DBUS_WATCH_WRITABLE) &&
308                   FD_ISSET (fd, &write_set))
309                 condition |= DBUS_WATCH_WRITABLE;
310
311               if (FD_ISSET (fd, &err_set))
312                 condition |= DBUS_WATCH_ERROR;
313
314               if (condition != 0)
315                 {
316                   WatchData *wd;
317
318                   wd = dbus_watch_get_data (watch);
319
320                   if (wd->type == WATCH_CONNECTION)
321                     {
322                       DBusConnection *connection = wd->data;
323
324                       dbus_connection_handle_watch (connection,
325                                                     watch,
326                                                     condition);
327                     }
328                   else if (wd->type == WATCH_SERVER)
329                     {
330                       DBusServer *server = wd->data;
331                   
332                       dbus_server_handle_watch (server,
333                                                 watch,
334                                                 condition);
335                     }
336                 }
337             }
338           
339           link = next;
340         }
341     }
342 }
343
344 void
345 quit_mainloop (void)
346 {
347   exited = TRUE;
348 }
349
350
351 void
352 setup_connection (DBusConnection *connection)
353 {
354   if (!dbus_connection_set_watch_functions (connection,
355                                             (DBusAddWatchFunction) add_connection_watch,
356                                             (DBusRemoveWatchFunction) remove_connection_watch,
357                                             NULL,
358                                             connection,
359                                             NULL))
360     _dbus_assert_not_reached ("not enough memory");
361
362   dbus_connection_ref (connection);
363   _dbus_list_append (&connections, connection);
364 }
365
366 void
367 setup_server (DBusServer *server)
368 {
369   if (!dbus_server_set_watch_functions (server,
370                                         (DBusAddWatchFunction) add_server_watch,
371                                         (DBusRemoveWatchFunction) remove_server_watch,
372                                         NULL,
373                                         server,
374                                         NULL))
375     _dbus_assert_not_reached ("not enough memory");
376 }