Ejemplo n.º 1
0
static void
free_ownership_restore_data (void *data)
{
  OwnershipRestoreData *d = data;

  if (d->service_link)
    _dbus_list_free_link (d->service_link);
  if (d->owner_link)
    _dbus_list_free_link (d->owner_link);
  if (d->hash_entry)
    _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
                                              d->hash_entry);

  dbus_connection_unref (d->owner->conn);
  bus_owner_unref (d->owner);
  bus_service_unref (d->service);
  
  dbus_free (d);
}
static void
service_dirs_append_link_unique_or_free (DBusList **service_dirs,
                                         DBusList *dir_link)
{
  if (!service_dirs_find_dir (service_dirs, dir_link->data))
    {
      _dbus_list_append_link (service_dirs, dir_link);
    }
  else
    {
      dbus_free (dir_link->data);
      _dbus_list_free_link (dir_link);
    }
}
Ejemplo n.º 3
0
static dbus_bool_t print_service_dirs()
{
  DBusList *dirs;
  DBusList *link;
  dirs = NULL;

  if (!_dbus_get_standard_session_servicedirs (&dirs))
    _dbus_assert_not_reached ("couldn't get standard dirs");

  while ((link = _dbus_list_pop_first_link (&dirs)))
    {
      printf ("default service dir: %s\n", (char *)link->data);
      dbus_free (link->data);
      _dbus_list_free_link (link);
    }
  dbus_free (dirs);
  return TRUE;
}
Ejemplo n.º 4
0
static void
_dbus_pending_call_last_unref (DBusPendingCall *pending)
{
  DBusConnection *connection;
  
  /* If we get here, we should be already detached
   * from the connection, or never attached.
   */
  _dbus_assert (!pending->timeout_added);  

  connection = pending->connection;

  /* this assumes we aren't holding connection lock... */
  _dbus_data_slot_list_free (&pending->slot_list);

  if (pending->timeout != NULL)
    _dbus_timeout_unref (pending->timeout);
      
  if (pending->timeout_link)
    {
      dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
      _dbus_list_free_link (pending->timeout_link);
      pending->timeout_link = NULL;
    }

  if (pending->reply)
    {
      dbus_message_unref (pending->reply);
      pending->reply = NULL;
    }
      
  dbus_free (pending);

  dbus_pending_call_free_data_slot (&notify_user_data_slot);

  /* connection lock should not be held. */
  /* Free the connection last to avoid a weird state while
   * calling out to application code where the pending exists
   * but not the connection.
   */
  dbus_connection_unref (connection);
}
Ejemplo n.º 5
0
/**
 * @ingroup DBusListInternals
 * Unit test for DBusList
 * @returns #TRUE on success.
 */
dbus_bool_t
_dbus_list_test (void)
{
  DBusList *list1;
  DBusList *list2;
  DBusList *link1;
  DBusList *link2;
  DBusList *copy1;
  DBusList *copy2;
  int i;
  
  list1 = NULL;
  list2 = NULL;

  /* Test append and prepend */
  
  i = 0;
  while (i < 10)
    {
      if (!_dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)))
        _dbus_assert_not_reached ("could not allocate for append");
      
      if (!_dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)))
        _dbus_assert_not_reached ("count not allocate for prepend");
      ++i;

      verify_list (&list1);
      verify_list (&list2);
      
      _dbus_assert (_dbus_list_get_length (&list1) == i);
      _dbus_assert (_dbus_list_get_length (&list2) == i);
    }

  _dbus_assert (is_ascending_sequence (&list1));
  _dbus_assert (is_descending_sequence (&list2));

  /* Test list clear */
  _dbus_list_clear (&list1);
  _dbus_list_clear (&list2);

  verify_list (&list1);
  verify_list (&list2);

  /* Test get_first, get_last, pop_first, pop_last */
  
  i = 0;
  while (i < 10)
    {
      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
      ++i;
    }

  --i;
  while (i >= 0)
    {
      void *got_data1;
      void *got_data2;
      
      void *data1;
      void *data2;

      got_data1 = _dbus_list_get_last (&list1);
      got_data2 = _dbus_list_get_first (&list2);
      
      data1 = _dbus_list_pop_last (&list1);
      data2 = _dbus_list_pop_first (&list2);

      _dbus_assert (got_data1 == data1);
      _dbus_assert (got_data2 == data2);
      
      _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i);
      _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i);

      verify_list (&list1);
      verify_list (&list2);

      _dbus_assert (is_ascending_sequence (&list1));
      _dbus_assert (is_descending_sequence (&list2));
      
      --i;
    }

  _dbus_assert (list1 == NULL);
  _dbus_assert (list2 == NULL);

  /* Test get_first_link, get_last_link, pop_first_link, pop_last_link */
  
  i = 0;
  while (i < 10)
    {
      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
      ++i;
    }

  --i;
  while (i >= 0)
    {
      DBusList *got_link1;
      DBusList *got_link2;

      DBusList *link1;
      DBusList *link2;
      
      void *data1;
      void *data2;
      
      got_link1 = _dbus_list_get_last_link (&list1);
      got_link2 = _dbus_list_get_first_link (&list2);
      
      link1 = _dbus_list_pop_last_link (&list1);
      link2 = _dbus_list_pop_first_link (&list2);

      _dbus_assert (got_link1 == link1);
      _dbus_assert (got_link2 == link2);

      data1 = link1->data;
      data2 = link2->data;

      _dbus_list_free_link (link1);
      _dbus_list_free_link (link2);
      
      _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i);
      _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i);

      verify_list (&list1);
      verify_list (&list2);

      _dbus_assert (is_ascending_sequence (&list1));
      _dbus_assert (is_descending_sequence (&list2));
      
      --i;
    }

  _dbus_assert (list1 == NULL);
  _dbus_assert (list2 == NULL);
  
  /* Test iteration */
  
  i = 0;
  while (i < 10)
    {
      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
      ++i;

      verify_list (&list1);
      verify_list (&list2);
      
      _dbus_assert (_dbus_list_get_length (&list1) == i);
      _dbus_assert (_dbus_list_get_length (&list2) == i);
    }

  _dbus_assert (is_ascending_sequence (&list1));
  _dbus_assert (is_descending_sequence (&list2));

  --i;
  link2 = _dbus_list_get_first_link (&list2);
  while (link2 != NULL)
    {
      verify_list (&link2); /* pretend this link is the head */
      
      _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
      
      link2 = _dbus_list_get_next_link (&list2, link2);
      --i;
    }

  i = 0;
  link1 = _dbus_list_get_first_link (&list1);
  while (link1 != NULL)
    {
      verify_list (&link1); /* pretend this link is the head */
      
      _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
      
      link1 = _dbus_list_get_next_link (&list1, link1);
      ++i;
    }

  --i;
  link1 = _dbus_list_get_last_link (&list1);
  while (link1 != NULL)
    {
      verify_list (&link1); /* pretend this link is the head */

      _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
      
      link1 = _dbus_list_get_prev_link (&list1, link1);
      --i;
    }

  _dbus_list_clear (&list1);
  _dbus_list_clear (&list2);

  /* Test remove */
  
  i = 0;
  while (i < 10)
    {
      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
      ++i;
    }

  --i;
  while (i >= 0)
    {
      if ((i % 2) == 0)
        {
          if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
            _dbus_assert_not_reached ("element should have been in list");
          if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
            _dbus_assert_not_reached ("element should have been in list");

          verify_list (&list1);
          verify_list (&list2);
        }
      --i;
    }

  _dbus_assert (all_odd_values (&list1));
  _dbus_assert (all_odd_values (&list2));

  _dbus_list_clear (&list1);
  _dbus_list_clear (&list2);

  /* test removing the other half of the elements */
  
  i = 0;
  while (i < 10)
    {
      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
      ++i;
    }

  --i;
  while (i >= 0)
    {
      if ((i % 2) != 0)
        {
          if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
            _dbus_assert_not_reached ("element should have been in list");
          if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
            _dbus_assert_not_reached ("element should have been in list");

          verify_list (&list1);
          verify_list (&list2);
        }
      --i;
    }

  _dbus_assert (all_even_values (&list1));
  _dbus_assert (all_even_values (&list2));

  /* clear list using remove_link */
  while (list1 != NULL)
    {
      _dbus_list_remove_link (&list1, list1);
      verify_list (&list1);
    }
  while (list2 != NULL)
    {
      _dbus_list_remove_link (&list2, list2);
      verify_list (&list2);
    }

  /* Test remove link more generally */
  i = 0;
  while (i < 10)
    {
      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
      ++i;
    }

  --i;
  link2 = _dbus_list_get_first_link (&list2);
  while (link2 != NULL)
    {
      DBusList *next = _dbus_list_get_next_link (&list2, link2);
      
      _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);

      if ((i % 2) == 0)
        _dbus_list_remove_link (&list2, link2);

      verify_list (&list2);
      
      link2 = next;
      --i;
    }

  _dbus_assert (all_odd_values (&list2));  
  _dbus_list_clear (&list2);
  
  i = 0;
  link1 = _dbus_list_get_first_link (&list1);
  while (link1 != NULL)
    {
      DBusList *next = _dbus_list_get_next_link (&list1, link1);

      _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);

      if ((i % 2) != 0)
        _dbus_list_remove_link (&list1, link1);

      verify_list (&list1);
      
      link1 = next;
      ++i;
    }

  _dbus_assert (all_even_values (&list1));
  _dbus_list_clear (&list1);

  /* Test copying a list */
  i = 0;
  while (i < 10)
    {
      _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
      _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
      ++i;
    }

  /* bad pointers, because they are allowed in the copy dest */
  copy1 = _DBUS_INT_TO_POINTER (0x342234);
  copy2 = _DBUS_INT_TO_POINTER (23);
  
  _dbus_list_copy (&list1, &copy1);
  verify_list (&list1);
  verify_list (&copy1);
  _dbus_assert (lists_equal (&list1, &copy1));
  
  _dbus_list_copy (&list2, &copy2);
  verify_list (&list2);
  verify_list (&copy2);
  _dbus_assert (lists_equal (&list2, &copy2));

  /* Now test copying empty lists */
  _dbus_list_clear (&list1);
  _dbus_list_clear (&list2);
  _dbus_list_clear (&copy1);
  _dbus_list_clear (&copy2);
  
  /* bad pointers, because they are allowed in the copy dest */
  copy1 = _DBUS_INT_TO_POINTER (0x342234);
  copy2 = _DBUS_INT_TO_POINTER (23);
  
  _dbus_list_copy (&list1, &copy1);
  verify_list (&list1);
  verify_list (&copy1);
  _dbus_assert (lists_equal (&list1, &copy1));
  
  _dbus_list_copy (&list2, &copy2);
  verify_list (&list2);
  verify_list (&copy2);
  _dbus_assert (lists_equal (&list2, &copy2));

  _dbus_list_clear (&list1);
  _dbus_list_clear (&list2);
  
  /* insert_before on empty list */
  _dbus_list_insert_before (&list1, NULL,
                            _DBUS_INT_TO_POINTER (0));
  verify_list (&list1);

  /* inserting before first element */
  _dbus_list_insert_before (&list1, list1,
                            _DBUS_INT_TO_POINTER (2));
  verify_list (&list1);
  _dbus_assert (is_descending_sequence (&list1));

  /* inserting in the middle */
  _dbus_list_insert_before (&list1, list1->next,
                            _DBUS_INT_TO_POINTER (1));
  verify_list (&list1);
  _dbus_assert (is_descending_sequence (&list1));  

  /* using insert_before to append */
  _dbus_list_insert_before (&list1, NULL,
                            _DBUS_INT_TO_POINTER (-1));
  verify_list (&list1);
  _dbus_assert (is_descending_sequence (&list1));
  
  _dbus_list_clear (&list1);

  /* insert_after on empty list */
  _dbus_list_insert_after (&list1, NULL,
                           _DBUS_INT_TO_POINTER (0));
  verify_list (&list1);

  /* inserting after first element */
  _dbus_list_insert_after (&list1, list1,
                           _DBUS_INT_TO_POINTER (1));
  verify_list (&list1);
  _dbus_assert (is_ascending_sequence (&list1));

  /* inserting at the end */
  _dbus_list_insert_after (&list1, list1->next,
                           _DBUS_INT_TO_POINTER (2));
  verify_list (&list1);
  _dbus_assert (is_ascending_sequence (&list1));

  /* using insert_after to prepend */
  _dbus_list_insert_after (&list1, NULL,
                           _DBUS_INT_TO_POINTER (-1));
  verify_list (&list1);
  _dbus_assert (is_ascending_sequence (&list1));
  
  _dbus_list_clear (&list1);

  /* using remove_last */
  _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (2));
  _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (1));
  _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (3));

  _dbus_list_remove_last (&list1, _DBUS_INT_TO_POINTER (2));
  
  verify_list (&list1);
  _dbus_assert (is_ascending_sequence (&list1));
  
  _dbus_list_clear (&list1);
  
  return TRUE;
}
Ejemplo n.º 6
0
dbus_bool_t
bus_registry_acquire_service (BusRegistry      *registry,
                              DBusConnection   *connection,
                              const DBusString *service_name,
                              dbus_uint32_t     flags,
                              dbus_uint32_t    *result,
                              BusTransaction   *transaction,
                              DBusError        *error)
{
  dbus_bool_t retval;
  DBusConnection *old_owner_conn;
  BusClientPolicy *policy;
  BusService *service;
  BusActivation  *activation;
  BusSELinuxID *sid;
  BusOwner *primary_owner;
 
  retval = FALSE;

  if (!_dbus_validate_bus_name (service_name, 0,
                                _dbus_string_get_length (service_name)))
    {
      dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
                      "Requested bus name \"%s\" is not valid",
                      _dbus_string_get_const_data (service_name));
      
      _dbus_verbose ("Attempt to acquire invalid service name\n");
      
      goto out;
    }
  
  if (_dbus_string_get_byte (service_name, 0) == ':')
    {
      /* Not allowed; only base services can start with ':' */
      dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
                      "Cannot acquire a service starting with ':' such as \"%s\"",
                      _dbus_string_get_const_data (service_name));
      
      _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"",
                     _dbus_string_get_const_data (service_name));
      
      goto out;
    }

  if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS))
    {
      dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
                      "Connection \"%s\" is not allowed to own the service \"%s\"because "
                      "it is reserved for D-Bus' use only",
                      bus_connection_is_active (connection) ?
                      bus_connection_get_name (connection) :
                      "(inactive)",
                      DBUS_SERVICE_DBUS);
      goto out;
    }

  policy = bus_connection_get_policy (connection);
  _dbus_assert (policy != NULL);

  /* Note that if sid is #NULL then the bus's own context gets used
   * in bus_connection_selinux_allows_acquire_service()
   */
  sid = bus_selinux_id_table_lookup (registry->service_sid_table,
                                     service_name);

  if (!bus_selinux_allows_acquire_service (connection, sid,
					   _dbus_string_get_const_data (service_name), error))
    {

      if (dbus_error_is_set (error) &&
	  dbus_error_has_name (error, DBUS_ERROR_NO_MEMORY))
	{
	  goto out;
	}

      dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
                      "Connection \"%s\" is not allowed to own the service \"%s\" due "
                      "to SELinux policy",
                      bus_connection_is_active (connection) ?
                      bus_connection_get_name (connection) :
                      "(inactive)",
                      _dbus_string_get_const_data (service_name));
      goto out;
    }
  
  if (!bus_client_policy_check_can_own (policy, service_name))
    {
      dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
                      "Connection \"%s\" is not allowed to own the service \"%s\" due "
                      "to security policies in the configuration file",
                      bus_connection_is_active (connection) ?
                      bus_connection_get_name (connection) :
                      "(inactive)",
                      _dbus_string_get_const_data (service_name));
      goto out;
    }

  if (bus_connection_get_n_services_owned (connection) >=
      bus_context_get_max_services_per_connection (registry->context))
    {
      dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
                      "Connection \"%s\" is not allowed to own more services "
                      "(increase limits in configuration file if required)",
                      bus_connection_is_active (connection) ?
                      bus_connection_get_name (connection) :
                      "(inactive)");
      goto out;
    }
  
  service = bus_registry_lookup (registry, service_name);

  if (service != NULL)
    {
      primary_owner = bus_service_get_primary_owner (service);
      if (primary_owner != NULL)
        old_owner_conn = primary_owner->conn;
      else
        old_owner_conn = NULL;
    }
  else
    old_owner_conn = NULL;
      
  if (service == NULL)
    {
      service = bus_registry_ensure (registry,
                                     service_name, connection, flags,
                                     transaction, error);
      if (service == NULL)
        goto out;
    }

  primary_owner = bus_service_get_primary_owner (service);
  if (primary_owner == NULL)
    goto out;

  if (old_owner_conn == NULL)
    {
      _dbus_assert (primary_owner->conn == connection);

      *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;      
    }
  else if (old_owner_conn == connection)
    {
      bus_owner_set_flags (primary_owner, flags);
      *result = DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
    }
  else if (((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
           !(bus_service_get_allow_replacement (service))) ||
	   ((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
           !(flags & DBUS_NAME_FLAG_REPLACE_EXISTING))) 
    {
      DBusList *link;
      BusOwner *temp_owner;
    /* Since we can't be queued if we are already in the queue
       remove us */

      link = _bus_service_find_owner_link (service, connection);
      if (link != NULL)
        {
          _dbus_list_unlink (&service->owners, link);
          temp_owner = (BusOwner *)link->data;
          bus_owner_unref (temp_owner); 
          _dbus_list_free_link (link);
        }
      
      *result = DBUS_REQUEST_NAME_REPLY_EXISTS;
    }
  else if (!(flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
           (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) ||
	    !(bus_service_get_allow_replacement (service))))
    {
      /* Queue the connection */
      if (!bus_service_add_owner (service, connection, 
                                  flags,
                                  transaction, error))
        goto out;
      
      *result = DBUS_REQUEST_NAME_REPLY_IN_QUEUE;
    }
  else
    {
      /* Replace the current owner */

      /* We enqueue the new owner and remove the first one because
       * that will cause NameAcquired and NameLost messages to
       * be sent.
       */
      
      if (!bus_service_add_owner (service, connection,
                                  flags,
                                  transaction, error))
        goto out;

      if (primary_owner->do_not_queue)
        {
          if (!bus_service_remove_owner (service, old_owner_conn,
                                         transaction, error))
            goto out;
        }
      else
        {
          if (!bus_service_swap_owner (service, old_owner_conn,
                                       transaction, error))
            goto out;
        }
        
    
      _dbus_assert (connection == bus_service_get_primary_owner (service)->conn);
      *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
    }

  activation = bus_context_get_activation (registry->context);
  retval = bus_activation_send_pending_auto_activation_messages (activation,
								 service,
								 transaction);
  if (!retval)
    BUS_SET_OOM (error);
  
 out:
  return retval;
}
Ejemplo n.º 7
0
/* this function is self-cancelling if you cancel the transaction */
dbus_bool_t
bus_service_remove_owner (BusService     *service,
                          DBusConnection *connection,
                          BusTransaction *transaction,
                          DBusError      *error)
{
  BusOwner *primary_owner;
  
  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
  
  /* We send out notifications before we do any work we
   * might have to undo if the notification-sending failed
   */
  
  /* Send service lost message */
  primary_owner = bus_service_get_primary_owner (service);
  if (primary_owner != NULL && primary_owner->conn == connection)
    {
      if (!bus_driver_send_service_lost (connection, service->name,
                                         transaction, error))
        return FALSE;
    }
  else
    {
      /* if we are not the primary owner then just remove us from the queue */
      DBusList *link;
      BusOwner *temp_owner;

      link = _bus_service_find_owner_link (service, connection);
      _dbus_list_unlink (&service->owners, link);
      temp_owner = (BusOwner *)link->data;
      bus_owner_unref (temp_owner); 
      _dbus_list_free_link (link);

      return TRUE; 
    }

  if (service->owners == NULL)
    {
      _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
    }
  else if (_dbus_list_length_is_one (&service->owners))
    {
      if (!bus_driver_send_service_owner_changed (service->name,
 						  bus_connection_get_name (connection),
 						  NULL,
 						  transaction, error))
        return FALSE;
    }
  else
    {
      DBusList *link;
      BusOwner *new_owner;
      DBusConnection *new_owner_conn;
      link = _dbus_list_get_first_link (&service->owners);
      _dbus_assert (link != NULL);
      link = _dbus_list_get_next_link (&service->owners, link);
      _dbus_assert (link != NULL);

      new_owner = (BusOwner *)link->data;
      new_owner_conn = new_owner->conn;

      if (!bus_driver_send_service_owner_changed (service->name,
 						  bus_connection_get_name (connection),
 						  bus_connection_get_name (new_owner_conn),
 						  transaction, error))
        return FALSE;

      /* This will be our new owner */
      if (!bus_driver_send_service_acquired (new_owner_conn,
                                             service->name,
                                             transaction,
                                             error))
        return FALSE;
    }

  if (!add_restore_ownership_to_transaction (transaction, service, primary_owner))
    {
      BUS_SET_OOM (error);
      return FALSE;
    }
 
  bus_service_unlink_owner (service, primary_owner);

  if (service->owners == NULL)
    bus_service_unlink (service);

  return TRUE;
}