Index: src/profile.c |
diff --git a/src/profile.c b/src/profile.c |
index 30fb5cec27866ff5844c5f24e2094c3664dc31f3..80ba988c122a1f2cdece5452545b94db45d890b9 100644 |
--- a/src/profile.c |
+++ b/src/profile.c |
@@ -39,6 +39,7 @@ |
struct connman_profile { |
struct connman_storage_ident ident; |
+ guint changed_timeout; |
char *path; |
char *name; |
connman_bool_t offlinemode; |
@@ -54,8 +55,42 @@ static struct connman_storage_ident default_ident = { |
}; |
static struct connman_profile *default_profile = NULL; |
+static guint changed_timeout = 0; /* for NULL profile handling */ |
+ |
static DBusConnection *connection = NULL; |
+/* |
+ * Loading/Saving objects. |
+ * |
+ * Service objects go to the profile they are pinned to (typically |
+ * the active profile at the time they were created but this can be |
+ * changed, e.g. from private -> global). |
+ * |
+ * Device and ipconfig objects go in the global profile (if any). |
+ * This ensures that enable/disable state is maintained between |
+ * users (and reboots); or possibly discarded (e.g. for testing). |
+ * |
+ * Likewise global state like offline mode is stored in the global |
+ * profile (see above). |
+ */ |
+ |
+/* |
+ * Return the active profile; it's on the top of the stack. |
+ */ |
+static inline struct connman_profile *active_profile(void) |
+{ |
+ return cur_profile >= 0 ? profile_stack[cur_profile] : NULL; |
+} |
+ |
+/* |
+ * Return the global profile; it's top-most non-user profile. |
+ */ |
+static struct connman_profile *global_profile(void) |
+{ |
+ /* TODO(sleffler) cheat for now */ |
+ return default_profile != NULL ? default_profile : NULL; |
+} |
+ |
static int ident_equal(const struct connman_storage_ident *a, |
const struct connman_storage_ident *b) |
{ |
@@ -86,63 +121,72 @@ static void profiles_changed(void) |
connman_bool_t __connman_profile_get_offlinemode(void) |
{ |
- return (default_profile == NULL) ? FALSE : default_profile->offlinemode; |
+ struct connman_profile *profile = global_profile(); |
+ return (profile == NULL) ? FALSE : profile->offlinemode; |
} |
int __connman_profile_set_offlinemode(connman_bool_t offlinemode) |
{ |
- _DBG_PROFILE("offlinemode %d", offlinemode); |
+ struct connman_profile *profile = global_profile(); |
+ int ret; |
+ |
+ _DBG_PROFILE("offlinemode %d profile %p", offlinemode, profile); |
- /* TODO(sleffler) disallow if no default profile? */ |
- if (default_profile != NULL) { |
+ /* NB: always succeeds (ATM) */ |
+ ret = __connman_device_set_offlinemode(offlinemode); |
+ if (ret != 0) |
+ return ret; |
+ |
+ /* TODO(sleffler) sallow even if no global profile? */ |
+ if (profile != NULL) { |
/* |
* OfflineMode is only saved to the default profile; |
* this ensures it is preserved across user changes. |
*/ |
- if (default_profile->offlinemode == offlinemode) |
+ if (profile->offlinemode == offlinemode) |
return -EALREADY; |
- default_profile->offlinemode = offlinemode; |
+ profile->offlinemode = offlinemode; |
connman_dbus_send_property_changed_variant( |
- default_profile->path, |
+ profile->path, |
CONNMAN_PROFILE_INTERFACE, "OfflineMode", |
DBUS_TYPE_BOOLEAN, &offlinemode); |
- } |
- return __connman_device_set_offlinemode(offlinemode); |
-} |
-/* |
- * Return the active profile; it's on the top of the stack. |
- */ |
-static inline struct connman_profile *active_profile(void) |
-{ |
- return cur_profile >= 0 ? profile_stack[cur_profile] : NULL; |
+ __connman_storage_save_profile(profile); |
+ } |
+ return 0; |
} |
-/* |
- * Load/Save objects. When loading we walk the stack of profiles |
- * until we find a successful load. Saving always happens to the |
- * top-most/active profile. |
- */ |
- |
static inline int load_continue(int err) |
{ |
- /* NB: ENXIO for no file, ESRCH for no group/key */ |
+ /* NB: ENXIO for no file, ESRCH for no entry */ |
return (err == -ENXIO || err == -ESRCH); |
} |
int __connman_profile_load_service(struct connman_service *service) |
{ |
- int i, err; |
+ struct connman_profile *profile = |
+ __connman_service_get_profile(service); |
+ int err, i; |
- _DBG_PROFILE("service %p", service); |
+ _DBG_PROFILE("service %p profile %p", service, profile); |
+ if (profile != NULL) |
+ return __connman_storage_load_service(service, &profile->ident); |
+ /* |
+ * Not bound to a profile yet, search the stack for an |
+ * entry and if found bind the profile to the service. |
+ */ |
err = 0; |
for (i = cur_profile; i >= 0; i--) { |
- const struct connman_profile *profile = profile_stack[i]; |
- |
+ profile = profile_stack[i]; |
err = __connman_storage_load_service(service, &profile->ident); |
+ if (err == 0) { |
+ _DBG_PROFILE("bind to profile %p", profile); |
+ __connman_service_set_profile(service, profile); |
+ return 0; |
+ } |
if (!load_continue(err)) |
break; |
} |
@@ -151,34 +195,34 @@ int __connman_profile_load_service(struct connman_service *service) |
int __connman_profile_save_service(struct connman_service *service) |
{ |
- struct connman_profile *profile = active_profile(); |
+ struct connman_profile *profile = |
+ __connman_service_get_profile(service); |
_DBG_PROFILE("service %p profile %p", service, profile); |
+ if (profile == NULL) { |
+ /* not bound yet, bind to the active profile */ |
+ profile = active_profile(); |
+ _DBG_PROFILE("bind to profile %p", profile); |
+ __connman_service_set_profile(service, profile); |
+ } |
return (profile == NULL) ? 0 : |
__connman_storage_save_service(service, &profile->ident); |
} |
int __connman_profile_load_device(struct connman_device *device) |
{ |
- int i, err; |
- |
- _DBG_PROFILE("device %p", device); |
+ struct connman_profile *profile = global_profile(); |
- err = 0; |
- for (i = cur_profile; i >= 0; i--) { |
- const struct connman_profile *profile = profile_stack[i]; |
+ _DBG_PROFILE("device %p profile %p", device, profile); |
- err = __connman_storage_load_device(device, &profile->ident); |
- if (!load_continue(err)) |
- break; |
- } |
- return err; |
+ return (profile == NULL) ? 0 : |
+ __connman_storage_load_device(device, &profile->ident); |
} |
int __connman_profile_save_device(struct connman_device *device) |
{ |
- struct connman_profile *profile = active_profile(); |
+ struct connman_profile *profile = global_profile(); |
_DBG_PROFILE("device %p profile %p", device, profile); |
@@ -188,25 +232,17 @@ int __connman_profile_save_device(struct connman_device *device) |
int __connman_profile_load_ipconfig(struct connman_ipconfig *ipconfig) |
{ |
- int i, err; |
+ struct connman_profile *profile = global_profile(); |
- _DBG_PROFILE("ipconfig %p", ipconfig); |
- |
- err = 0; |
- for (i = cur_profile; i >= 0; i--) { |
- const struct connman_profile *profile = profile_stack[i]; |
+ _DBG_PROFILE("ipconfig %p profile %p", ipconfig, profile); |
- err = __connman_storage_load_ipconfig(ipconfig, |
- &profile->ident); |
- if (!load_continue(err)) |
- break; |
- } |
- return err; |
+ return (profile == NULL) ? 0: |
+ __connman_storage_load_ipconfig(ipconfig, &profile->ident); |
} |
int __connman_profile_save_ipconfig(const struct connman_ipconfig *ipconfig) |
{ |
- struct connman_profile *profile = active_profile(); |
+ struct connman_profile *profile = global_profile(); |
_DBG_PROFILE("ipconfig %p profile %p", ipconfig, profile); |
@@ -236,29 +272,29 @@ int __connman_profile_append_hidden_ssids(GSList **hidden_ssids, |
} |
/* |
- * Save the default profile if registered. |
+ * Return the object path for the specified profile. |
*/ |
-int __connman_profile_save_default(void) |
+const char *__connman_profile_get_path(const struct connman_profile *profile) |
{ |
- if (default_profile != NULL) |
- __connman_storage_save_profile(default_profile); |
- return 0; |
+ return profile != NULL ? profile->path : NULL; |
} |
/* |
- * Save the active profile (if any). |
+ * Return the profile given an object path. |
*/ |
-int __connman_profile_save_active(void) |
+struct connman_profile *__connman_profile_lookup_profile(const char *path) |
{ |
- struct connman_profile *profile = active_profile(); |
- return (profile == NULL) ? -EINVAL : |
- __connman_storage_save_profile(profile); |
+ return g_hash_table_lookup(profile_hash, path); |
} |
/* |
- * Return the identifier for the active profile or NULL |
- * if there is none. |
+ * Return the active profile or NULL |
*/ |
+struct connman_profile *__connman_profile_active_profile(void) |
+{ |
+ return active_profile(); |
+} |
+ |
const struct connman_storage_ident *__connman_profile_active_ident(void) |
{ |
struct connman_profile *profile = active_profile(); |
@@ -275,31 +311,65 @@ const char *__connman_profile_active_path(void) |
return profile != NULL ? profile->path : NULL; |
} |
-static guint changed_timeout = 0; |
+/* |
+ * Delete an entry in the specified profile. |
+ */ |
+int __connman_profile_delete_entry(struct connman_profile *profile, |
+ const char *group) |
+{ |
+ GKeyFile *keyfile; |
+ gboolean status; |
-static void clear_timeout(void) |
+ _DBG_PROFILE("profile %p group %s", profile, group); |
+ |
+ keyfile = __connman_storage_open(&profile->ident); |
+ if (keyfile == NULL) { |
+ _DBG_PROFILE("cannot open key file"); |
+ return -EINVAL; |
+ } |
+ |
+ status = g_key_file_remove_group(keyfile, group, NULL); |
+ __connman_storage_close(&profile->ident, keyfile, status); |
+ |
+ if (status == FALSE) { |
+ _DBG_PROFILE("cannot remove %s", group); |
+ return -ENXIO; |
+ } |
+ return 0; |
+} |
+ |
+static void __clear_timeout(guint *pchanged_timeout) |
{ |
- if (changed_timeout > 0) { |
- g_source_remove(changed_timeout); |
- changed_timeout = 0; |
+ if (*pchanged_timeout > 0) { |
+ g_source_remove(*pchanged_timeout); |
+ *pchanged_timeout = 0; |
} |
} |
+static void clear_timeout(struct connman_profile *profile) |
+{ |
+ if (profile != NULL) |
+ __clear_timeout(&profile->changed_timeout); |
+ else |
+ __clear_timeout(&changed_timeout); |
+} |
+ |
static void append_services(DBusMessageIter *iter, void *arg) |
{ |
__connman_service_list(iter, arg); |
} |
static gboolean services_changed(gpointer user_data) |
{ |
- struct connman_profile *profile = active_profile(); |
- |
- changed_timeout = 0; |
+ struct connman_profile *profile = user_data; |
if (profile != NULL) { |
+ profile->changed_timeout = 0; |
+ |
connman_dbus_send_property_changed_array(profile->path, |
CONNMAN_PROFILE_INTERFACE, "Services", |
DBUS_TYPE_OBJECT_PATH, append_services, NULL); |
- } |
+ } else |
+ changed_timeout = 0; |
connman_dbus_send_property_changed_array(CONNMAN_MANAGER_PATH, |
CONNMAN_MANAGER_INTERFACE, "Services", |
@@ -308,22 +378,29 @@ static gboolean services_changed(gpointer user_data) |
} |
/* |
- * Handle changes to the profile. Generate PropertyChanged signals |
+ * Handle changes to a profile. Generate PropertyChanged signals |
* on Manager.Services and Profile.Services for the currently active |
* profile. To minimize overhead requests may be coalesced using a |
* 1 second delay on the signals. |
*/ |
-void __connman_profile_changed(gboolean delayed) |
+void __connman_profile_changed(struct connman_profile *profile, |
+ gboolean delayed) |
{ |
- _DBG_PROFILE("delayed %d changed_timeout %d", delayed, changed_timeout); |
+ _DBG_PROFILE("profile %p delayed %d changed_timeout %d", |
+ profile, delayed, profile != NULL ? |
+ profile->changed_timeout : changed_timeout); |
- clear_timeout(); |
+ clear_timeout(profile); |
if (delayed == TRUE) { |
- changed_timeout = g_timeout_add_seconds(1, services_changed, |
- NULL); |
+ guint timeout = g_timeout_add_seconds(1, |
+ services_changed, profile); |
+ if (profile != NULL) |
+ profile->changed_timeout = timeout; |
+ else |
+ changed_timeout = timeout; |
} else |
- services_changed(NULL); |
+ services_changed(profile); |
} |
int __connman_profile_add_device(struct connman_device *device) |
@@ -461,7 +538,7 @@ static DBusMessage *get_properties(DBusConnection *conn, |
connman_dbus_dict_append_variant(&dict, "Name", |
DBUS_TYPE_STRING, &profile->name); |
- if (profile == default_profile) |
+ if (profile == global_profile()) |
connman_dbus_dict_append_variant(&dict, "OfflineMode", |
DBUS_TYPE_BOOLEAN, &profile->offlinemode); |
@@ -650,8 +727,7 @@ static DBusMessage *delete_entry(DBusConnection *conn, |
DBusMessageIter iter; |
const char *identifier; |
struct connman_service *service; |
- GKeyFile *keyfile; |
- gboolean status; |
+ int err; |
_DBG_PROFILE("profile %s:%s", profile->ident.user, |
profile->ident.ident); |
@@ -672,19 +748,9 @@ static DBusMessage *delete_entry(DBusConnection *conn, |
} |
/* Remove directly from profile */ |
- keyfile = __connman_storage_open(&profile->ident); |
- if (keyfile == NULL) { |
- _DBG_PROFILE("cannot open key file"); |
- return __connman_error_invalid_arguments(msg); |
- } |
- |
- status = g_key_file_remove_group(keyfile, identifier, NULL); |
- __connman_storage_close(&profile->ident, keyfile, status); |
- |
- if (status == FALSE) { |
- _DBG_PROFILE("cannot remove %s", identifier); |
- return __connman_error_not_found(msg); |
- } |
+ err = __connman_profile_delete_entry(profile, identifier); |
+ if (err) |
+ return __connman_error_failed(msg, -err); |
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); |
} |
@@ -731,6 +797,8 @@ static void unregister_profile(gpointer data) |
g_dbus_unregister_interface(connection, profile->path, |
CONNMAN_PROFILE_INTERFACE); |
+ clear_timeout(profile); |
+ |
if (profile == default_profile) |
default_profile = NULL; |
@@ -950,8 +1018,7 @@ int __connman_profile_push(const char *ident, const char *name, |
profiles_changed(); |
- /* NB: this is a noop if we are online (or trying to get online) */ |
- __connman_service_auto_connect_any(); |
+ __connman_notifier_profile_push(profile); |
return 0; |
} |
@@ -998,15 +1065,20 @@ int __connman_profile_pop(const char *ident) |
free_ident(&sid); |
} |
- __connman_service_invalidate_profile(&profile->ident); |
+ /* |
+ * Pop the stack, invalidate all objects holding references |
+ * to the profile, then remove it from the in-memory table. |
+ * We do the reclaim after notification in case anyone holding |
+ * a reference tries to use it. |
+ */ |
+ cur_profile--; |
+ |
+ __connman_notifier_profile_pop(profile); |
g_hash_table_remove(profile_hash, profile->path); |
- cur_profile--; |
profiles_changed(); |
- /* NB: no need to kick auto-connect; it will happen due to invalidate */ |
- |
return 0; |
} |
@@ -1219,11 +1291,11 @@ void __connman_profile_cleanup(void) |
while (cur_profile >= 0) |
__connman_profile_pop(NULL); |
+ clear_timeout(NULL); /* NB: clear global timer */ |
+ |
g_hash_table_destroy(profile_hash); |
profile_hash = NULL; |
- clear_timeout(); /* cancel any pending timer */ |
- |
connman_storage_unregister(&profile_storage); |
dbus_connection_unref(connection); |