2003-02-13 Anders Carlsson <andersca@codefactory.se>
[platform/upstream/dbus.git] / bus / driver.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* driver.c  Bus client (driver)
3  *
4  * Copyright (C) 2003  CodeFactory AB
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "connection.h"
25 #include "driver.h"
26 #include "dispatch.h"
27 #include "services.h"
28 #include "utils.h"
29 #include <dbus/dbus-string.h>
30 #include <dbus/dbus-internals.h>
31 #include <string.h>
32
33 static void bus_driver_send_welcome_message (DBusConnection *connection,
34                                              DBusMessage    *hello_message);
35
36 void
37 bus_driver_send_service_deleted (const char *service_name)
38 {
39   DBusMessage *message;
40
41   _dbus_verbose ("sending service deleted: %s\n", service_name);
42   
43   BUS_HANDLE_OOM (message = dbus_message_new (DBUS_SERVICE_BROADCAST,
44                                               DBUS_MESSAGE_SERVICE_DELETED));
45   
46   BUS_HANDLE_OOM (dbus_message_set_sender (message, DBUS_SERVICE_DBUS));
47
48   BUS_HANDLE_OOM (dbus_message_append_fields (message,
49                                               DBUS_TYPE_STRING, service_name,
50                                               0));
51   bus_dispatch_broadcast_message (message);
52   dbus_message_unref (message);  
53 }
54
55 static void
56 bus_driver_send_service_created (const char *service_name)
57 {
58   DBusMessage *message;
59
60   BUS_HANDLE_OOM (message = dbus_message_new (DBUS_SERVICE_BROADCAST,
61                                               DBUS_MESSAGE_SERVICE_CREATED));
62   
63   BUS_HANDLE_OOM (dbus_message_set_sender (message, DBUS_SERVICE_DBUS));
64   
65   BUS_HANDLE_OOM (dbus_message_append_fields (message,
66                                               DBUS_TYPE_STRING, service_name,
67                                               0));
68   bus_dispatch_broadcast_message (message);
69   dbus_message_unref (message);
70 }
71
72 void
73 bus_driver_send_service_lost (DBusConnection *connection,
74                               const char *service_name)
75 {
76   DBusMessage *message;
77
78   BUS_HANDLE_OOM (message = dbus_message_new (DBUS_SERVICE_BROADCAST,
79                                               DBUS_MESSAGE_SERVICE_LOST));
80   
81   BUS_HANDLE_OOM (dbus_message_set_sender (message, DBUS_SERVICE_DBUS));
82   BUS_HANDLE_OOM (dbus_message_append_fields (message,
83                                               DBUS_TYPE_STRING, service_name,
84                                               0));
85   BUS_HANDLE_OOM (dbus_connection_send_message (connection, message, NULL, NULL));
86   
87   dbus_message_unref (message);
88 }
89
90 void
91 bus_driver_send_service_acquired (DBusConnection *connection,
92                                   const char *service_name)
93 {
94   DBusMessage *message;
95
96   BUS_HANDLE_OOM (message = dbus_message_new (DBUS_SERVICE_BROADCAST,
97                                               DBUS_MESSAGE_SERVICE_ACQUIRED));
98   
99   BUS_HANDLE_OOM (dbus_message_set_sender (message, DBUS_SERVICE_DBUS));
100   BUS_HANDLE_OOM (dbus_message_append_fields (message,
101                                               DBUS_TYPE_STRING, service_name,
102                                               0));
103   BUS_HANDLE_OOM (dbus_connection_send_message (connection, message, NULL, NULL));
104   
105   dbus_message_unref (message);
106 }
107
108 static dbus_bool_t
109 create_unique_client_name (DBusString *str)
110 {
111   /* We never want to use the same unique client name twice, because
112    * we want to guarantee that if you send a message to a given unique
113    * name, you always get the same application. So we use two numbers
114    * for INT_MAX * INT_MAX combinations, should be pretty safe against
115    * wraparound.
116    */
117   static int next_major_number = 0;
118   static int next_minor_number = 0;
119   int len;
120
121   len = _dbus_string_get_length (str);
122   
123   while (TRUE)
124     {
125       /* start out with 1-0, go to 1-1, 1-2, 1-3,
126        * up to 1-MAXINT, then 2-0, 2-1, etc.
127        */
128       if (next_minor_number <= 0)
129         {
130           next_major_number += 1;
131           next_minor_number = 0;
132           if (next_major_number <= 0)
133             _dbus_assert_not_reached ("INT_MAX * INT_MAX clients were added");
134         }
135
136       _dbus_assert (next_major_number > 0);
137       _dbus_assert (next_minor_number >= 0);
138
139       /* appname:MAJOR-MINOR */
140       
141       if (!_dbus_string_append (str, ":"))
142         return FALSE;
143       
144       if (!_dbus_string_append_int (str, next_major_number))
145         return FALSE;
146
147       if (!_dbus_string_append (str, "-"))
148         return FALSE;
149       
150       if (!_dbus_string_append_int (str, next_minor_number))
151         return FALSE;
152
153       next_minor_number += 1;
154       
155       /* Check if a client with the name exists */
156       if (bus_service_lookup (str, FALSE) == NULL)
157         break;
158
159       /* drop the number again, try the next one. */
160       _dbus_string_set_length (str, len);
161     }
162
163   return TRUE;
164 }
165
166 static void
167 bus_driver_handle_hello (DBusConnection *connection,
168                          DBusMessage    *message)
169 {
170   DBusString unique_name;
171   BusService *service;
172   
173   BUS_HANDLE_OOM (_dbus_string_init (&unique_name, _DBUS_INT_MAX));
174   BUS_HANDLE_OOM (create_unique_client_name (&unique_name));
175   
176   /* Create the service */
177   BUS_HANDLE_OOM (service = bus_service_lookup (&unique_name, TRUE));
178   bus_service_set_prohibit_replacement (service, TRUE);
179   
180   /* Add the connection as the owner */
181   BUS_HANDLE_OOM (bus_service_add_owner (service, connection));
182   BUS_HANDLE_OOM (bus_connection_set_name (connection, &unique_name));
183
184   BUS_HANDLE_OOM (dbus_message_set_sender (message,
185                                              bus_connection_get_name (connection)));
186   
187   _dbus_string_free (&unique_name);
188
189   BUS_HANDLE_OOM (bus_driver_send_welcome_message (connection, message));
190
191   /* Broadcast a service created message */
192   bus_driver_send_service_created (bus_service_get_name (service));
193 }
194
195 static void
196 bus_driver_send_welcome_message (DBusConnection *connection,
197                                  DBusMessage    *hello_message)
198 {
199   DBusMessage *welcome;
200   const char *name;
201   
202   name = bus_connection_get_name (connection);
203   _dbus_assert (name != NULL);
204   
205   BUS_HANDLE_OOM (welcome = dbus_message_new_reply (hello_message));
206   
207   BUS_HANDLE_OOM (dbus_message_set_sender (welcome, DBUS_SERVICE_DBUS));
208   
209   BUS_HANDLE_OOM (dbus_message_append_fields (welcome,
210                                                 DBUS_TYPE_STRING, name,
211                                                 NULL));
212   
213   BUS_HANDLE_OOM (dbus_connection_send_message (connection, welcome, NULL, NULL));
214
215   dbus_message_unref (welcome);
216 }
217
218 static void
219 bus_driver_handle_list_services (DBusConnection *connection,
220                                  DBusMessage    *message)
221 {
222   DBusMessage *reply;
223   int len, i;
224   char **services;
225
226   BUS_HANDLE_OOM (reply = dbus_message_new_reply (message));
227
228   BUS_HANDLE_OOM (services = bus_services_list (&len));
229
230   BUS_HANDLE_OOM (dbus_message_append_fields (reply,
231                                                 DBUS_TYPE_STRING_ARRAY, services, len,
232                                                 0));
233
234   BUS_HANDLE_OOM (dbus_connection_send_message (connection, reply, NULL, NULL));
235
236   dbus_message_unref (reply);
237
238   if (services != NULL)
239     {
240       for (i = 0; i < len; i++)
241         dbus_free (services[i]);
242       dbus_free (services);
243     }
244 }
245
246 static void
247 bus_driver_handle_acquire_service (DBusConnection *connection,
248                                    DBusMessage    *message)
249 {
250   DBusMessage *reply;
251   DBusResultCode result;
252   DBusString service_name;
253   BusService *service;  
254   char *name;
255   int service_reply;
256   int flags;
257   
258   BUS_HANDLE_OOM ((result = dbus_message_get_fields (message,
259                                                        DBUS_TYPE_STRING, &name,
260                                                        DBUS_TYPE_UINT32, &flags,
261                                                        0)) != DBUS_RESULT_NO_MEMORY);
262   
263   if (result != DBUS_RESULT_SUCCESS)
264     {
265       dbus_free (name);
266       dbus_connection_disconnect (connection);
267       return;
268     }
269
270   _dbus_verbose ("Trying to own service %s with flags %d\n", name, flags);
271
272   _dbus_string_init_const (&service_name, name);
273   service = bus_service_lookup (&service_name, TRUE);
274
275   BUS_HANDLE_OOM ((reply = dbus_message_new_reply (message)));
276   
277   /*
278    * Check if the service already has an owner
279    */
280   if (bus_service_get_primary_owner (service) != NULL)
281     {
282       if (bus_service_has_owner (service, connection))
283         service_reply = DBUS_SERVICE_REPLY_ALREADY_OWNER;
284       else if (!(flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING))
285         service_reply = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
286       else
287         {
288           if (bus_service_get_prohibit_replacement (service))
289             {
290               
291               /* Queue the connection */
292               BUS_HANDLE_OOM (bus_service_add_owner (service, connection));
293               
294               service_reply = DBUS_SERVICE_REPLY_IN_QUEUE;
295             }
296           else
297             {
298               DBusConnection *owner;
299               
300               /* We can replace the primary owner */
301               owner = bus_service_get_primary_owner (service);
302
303               /* We enqueue the new owner and remove the first one because
304                * that will cause ServiceAcquired and ServiceLost messages to
305                * be sent.
306                */
307               BUS_HANDLE_OOM (bus_service_add_owner (service, connection));
308               bus_service_remove_owner (service, owner);
309               _dbus_assert (connection == bus_service_get_primary_owner (service));
310               service_reply = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
311             }
312         }
313     }
314   else
315     {
316       bus_service_set_prohibit_replacement (service,
317                                             (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));
318       
319       /* Broadcast service created message */
320       bus_driver_send_service_created (bus_service_get_name (service));
321       
322       BUS_HANDLE_OOM (bus_service_add_owner (service, connection));
323                         
324       service_reply = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
325     }
326
327   BUS_HANDLE_OOM (dbus_message_append_fields (reply, DBUS_TYPE_UINT32, service_reply, 0));
328
329   /* Send service reply */
330   BUS_HANDLE_OOM (dbus_connection_send_message (connection, reply, NULL, NULL));
331   dbus_free (name);
332   dbus_message_unref (reply);
333 }
334
335 static void
336 bus_driver_handle_service_exists (DBusConnection *connection,
337                                   DBusMessage    *message)
338 {
339   DBusMessage *reply;
340   DBusResultCode result;
341   DBusString service_name;
342   BusService *service;
343   char *name;
344   
345   BUS_HANDLE_OOM ((result = dbus_message_get_fields (message,
346                                                        DBUS_TYPE_STRING, &name,
347                                                        0)) != DBUS_RESULT_NO_MEMORY);
348  if (result != DBUS_RESULT_SUCCESS)
349     {
350       dbus_free (name);
351       dbus_connection_disconnect (connection);
352       return;
353     }
354
355  _dbus_string_init_const (&service_name, name);
356  service = bus_service_lookup (&service_name, FALSE);
357  
358  BUS_HANDLE_OOM ((reply = dbus_message_new_reply (message)));
359  BUS_HANDLE_OOM (dbus_message_set_sender (reply, DBUS_SERVICE_DBUS));
360
361  BUS_HANDLE_OOM (dbus_message_append_fields (reply,
362                                                DBUS_TYPE_UINT32, service != NULL,
363                                                0));
364  BUS_HANDLE_OOM (dbus_connection_send_message (connection, reply, NULL, NULL));
365  dbus_message_unref (reply);
366  dbus_free (name);
367 }
368
369 void
370 bus_driver_handle_message (DBusConnection *connection,
371                            DBusMessage    *message)
372 {
373   const char *name, *sender;
374
375   _dbus_verbose ("Driver got a message: %s\n",
376                  dbus_message_get_name (message));
377   
378   name = dbus_message_get_name (message);
379   sender = dbus_message_get_sender (message);
380
381   if (sender == NULL && (strcmp (name, DBUS_MESSAGE_HELLO) != 0))
382     {
383       _dbus_verbose ("Trying to send a message without being registered. Disconnecting.\n");
384       dbus_connection_disconnect (connection);
385       return;
386     }
387
388   /* Now check names. */
389   if (strcmp (name, DBUS_MESSAGE_HELLO) == 0)
390     bus_driver_handle_hello (connection, message);
391   else if (strcmp (name, DBUS_MESSAGE_LIST_SERVICES) == 0)
392     bus_driver_handle_list_services (connection, message);
393   else if (strcmp (name, DBUS_MESSAGE_ACQUIRE_SERVICE) == 0)
394     bus_driver_handle_acquire_service (connection, message);
395   else if (strcmp (name, DBUS_MESSAGE_SERVICE_EXISTS) == 0)
396     bus_driver_handle_service_exists (connection, message);
397   
398 }
399
400 void
401 bus_driver_remove_connection (DBusConnection *connection)
402 {
403   /* Does nothing for now */
404 }