2004-02-24 Joe Shaw <joe@ximian.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 2.0
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                       service_name);
304       goto out;
305     }
306
307   if (bus_connection_get_n_services_owned (connection) >=
308       bus_context_get_max_services_per_connection (registry->context))
309     {
310       dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
311                       "Connection \"%s\" is not allowed to own more services "
312                       "(increase limits in configuration file if required)",
313                       bus_connection_is_active (connection) ?
314                       bus_connection_get_name (connection) :
315                       "(inactive)");
316       goto out;
317     }
318   
319   service = bus_registry_lookup (registry, service_name);
320
321   if (service != NULL)
322     old_owner = bus_service_get_primary_owner (service);
323   else
324     old_owner = NULL;
325       
326   if (service == NULL)
327     {
328       service = bus_registry_ensure (registry,
329                                      service_name, connection, transaction, error);
330       if (service == NULL)
331         goto out;
332     }
333
334   current_owner = bus_service_get_primary_owner (service);
335
336   if (old_owner == NULL)
337     {
338       _dbus_assert (current_owner == connection);
339
340       bus_service_set_prohibit_replacement (service,
341                                             (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));      
342                         
343       *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;      
344     }
345   else if (old_owner == connection)
346     *result = DBUS_SERVICE_REPLY_ALREADY_OWNER;
347   else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING)))
348     *result = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
349   else if (bus_service_get_prohibit_replacement (service))
350     {
351       /* Queue the connection */
352       if (!bus_service_add_owner (service, connection,
353                                   transaction, error))
354         goto out;
355       
356       *result = DBUS_SERVICE_REPLY_IN_QUEUE;
357     }
358   else
359     {
360       /* Replace the current owner */
361
362       /* We enqueue the new owner and remove the first one because
363        * that will cause ServiceAcquired and ServiceLost messages to
364        * be sent.
365        */
366       
367       if (!bus_service_add_owner (service, connection,
368                                   transaction, error))
369         goto out;
370
371       if (!bus_service_remove_owner (service, old_owner,
372                                      transaction, error))
373         goto out;
374       
375       _dbus_assert (connection == bus_service_get_primary_owner (service));
376       *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
377     }
378
379   retval = TRUE;
380   
381  out:
382   return retval;
383 }
384
385 static void
386 bus_service_unlink_owner (BusService      *service,
387                           DBusConnection  *owner)
388 {
389   _dbus_list_remove_last (&service->owners, owner);
390   bus_connection_remove_owned_service (owner, service);
391 }
392
393 static void
394 bus_service_unlink (BusService *service)
395 {
396   _dbus_assert (service->owners == NULL);
397
398   /* the service may not be in the hash, if
399    * the failure causing transaction cancel
400    * was in the right place, but that's OK
401    */
402   _dbus_hash_table_remove_string (service->registry->service_hash,
403                                   service->name);
404   
405   bus_service_unref (service);
406 }
407
408 static void
409 bus_service_relink (BusService           *service,
410                     DBusPreallocatedHash *preallocated)
411 {
412   _dbus_assert (service->owners == NULL);
413   _dbus_assert (preallocated != NULL);
414
415   _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
416                                                preallocated,
417                                                service->name,
418                                                service);
419   
420   bus_service_ref (service);
421 }
422
423 /**
424  * Data used to represent an ownership cancellation in
425  * a bus transaction.
426  */
427 typedef struct
428 {
429   DBusConnection *connection; /**< the connection */
430   BusService *service;        /**< service to cancel ownership of */
431 } OwnershipCancelData;
432
433 static void
434 cancel_ownership (void *data)
435 {
436   OwnershipCancelData *d = data;
437
438   /* We don't need to send messages notifying of these
439    * changes, since we're reverting something that was
440    * cancelled (effectively never really happened)
441    */
442   bus_service_unlink_owner (d->service, d->connection);
443   
444   if (d->service->owners == NULL)
445     bus_service_unlink (d->service);
446 }
447
448 static void
449 free_ownership_cancel_data (void *data)
450 {
451   OwnershipCancelData *d = data;
452
453   dbus_connection_unref (d->connection);
454   bus_service_unref (d->service);
455   
456   dbus_free (d);
457 }
458
459 static dbus_bool_t
460 add_cancel_ownership_to_transaction (BusTransaction *transaction,
461                                      BusService     *service,
462                                      DBusConnection *connection)
463 {
464   OwnershipCancelData *d;
465
466   d = dbus_new (OwnershipCancelData, 1);
467   if (d == NULL)
468     return FALSE;
469   
470   d->service = service;
471   d->connection = connection;
472
473   if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
474                                         free_ownership_cancel_data))
475     {
476       dbus_free (d);
477       return FALSE;
478     }
479
480   bus_service_ref (d->service);
481   dbus_connection_ref (d->connection);
482   
483   return TRUE;
484 }
485
486 /* this function is self-cancelling if you cancel the transaction */
487 dbus_bool_t
488 bus_service_add_owner (BusService     *service,
489                        DBusConnection *owner,
490                        BusTransaction *transaction,
491                        DBusError      *error)
492 {
493   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
494   
495  /* Send service acquired message first, OOM will result
496   * in cancelling the transaction
497   */
498   if (service->owners == NULL)
499     {
500       if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
501         return FALSE;
502     }
503   
504   if (!_dbus_list_append (&service->owners,
505                           owner))
506     {
507       BUS_SET_OOM (error);
508       return FALSE;
509     }
510
511   if (!bus_connection_add_owned_service (owner, service))
512     {
513       _dbus_list_remove_last (&service->owners, owner);
514       BUS_SET_OOM (error);
515       return FALSE;
516     }
517
518   if (!add_cancel_ownership_to_transaction (transaction,
519                                             service,
520                                             owner))
521     {
522       bus_service_unlink_owner (service, owner);
523       BUS_SET_OOM (error);
524       return FALSE;
525     }
526   
527   return TRUE;
528 }
529
530 typedef struct
531 {
532   DBusConnection *connection;
533   BusService     *service;
534   DBusConnection *before_connection; /* restore to position before this connection in owners list */
535   DBusList       *connection_link;
536   DBusList       *service_link;
537   DBusPreallocatedHash *hash_entry;
538 } OwnershipRestoreData;
539
540 static void
541 restore_ownership (void *data)
542 {
543   OwnershipRestoreData *d = data;
544   DBusList *link;
545
546   _dbus_assert (d->service_link != NULL);
547   _dbus_assert (d->connection_link != NULL);
548   
549   if (d->service->owners == NULL)
550     {
551       _dbus_assert (d->hash_entry != NULL);
552       bus_service_relink (d->service, d->hash_entry);
553     }
554   else
555     {
556       _dbus_assert (d->hash_entry == NULL);
557     }
558   
559   /* We don't need to send messages notifying of these
560    * changes, since we're reverting something that was
561    * cancelled (effectively never really happened)
562    */
563   link = _dbus_list_get_first_link (&d->service->owners);
564   while (link != NULL)
565     {
566       if (link->data == d->before_connection)
567         break;
568
569       link = _dbus_list_get_next_link (&d->service->owners, link);
570     }
571   
572   _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
573
574   /* Note that removing then restoring this changes the order in which
575    * ServiceDeleted messages are sent on destruction of the
576    * connection.  This should be OK as the only guarantee there is
577    * that the base service is destroyed last, and we never even
578    * tentatively remove the base service.
579    */
580   bus_connection_add_owned_service_link (d->connection, d->service_link);
581   
582   d->hash_entry = NULL;
583   d->service_link = NULL;
584   d->connection_link = NULL;
585 }
586
587 static void
588 free_ownership_restore_data (void *data)
589 {
590   OwnershipRestoreData *d = data;
591
592   if (d->service_link)
593     _dbus_list_free_link (d->service_link);
594   if (d->connection_link)
595     _dbus_list_free_link (d->connection_link);
596   if (d->hash_entry)
597     _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
598                                               d->hash_entry);
599
600   dbus_connection_unref (d->connection);
601   bus_service_unref (d->service);
602   
603   dbus_free (d);
604 }
605
606 static dbus_bool_t
607 add_restore_ownership_to_transaction (BusTransaction *transaction,
608                                       BusService     *service,
609                                       DBusConnection *connection)
610 {
611   OwnershipRestoreData *d;
612   DBusList *link;
613
614   d = dbus_new (OwnershipRestoreData, 1);
615   if (d == NULL)
616     return FALSE;
617   
618   d->service = service;
619   d->connection = connection;
620   d->service_link = _dbus_list_alloc_link (service);
621   d->connection_link = _dbus_list_alloc_link (connection);
622   d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
623   
624   bus_service_ref (d->service);
625   dbus_connection_ref (d->connection);
626
627   d->before_connection = NULL;
628   link = _dbus_list_get_first_link (&service->owners);
629   while (link != NULL)
630     {
631       if (link->data == connection)
632         {
633           link = _dbus_list_get_next_link (&service->owners, link);
634
635           if (link)
636             d->before_connection = link->data;
637
638           break;
639         }
640       
641       link = _dbus_list_get_next_link (&service->owners, link);
642     }
643   
644   if (d->service_link == NULL ||
645       d->connection_link == NULL ||
646       d->hash_entry == NULL ||
647       !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
648                                         free_ownership_restore_data))
649     {
650       free_ownership_restore_data (d);
651       return FALSE;
652     }
653   
654   return TRUE;
655 }
656
657 /* this function is self-cancelling if you cancel the transaction */
658 dbus_bool_t
659 bus_service_remove_owner (BusService     *service,
660                           DBusConnection *owner,
661                           BusTransaction *transaction,
662                           DBusError      *error)
663 {
664   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
665   
666   /* We send out notifications before we do any work we
667    * might have to undo if the notification-sending failed
668    */
669   
670   /* Send service lost message */
671   if (bus_service_get_primary_owner (service) == owner)
672     {
673       if (!bus_driver_send_service_lost (owner, service->name,
674                                          transaction, error))
675         return FALSE;
676     }
677
678   if (service->owners == NULL)
679     {
680       _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
681     }
682   else if (_dbus_list_length_is_one (&service->owners))
683     {
684       if (!bus_driver_send_service_deleted (service->name,
685                                             transaction, error))
686         return FALSE;
687     }
688   else
689     {
690       DBusList *link;
691       link = _dbus_list_get_first (&service->owners);
692       _dbus_assert (link != NULL);
693       link = _dbus_list_get_next_link (&service->owners, link);
694
695       _dbus_assert (link != NULL);
696
697       /* This will be our new owner */
698       if (!bus_driver_send_service_acquired (link->data,
699                                              service->name,
700                                              transaction,
701                                              error))
702         return FALSE;
703     }
704
705   if (!add_restore_ownership_to_transaction (transaction, service, owner))
706     {
707       BUS_SET_OOM (error);
708       return FALSE;
709     }
710   
711   bus_service_unlink_owner (service, owner);
712
713   if (service->owners == NULL)
714     bus_service_unlink (service);
715
716   return TRUE;
717 }
718
719 BusService *
720 bus_service_ref (BusService *service)
721 {
722   _dbus_assert (service->refcount > 0);
723   
724   service->refcount += 1;
725
726   return service;
727 }
728
729 void
730 bus_service_unref (BusService *service)
731 {
732   _dbus_assert (service->refcount > 0);
733   
734   service->refcount -= 1;
735
736   if (service->refcount == 0)
737     {
738       _dbus_assert (service->owners == NULL);
739       
740       dbus_free (service->name);
741       _dbus_mem_pool_dealloc (service->registry->service_pool, service);
742     }
743 }
744
745 DBusConnection*
746 bus_service_get_primary_owner (BusService *service)
747 {
748   return _dbus_list_get_first (&service->owners);
749 }
750
751 const char*
752 bus_service_get_name (BusService *service)
753 {
754   return service->name;
755 }
756
757 void
758 bus_service_set_prohibit_replacement (BusService  *service,
759                                       dbus_bool_t  prohibit_replacement)
760 {
761   service->prohibit_replacement = prohibit_replacement != FALSE;
762 }
763
764 dbus_bool_t
765 bus_service_get_prohibit_replacement (BusService *service)
766 {
767   return service->prohibit_replacement;
768 }
769
770 dbus_bool_t
771 bus_service_has_owner (BusService     *service,
772                        DBusConnection *owner)
773 {
774   DBusList *link;
775
776   link = _dbus_list_get_first_link (&service->owners);
777   
778   while (link != NULL)
779     {
780       if (link->data == owner)
781         return TRUE;
782       
783       link = _dbus_list_get_next_link (&service->owners, link);
784     }
785
786   return FALSE;
787 }