service: Load/store AutoConnect and Favorite properties for ethernet
[framework/connectivity/connman.git] / src / service.c
index e74c7ba..becbe53 100644 (file)
@@ -112,26 +112,6 @@ struct connman_service {
        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;
@@ -315,7 +295,6 @@ static int service_load(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:
@@ -376,12 +355,6 @@ static int service_load(struct connman_service *service)
                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) {
@@ -391,6 +364,14 @@ static int service_load(struct connman_service *service)
                        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;
        }
 
@@ -485,7 +466,6 @@ static int service_save(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:
@@ -531,10 +511,6 @@ static int service_save(struct connman_service *service)
                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);
@@ -546,6 +522,12 @@ static int service_save(struct connman_service *service)
                        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;
        }
 
@@ -631,43 +613,6 @@ done:
        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)
@@ -1023,7 +968,8 @@ static void nameserver_add_routes(int index, char **nameservers,
        }
 }
 
-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;
@@ -1042,11 +988,18 @@ static void nameserver_del_routes(int index, char **nameservers)
                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);
        }
@@ -1083,7 +1036,8 @@ void __connman_service_nameserver_add_routes(struct connman_service *service,
        }
 }
 
-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;
 
@@ -1096,9 +1050,10 @@ void __connman_service_nameserver_del_routes(struct connman_service *service)
                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)
@@ -2053,27 +2008,43 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
                                                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);
@@ -2458,11 +2429,11 @@ static int update_proxy_configuration(struct connman_service *service,
                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) {
@@ -2664,8 +2635,15 @@ static DBusMessage *set_property(DBusConnection *conn,
        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);
@@ -2706,7 +2684,8 @@ static DBusMessage *set_property(DBusConnection *conn,
                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);
 
@@ -3291,7 +3270,7 @@ static DBusMessage *move_service(DBusConnection *conn,
                downgrade_state(target);
        }
 
-       services_changed(FALSE);
+       __connman_connection_update_gateway();
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
@@ -3320,18 +3299,23 @@ static DBusMessage *reset_counters(DBusConnection *conn,
 
 static struct _services_notify {
        int id;
-       GSList *added;
-       GSList *removed;
+       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;
-       GSList *list, *next;
-
-       if (services_notify->removed == NULL)
-               return;
 
        signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
                        CONNMAN_MANAGER_INTERFACE, "ServicesRemoved");
@@ -3342,66 +3326,66 @@ static void service_send_removed(void)
        dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
                        DBUS_TYPE_OBJECT_PATH_AS_STRING, &array);
 
-       list = services_notify->removed;
-       services_notify->removed = NULL;
+       g_hash_table_foreach(services_notify->remove, append_removed, &array);
 
-       while (list != NULL) {
-               char *path = list->data;
-               DBG("removing %s", path);
-               next = list->next;
-               dbus_message_iter_append_basic(&array,
-                               DBUS_TYPE_OBJECT_PATH, &list->data);
-               g_free(list->data);
-               g_slist_free_1(list);
-               list = next;
-       }
        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 append_service_structs(DBusMessageIter *iter, void *user_data)
+static void service_send_added_foreach(gpointer data, gpointer user_data)
 {
-       GSList *list = user_data;
-       GSList *next;
+       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);
 
-       while (list != NULL) {
-               struct connman_service *srv = list->data;
-               DBG("adding %s", srv->path);
-               next = list->next;
-               append_struct(list->data, iter);
-               g_slist_free_1(list);
-               list = next;
+       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;
-       GSList *list;
-
-       if (services_notify->added == NULL)
-               return;
 
        signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
                        CONNMAN_MANAGER_INTERFACE, "ServicesAdded");
        if (signal == NULL)
                return;
 
-       list = services_notify->added;
-       services_notify->added = NULL;
        __connman_dbus_append_objpath_dict_array(signal,
-                       append_service_structs, list);
+                       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)
 {
-       service_send_removed();
-       service_send_added();
+       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;
@@ -3419,37 +3403,24 @@ static void service_schedule_added(struct connman_service *service)
 {
        DBG("service %p", service);
 
-       services_notify->added = g_slist_prepend(services_notify->added,
-                       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)
 {
-       GSList *list;
-
-       DBG("service %p", service);
+       DBG("service %p %s", service, service->path);
 
        if (service == NULL || service->path == NULL) {
                DBG("service %p or path is NULL", service);
                return;
        }
 
-       for (list = services_notify->added; list != NULL; list = list->next) {
-               struct connman_service *srv = list->data;
-               if (service == srv) {
-                       DBG("delete service %p from added list", srv);
-                       break;
-               }
-       }
-
-       if (list != NULL)
-               services_notify->added =
-                       g_slist_delete_link(services_notify->added, list);
-
-       services_notify->removed = g_slist_prepend(services_notify->removed,
-                       g_strdup(service->path));
+       g_hash_table_remove(services_notify->add, service->path);
+       g_hash_table_insert(services_notify->remove, g_strdup(service->path),
+                       NULL);
 
        service_schedule_signals();
 }
@@ -3492,7 +3463,7 @@ static void service_free(gpointer user_data)
        service->path = NULL;
 
        if (path != NULL) {
-               services_changed(FALSE);
+               __connman_connection_update_gateway();
 
                g_dbus_unregister_interface(connection, path,
                                                CONNMAN_SERVICE_INTERFACE);
@@ -3852,6 +3823,23 @@ __connman_service_get_ipconfig(struct connman_service *service, int family)
 
 }
 
+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)
@@ -3904,7 +3892,7 @@ int __connman_service_set_favorite(struct connman_service *service,
 
        g_sequence_sort_changed(iter, service_compare, NULL);
 
-       services_changed(FALSE);
+       __connman_connection_update_gateway();
 
        return 0;
 }
@@ -3971,7 +3959,7 @@ static void report_error_cb(struct connman_service *service,
                __connman_service_connect(service);
        else {
                service_complete(service);
-               services_changed(FALSE);
+               __connman_connection_update_gateway();
                __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN);
        }
 }
@@ -4019,7 +4007,7 @@ static void request_input_cb (struct connman_service *service,
 
        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;
        }
@@ -4209,7 +4197,7 @@ static int service_indicate_state(struct connman_service *service)
        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();
@@ -4275,7 +4263,12 @@ int __connman_service_clear_error(struct connman_service *service)
 
 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();
 
@@ -4916,7 +4909,7 @@ static int service_register(struct connman_service *service)
        if (iter != NULL)
                g_sequence_sort_changed(iter, service_compare, NULL);
 
-       services_changed(TRUE);
+       __connman_connection_update_gateway();
 
        return 0;
 }
@@ -5339,7 +5332,7 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
 
        if (service->path != NULL) {
                update_from_network(service, network);
-               services_changed(TRUE);
+               __connman_connection_update_gateway();
                return service;
        }
 
@@ -5544,6 +5537,9 @@ int __connman_service_init(void)
        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;
 }
@@ -5567,6 +5563,8 @@ void __connman_service_cleanup(void)
        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);