service: Allow state downgrade from online to ready
[platform/upstream/connman.git] / src / service.c
index 7cd21b5..0380dbe 100644 (file)
@@ -2912,6 +2912,22 @@ static DBusMessage *remove_service(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
+static gboolean check_suitable_state(enum connman_service_state a,
+                                       enum connman_service_state b)
+{
+       /*
+        * Special check so that "ready" service can be moved before
+        * "online" one.
+        */
+       if ((a == CONNMAN_SERVICE_STATE_ONLINE &&
+                       b == CONNMAN_SERVICE_STATE_READY) ||
+               (b == CONNMAN_SERVICE_STATE_ONLINE &&
+                       a == CONNMAN_SERVICE_STATE_READY))
+               return TRUE;
+
+       return a == b;
+}
+
 static DBusMessage *move_service(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data,
                                                                gboolean before)
@@ -2920,6 +2936,8 @@ static DBusMessage *move_service(DBusConnection *conn,
        struct connman_service *target;
        const char *path;
        GSequenceIter *src, *dst;
+       enum connman_ipconfig_method target4, target6;
+       enum connman_ipconfig_method service4, service6;
 
        DBG("service %p", service);
 
@@ -2933,11 +2951,54 @@ static DBusMessage *move_service(DBusConnection *conn,
        if (target == NULL || target->favorite == FALSE || target == service)
                return __connman_error_invalid_service(msg);
 
-       DBG("target %s", target->identifier);
+       target4 = __connman_ipconfig_get_method(target->ipconfig_ipv4);
+       target6 = __connman_ipconfig_get_method(target->ipconfig_ipv6);
+       service4 = __connman_ipconfig_get_method(service->ipconfig_ipv4);
+       service6 = __connman_ipconfig_get_method(service->ipconfig_ipv6);
 
-       if (target->state_ipv4 != service->state_ipv4 &&
-                               target->state_ipv6 != service->state_ipv6)
-               return __connman_error_invalid_service(msg);
+       DBG("target %s method %d/%d state %d/%d", target->identifier,
+                               target4, target6,
+                               target->state_ipv4, target->state_ipv6);
+
+       DBG("service %s method %d/%d state %d/%d", service->identifier,
+                               service4, service6,
+                               service->state_ipv4, service->state_ipv6);
+
+       /*
+        * If method is OFF, then we do not need to check the corresponding
+        * ipconfig state.
+        */
+       if (target4 == CONNMAN_IPCONFIG_METHOD_OFF) {
+               if (service6 != CONNMAN_IPCONFIG_METHOD_OFF) {
+                       if (check_suitable_state(target->state_ipv6,
+                                               service->state_ipv6) == FALSE)
+                               return __connman_error_invalid_service(msg);
+               }
+       }
+
+       if (target6 == CONNMAN_IPCONFIG_METHOD_OFF) {
+               if (service4 != CONNMAN_IPCONFIG_METHOD_OFF) {
+                       if (check_suitable_state(target->state_ipv4,
+                                               service->state_ipv4) == FALSE)
+                               return __connman_error_invalid_service(msg);
+               }
+       }
+
+       if (service4 == CONNMAN_IPCONFIG_METHOD_OFF) {
+               if (target6 != CONNMAN_IPCONFIG_METHOD_OFF) {
+                       if (check_suitable_state(target->state_ipv6,
+                                               service->state_ipv6) == FALSE)
+                               return __connman_error_invalid_service(msg);
+               }
+       }
+
+       if (service6 == CONNMAN_IPCONFIG_METHOD_OFF) {
+               if (target4 != CONNMAN_IPCONFIG_METHOD_OFF) {
+                       if (check_suitable_state(target->state_ipv4,
+                                               service->state_ipv4) == FALSE)
+                               return __connman_error_invalid_service(msg);
+               }
+       }
 
        g_get_current_time(&service->modified);
        __connman_storage_save_service(service);
@@ -4623,57 +4684,70 @@ static void setup_ip6config(struct connman_service *service, int index)
        connman_ipconfig_set_ops(service->ipconfig_ipv6, &service_ops);
 }
 
-void __connman_service_create_ip4config(struct connman_service *service,
-                                                               int index)
+void __connman_service_read_ip4config(struct connman_service *service)
 {
        const char *ident = service->profile;
        GKeyFile *keyfile;
 
+       if (ident == NULL)
+               return;
+
+       if (service->ipconfig_ipv4 == NULL)
+               return;
+
+       keyfile = __connman_storage_open_profile(ident);
+       if (keyfile == NULL)
+               return;
+
+       __connman_ipconfig_load(service->ipconfig_ipv4, keyfile,
+                               service->identifier, "IPv4.");
+
+       g_key_file_free(keyfile);
+}
+
+void __connman_service_create_ip4config(struct connman_service *service,
+                                       int index)
+{
        DBG("ipv4 %p", service->ipconfig_ipv4);
 
        if (service->ipconfig_ipv4 != NULL)
                return;
 
        setup_ip4config(service, index, CONNMAN_IPCONFIG_METHOD_DHCP);
+       __connman_service_read_ip4config(service);
+}
+
+void __connman_service_read_ip6config(struct connman_service *service)
+{
+       const char *ident = service->profile;
+       GKeyFile *keyfile;
 
        if (ident == NULL)
                return;
+       if (service->ipconfig_ipv6 == NULL)
+               return;
 
        keyfile = __connman_storage_open_profile(ident);
+
        if (keyfile == NULL)
                return;
 
-       if (service->ipconfig_ipv4)
-               __connman_ipconfig_load(service->ipconfig_ipv4, keyfile,
-                                       service->identifier, "IPv4.");
+       __connman_ipconfig_load(service->ipconfig_ipv6, keyfile,
+                               service->identifier, "IPv6.");
+
        g_key_file_free(keyfile);
 }
 
 void __connman_service_create_ip6config(struct connman_service *service,
                                                                int index)
 {
-       const char *ident = service->profile;
-       GKeyFile *keyfile;
-
        DBG("ipv6 %p", service->ipconfig_ipv6);
 
        if (service->ipconfig_ipv6 != NULL)
                return;
 
        setup_ip6config(service, index);
-
-       if (ident == NULL)
-               return;
-
-       keyfile = __connman_storage_open_profile(ident);
-       if (keyfile == NULL)
-               return;
-
-       if (service->ipconfig_ipv6 != NULL)
-               __connman_ipconfig_load(service->ipconfig_ipv6, keyfile,
-                                       service->identifier, "IPv6.");
-
-       g_key_file_free(keyfile);
+       __connman_service_read_ip6config(service);
 }
 
 /**
@@ -5084,6 +5158,25 @@ __connman_service_create_from_provider(struct connman_provider *provider)
        return service;
 }
 
+void __connman_service_downgrade_state(struct connman_service *service)
+{
+       if (service == NULL)
+               return;
+
+       DBG("service %p state4 %d state6 %d", service, service->state_ipv4,
+                                               service->state_ipv6);
+
+       if (service->state_ipv4 == CONNMAN_SERVICE_STATE_ONLINE)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       if (service->state_ipv6 == CONNMAN_SERVICE_STATE_ONLINE)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+}
+
 static int service_load(struct connman_service *service)
 {
        const char *ident = service->profile;
@@ -5387,13 +5480,26 @@ update:
                g_key_file_remove_key(keyfile, service->identifier,
                                                        "Passphrase", NULL);
 
-       if (service->ipconfig_ipv4 != NULL)
-               __connman_ipconfig_save(service->ipconfig_ipv4, keyfile,
-                                       service->identifier, "IPv4.");
+       switch (service->state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+               break;
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               if (service->ipconfig_ipv4 != NULL)
+                       __connman_ipconfig_save(service->ipconfig_ipv4,
+                                               keyfile, service->identifier,
+                                               "IPv4.");
 
-       if (service->ipconfig_ipv6 != NULL)
-               __connman_ipconfig_save(service->ipconfig_ipv6, keyfile,
-                                               service->identifier, "IPv6.");
+               if (service->ipconfig_ipv6 != NULL)
+                       __connman_ipconfig_save(service->ipconfig_ipv6,
+                                               keyfile, service->identifier,
+                                               "IPv6.");
+       }
 
        if (service->nameservers_config != NULL) {
                guint len = g_strv_length(service->nameservers_config);