int online_check_count;
};
-static void append_path(gpointer value, gpointer user_data)
-{
- struct connman_service *service = value;
- DBusMessageIter *iter = user_data;
-
- if (service->path == NULL)
- return;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
- &service->path);
-}
-
-void __connman_service_list(DBusMessageIter *iter, void *user_data)
-{
- if (service_list == NULL)
- return;
-
- g_sequence_foreach(service_list, append_path, iter);
-}
-
struct find_data {
const char *path;
struct connman_service *service;
switch (service->type) {
case CONNMAN_SERVICE_TYPE_UNKNOWN:
case CONNMAN_SERVICE_TYPE_SYSTEM:
- case CONNMAN_SERVICE_TYPE_ETHERNET:
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_GADGET:
service->favorite = g_key_file_get_boolean(keyfile,
service->identifier, "Favorite", NULL);
- autoconnect = g_key_file_get_boolean(keyfile,
- service->identifier, "AutoConnect", &error);
- if (error == NULL)
- service->autoconnect = autoconnect;
- g_clear_error(&error);
-
str = g_key_file_get_string(keyfile,
service->identifier, "Failure", NULL);
if (str != NULL) {
service->error = string2error(str);
g_free(str);
}
+ /* fall through */
+
+ case CONNMAN_SERVICE_TYPE_ETHERNET:
+ autoconnect = g_key_file_get_boolean(keyfile,
+ service->identifier, "AutoConnect", &error);
+ if (error == NULL)
+ service->autoconnect = autoconnect;
+ g_clear_error(&error);
break;
}
switch (service->type) {
case CONNMAN_SERVICE_TYPE_UNKNOWN:
case CONNMAN_SERVICE_TYPE_SYSTEM:
- case CONNMAN_SERVICE_TYPE_ETHERNET:
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_GADGET:
g_key_file_set_boolean(keyfile, service->identifier,
"Favorite", service->favorite);
- if (service->favorite == TRUE)
- g_key_file_set_boolean(keyfile, service->identifier,
- "AutoConnect", service->autoconnect);
-
if (service->state_ipv4 == CONNMAN_SERVICE_STATE_FAILURE ||
service->state_ipv6 == CONNMAN_SERVICE_STATE_FAILURE) {
const char *failure = error2string(service->error);
g_key_file_remove_key(keyfile, service->identifier,
"Failure", NULL);
}
+ /* fall through */
+
+ case CONNMAN_SERVICE_TYPE_ETHERNET:
+ if (service->favorite == TRUE)
+ g_key_file_set_boolean(keyfile, service->identifier,
+ "AutoConnect", service->autoconnect);
break;
}
return err;
}
-static guint changed_timeout = 0;
-
-static gboolean notify_services_changed(gpointer user_data)
-{
- changed_timeout = 0;
-
- connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
- CONNMAN_MANAGER_INTERFACE, "Services",
- DBUS_TYPE_OBJECT_PATH, __connman_service_list,
- NULL);
-
- return FALSE;
-}
-
-static void services_changed(gboolean delayed)
-{
- DBG("");
-
- if (changed_timeout > 0) {
- g_source_remove(changed_timeout);
- changed_timeout = 0;
- }
-
- if (__connman_connection_update_gateway() == TRUE) {
- notify_services_changed(NULL);
- return;
- }
-
- if (delayed == FALSE) {
- notify_services_changed(NULL);
- return;
- }
-
- changed_timeout = g_timeout_add_seconds(1, notify_services_changed,
- NULL);
-}
-
static enum connman_service_state combine_state(
enum connman_service_state state_a,
enum connman_service_state state_b)
}
}
-static void nameserver_del_routes(int index, char **nameservers)
+static void nameserver_del_routes(int index, char **nameservers,
+ enum connman_ipconfig_type type)
{
int i, ret, family;
struct addrinfo hints;
else
family = addr->ai_family;
- if (family == AF_INET)
- connman_inet_del_host_route(index, nameservers[i]);
- else if (family == AF_INET6)
- connman_inet_del_ipv6_host_route(index,
+ switch (family) {
+ case AF_INET:
+ if (type != CONNMAN_IPCONFIG_TYPE_IPV6)
+ connman_inet_del_host_route(index,
+ nameservers[i]);
+ break;
+ case AF_INET6:
+ if (type != CONNMAN_IPCONFIG_TYPE_IPV4)
+ connman_inet_del_ipv6_host_route(index,
nameservers[i]);
+ break;
+ }
freeaddrinfo(addr);
}
}
}
-void __connman_service_nameserver_del_routes(struct connman_service *service)
+void __connman_service_nameserver_del_routes(struct connman_service *service,
+ enum connman_ipconfig_type type)
{
int index = -1;
index = connman_provider_get_index(service->provider);
if (service->nameservers_config != NULL)
- nameserver_del_routes(index, service->nameservers_config);
+ nameserver_del_routes(index, service->nameservers_config,
+ type);
else if (service->nameservers != NULL)
- nameserver_del_routes(index, service->nameservers);
+ nameserver_del_routes(index, service->nameservers, type);
}
static struct connman_stats *stats_get(struct connman_service *service)
append_provider, service);
}
-static void append_struct(gpointer value, gpointer user_data)
+static void append_struct_service(DBusMessageIter *iter,
+ connman_dbus_append_cb_t function,
+ struct connman_service *service)
{
- struct connman_service *service = value;
- DBusMessageIter *iter = user_data;
DBusMessageIter entry, dict;
- if (service->path == NULL)
- return;
-
dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry);
dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
&service->path);
connman_dbus_dict_open(&entry, &dict);
- append_properties(&dict, TRUE, service);
+ if (function != NULL)
+ function(&dict, service);
connman_dbus_dict_close(&entry, &dict);
dbus_message_iter_close_container(iter, &entry);
}
+static void append_dict_properties(DBusMessageIter *dict, void *user_data)
+{
+ struct connman_service *service = user_data;
+
+ append_properties(dict, TRUE, service);
+}
+
+static void append_struct(gpointer value, gpointer user_data)
+{
+ struct connman_service *service = value;
+ DBusMessageIter *iter = user_data;
+
+ if (service->path == NULL)
+ return;
+
+ append_struct_service(iter, append_dict_properties, service);
+}
+
void __connman_service_list_struct(DBusMessageIter *iter)
{
g_sequence_foreach(service_list, append_struct, iter);
return NULL;
}
-static void update_timeservers(struct connman_service *service)
-{
- int i;
-
- if (service->timeservers == NULL)
- return;
-
- switch (service->state) {
- case CONNMAN_SERVICE_STATE_UNKNOWN:
- case CONNMAN_SERVICE_STATE_IDLE:
- case CONNMAN_SERVICE_STATE_ASSOCIATION:
- case CONNMAN_SERVICE_STATE_CONFIGURATION:
- return;
- case CONNMAN_SERVICE_STATE_FAILURE:
- case CONNMAN_SERVICE_STATE_DISCONNECT:
- for (i = 0; service->timeservers[i] != NULL; i++)
- connman_timeserver_remove(service->timeservers[i]);
- return;
- case CONNMAN_SERVICE_STATE_READY:
- case CONNMAN_SERVICE_STATE_ONLINE:
- break;
- }
-
- for (i = 0; service->timeservers[i] != NULL; i++)
- connman_timeserver_append(service->timeservers[i]);
-}
-
int __connman_service_timeserver_append(struct connman_service *service,
const char *timeserver)
{
service->timeservers[len] = g_strdup(timeserver);
service->timeservers[len + 1] = NULL;
- update_timeservers(service);
-
return 0;
}
g_strfreev(service->timeservers);
service->timeservers = servers;
- update_timeservers(service);
-
return 0;
}
dbus_message_iter_get_basic(&entry, &key);
dbus_message_iter_next(&entry);
- if (dbus_message_iter_get_arg_type(&entry) !=
- DBUS_TYPE_VARIANT)
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
goto error;
dbus_message_iter_recurse(&entry, &variant);
+
type = dbus_message_iter_get_arg_type(&variant);
if (g_str_equal(key, "Method") == TRUE) {
if (dbus_message_iter_init(msg, &iter) == FALSE)
return __connman_error_invalid_arguments(msg);
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
dbus_message_iter_get_basic(&iter, &name);
dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return __connman_error_invalid_arguments(msg);
+
dbus_message_iter_recurse(&iter, &value);
type = dbus_message_iter_get_arg_type(&value);
gw = __connman_ipconfig_get_gateway_from_index(index);
if (gw && strlen(gw))
- __connman_service_nameserver_del_routes(service);
+ __connman_service_nameserver_del_routes(service,
+ CONNMAN_IPCONFIG_TYPE_ALL);
dbus_message_iter_recurse(&value, &entry);
downgrade_state(target);
}
- services_changed(FALSE);
+ __connman_connection_update_gateway();
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
+static struct _services_notify {
+ int id;
+ GHashTable *add;
+ GHashTable *remove;
+} *services_notify;
+
+static void append_removed(gpointer key, gpointer value, gpointer user_data)
+{
+ char *objpath = key;
+ DBusMessageIter *iter = user_data;
+
+ DBG("removed %s", objpath);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath);
+}
+
+static void service_send_removed(void)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter, array;
+
+ signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+ CONNMAN_MANAGER_INTERFACE, "ServicesRemoved");
+ if (signal == NULL)
+ return;
+
+ dbus_message_iter_init_append(signal, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_OBJECT_PATH_AS_STRING, &array);
+
+ g_hash_table_foreach(services_notify->remove, append_removed, &array);
+
+ dbus_message_iter_close_container(&iter, &array);
+
+ dbus_connection_send(connection, signal, NULL);
+ dbus_message_unref(signal);
+
+ g_hash_table_remove_all(services_notify->remove);
+}
+
+static void service_send_added_foreach(gpointer data, gpointer user_data)
+{
+ struct connman_service *service = data;
+ DBusMessageIter *iter = user_data;
+
+ if (service == NULL || service->path == NULL) {
+ DBG("service %p or path is NULL", service);
+ return;
+ }
+
+ DBG("added %s", service->path);
+
+ if (g_hash_table_lookup(services_notify->add, service->path) != NULL) {
+ append_struct(service, iter);
+ g_hash_table_remove(services_notify->add, service->path);
+ } else {
+ append_struct_service(iter, NULL, service);
+ }
+}
+
+static void service_send_added_ordered(DBusMessageIter *iter, void *user_data)
+{
+ g_sequence_foreach(service_list, service_send_added_foreach, iter);
+}
+
+static void service_send_added(void)
+{
+ DBusMessage *signal;
+
+ signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+ CONNMAN_MANAGER_INTERFACE, "ServicesAdded");
+ if (signal == NULL)
+ return;
+
+ __connman_dbus_append_objpath_dict_array(signal,
+ service_send_added_ordered, NULL);
+
+ dbus_connection_send(connection, signal, NULL);
+ dbus_message_unref(signal);
+
+ g_hash_table_remove_all(services_notify->add);
+}
+
+static gboolean service_send_signals(gpointer data)
+{
+ if (g_hash_table_size(services_notify->remove) > 0)
+ service_send_removed();
+
+ if (g_hash_table_size(services_notify->add) > 0)
+ service_send_added();
+
+ services_notify->id = 0;
+ return FALSE;
+}
+
+static void service_schedule_signals(void)
+{
+ if (services_notify->id != 0)
+ g_source_remove(services_notify->id);
+
+ services_notify->id = g_timeout_add(100, service_send_signals, NULL);
+}
+
+static void service_schedule_added(struct connman_service *service)
+{
+ DBG("service %p", service);
+
+ g_hash_table_remove(services_notify->remove, service->path);
+ g_hash_table_insert(services_notify->add, service->path, service);
+
+ service_schedule_signals();
+}
+
+static void service_schedule_removed(struct connman_service *service)
+{
+ DBG("service %p %s", service, service->path);
+
+ if (service == NULL || service->path == NULL) {
+ DBG("service %p or path is NULL", service);
+ return;
+ }
+
+ g_hash_table_remove(services_notify->add, service->path);
+ g_hash_table_insert(services_notify->remove, g_strdup(service->path),
+ NULL);
+
+ service_schedule_signals();
+}
+
static GDBusMethodTable service_methods[] = {
{ "GetProperties", "", "a{sv}", get_properties },
{ "SetProperty", "sv", "", set_property },
g_hash_table_remove(service_hash, service->identifier);
__connman_notifier_service_remove(service);
+ service_schedule_removed(service);
stats_stop(service);
service->path = NULL;
if (path != NULL) {
- services_changed(FALSE);
+ __connman_connection_update_gateway();
g_dbus_unregister_interface(connection, path,
CONNMAN_SERVICE_INTERFACE);
}
+connman_bool_t __connman_service_is_connected_state(struct connman_service *service,
+ enum connman_ipconfig_type type)
+{
+ if (service == NULL)
+ return FALSE;
+
+ switch (type) {
+ case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
+ break;
+ case CONNMAN_IPCONFIG_TYPE_IPV4:
+ return is_connected_state(service, service->state_ipv4);
+ case CONNMAN_IPCONFIG_TYPE_IPV6:
+ return is_connected_state(service, service->state_ipv6);
+ }
+
+ return FALSE;
+}
enum connman_service_security __connman_service_get_security(struct connman_service *service)
{
if (service == NULL)
g_sequence_sort_changed(iter, service_compare, NULL);
- services_changed(FALSE);
+ __connman_connection_update_gateway();
return 0;
}
__connman_service_connect(service);
else {
service_complete(service);
- services_changed(FALSE);
+ __connman_connection_update_gateway();
__connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN);
}
}
if (values_received == FALSE || service->hidden == TRUE) {
service_complete(service);
- services_changed(FALSE);
+ __connman_connection_update_gateway();
__connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN);
return;
}
if (iter != NULL)
g_sequence_sort_changed(iter, service_compare, NULL);
- services_changed(FALSE);
+ __connman_connection_update_gateway();
if (new_state == CONNMAN_SERVICE_STATE_ONLINE)
default_changed();
int __connman_service_indicate_default(struct connman_service *service)
{
- DBG("service %p", service);
+ struct connman_service *current = get_default();
+
+ DBG("service %p default %p", service, current);
+
+ if (current == service)
+ return 0;
default_changed();
if (iter != NULL)
g_sequence_sort_changed(iter, service_compare, NULL);
- services_changed(TRUE);
+ __connman_connection_update_gateway();
return 0;
}
if (service->path != NULL) {
update_from_network(service, network);
- services_changed(TRUE);
+ __connman_connection_update_gateway();
return service;
}
}
__connman_notifier_service_add(service, service->name);
+ service_schedule_added(service);
return service;
}
service->userconnect = TRUE;
service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
+ service->state = combine_state(service->state_ipv4, service->state_ipv6);
str = connman_provider_get_string(provider, "Name");
if (str != NULL) {
service_register(service);
__connman_notifier_service_add(service, service->name);
+ service_schedule_added(service);
return service;
}
service_list = g_sequence_new(service_free);
+ services_notify = g_new0(struct _services_notify, 1);
+ services_notify->remove = g_hash_table_new_full(g_str_hash,
+ g_str_equal, g_free, NULL);
+ services_notify->add = g_hash_table_new(g_str_hash, g_str_equal);
+
return 0;
}
g_slist_free(counter_list);
counter_list = NULL;
+ if (services_notify->id != 0) {
+ g_source_remove(services_notify->id);
+ service_send_signals(NULL);
+ g_hash_table_destroy(services_notify->remove);
+ g_hash_table_destroy(services_notify->add);
+ }
+ g_free(services_notify);
+
dbus_connection_unref(connection);
}