2003-04-08 Havoc Pennington <hp@redhat.com>
[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 "activation.h"
25 #include "connection.h"
26 #include "driver.h"
27 #include "dispatch.h"
28 #include "services.h"
29 #include "utils.h"
30 #include <dbus/dbus-string.h>
31 #include <dbus/dbus-internals.h>
32 #include <string.h>
33
34 static dbus_bool_t bus_driver_send_welcome_message (DBusConnection *connection,
35                                                     DBusMessage    *hello_message,
36                                                     BusTransaction *transaction,
37                                                     DBusError      *error);
38
39 dbus_bool_t
40 bus_driver_send_service_deleted (const char     *service_name,
41                                  BusTransaction *transaction,
42                                  DBusError      *error)
43 {
44   DBusMessage *message;
45   dbus_bool_t retval;
46
47   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
48   
49   _dbus_verbose ("sending service deleted: %s\n", service_name);
50
51   message = dbus_message_new (DBUS_SERVICE_BROADCAST,
52                               DBUS_MESSAGE_SERVICE_DELETED);
53   if (message == NULL)
54     {
55       BUS_SET_OOM (error);
56       return FALSE;
57     }
58   
59   if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS) ||
60       !dbus_message_append_args (message,
61                                  DBUS_TYPE_STRING, service_name,
62                                  0))
63     {
64       dbus_message_unref (message);
65       BUS_SET_OOM (error);
66       return FALSE;
67     }
68
69   retval = bus_dispatch_broadcast_message (transaction, message, error);
70   dbus_message_unref (message);
71
72   return retval;
73 }
74
75 dbus_bool_t
76 bus_driver_send_service_created (const char     *service_name,
77                                  BusTransaction *transaction,
78                                  DBusError      *error)
79 {
80   DBusMessage *message;
81   dbus_bool_t retval;
82
83   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
84   
85   message = dbus_message_new (DBUS_SERVICE_BROADCAST,
86                               DBUS_MESSAGE_SERVICE_CREATED);
87   if (message == NULL)
88     {
89       BUS_SET_OOM (error);
90       return FALSE;
91     }
92   
93   if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS))
94     {
95       dbus_message_unref (message);
96       BUS_SET_OOM (error);
97       return FALSE;
98     }
99   
100   if (!dbus_message_append_args (message,
101                                  DBUS_TYPE_STRING, service_name,
102                                  0))
103     {
104       dbus_message_unref (message);
105       BUS_SET_OOM (error);
106       return FALSE;
107     }
108   
109   retval = bus_dispatch_broadcast_message (transaction, message, error);
110   dbus_message_unref (message);
111
112   return retval;
113 }
114
115 dbus_bool_t
116 bus_driver_send_service_lost (DBusConnection *connection,
117                               const char     *service_name,
118                               BusTransaction *transaction,
119                               DBusError      *error)
120 {
121   DBusMessage *message;
122
123   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
124   
125   message = dbus_message_new (bus_connection_get_name (connection),
126                               DBUS_MESSAGE_SERVICE_LOST);
127   if (message == NULL)
128     {
129       BUS_SET_OOM (error);
130       return FALSE;
131     }
132   
133   if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS))
134     {
135       dbus_message_unref (message);
136       BUS_SET_OOM (error);
137       return FALSE;
138     }
139   
140   if (!dbus_message_append_args (message,
141                                  DBUS_TYPE_STRING, service_name,
142                                  0))
143     {
144       dbus_message_unref (message);
145       BUS_SET_OOM (error);
146       return FALSE;
147     }
148
149   if (!bus_transaction_send_message (transaction, connection, message))
150     {
151       dbus_message_unref (message);
152       BUS_SET_OOM (error);
153       return FALSE;
154     }
155   else
156     {
157       dbus_message_unref (message);
158       return TRUE;
159     }
160 }
161
162 dbus_bool_t
163 bus_driver_send_service_acquired (DBusConnection *connection,
164                                   const char     *service_name,
165                                   BusTransaction *transaction,
166                                   DBusError      *error)
167 {
168   DBusMessage *message;
169
170   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
171   
172   message = dbus_message_new (bus_connection_get_name (connection),
173                               DBUS_MESSAGE_SERVICE_ACQUIRED);
174   if (message == NULL)
175     {
176       BUS_SET_OOM (error);
177       return FALSE;
178     }
179   
180   if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS))
181     {
182       dbus_message_unref (message);
183       BUS_SET_OOM (error);
184       return FALSE;
185     }
186   
187   if (!dbus_message_append_args (message,
188                                  DBUS_TYPE_STRING, service_name,
189                                  0))
190     {
191       dbus_message_unref (message);
192       BUS_SET_OOM (error);
193       return FALSE;
194     }
195
196   if (!bus_transaction_send_message (transaction, connection, message))
197     {
198       dbus_message_unref (message);
199       BUS_SET_OOM (error);
200       return FALSE;
201     }
202   else
203     {
204       dbus_message_unref (message);
205       return TRUE;
206     }
207 }
208
209 static dbus_bool_t
210 create_unique_client_name (BusRegistry *registry,
211                            DBusString  *str)
212 {
213   /* We never want to use the same unique client name twice, because
214    * we want to guarantee that if you send a message to a given unique
215    * name, you always get the same application. So we use two numbers
216    * for INT_MAX * INT_MAX combinations, should be pretty safe against
217    * wraparound.
218    */
219   static int next_major_number = 0;
220   static int next_minor_number = 0;
221   int len;
222
223   len = _dbus_string_get_length (str);
224   
225   while (TRUE)
226     {
227       /* start out with 1-0, go to 1-1, 1-2, 1-3,
228        * up to 1-MAXINT, then 2-0, 2-1, etc.
229        */
230       if (next_minor_number <= 0)
231         {
232           next_major_number += 1;
233           next_minor_number = 0;
234           if (next_major_number <= 0)
235             _dbus_assert_not_reached ("INT_MAX * INT_MAX clients were added");
236         }
237
238       _dbus_assert (next_major_number > 0);
239       _dbus_assert (next_minor_number >= 0);
240
241       /* appname:MAJOR-MINOR */
242       
243       if (!_dbus_string_append (str, ":"))
244         return FALSE;
245       
246       if (!_dbus_string_append_int (str, next_major_number))
247         return FALSE;
248
249       if (!_dbus_string_append (str, "-"))
250         return FALSE;
251       
252       if (!_dbus_string_append_int (str, next_minor_number))
253         return FALSE;
254
255       next_minor_number += 1;
256       
257       /* Check if a client with the name exists */
258       if (bus_registry_lookup (registry, str) == NULL)
259         break;
260
261       /* drop the number again, try the next one. */
262       _dbus_string_set_length (str, len);
263     }
264
265   return TRUE;
266 }
267
268 static dbus_bool_t
269 bus_driver_handle_hello (DBusConnection *connection,
270                          BusTransaction *transaction,
271                          DBusMessage    *message,
272                          DBusError      *error)
273 {
274   DBusString unique_name;
275   BusService *service;
276   dbus_bool_t retval;
277   BusRegistry *registry;
278
279   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
280   
281   if (!_dbus_string_init (&unique_name))
282     {
283       BUS_SET_OOM (error);
284       return FALSE;
285     }
286
287   retval = FALSE;
288
289   registry = bus_connection_get_registry (connection);
290   
291   if (!create_unique_client_name (registry, &unique_name))
292     {
293       BUS_SET_OOM (error);
294       goto out_0;
295     }
296
297   if (!bus_connection_set_name (connection, &unique_name))
298     {
299       BUS_SET_OOM (error);
300       goto out_0;
301     }
302   
303   if (!dbus_message_set_sender (message,
304                                 bus_connection_get_name (connection)))
305     {
306       BUS_SET_OOM (error);
307       goto out_0;
308     }
309   
310   if (!bus_driver_send_welcome_message (connection, message, transaction, error))
311     goto out_0;
312
313   /* Create the service */
314   service = bus_registry_ensure (registry,
315                                  &unique_name, connection, transaction, error);
316   if (service == NULL)
317     goto out_0;
318   
319   bus_service_set_prohibit_replacement (service, TRUE);
320
321   retval = TRUE;
322   
323  out_0:
324   _dbus_string_free (&unique_name);
325   return retval;
326 }
327
328 static dbus_bool_t
329 bus_driver_send_welcome_message (DBusConnection *connection,
330                                  DBusMessage    *hello_message,
331                                  BusTransaction *transaction,
332                                  DBusError      *error)
333 {
334   DBusMessage *welcome;
335   const char *name;
336
337   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
338   
339   name = bus_connection_get_name (connection);
340   _dbus_assert (name != NULL);
341   
342   welcome = dbus_message_new_reply (hello_message);
343   if (welcome == NULL)
344     {
345       BUS_SET_OOM (error);
346       return FALSE;
347     }
348   
349   if (!dbus_message_set_sender (welcome, DBUS_SERVICE_DBUS))
350     {
351       dbus_message_unref (welcome);
352       BUS_SET_OOM (error);
353       return FALSE;
354     }
355   
356   if (!dbus_message_append_args (welcome,
357                                  DBUS_TYPE_STRING, name,
358                                  NULL))
359     {
360       dbus_message_unref (welcome);
361       BUS_SET_OOM (error);
362       return FALSE;
363     }
364
365   if (!bus_transaction_send_message (transaction, connection, welcome))
366     {
367       dbus_message_unref (welcome);
368       BUS_SET_OOM (error);
369       return FALSE;
370     }
371   else
372     {
373       dbus_message_unref (welcome);
374       return TRUE;
375     }
376 }
377
378 static dbus_bool_t
379 bus_driver_handle_list_services (DBusConnection *connection,
380                                  BusTransaction *transaction,
381                                  DBusMessage    *message,
382                                  DBusError      *error)
383 {
384   DBusMessage *reply;
385   int len;
386   char **services;
387   BusRegistry *registry;
388
389   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
390   
391   registry = bus_connection_get_registry (connection);
392   
393   reply = dbus_message_new_reply (message);
394   if (reply == NULL)
395     {
396       BUS_SET_OOM (error);
397       return FALSE;
398     }
399
400   if (!bus_registry_list_services (registry, &services, &len))
401     {
402       dbus_message_unref (reply);
403       BUS_SET_OOM (error);
404       return FALSE;
405     }
406   
407   if (!dbus_message_append_args (reply,
408                                  DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, services, len,
409                                  0))
410     {
411       dbus_free_string_array (services);
412       dbus_message_unref (reply);
413       BUS_SET_OOM (error);
414       return FALSE;
415     }
416
417   dbus_free_string_array (services);
418   
419   if (!bus_transaction_send_message (transaction, connection, reply))
420     {
421       dbus_message_unref (reply);
422       BUS_SET_OOM (error);
423       return FALSE;
424     }
425   else
426     {
427       dbus_message_unref (reply);
428       return TRUE;
429     }
430 }
431
432 static dbus_bool_t
433 bus_driver_handle_acquire_service (DBusConnection *connection,
434                                    BusTransaction *transaction,
435                                    DBusMessage    *message,
436                                    DBusError      *error)
437 {
438   DBusMessage *reply;
439   DBusString service_name;
440   BusService *service;  
441   char *name;
442   int service_reply;
443   int flags;
444   dbus_bool_t retval;
445   DBusConnection *old_owner;
446   DBusConnection *current_owner;
447   BusRegistry *registry;
448
449   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
450   
451   registry = bus_connection_get_registry (connection);
452   
453   if (!dbus_message_get_args (message, error,
454                               DBUS_TYPE_STRING, &name,
455                               DBUS_TYPE_UINT32, &flags,
456                               0))
457     return FALSE;
458   
459   _dbus_verbose ("Trying to own service %s with flags 0x%x\n", name, flags);
460   
461   retval = FALSE;
462   reply = NULL;
463   
464   if (*name == ':')
465     {
466       /* Not allowed; only base services can start with ':' */
467       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
468                       "Cannot acquire a service starting with ':' such as \"%s\"",
469                       name);
470       
471       goto out;
472     }
473
474   _dbus_string_init_const (&service_name, name);
475   
476   service = bus_registry_lookup (registry, &service_name);
477
478   if (service != NULL)
479     old_owner = bus_service_get_primary_owner (service);
480   else
481     old_owner = NULL;  
482   
483   reply = dbus_message_new_reply (message);
484   if (reply == NULL)
485     {
486       BUS_SET_OOM (error);
487       goto out;
488     }
489
490   if (!dbus_message_set_sender (reply, DBUS_SERVICE_DBUS))
491     {
492       BUS_SET_OOM (error);
493       goto out;
494     }
495       
496   if (service == NULL)
497     {
498       service = bus_registry_ensure (registry,
499                                      &service_name, connection, transaction, error);
500       if (service == NULL)
501         goto out;
502     }
503
504   current_owner = bus_service_get_primary_owner (service);
505
506   if (old_owner == NULL)
507     {
508       _dbus_assert (current_owner == connection);
509
510       bus_service_set_prohibit_replacement (service,
511                                             (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));      
512                         
513       service_reply = DBUS_SERVICE_REPLY_PRIMARY_OWNER;      
514     }
515   else if (old_owner == connection)
516     service_reply = DBUS_SERVICE_REPLY_ALREADY_OWNER;
517   else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING)))
518     service_reply = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
519   else if (bus_service_get_prohibit_replacement (service))
520     {
521       /* Queue the connection */
522       if (!bus_service_add_owner (service, connection,
523                                   transaction, error))
524         goto out;
525       
526       service_reply = DBUS_SERVICE_REPLY_IN_QUEUE;
527     }
528   else
529     {
530       /* Replace the current owner */
531
532       /* We enqueue the new owner and remove the first one because
533        * that will cause ServiceAcquired and ServiceLost messages to
534        * be sent.
535        */
536       
537       /* FIXME this is broken, if the remove_owner fails
538        * we don't undo the add_owner
539        * (easiest fix is probably to move all this to
540        * services.c and have a single routine for it)
541        */
542       
543       if (!bus_service_add_owner (service, connection,
544                                   transaction, error))
545         goto out;
546       
547       if (!bus_service_remove_owner (service, old_owner,
548                                      transaction, error))
549         goto out;
550       
551       _dbus_assert (connection == bus_service_get_primary_owner (service));
552       service_reply = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
553     }
554
555   if (!dbus_message_append_args (reply, DBUS_TYPE_UINT32, service_reply, 0))
556     {
557       BUS_SET_OOM (error);
558       goto out;
559     }
560
561   if (!bus_transaction_send_message (transaction, connection, reply))
562     {
563       BUS_SET_OOM (error);
564       goto out;
565     }
566
567   retval = TRUE;
568   
569  out:
570   dbus_free (name);
571   if (reply)
572     dbus_message_unref (reply);
573   return retval;
574
575
576 static dbus_bool_t
577 bus_driver_handle_service_exists (DBusConnection *connection,
578                                   BusTransaction *transaction,
579                                   DBusMessage    *message,
580                                   DBusError      *error)
581 {
582   DBusMessage *reply;
583   DBusString service_name;
584   BusService *service;
585   char *name;
586   dbus_bool_t retval;
587   BusRegistry *registry;
588
589   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
590   
591   registry = bus_connection_get_registry (connection);
592   
593   if (!dbus_message_get_args (message, error,
594                               DBUS_TYPE_STRING, &name,
595                               0))
596     return FALSE;
597
598   retval = FALSE;
599   
600   _dbus_string_init_const (&service_name, name);
601   service = bus_registry_lookup (registry, &service_name);
602  
603   reply = dbus_message_new_reply (message);
604   if (reply == NULL)
605     {
606       BUS_SET_OOM (error);
607       goto out;
608     }
609   
610   if (!dbus_message_set_sender (reply, DBUS_SERVICE_DBUS))
611     {
612       BUS_SET_OOM (error);
613       goto out;
614     }
615
616   if (!dbus_message_append_args (reply,
617                                  DBUS_TYPE_UINT32, service != NULL,
618                                  0))
619     {
620       BUS_SET_OOM (error);
621       goto out;
622     }
623
624   if (!bus_transaction_send_message (transaction, connection, reply))
625     {
626       BUS_SET_OOM (error);
627       goto out;
628     }
629
630   retval = TRUE;
631   
632  out:
633   if (reply)
634     dbus_message_unref (reply);
635   dbus_free (name);
636
637   return retval;
638 }
639
640 static dbus_bool_t
641 bus_driver_handle_activate_service (DBusConnection *connection,
642                                     BusTransaction *transaction,
643                                     DBusMessage    *message,
644                                     DBusError      *error)
645 {
646   dbus_uint32_t flags;
647   char *name;
648   dbus_bool_t retval;
649   BusActivation *activation;
650
651   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
652   
653   activation = bus_connection_get_activation (connection);
654   
655   if (!dbus_message_get_args (message, error,
656                               DBUS_TYPE_STRING, &name,
657                               DBUS_TYPE_UINT32, &flags,
658                               0))
659     return FALSE;
660
661   retval = FALSE;
662
663   if (!bus_activation_activate_service (activation, connection, transaction,
664                                         message, name, error))
665     goto out;
666
667   retval = TRUE;
668   
669  out:
670   dbus_free (name);
671   return retval;
672 }
673
674 /* For speed it might be useful to sort this in order of
675  * frequency of use (but doesn't matter with only a few items
676  * anyhow)
677  */
678 struct
679 {
680   const char *name;
681   dbus_bool_t (* handler) (DBusConnection *connection,
682                            BusTransaction *transaction,
683                            DBusMessage    *message,
684                            DBusError      *error);
685 } message_handlers[] = {
686   { DBUS_MESSAGE_ACQUIRE_SERVICE, bus_driver_handle_acquire_service },
687   { DBUS_MESSAGE_ACTIVATE_SERVICE, bus_driver_handle_activate_service },
688   { DBUS_MESSAGE_HELLO, bus_driver_handle_hello },
689   { DBUS_MESSAGE_SERVICE_EXISTS, bus_driver_handle_service_exists },
690   { DBUS_MESSAGE_LIST_SERVICES, bus_driver_handle_list_services }
691 };
692
693 dbus_bool_t
694 bus_driver_handle_message (DBusConnection *connection,
695                            BusTransaction *transaction,
696                            DBusMessage    *message,
697                            DBusError      *error)
698 {
699   const char *name, *sender;
700   int i;
701
702   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
703   
704   _dbus_verbose ("Driver got a message: %s\n",
705                  dbus_message_get_name (message));
706   
707   name = dbus_message_get_name (message);
708   sender = dbus_message_get_sender (message);
709
710   if (sender == NULL && (strcmp (name, DBUS_MESSAGE_HELLO) != 0))
711     {
712       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
713                       "Client tried to send a message other than %s without being registered",
714                       DBUS_MESSAGE_HELLO);
715
716       dbus_connection_disconnect (connection);
717       return FALSE;
718     }
719
720   i = 0;
721   while (i < _DBUS_N_ELEMENTS (message_handlers))
722     {
723       if (strcmp (message_handlers[i].name, name) == 0)
724         {
725           if ((* message_handlers[i].handler) (connection, transaction, message, error))
726             return TRUE;
727           else
728             return FALSE;
729         }
730       
731       ++i;
732     }
733
734   dbus_set_error (error, DBUS_ERROR_UNKNOWN_MESSAGE,
735                   "%s does not understand message %s",
736                   DBUS_SERVICE_DBUS, name);
737   
738   return FALSE;
739 }
740
741 void
742 bus_driver_remove_connection (DBusConnection *connection)
743 {
744   /* FIXME Does nothing for now, should unregister the connection
745    * with the bus driver.
746    */
747 }