fb27ea0f99345d49af5678939de3a3d4d4f9683f
[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 void
421 bus_registry_set_service_sid_table (BusRegistry   *registry,
422                                     DBusHashTable *table)
423 {
424   _dbus_assert (registry->service_sid_table != table);
425   
426   if (registry->service_sid_table)
427     _dbus_hash_table_unref (registry->service_sid_table);
428
429   registry->service_sid_table = table;
430   _dbus_hash_table_ref (table);
431 }
432
433 static void
434 bus_service_unlink_owner (BusService      *service,
435                           DBusConnection  *owner)
436 {
437   _dbus_list_remove_last (&service->owners, owner);
438   bus_connection_remove_owned_service (owner, service);
439 }
440
441 static void
442 bus_service_unlink (BusService *service)
443 {
444   _dbus_assert (service->owners == NULL);
445
446   /* the service may not be in the hash, if
447    * the failure causing transaction cancel
448    * was in the right place, but that's OK
449    */
450   _dbus_hash_table_remove_string (service->registry->service_hash,
451                                   service->name);
452   
453   bus_service_unref (service);
454 }
455
456 static void
457 bus_service_relink (BusService           *service,
458                     DBusPreallocatedHash *preallocated)
459 {
460   _dbus_assert (service->owners == NULL);
461   _dbus_assert (preallocated != NULL);
462
463   _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
464                                                preallocated,
465                                                service->name,
466                                                service);
467   
468   bus_service_ref (service);
469 }
470
471 /**
472  * Data used to represent an ownership cancellation in
473  * a bus transaction.
474  */
475 typedef struct
476 {
477   DBusConnection *connection; /**< the connection */
478   BusService *service;        /**< service to cancel ownership of */
479 } OwnershipCancelData;
480
481 static void
482 cancel_ownership (void *data)
483 {
484   OwnershipCancelData *d = data;
485
486   /* We don't need to send messages notifying of these
487    * changes, since we're reverting something that was
488    * cancelled (effectively never really happened)
489    */
490   bus_service_unlink_owner (d->service, d->connection);
491   
492   if (d->service->owners == NULL)
493     bus_service_unlink (d->service);
494 }
495
496 static void
497 free_ownership_cancel_data (void *data)
498 {
499   OwnershipCancelData *d = data;
500
501   dbus_connection_unref (d->connection);
502   bus_service_unref (d->service);
503   
504   dbus_free (d);
505 }
506
507 static dbus_bool_t
508 add_cancel_ownership_to_transaction (BusTransaction *transaction,
509                                      BusService     *service,
510                                      DBusConnection *connection)
511 {
512   OwnershipCancelData *d;
513
514   d = dbus_new (OwnershipCancelData, 1);
515   if (d == NULL)
516     return FALSE;
517   
518   d->service = service;
519   d->connection = connection;
520
521   if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
522                                         free_ownership_cancel_data))
523     {
524       dbus_free (d);
525       return FALSE;
526     }
527
528   bus_service_ref (d->service);
529   dbus_connection_ref (d->connection);
530   
531   return TRUE;
532 }
533
534 /* this function is self-cancelling if you cancel the transaction */
535 dbus_bool_t
536 bus_service_add_owner (BusService     *service,
537                        DBusConnection *owner,
538                        BusTransaction *transaction,
539                        DBusError      *error)
540 {
541   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
542   
543  /* Send service acquired message first, OOM will result
544   * in cancelling the transaction
545   */
546   if (service->owners == NULL)
547     {
548       if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
549         return FALSE;
550     }
551   
552   if (!_dbus_list_append (&service->owners,
553                           owner))
554     {
555       BUS_SET_OOM (error);
556       return FALSE;
557     }
558
559   if (!bus_connection_add_owned_service (owner, service))
560     {
561       _dbus_list_remove_last (&service->owners, owner);
562       BUS_SET_OOM (error);
563       return FALSE;
564     }
565
566   if (!add_cancel_ownership_to_transaction (transaction,
567                                             service,
568                                             owner))
569     {
570       bus_service_unlink_owner (service, owner);
571       BUS_SET_OOM (error);
572       return FALSE;
573     }
574   
575   return TRUE;
576 }
577
578 typedef struct
579 {
580   DBusConnection *connection;
581   BusService     *service;
582   DBusConnection *before_connection; /* restore to position before this connection in owners list */
583   DBusList       *connection_link;
584   DBusList       *service_link;
585   DBusPreallocatedHash *hash_entry;
586 } OwnershipRestoreData;
587
588 static void
589 restore_ownership (void *data)
590 {
591   OwnershipRestoreData *d = data;
592   DBusList *link;
593
594   _dbus_assert (d->service_link != NULL);
595   _dbus_assert (d->connection_link != NULL);
596   
597   if (d->service->owners == NULL)
598     {
599       _dbus_assert (d->hash_entry != NULL);
600       bus_service_relink (d->service, d->hash_entry);
601     }
602   else
603     {
604       _dbus_assert (d->hash_entry == NULL);
605     }
606   
607   /* We don't need to send messages notifying of these
608    * changes, since we're reverting something that was
609    * cancelled (effectively never really happened)
610    */
611   link = _dbus_list_get_first_link (&d->service->owners);
612   while (link != NULL)
613     {
614       if (link->data == d->before_connection)
615         break;
616
617       link = _dbus_list_get_next_link (&d->service->owners, link);
618     }
619   
620   _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
621
622   /* Note that removing then restoring this changes the order in which
623    * ServiceDeleted messages are sent on destruction of the
624    * connection.  This should be OK as the only guarantee there is
625    * that the base service is destroyed last, and we never even
626    * tentatively remove the base service.
627    */
628   bus_connection_add_owned_service_link (d->connection, d->service_link);
629   
630   d->hash_entry = NULL;
631   d->service_link = NULL;
632   d->connection_link = NULL;
633 }
634
635 static void
636 free_ownership_restore_data (void *data)
637 {
638   OwnershipRestoreData *d = data;
639
640   if (d->service_link)
641     _dbus_list_free_link (d->service_link);
642   if (d->connection_link)
643     _dbus_list_free_link (d->connection_link);
644   if (d->hash_entry)
645     _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
646                                               d->hash_entry);
647
648   dbus_connection_unref (d->connection);
649   bus_service_unref (d->service);
650   
651   dbus_free (d);
652 }
653
654 static dbus_bool_t
655 add_restore_ownership_to_transaction (BusTransaction *transaction,
656                                       BusService     *service,
657                                       DBusConnection *connection)
658 {
659   OwnershipRestoreData *d;
660   DBusList *link;
661
662   d = dbus_new (OwnershipRestoreData, 1);
663   if (d == NULL)
664     return FALSE;
665   
666   d->service = service;
667   d->connection = connection;
668   d->service_link = _dbus_list_alloc_link (service);
669   d->connection_link = _dbus_list_alloc_link (connection);
670   d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
671   
672   bus_service_ref (d->service);
673   dbus_connection_ref (d->connection);
674
675   d->before_connection = NULL;
676   link = _dbus_list_get_first_link (&service->owners);
677   while (link != NULL)
678     {
679       if (link->data == connection)
680         {
681           link = _dbus_list_get_next_link (&service->owners, link);
682
683           if (link)
684             d->before_connection = link->data;
685
686           break;
687         }
688       
689       link = _dbus_list_get_next_link (&service->owners, link);
690     }
691   
692   if (d->service_link == NULL ||
693       d->connection_link == NULL ||
694       d->hash_entry == NULL ||
695       !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
696                                         free_ownership_restore_data))
697     {
698       free_ownership_restore_data (d);
699       return FALSE;
700     }
701   
702   return TRUE;
703 }
704
705 /* this function is self-cancelling if you cancel the transaction */
706 dbus_bool_t
707 bus_service_remove_owner (BusService     *service,
708                           DBusConnection *owner,
709                           BusTransaction *transaction,
710                           DBusError      *error)
711 {
712   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
713   
714   /* We send out notifications before we do any work we
715    * might have to undo if the notification-sending failed
716    */
717   
718   /* Send service lost message */
719   if (bus_service_get_primary_owner (service) == owner)
720     {
721       if (!bus_driver_send_service_lost (owner, service->name,
722                                          transaction, error))
723         return FALSE;
724     }
725
726   if (service->owners == NULL)
727     {
728       _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
729     }
730   else if (_dbus_list_length_is_one (&service->owners))
731     {
732       if (!bus_driver_send_service_owner_changed (service->name,
733                                                   bus_connection_get_name (owner),
734                                                   NULL,
735                                                   transaction, error))
736         return FALSE;
737     }
738   else
739     {
740       DBusList *link;
741       DBusConnection *new_owner;
742       link = _dbus_list_get_first_link (&service->owners);
743       _dbus_assert (link != NULL);
744       link = _dbus_list_get_next_link (&service->owners, link);
745       _dbus_assert (link != NULL);
746
747       new_owner = link->data;
748
749       if (!bus_driver_send_service_owner_changed (service->name,
750                                                   bus_connection_get_name (owner),
751                                                   bus_connection_get_name (new_owner),
752                                                   transaction, error))
753         return FALSE;
754
755       /* This will be our new owner */
756       if (!bus_driver_send_service_acquired (new_owner,
757                                              service->name,
758                                              transaction,
759                                              error))
760         return FALSE;
761     }
762
763   if (!add_restore_ownership_to_transaction (transaction, service, owner))
764     {
765       BUS_SET_OOM (error);
766       return FALSE;
767     }
768   
769   bus_service_unlink_owner (service, owner);
770
771   if (service->owners == NULL)
772     bus_service_unlink (service);
773
774   return TRUE;
775 }
776
777 BusService *
778 bus_service_ref (BusService *service)
779 {
780   _dbus_assert (service->refcount > 0);
781   
782   service->refcount += 1;
783
784   return service;
785 }
786
787 void
788 bus_service_unref (BusService *service)
789 {
790   _dbus_assert (service->refcount > 0);
791   
792   service->refcount -= 1;
793
794   if (service->refcount == 0)
795     {
796       _dbus_assert (service->owners == NULL);
797       
798       dbus_free (service->name);
799       _dbus_mem_pool_dealloc (service->registry->service_pool, service);
800     }
801 }
802
803 DBusConnection*
804 bus_service_get_primary_owner (BusService *service)
805 {
806   return _dbus_list_get_first (&service->owners);
807 }
808
809 const char*
810 bus_service_get_name (BusService *service)
811 {
812   return service->name;
813 }
814
815 void
816 bus_service_set_prohibit_replacement (BusService  *service,
817                                       dbus_bool_t  prohibit_replacement)
818 {
819   service->prohibit_replacement = prohibit_replacement != FALSE;
820 }
821
822 dbus_bool_t
823 bus_service_get_prohibit_replacement (BusService *service)
824 {
825   return service->prohibit_replacement;
826 }
827
828 dbus_bool_t
829 bus_service_has_owner (BusService     *service,
830                        DBusConnection *owner)
831 {
832   DBusList *link;
833
834   link = _dbus_list_get_first_link (&service->owners);
835   
836   while (link != NULL)
837     {
838       if (link->data == owner)
839         return TRUE;
840       
841       link = _dbus_list_get_next_link (&service->owners, link);
842     }
843
844   return FALSE;
845 }