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