services.c revision 43605a6f4e78a8c28afb4b1e924dff0301e0e95c
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
37struct BusService
38{
39  int refcount;
40
41  BusRegistry *registry;
42  char *name;
43  DBusList *owners;
44
45  unsigned int prohibit_replacement : 1;
46};
47
48struct 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
60BusRegistry*
61bus_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
91BusRegistry *
92bus_registry_ref (BusRegistry *registry)
93{
94  _dbus_assert (registry->refcount > 0);
95  registry->refcount += 1;
96
97  return registry;
98}
99
100void
101bus_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
119BusService*
120bus_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
131BusService*
132bus_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
199void
200bus_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
215dbus_bool_t
216bus_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
259dbus_bool_t
260bus_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
417void
418bus_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
430static void
431bus_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
438static void
439bus_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
453static void
454bus_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 */
472typedef struct
473{
474  DBusConnection *connection; /**< the connection */
475  BusService *service;        /**< service to cancel ownership of */
476} OwnershipCancelData;
477
478static void
479cancel_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
493static void
494free_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
504static dbus_bool_t
505add_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 */
532dbus_bool_t
533bus_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
575typedef 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
585static void
586restore_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
632static void
633free_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
651static dbus_bool_t
652add_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 */
703dbus_bool_t
704bus_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
763BusService *
764bus_service_ref (BusService *service)
765{
766  _dbus_assert (service->refcount > 0);
767
768  service->refcount += 1;
769
770  return service;
771}
772
773void
774bus_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
789DBusConnection*
790bus_service_get_primary_owner (BusService *service)
791{
792  return _dbus_list_get_first (&service->owners);
793}
794
795const char*
796bus_service_get_name (BusService *service)
797{
798  return service->name;
799}
800
801void
802bus_service_set_prohibit_replacement (BusService  *service,
803				      dbus_bool_t  prohibit_replacement)
804{
805  service->prohibit_replacement = prohibit_replacement != FALSE;
806}
807
808dbus_bool_t
809bus_service_get_prohibit_replacement (BusService *service)
810{
811  return service->prohibit_replacement;
812}
813
814dbus_bool_t
815bus_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}
832