2003-11-26 Mikael Hallendal <micke@imendio.com>
[platform/upstream/dbus.git] / bus / services.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* services.c  Service management
3  *
4  * Copyright (C) 2003  Red Hat, Inc.
5  * Copyright (C) 2003  CodeFactory AB
6  *
7  * Licensed under the Academic Free License version 1.2
8  * 
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24 #include <dbus/dbus-hash.h>
25 #include <dbus/dbus-list.h>
26 #include <dbus/dbus-mempool.h>
27
28 #include "driver.h"
29 #include "services.h"
30 #include "connection.h"
31 #include "utils.h"
32 #include "activation.h"
33 #include "policy.h"
34
35 struct BusService
36 {
37   int refcount;
38
39   BusRegistry *registry;
40   char *name;
41   DBusList *owners;
42   
43   unsigned int prohibit_replacement : 1;
44 };
45
46 struct BusRegistry
47 {
48   int refcount;
49
50   BusContext *context;
51   
52   DBusHashTable *service_hash;
53   DBusMemPool   *service_pool;
54 };
55
56 BusRegistry*
57 bus_registry_new (BusContext *context)
58 {
59   BusRegistry *registry;
60
61   registry = dbus_new0 (BusRegistry, 1);
62   if (registry == NULL)
63     return NULL;
64
65   registry->refcount = 1;
66   registry->context = context;
67   
68   registry->service_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
69                                                  NULL, NULL);
70   if (registry->service_hash == NULL)
71     goto failed;
72   
73   registry->service_pool = _dbus_mem_pool_new (sizeof (BusService),
74                                                TRUE);
75   if (registry->service_pool == NULL)
76     goto failed;
77
78   return registry;
79
80  failed:
81   bus_registry_unref (registry);
82   return NULL;
83 }
84
85 BusRegistry *
86 bus_registry_ref (BusRegistry *registry)
87 {
88   _dbus_assert (registry->refcount > 0);
89   registry->refcount += 1;
90
91   return registry;
92 }
93
94 void
95 bus_registry_unref  (BusRegistry *registry)
96 {
97   _dbus_assert (registry->refcount > 0);
98   registry->refcount -= 1;
99
100   if (registry->refcount == 0)
101     {
102       if (registry->service_hash)
103         _dbus_hash_table_unref (registry->service_hash);
104       if (registry->service_pool)
105         _dbus_mem_pool_free (registry->service_pool);
106
107       dbus_free (registry);
108     }
109 }
110
111 BusService*
112 bus_registry_lookup (BusRegistry      *registry,
113                      const DBusString *service_name)
114 {
115   BusService *service;
116
117   service = _dbus_hash_table_lookup_string (registry->service_hash,
118                                             _dbus_string_get_const_data (service_name));
119
120   return service;
121 }
122
123 BusService*
124 bus_registry_ensure (BusRegistry               *registry,
125                      const DBusString          *service_name,
126                      DBusConnection            *owner_if_created,
127                      BusTransaction            *transaction,
128                      DBusError                 *error)
129 {
130   BusService *service;
131
132   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
133   
134   _dbus_assert (owner_if_created != NULL);
135   _dbus_assert (transaction != NULL);
136
137   service = _dbus_hash_table_lookup_string (registry->service_hash,
138                                             _dbus_string_get_const_data (service_name));
139   if (service != NULL)
140     return service;
141   
142   service = _dbus_mem_pool_alloc (registry->service_pool);
143   if (service == NULL)
144     {
145       BUS_SET_OOM (error);
146       return NULL;
147     }
148
149   service->registry = registry;  
150   service->refcount = 1;
151   
152   if (!_dbus_string_copy_data (service_name, &service->name))
153     {
154       _dbus_mem_pool_dealloc (registry->service_pool, service);
155       BUS_SET_OOM (error);
156       return NULL;
157     }
158
159   if (!bus_driver_send_service_created (service->name, transaction, error))
160     {
161       bus_service_unref (service);
162       return NULL;
163     }
164
165   if (!bus_activation_service_created (bus_context_get_activation (registry->context),
166                                        service->name, transaction, error))
167     {
168       bus_service_unref (service);
169       return NULL;
170     }
171   
172   if (!bus_service_add_owner (service, owner_if_created,
173                               transaction, error))
174     {
175       bus_service_unref (service);
176       return NULL;
177     }
178   
179   if (!_dbus_hash_table_insert_string (registry->service_hash,
180                                        service->name,
181                                        service))
182     {
183       /* The add_owner gets reverted on transaction cancel */
184       BUS_SET_OOM (error);
185       return NULL;
186     }
187   
188   return service;
189 }
190
191 void
192 bus_registry_foreach (BusRegistry               *registry,
193                       BusServiceForeachFunction  function,
194                       void                      *data)
195 {
196   DBusHashIter iter;
197   
198   _dbus_hash_iter_init (registry->service_hash, &iter);
199   while (_dbus_hash_iter_next (&iter))
200     {
201       BusService *service = _dbus_hash_iter_get_value (&iter);
202
203       (* function) (service, data);
204     }
205 }
206
207 dbus_bool_t
208 bus_registry_list_services (BusRegistry *registry,
209                             char      ***listp,
210                             int         *array_len)
211 {
212   int i, j, len;
213   char **retval;
214   DBusHashIter iter;
215    
216   len = _dbus_hash_table_get_n_entries (registry->service_hash);
217   retval = dbus_new (char *, len + 1);
218
219   if (retval == NULL)
220     return FALSE;
221
222   _dbus_hash_iter_init (registry->service_hash, &iter);
223   i = 0;
224   while (_dbus_hash_iter_next (&iter))
225     {
226       BusService *service = _dbus_hash_iter_get_value (&iter);
227
228       retval[i] = _dbus_strdup (service->name);
229       if (retval[i] == NULL)
230         goto error;
231
232       i++;
233     }
234
235   retval[i] = NULL;
236   
237   if (array_len)
238     *array_len = len;
239   
240   *listp = retval;
241   return TRUE;
242   
243  error:
244   for (j = 0; j < i; j++)
245     dbus_free (retval[i]);
246   dbus_free (retval);
247
248   return FALSE;
249 }
250
251 dbus_bool_t
252 bus_registry_acquire_service (BusRegistry      *registry,
253                               DBusConnection   *connection,
254                               const DBusString *service_name,
255                               dbus_uint32_t     flags,
256                               dbus_uint32_t    *result,
257                               BusTransaction   *transaction,
258                               DBusError        *error)
259 {
260   dbus_bool_t retval;
261   DBusConnection *old_owner;
262   DBusConnection *current_owner;
263   BusClientPolicy *policy;
264   BusService *service;
265   
266   retval = FALSE;
267
268   if (_dbus_string_get_length (service_name) == 0)
269     {
270       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
271                       "Zero-length service name is not allowed");
272       
273       _dbus_verbose ("Attempt to acquire zero-length service name\n");
274       
275       goto out;
276     }
277   
278   if (_dbus_string_get_byte (service_name, 0) == ':')
279     {
280       /* Not allowed; only base services can start with ':' */
281       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
282                       "Cannot acquire a service starting with ':' such as \"%s\"",
283                       _dbus_string_get_const_data (service_name));
284       
285       _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"",
286                      _dbus_string_get_const_data (service_name));
287       
288       goto out;
289     }
290
291   policy = bus_connection_get_policy (connection);
292   _dbus_assert (policy != NULL);
293
294   if (!bus_client_policy_check_can_own (policy, connection,
295                                         service_name))
296     {
297       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
298                       "Connection \"%s\" is not allowed to own the service \"%s\" due "
299                       "to security policies in the configuration file",
300                       bus_connection_is_active (connection) ?
301                       bus_connection_get_name (connection) :
302                       "(inactive)");
303       goto out;
304     }
305
306   if (bus_connection_get_n_services_owned (connection) >=
307       bus_context_get_max_services_per_connection (registry->context))
308     {
309       dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
310                       "Connection \"%s\" is not allowed to own more services "
311                       "(increase limits in configuration file if required)",
312                       bus_connection_is_active (connection) ?
313                       bus_connection_get_name (connection) :
314                       "(inactive)");
315       goto out;
316     }
317   
318   service = bus_registry_lookup (registry, service_name);
319
320   if (service != NULL)
321     old_owner = bus_service_get_primary_owner (service);
322   else
323     old_owner = NULL;
324       
325   if (service == NULL)
326     {
327       service = bus_registry_ensure (registry,
328                                      service_name, connection, transaction, error);
329       if (service == NULL)
330         goto out;
331     }
332
333   current_owner = bus_service_get_primary_owner (service);
334
335   if (old_owner == NULL)
336     {
337       _dbus_assert (current_owner == connection);
338
339       bus_service_set_prohibit_replacement (service,
340                                             (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));      
341                         
342       *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;      
343     }
344   else if (old_owner == connection)
345     *result = DBUS_SERVICE_REPLY_ALREADY_OWNER;
346   else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING)))
347     *result = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
348   else if (bus_service_get_prohibit_replacement (service))
349     {
350       /* Queue the connection */
351       if (!bus_service_add_owner (service, connection,
352                                   transaction, error))
353         goto out;
354       
355       *result = DBUS_SERVICE_REPLY_IN_QUEUE;
356     }
357   else
358     {
359       /* Replace the current owner */
360
361       /* We enqueue the new owner and remove the first one because
362        * that will cause ServiceAcquired and ServiceLost messages to
363        * be sent.
364        */
365       
366       if (!bus_service_add_owner (service, connection,
367                                   transaction, error))
368         goto out;
369
370       if (!bus_service_remove_owner (service, old_owner,
371                                      transaction, error))
372         goto out;
373       
374       _dbus_assert (connection == bus_service_get_primary_owner (service));
375       *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
376     }
377
378   retval = TRUE;
379   
380  out:
381   return retval;
382 }
383
384 static void
385 bus_service_unlink_owner (BusService      *service,
386                           DBusConnection  *owner)
387 {
388   _dbus_list_remove_last (&service->owners, owner);
389   bus_connection_remove_owned_service (owner, service);
390 }
391
392 static void
393 bus_service_unlink (BusService *service)
394 {
395   _dbus_assert (service->owners == NULL);
396
397   /* the service may not be in the hash, if
398    * the failure causing transaction cancel
399    * was in the right place, but that's OK
400    */
401   _dbus_hash_table_remove_string (service->registry->service_hash,
402                                   service->name);
403   
404   bus_service_unref (service);
405 }
406
407 static void
408 bus_service_relink (BusService           *service,
409                     DBusPreallocatedHash *preallocated)
410 {
411   _dbus_assert (service->owners == NULL);
412   _dbus_assert (preallocated != NULL);
413
414   _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
415                                                preallocated,
416                                                service->name,
417                                                service);
418   
419   bus_service_ref (service);
420 }
421
422 /**
423  * Data used to represent an ownership cancellation in
424  * a bus transaction.
425  */
426 typedef struct
427 {
428   DBusConnection *connection; /**< the connection */
429   BusService *service;        /**< service to cancel ownership of */
430 } OwnershipCancelData;
431
432 static void
433 cancel_ownership (void *data)
434 {
435   OwnershipCancelData *d = data;
436
437   /* We don't need to send messages notifying of these
438    * changes, since we're reverting something that was
439    * cancelled (effectively never really happened)
440    */
441   bus_service_unlink_owner (d->service, d->connection);
442   
443   if (d->service->owners == NULL)
444     bus_service_unlink (d->service);
445 }
446
447 static void
448 free_ownership_cancel_data (void *data)
449 {
450   OwnershipCancelData *d = data;
451
452   dbus_connection_unref (d->connection);
453   bus_service_unref (d->service);
454   
455   dbus_free (d);
456 }
457
458 static dbus_bool_t
459 add_cancel_ownership_to_transaction (BusTransaction *transaction,
460                                      BusService     *service,
461                                      DBusConnection *connection)
462 {
463   OwnershipCancelData *d;
464
465   d = dbus_new (OwnershipCancelData, 1);
466   if (d == NULL)
467     return FALSE;
468   
469   d->service = service;
470   d->connection = connection;
471
472   if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
473                                         free_ownership_cancel_data))
474     {
475       dbus_free (d);
476       return FALSE;
477     }
478
479   bus_service_ref (d->service);
480   dbus_connection_ref (d->connection);
481   
482   return TRUE;
483 }
484
485 /* this function is self-cancelling if you cancel the transaction */
486 dbus_bool_t
487 bus_service_add_owner (BusService     *service,
488                        DBusConnection *owner,
489                        BusTransaction *transaction,
490                        DBusError      *error)
491 {
492   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
493   
494  /* Send service acquired message first, OOM will result
495   * in cancelling the transaction
496   */
497   if (service->owners == NULL)
498     {
499       if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
500         return FALSE;
501     }
502   
503   if (!_dbus_list_append (&service->owners,
504                           owner))
505     {
506       BUS_SET_OOM (error);
507       return FALSE;
508     }
509
510   if (!bus_connection_add_owned_service (owner, service))
511     {
512       _dbus_list_remove_last (&service->owners, owner);
513       BUS_SET_OOM (error);
514       return FALSE;
515     }
516
517   if (!add_cancel_ownership_to_transaction (transaction,
518                                             service,
519                                             owner))
520     {
521       bus_service_unlink_owner (service, owner);
522       BUS_SET_OOM (error);
523       return FALSE;
524     }
525   
526   return TRUE;
527 }
528
529 typedef struct
530 {
531   DBusConnection *connection;
532   BusService     *service;
533   DBusConnection *before_connection; /* restore to position before this connection in owners list */
534   DBusList       *connection_link;
535   DBusList       *service_link;
536   DBusPreallocatedHash *hash_entry;
537 } OwnershipRestoreData;
538
539 static void
540 restore_ownership (void *data)
541 {
542   OwnershipRestoreData *d = data;
543   DBusList *link;
544
545   _dbus_assert (d->service_link != NULL);
546   _dbus_assert (d->connection_link != NULL);
547   
548   if (d->service->owners == NULL)
549     {
550       _dbus_assert (d->hash_entry != NULL);
551       bus_service_relink (d->service, d->hash_entry);
552     }
553   else
554     {
555       _dbus_assert (d->hash_entry == NULL);
556     }
557   
558   /* We don't need to send messages notifying of these
559    * changes, since we're reverting something that was
560    * cancelled (effectively never really happened)
561    */
562   link = _dbus_list_get_first_link (&d->service->owners);
563   while (link != NULL)
564     {
565       if (link->data == d->before_connection)
566         break;
567
568       link = _dbus_list_get_next_link (&d->service->owners, link);
569     }
570   
571   _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
572
573   /* Note that removing then restoring this changes the order in which
574    * ServiceDeleted messages are sent on destruction of the
575    * connection.  This should be OK as the only guarantee there is
576    * that the base service is destroyed last, and we never even
577    * tentatively remove the base service.
578    */
579   bus_connection_add_owned_service_link (d->connection, d->service_link);
580   
581   d->hash_entry = NULL;
582   d->service_link = NULL;
583   d->connection_link = NULL;
584 }
585
586 static void
587 free_ownership_restore_data (void *data)
588 {
589   OwnershipRestoreData *d = data;
590
591   if (d->service_link)
592     _dbus_list_free_link (d->service_link);
593   if (d->connection_link)
594     _dbus_list_free_link (d->connection_link);
595   if (d->hash_entry)
596     _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
597                                               d->hash_entry);
598
599   dbus_connection_unref (d->connection);
600   bus_service_unref (d->service);
601   
602   dbus_free (d);
603 }
604
605 static dbus_bool_t
606 add_restore_ownership_to_transaction (BusTransaction *transaction,
607                                       BusService     *service,
608                                       DBusConnection *connection)
609 {
610   OwnershipRestoreData *d;
611   DBusList *link;
612
613   d = dbus_new (OwnershipRestoreData, 1);
614   if (d == NULL)
615     return FALSE;
616   
617   d->service = service;
618   d->connection = connection;
619   d->service_link = _dbus_list_alloc_link (service);
620   d->connection_link = _dbus_list_alloc_link (connection);
621   d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
622   
623   bus_service_ref (d->service);
624   dbus_connection_ref (d->connection);
625
626   d->before_connection = NULL;
627   link = _dbus_list_get_first_link (&service->owners);
628   while (link != NULL)
629     {
630       if (link->data == connection)
631         {
632           link = _dbus_list_get_next_link (&service->owners, link);
633
634           if (link)
635             d->before_connection = link->data;
636
637           break;
638         }
639       
640       link = _dbus_list_get_next_link (&service->owners, link);
641     }
642   
643   if (d->service_link == NULL ||
644       d->connection_link == NULL ||
645       d->hash_entry == NULL ||
646       !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
647                                         free_ownership_restore_data))
648     {
649       free_ownership_restore_data (d);
650       return FALSE;
651     }
652   
653   return TRUE;
654 }
655
656 /* this function is self-cancelling if you cancel the transaction */
657 dbus_bool_t
658 bus_service_remove_owner (BusService     *service,
659                           DBusConnection *owner,
660                           BusTransaction *transaction,
661                           DBusError      *error)
662 {
663   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
664   
665   /* We send out notifications before we do any work we
666    * might have to undo if the notification-sending failed
667    */
668   
669   /* Send service lost message */
670   if (bus_service_get_primary_owner (service) == owner)
671     {
672       if (!bus_driver_send_service_lost (owner, service->name,
673                                          transaction, error))
674         return FALSE;
675     }
676
677   if (service->owners == NULL)
678     {
679       _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
680     }
681   else if (_dbus_list_length_is_one (&service->owners))
682     {
683       if (!bus_driver_send_service_deleted (service->name,
684                                             transaction, error))
685         return FALSE;
686     }
687   else
688     {
689       DBusList *link;
690       link = _dbus_list_get_first (&service->owners);
691       _dbus_assert (link != NULL);
692       link = _dbus_list_get_next_link (&service->owners, link);
693
694       _dbus_assert (link != NULL);
695
696       /* This will be our new owner */
697       if (!bus_driver_send_service_acquired (link->data,
698                                              service->name,
699                                              transaction,
700                                              error))
701         return FALSE;
702     }
703
704   if (!add_restore_ownership_to_transaction (transaction, service, owner))
705     {
706       BUS_SET_OOM (error);
707       return FALSE;
708     }
709   
710   bus_service_unlink_owner (service, owner);
711
712   if (service->owners == NULL)
713     bus_service_unlink (service);
714
715   return TRUE;
716 }
717
718 BusService *
719 bus_service_ref (BusService *service)
720 {
721   _dbus_assert (service->refcount > 0);
722   
723   service->refcount += 1;
724
725   return service;
726 }
727
728 void
729 bus_service_unref (BusService *service)
730 {
731   _dbus_assert (service->refcount > 0);
732   
733   service->refcount -= 1;
734
735   if (service->refcount == 0)
736     {
737       _dbus_assert (service->owners == NULL);
738       
739       dbus_free (service->name);
740       _dbus_mem_pool_dealloc (service->registry->service_pool, service);
741     }
742 }
743
744 DBusConnection*
745 bus_service_get_primary_owner (BusService *service)
746 {
747   return _dbus_list_get_first (&service->owners);
748 }
749
750 const char*
751 bus_service_get_name (BusService *service)
752 {
753   return service->name;
754 }
755
756 void
757 bus_service_set_prohibit_replacement (BusService  *service,
758                                       dbus_bool_t  prohibit_replacement)
759 {
760   service->prohibit_replacement = prohibit_replacement != FALSE;
761 }
762
763 dbus_bool_t
764 bus_service_get_prohibit_replacement (BusService *service)
765 {
766   return service->prohibit_replacement;
767 }
768
769 dbus_bool_t
770 bus_service_has_owner (BusService     *service,
771                        DBusConnection *owner)
772 {
773   DBusList *link;
774
775   link = _dbus_list_get_first_link (&service->owners);
776   
777   while (link != NULL)
778     {
779       if (link->data == owner)
780         return TRUE;
781       
782       link = _dbus_list_get_next_link (&service->owners, link);
783     }
784
785   return FALSE;
786 }