driver.c revision aa4b4ef5955606659557a40a2220090ee9c108d4
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
33static void bus_driver_send_welcome_message (DBusConnection *connection,
34					     DBusMessage    *hello_message);
35
36void
37bus_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_args (message,
49					    DBUS_TYPE_STRING, service_name,
50					    0));
51  bus_dispatch_broadcast_message (message);
52  dbus_message_unref (message);
53}
54
55static void
56bus_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_args (message,
66					    DBUS_TYPE_STRING, service_name,
67					    0));
68  bus_dispatch_broadcast_message (message);
69  dbus_message_unref (message);
70}
71
72void
73bus_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_args (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
90void
91bus_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_args (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
108static dbus_bool_t
109create_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
166static void
167bus_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
195static void
196bus_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_args (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
218static void
219bus_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_args (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
246static void
247bus_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_args (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_args (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
335static void
336bus_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_args (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_args (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
369void
370bus_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
400void
401bus_driver_remove_connection (DBusConnection *connection)
402{
403  /* Does nothing for now */
404}
405