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