driver.c revision d5ad082fecbbd803fd89b3574ac137b3fa964bc7
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 <dbus/dbus-string.h>
29#include <dbus/dbus-internals.h>
30#include <string.h>
31
32static void bus_driver_send_welcome_message (DBusConnection *connection,
33					     DBusMessage    *hello_message);
34
35static void
36bus_driver_send_service_deleted (DBusConnection *connection, const char *name)
37{
38  DBusMessage *message;
39
40  _dbus_verbose ("sending service deleted: %s\n", name);
41
42  _DBUS_HANDLE_OOM (message = dbus_message_new (DBUS_SERVICE_BROADCAST,
43						DBUS_MESSAGE_SERVICE_DELETED));
44
45  _DBUS_HANDLE_OOM (dbus_message_set_sender (message, DBUS_SERVICE_DBUS));
46
47  _DBUS_HANDLE_OOM (dbus_message_append_fields (message,
48						DBUS_TYPE_STRING, name,
49						0));
50  bus_dispatch_broadcast_message (message);
51  dbus_message_unref (message);
52}
53
54static void
55bus_driver_send_service_created (DBusConnection *connection, const char *name)
56{
57  DBusMessage *message;
58
59  _DBUS_HANDLE_OOM (message = dbus_message_new (DBUS_SERVICE_BROADCAST,
60						DBUS_MESSAGE_SERVICE_CREATED));
61
62  _DBUS_HANDLE_OOM (dbus_message_set_sender (message, DBUS_SERVICE_DBUS));
63
64  _DBUS_HANDLE_OOM (dbus_message_append_fields (message,
65						DBUS_TYPE_STRING, name,
66						0));
67  bus_dispatch_broadcast_message (message);
68  dbus_message_unref (message);
69}
70
71static dbus_bool_t
72create_unique_client_name (const char *name,
73                           DBusString *str)
74{
75  /* We never want to use the same unique client name twice, because
76   * we want to guarantee that if you send a message to a given unique
77   * name, you always get the same application. So we use two numbers
78   * for INT_MAX * INT_MAX combinations, should be pretty safe against
79   * wraparound.
80   */
81  static int next_major_number = 0;
82  static int next_minor_number = 0;
83  int len;
84
85  if (!_dbus_string_append (str, name))
86    return FALSE;
87
88  len = _dbus_string_get_length (str);
89
90  while (TRUE)
91    {
92      /* start out with 1-0, go to 1-1, 1-2, 1-3,
93       * up to 1-MAXINT, then 2-0, 2-1, etc.
94       */
95      if (next_minor_number <= 0)
96        {
97          next_major_number += 1;
98          next_minor_number = 0;
99          if (next_major_number <= 0)
100            _dbus_assert_not_reached ("INT_MAX * INT_MAX clients were added");
101        }
102
103      _dbus_assert (next_major_number > 0);
104      _dbus_assert (next_minor_number >= 0);
105
106      /* appname:MAJOR-MINOR */
107
108      if (!_dbus_string_append (str, ":"))
109        return FALSE;
110
111      if (!_dbus_string_append_int (str, next_major_number))
112        return FALSE;
113
114      if (!_dbus_string_append (str, "-"))
115        return FALSE;
116
117      if (!_dbus_string_append_int (str, next_minor_number))
118        return FALSE;
119
120      next_minor_number += 1;
121
122      /* Check if a client with the name exists */
123      if (bus_service_lookup (str, FALSE) == NULL)
124	break;
125
126      /* drop the number again, try the next one. */
127      _dbus_string_set_length (str, len);
128    }
129
130  return TRUE;
131}
132
133static void
134bus_driver_handle_hello (DBusConnection *connection,
135			 DBusMessage    *message)
136{
137  DBusResultCode result;
138  char *name;
139  DBusString unique_name;
140  BusService *service;
141
142  _DBUS_HANDLE_OOM ((result = dbus_message_get_fields (message,
143						       DBUS_TYPE_STRING, &name,
144						       0)) != DBUS_RESULT_NO_MEMORY);
145
146  if (result != DBUS_RESULT_SUCCESS)
147    {
148      dbus_free (name);
149      dbus_connection_disconnect (connection);
150    }
151
152  _DBUS_HANDLE_OOM (_dbus_string_init (&unique_name, _DBUS_INT_MAX));
153
154  _DBUS_HANDLE_OOM (create_unique_client_name (name, &unique_name));
155
156  dbus_free (name);
157
158  /* Create the service */
159  _DBUS_HANDLE_OOM (service = bus_service_lookup (&unique_name, TRUE));
160
161  /* Add the connection as the owner */
162  _DBUS_HANDLE_OOM (bus_service_add_owner (service, connection));
163  _DBUS_HANDLE_OOM (bus_connection_set_name (connection, &unique_name));
164
165  _DBUS_HANDLE_OOM (dbus_message_set_sender (message,
166					     bus_connection_get_name (connection)));
167
168  _dbus_string_free (&unique_name);
169
170  _DBUS_HANDLE_OOM (bus_driver_send_welcome_message (connection, message));
171
172  /* Broadcast a service created message */
173  bus_driver_send_service_created (connection, bus_service_get_name (service));
174}
175
176static void
177bus_driver_send_welcome_message (DBusConnection *connection,
178				 DBusMessage    *hello_message)
179{
180  DBusMessage *welcome;
181  const char *name;
182
183  name = bus_connection_get_name (connection);
184  _dbus_assert (name != NULL);
185
186  _DBUS_HANDLE_OOM (welcome = dbus_message_new_reply (DBUS_MESSAGE_WELCOME,
187						      hello_message));
188
189  _DBUS_HANDLE_OOM (dbus_message_set_sender (welcome, DBUS_SERVICE_DBUS));
190
191  _DBUS_HANDLE_OOM (dbus_message_append_fields (welcome,
192						DBUS_TYPE_STRING, name,
193						NULL));
194
195  _DBUS_HANDLE_OOM (dbus_connection_send_message (connection, welcome, NULL, NULL));
196
197  dbus_message_unref (welcome);
198}
199
200static void
201bus_driver_handle_list_services (DBusConnection *connection,
202				 DBusMessage    *message)
203{
204  DBusMessage *reply;
205  int len, i;
206  char **services;
207
208  _DBUS_HANDLE_OOM (reply = dbus_message_new_reply (DBUS_MESSAGE_SERVICES, message));
209
210  _DBUS_HANDLE_OOM (services = bus_services_list (&len));
211
212  _DBUS_HANDLE_OOM (dbus_message_append_fields (reply,
213						DBUS_TYPE_STRING_ARRAY, services, len,
214						0));
215
216  _DBUS_HANDLE_OOM (dbus_connection_send_message (connection, reply, NULL, NULL));
217
218  dbus_message_unref (reply);
219
220  if (services != NULL)
221    {
222      for (i = 0; i < len; i++)
223        dbus_free (services[i]);
224      dbus_free (services);
225    }
226}
227
228void
229bus_driver_remove_connection (DBusConnection *connection)
230{
231  BusService *service;
232  DBusString service_name;
233  const char *name;
234
235  name = bus_connection_get_name (connection);
236
237  if (name == NULL)
238    return;
239
240  _dbus_string_init_const (&service_name, name);
241
242  service = bus_service_lookup (&service_name, FALSE);
243
244  bus_driver_send_service_deleted (connection, name);
245
246  if (service)
247    bus_service_free (service);
248}
249
250void
251bus_driver_handle_message (DBusConnection *connection,
252			   DBusMessage    *message)
253{
254  const char *name, *sender;
255
256  _dbus_verbose ("Driver got a message: %s\n",
257		 dbus_message_get_name (message));
258
259  name = dbus_message_get_name (message);
260  sender = dbus_message_get_sender (message);
261
262  if (sender == NULL && (strcmp (name, DBUS_MESSAGE_HELLO) != 0))
263    {
264      _dbus_verbose ("Trying to send a message without being registered. Disconnecting.\n");
265      dbus_connection_disconnect (connection);
266      return;
267    }
268
269  /* Now check names. */
270  if (strcmp (name, DBUS_MESSAGE_HELLO) == 0)
271    bus_driver_handle_hello (connection, message);
272  else if (strcmp (name, DBUS_MESSAGE_LIST_SERVICES) == 0)
273    bus_driver_handle_list_services (connection, message);
274}
275