Chromium Code Reviews| Index: src/profile.c |
| diff --git a/src/profile.c b/src/profile.c |
| index 2bc14c781dd3f73ee03caf0dbf0c613d08927480..5f02377d298caaceb2ba6a56664cf34e75c083b2 100644 |
| --- a/src/profile.c |
| +++ b/src/profile.c |
| @@ -28,24 +28,41 @@ |
| #include <glib.h> |
| #include <gdbus.h> |
| +#include <connman/assert.h> |
| + |
| #include "connman.h" |
| #define PROFILE_DEFAULT_IDENT "default" |
| +#define PROFILE_MAX 3 /* 2 is probably sufficient */ |
| #define _DBG_PROFILE(fmt, arg...) DBG(DBG_PROFILE, fmt, ## arg) |
| struct connman_profile { |
| - char *ident; |
| + struct connman_storage_ident ident; |
| char *path; |
| char *name; |
| connman_bool_t offlinemode; |
| }; |
| static GHashTable *profile_hash = NULL; |
| + |
| +static struct connman_profile *profile_stack[PROFILE_MAX]; |
| +static int cur_profile = -1; |
| + |
| +static struct connman_storage_ident default_ident = { |
| + .ident = PROFILE_DEFAULT_IDENT |
| +}; |
| static struct connman_profile *default_profile = NULL; |
| static DBusConnection *connection = NULL; |
| +static int ident_equal(const struct connman_storage_ident *a, |
| + const struct connman_storage_ident *b) |
| +{ |
| + return (g_strcmp0(a->user, b->user) == 0 && |
| + g_strcmp0(a->ident, b->ident) == 0); |
| +} |
| + |
| static void append_path(gpointer key, gpointer value, gpointer user_data) |
| { |
| struct connman_profile *profile = value; |
| @@ -57,136 +74,256 @@ static void append_path(gpointer key, gpointer value, gpointer user_data) |
| void __connman_profile_list(DBusMessageIter *iter, void *arg) |
| { |
| - _DBG_PROFILE(""); |
| - |
| g_hash_table_foreach(profile_hash, append_path, iter); |
| } |
| static void profiles_changed(void) |
| { |
| - _DBG_PROFILE(""); |
| - |
| connman_dbus_send_property_changed_array(CONNMAN_MANAGER_PATH, |
| CONNMAN_MANAGER_INTERFACE, "Profiles", |
| DBUS_TYPE_OBJECT_PATH, __connman_profile_list, NULL); |
| } |
| -static void name_changed(struct connman_profile *profile) |
| +connman_bool_t __connman_profile_get_offlinemode(void) |
| { |
| - _DBG_PROFILE("profile %p", profile); |
| + return (default_profile == NULL) ? FALSE : default_profile->offlinemode; |
| +} |
| - connman_dbus_send_property_changed_variant(profile->path, |
| - CONNMAN_PROFILE_INTERFACE, "Name", |
| - DBUS_TYPE_STRING, &profile->name); |
| +int __connman_profile_set_offlinemode(connman_bool_t offlinemode) |
| +{ |
| + _DBG_PROFILE("offlinemode %d", offlinemode); |
| + |
| + /* TODO(sleffler) disallow if no default profile? */ |
| + if (default_profile != NULL) { |
| + /* |
| + * OfflineMode is only saved to the default profile; |
| + * this ensures it is preserved across user changes. |
| + */ |
| + if (default_profile->offlinemode == offlinemode) |
| + return -EALREADY; |
| + |
| + default_profile->offlinemode = offlinemode; |
| + |
| + connman_dbus_send_property_changed_variant( |
| + default_profile->path, |
| + CONNMAN_PROFILE_INTERFACE, "OfflineMode", |
| + DBUS_TYPE_BOOLEAN, &offlinemode); |
| + } |
| + return __connman_device_set_offlinemode(offlinemode); |
| } |
| -static void offlinemode_changed(struct connman_profile *profile) |
| +/* |
| + * Return the active profile; it's on the top of the stack. |
| + */ |
| +static inline struct connman_profile *active_profile(void) |
| { |
| - _DBG_PROFILE("profile %p", profile); |
| + return cur_profile >= 0 ? profile_stack[cur_profile] : NULL; |
| +} |
| + |
| +/* |
| + * 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. |
| + */ |
| - connman_dbus_send_property_changed_variant(profile->path, |
| - CONNMAN_PROFILE_INTERFACE, "OfflineMode", |
| - DBUS_TYPE_BOOLEAN, &profile->offlinemode); |
| +static inline int load_continue(int err) |
| +{ |
| + /* NB: ENXIO for no file, ESRCH for no group/key */ |
| + return (err == -ENXIO || err == -ESRCH); |
| } |
| -connman_bool_t __connman_profile_get_offlinemode(void) |
| +int __connman_profile_load_service(struct connman_service *service) |
| { |
| - if (default_profile == NULL) |
| - return FALSE; |
| + int i, err; |
| - _DBG_PROFILE("offlinemode %d", default_profile->offlinemode); |
| + _DBG_PROFILE("service %p", service); |
| - return default_profile->offlinemode; |
| + err = 0; |
| + for (i = cur_profile; i >= 0; i--) { |
| + const struct connman_profile *profile = profile_stack[i]; |
| + |
| + err = __connman_storage_load_service(service, &profile->ident); |
| + if (!load_continue(err)) |
| + break; |
| + } |
| + return err; |
| } |
| -int __connman_profile_set_offlinemode(connman_bool_t offlinemode) |
| +int __connman_profile_save_service(struct connman_service *service) |
| { |
| - _DBG_PROFILE("offlinemode %d", offlinemode); |
| + struct connman_profile *profile = active_profile(); |
| - if (default_profile == NULL) |
| - return -EINVAL; |
| + _DBG_PROFILE("service %p profile %p", service, profile); |
| - if (default_profile->offlinemode == offlinemode) |
| - return -EALREADY; |
| + return (profile == NULL) ? 0 : |
| + __connman_storage_save_service(service, &profile->ident); |
| +} |
| - default_profile->offlinemode = offlinemode; |
| - offlinemode_changed(default_profile); |
| +int __connman_profile_load_device(struct connman_device *device) |
| +{ |
| + int i, err; |
| - __connman_device_set_offlinemode(offlinemode); |
| + _DBG_PROFILE("device %p", device); |
| - return 0; |
| + err = 0; |
| + for (i = cur_profile; i >= 0; i--) { |
| + const struct connman_profile *profile = profile_stack[i]; |
| + |
| + err = __connman_storage_load_device(device, &profile->ident); |
| + if (!load_continue(err)) |
| + break; |
| + } |
| + return err; |
| } |
| -int __connman_profile_save_default(void) |
| +int __connman_profile_save_device(struct connman_device *device) |
| { |
| + struct connman_profile *profile = active_profile(); |
| + |
| + _DBG_PROFILE("device %p profile %p", device, profile); |
| + |
| + return (profile == NULL) ? 0: |
| + __connman_storage_save_device(device, &profile->ident); |
| +} |
| + |
| +int __connman_profile_load_ipconfig(struct connman_ipconfig *ipconfig) |
| +{ |
| + int i, err; |
| + |
| + _DBG_PROFILE("ipconfig %p", ipconfig); |
| + |
| + err = 0; |
| + for (i = cur_profile; i >= 0; i--) { |
| + const struct connman_profile *profile = profile_stack[i]; |
| + |
| + err = __connman_storage_load_ipconfig(ipconfig, |
| + &profile->ident); |
| + if (!load_continue(err)) |
| + break; |
| + } |
|
Eric Shienbrood
2011/03/23 21:14:21
Is it worth factoring out this stack-walking patte
Sam Leffler
2011/03/29 18:25:35
You can't refactor this w/o dealing with the diffe
|
| + return err; |
| +} |
| + |
| +int __connman_profile_save_ipconfig(const struct connman_ipconfig *ipconfig) |
| +{ |
| + struct connman_profile *profile = active_profile(); |
| + |
| + _DBG_PROFILE("ipconfig %p profile %p", ipconfig, profile); |
| + |
| + return (profile == NULL) ? 0 : |
| + __connman_storage_save_ipconfig(ipconfig, &profile->ident); |
| +} |
| + |
| +int __connman_profile_append_hidden_ssids(GSList **hidden_ssids, |
| + void (*append_hidden_ssids)(GKeyFile *keyfile, GSList **hidden_ssids)) |
| +{ |
| + int i; |
| + |
| _DBG_PROFILE(""); |
| + for (i = cur_profile; i >= 0; i--) { |
| + const struct connman_profile *profile = profile_stack[i]; |
| + GKeyFile *keyfile; |
| + |
| + keyfile = __connman_storage_open(&profile->ident); |
| + if (keyfile != NULL) { |
| + append_hidden_ssids(keyfile, hidden_ssids); |
| + __connman_storage_close(&profile->ident, keyfile, |
| + FALSE); |
| + } |
| + } |
| + return 0; |
| +} |
| + |
| +/* |
| + * Save the default profile if registered. |
| + */ |
| +int __connman_profile_save_default(void) |
| +{ |
| if (default_profile != NULL) |
| __connman_storage_save_profile(default_profile); |
| - |
| return 0; |
| } |
| -const char *__connman_profile_active_ident(void) |
| +/* |
| + * Save the active profile (if any). |
| + */ |
| +int __connman_profile_save_active(void) |
| { |
| - _DBG_PROFILE(""); |
| + struct connman_profile *profile = active_profile(); |
| + return (profile == NULL) ? -EINVAL : |
| + __connman_storage_save_profile(profile); |
| +} |
| - return PROFILE_DEFAULT_IDENT; |
| +/* |
| + * Return the identifier for the active profile or NULL |
| + * if there is none. |
| + */ |
| +const struct connman_storage_ident *__connman_profile_active_ident(void) |
| +{ |
| + struct connman_profile *profile = active_profile(); |
| + return profile != NULL ? &profile->ident : NULL; |
| } |
| +/* |
| + * Return the object path for the active profile or NULL |
| + * if there is none. |
| + */ |
| const char *__connman_profile_active_path(void) |
| { |
| - _DBG_PROFILE(""); |
| - |
| - if (default_profile == NULL) |
| - return NULL; |
| - |
| - return default_profile->path; |
| + struct connman_profile *profile = active_profile(); |
| + return profile != NULL ? profile->path : NULL; |
| } |
| static guint changed_timeout = 0; |
| +static void clear_timeout(void) |
| +{ |
| + if (changed_timeout > 0) { |
| + g_source_remove(changed_timeout); |
| + changed_timeout = 0; |
| + } |
| +} |
| + |
| static void append_services(DBusMessageIter *iter, void *arg) |
| { |
| - const struct connman_profile *profile = arg; |
| - if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) == 0) |
| - __connman_service_list(iter, arg); |
| + __connman_service_list(iter, arg); |
| } |
| static gboolean services_changed(gpointer user_data) |
| { |
| - struct connman_profile *profile = default_profile; |
| + struct connman_profile *profile = active_profile(); |
| changed_timeout = 0; |
| if (profile != NULL) { |
| connman_dbus_send_property_changed_array(profile->path, |
| CONNMAN_PROFILE_INTERFACE, "Services", |
| - DBUS_TYPE_OBJECT_PATH, append_services, profile); |
| - |
| - if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) == 0) |
| - connman_dbus_send_property_changed_array( |
| - CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE, |
| - "Services", |
| - DBUS_TYPE_OBJECT_PATH, append_services, profile); |
| + DBUS_TYPE_OBJECT_PATH, append_services, NULL); |
| } |
| + |
| + connman_dbus_send_property_changed_array(CONNMAN_MANAGER_PATH, |
| + CONNMAN_MANAGER_INTERFACE, "Services", |
| + DBUS_TYPE_OBJECT_PATH, append_services, NULL); |
| return FALSE; |
| } |
| +/* |
| + * Handle changes to the 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) |
| { |
| - _DBG_PROFILE(""); |
| + _DBG_PROFILE("delayed %d changed_timeout %d", delayed, changed_timeout); |
| - if (changed_timeout > 0) { |
| - g_source_remove(changed_timeout); |
| - changed_timeout = 0; |
| - } |
| + clear_timeout(); |
| - if (delayed == FALSE) { |
| + if (delayed == TRUE) { |
| + changed_timeout = g_timeout_add_seconds(1, services_changed, |
| + NULL); |
| + } else |
| services_changed(NULL); |
| - return; |
| - } |
| - |
| - changed_timeout = g_timeout_add_seconds(1, services_changed, NULL); |
| } |
| int __connman_profile_add_device(struct connman_device *device) |
| @@ -274,7 +411,7 @@ static void __profile_entry_list(DBusMessageIter *iter, void *arg) |
| _DBG_PROFILE("profile %p", profile); |
| - keyfile = __connman_storage_open(profile->ident); |
| + keyfile = __connman_storage_open(&profile->ident); |
| if (keyfile == NULL) |
| return; |
| @@ -297,7 +434,7 @@ static void __profile_entry_list(DBusMessageIter *iter, void *arg) |
| } |
| } |
| g_strfreev(groups); |
| - __connman_storage_close(profile->ident, keyfile, FALSE); |
| + __connman_storage_close(&profile->ident, keyfile, FALSE); |
| } |
| static DBusMessage *get_properties(DBusConnection *conn, |
| @@ -324,11 +461,13 @@ static DBusMessage *get_properties(DBusConnection *conn, |
| connman_dbus_dict_append_variant(&dict, "Name", |
| DBUS_TYPE_STRING, &profile->name); |
| - connman_dbus_dict_append_variant(&dict, "OfflineMode", |
| - DBUS_TYPE_BOOLEAN, &profile->offlinemode); |
| + if (profile == default_profile) |
| + connman_dbus_dict_append_variant(&dict, "OfflineMode", |
| + DBUS_TYPE_BOOLEAN, &profile->offlinemode); |
| - connman_dbus_dict_append_variant_array(&dict, "Services", |
| - DBUS_TYPE_OBJECT_PATH, __connman_service_list, NULL); |
| + if (profile == active_profile()) |
| + connman_dbus_dict_append_variant_array(&dict, "Services", |
| + DBUS_TYPE_OBJECT_PATH, __connman_service_list, NULL); |
| connman_dbus_dict_append_variant_array(&dict, "Entries", |
| DBUS_TYPE_STRING, __profile_entry_list, profile); |
| @@ -372,23 +511,11 @@ static DBusMessage *set_property(DBusConnection *conn, |
| g_free(profile->name); |
| profile->name = g_strdup(name); |
| - if (profile->name != NULL) |
| - name_changed(profile); |
| - |
| - __connman_storage_save_profile(profile); |
| - } else if (g_str_equal(name, "OfflineMode") == TRUE) { |
| - connman_bool_t offlinemode; |
| - |
| - if (type != DBUS_TYPE_BOOLEAN) |
| - return __connman_error_invalid_arguments(msg); |
| - |
| - dbus_message_iter_get_basic(&value, &offlinemode); |
| - |
| - if (profile->offlinemode == offlinemode) |
| - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); |
| - |
| - profile->offlinemode = offlinemode; |
| - offlinemode_changed(profile); |
| + if (profile->name != NULL) { |
| + connman_dbus_send_property_changed_variant( |
| + profile->path, CONNMAN_PROFILE_INTERFACE, "Name", |
| + DBUS_TYPE_STRING, &profile->name); |
| + } |
| __connman_storage_save_profile(profile); |
| } else |
| @@ -415,6 +542,7 @@ static DBusMessage *get_entry(DBusConnection *conn, |
| DBusMessageIter iter, array, dict; |
| DBusMessage *reply; |
| connman_bool_t autoconnect, hidden_ssid; |
| + int len; |
| _DBG_PROFILE("profile %p", profile); |
| @@ -423,15 +551,16 @@ static DBusMessage *get_entry(DBusConnection *conn, |
| dbus_message_iter_get_basic(&iter, &ident); |
| - keyfile = __connman_storage_open(profile->ident); |
| + keyfile = __connman_storage_open(&profile->ident); |
| if (keyfile == NULL) { |
| - _DBG_PROFILE("cannot open keyfile %s", profile->ident); |
| + _DBG_PROFILE("cannot open keyfile %s:%s", |
| + profile->ident.user, profile->ident.ident); |
| return __connman_error_not_found(msg); /* XXX */ |
| } |
| if (g_key_file_has_group(keyfile, ident) == FALSE) { |
| _DBG_PROFILE("ident %s not found", ident); |
| - __connman_storage_close(profile->ident, keyfile, FALSE); |
| + __connman_storage_close(&profile->ident, keyfile, FALSE); |
| return __connman_error_not_found(msg); |
| } |
| @@ -440,10 +569,11 @@ static DBusMessage *get_entry(DBusConnection *conn, |
| * <type>_<device>_<ssid>_<mode>_<security> for wifi |
| */ |
| tokens = g_strsplit(ident, "_", 0); |
| - /* NB: tokens[0] is NULL if ident is "" */ |
| - if (tokens == NULL || tokens[0] == NULL) { |
| - _DBG_PROFILE("ident %s malformed", ident); |
| - __connman_storage_close(profile->ident, keyfile, FALSE); |
| + len = g_strv_length(tokens); |
| + if (len < 2) { |
| + _DBG_PROFILE("ident %s malformed, len %d", ident, len); |
| + g_strfreev(tokens); |
| + __connman_storage_close(&profile->ident, keyfile, FALSE); |
| return __connman_error_invalid_arguments(msg); |
| } |
| @@ -451,7 +581,7 @@ static DBusMessage *get_entry(DBusConnection *conn, |
| if (reply == NULL) { |
| _DBG_PROFILE("cannot allocate reply"); |
| g_strfreev(tokens); |
| - __connman_storage_close(profile->ident, keyfile, FALSE); |
| + __connman_storage_close(&profile->ident, keyfile, FALSE); |
| return NULL; |
| } |
| @@ -467,14 +597,17 @@ static DBusMessage *get_entry(DBusConnection *conn, |
| switch (__connman_service_string2type(val)) { |
| case CONNMAN_SERVICE_TYPE_WIFI: |
| - if (g_strv_length(tokens) != 5) { |
| - /* TODO(sleffler) accept > 5? */ |
| - _DBG_PROFILE("bad token cnt %d", g_strv_length(tokens)); |
| + if (len != 5 && len != 6) { |
| + _DBG_PROFILE("bad token cnt %d for ident %s", |
| + len, ident); |
| break; |
| } |
| val = tokens[3]; |
| ADD_STR("Mode", &val); |
| val = tokens[4]; |
| + /* NB: g_strsplit breaks 802_1x into 802+1x; restore */ |
| + if (g_strcmp0(val, "802") == 0) |
| + val = "802_1x"; |
| ADD_STR("Security", &val); |
| break; |
| case CONNMAN_SERVICE_TYPE_CELLULAR: |
| @@ -503,7 +636,7 @@ static DBusMessage *get_entry(DBusConnection *conn, |
| dbus_message_iter_close_container(&array, &dict); |
| g_strfreev(tokens); |
| - __connman_storage_close(profile->ident, keyfile, FALSE); |
| + __connman_storage_close(&profile->ident, keyfile, FALSE); |
| return reply; |
| #undef VAL_STR |
| @@ -520,7 +653,8 @@ static DBusMessage *delete_entry(DBusConnection *conn, |
| GKeyFile *keyfile; |
| gboolean status; |
| - _DBG_PROFILE("profile %s", profile->ident); |
| + _DBG_PROFILE("profile %s:%s", profile->ident.user, |
| + profile->ident.ident); |
| if (dbus_message_iter_init(msg, &iter) == FALSE) |
| return __connman_error_invalid_arguments(msg); |
| @@ -538,14 +672,14 @@ static DBusMessage *delete_entry(DBusConnection *conn, |
| } |
| /* Remove directly from profile */ |
| - keyfile = __connman_storage_open(profile->ident); |
| + 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); |
| + __connman_storage_close(&profile->ident, keyfile, status); |
| if (status == FALSE) { |
| _DBG_PROFILE("cannot remove %s", identifier); |
| @@ -568,11 +702,20 @@ static GDBusSignalTable profile_signals[] = { |
| { }, |
| }; |
| +static void free_ident(struct connman_storage_ident *ident) |
| +{ |
| + /* NB: blech, don't reclaim, it's statically allocated */ |
| + if (!ident_equal(ident, &default_ident)) { |
| + g_free(ident->user); |
| + g_free(ident->ident); |
| + } |
| +} |
| + |
| static void free_profile(struct connman_profile *profile) |
| { |
| - g_free(profile->name); |
| + free_ident(&profile->ident); |
| g_free(profile->path); |
| - g_free(profile->ident); |
| + g_free(profile->name); |
| g_free(profile); |
| } |
| @@ -582,32 +725,50 @@ static void unregister_profile(gpointer data) |
| _DBG_PROFILE("profile %p", profile); |
| - connman_info("Removing profile %s", profile->ident); |
| + connman_info("Remove profile %s:%s", profile->ident.user, |
| + profile->ident.ident); |
| g_dbus_unregister_interface(connection, profile->path, |
| CONNMAN_PROFILE_INTERFACE); |
| - if (g_strcmp0(profile->ident, PROFILE_DEFAULT_IDENT) == 0) |
| + if (profile == default_profile) |
| default_profile = NULL; |
| free_profile(profile); |
| } |
| -static int create_profile(const char *ident, const char *name, |
| - const char **path) |
| +static char *getpath(const struct connman_storage_ident *ident) |
| +{ |
| + if (ident->user != NULL) { |
| + /* NB: check for two tokens done in validate_ident */ |
| + return g_strdup_printf("/profile/%s/%s", |
| + ident->user, ident->ident); |
| + } else |
| + return g_strdup_printf("/profile/%s", ident->ident); |
| +} |
| + |
| +/* |
| + * Create an in-memory profile using the specified identifier |
| + * and name. The object path and a reference to the profile |
| + * data structure are returned on success. |
| + */ |
| +static int create_profile(struct connman_storage_ident *ident, |
| + const char *name, const char **path, struct connman_profile **pprofile) |
| { |
| struct connman_profile *profile; |
| - _DBG_PROFILE("ident %s name %s", ident, name); |
| + _DBG_PROFILE("ident %s:%s name %s", ident->user, ident->ident, name); |
| profile = g_try_new0(struct connman_profile, 1); |
| - if (profile == NULL) |
| + if (profile == NULL) { |
| + free_ident(ident); |
| return -ENOMEM; |
| + } |
| - profile->ident = g_strdup(ident); |
| - profile->path = g_strdup_printf("/profile/%s", ident); |
| - |
| - if (profile->ident == NULL || profile->path == NULL) { |
| + profile->ident = *ident; |
| + profile->path = getpath(ident); |
| + /* TODO(sleffler) check ident.user */ |
| + if (profile->ident.ident == NULL || profile->path == NULL) { |
| free_profile(profile); |
| return -ENOMEM; |
| } |
| @@ -623,9 +784,9 @@ static int create_profile(const char *ident, const char *name, |
| g_hash_table_insert(profile_hash, g_strdup(profile->path), profile); |
| - connman_info("Adding profile %s", ident); |
| + connman_info("Add profile %s:%s", ident->user, ident->ident); |
| - if (g_strcmp0(ident, PROFILE_DEFAULT_IDENT) == 0) |
| + if (ident_equal(ident, &default_ident)) |
| default_profile = profile; |
| g_dbus_register_interface(connection, profile->path, |
| @@ -638,10 +799,16 @@ static int create_profile(const char *ident, const char *name, |
| _DBG_PROFILE("profile %p path %s", profile, profile->path); |
| + *pprofile = profile; |
| return 0; |
| } |
| -static gboolean validate_ident(const char *ident) |
| +/* |
| + * Check a profile identifier token. This must be non-null and |
| + * made up of alpha-numeric chars suitable for use in a D-bus |
| + * object path. |
| + */ |
| +static gboolean validate_token(const char *ident) |
| { |
| unsigned int i; |
| unsigned int len = strlen(ident); |
| @@ -662,95 +829,291 @@ static gboolean validate_ident(const char *ident) |
| return TRUE; |
| } |
| -int __connman_profile_create(const char *name, const char **path) |
| +/* |
| + * Validate the profile identifier. It must be suitable |
| + * for use in a D-Bus object path and, optionally, be of |
| + * the form ~user/ident to signify a per-user profile. |
| + */ |
| +static gboolean parse_ident(const char *str, |
| + struct connman_storage_ident *ident) |
| +{ |
| + gboolean is_valid; |
| + |
| + if (str[0] == '~') { |
| + /* syntax is ~user/name for profile in cryptohome */ |
| + gchar **tokens = g_strsplit_set(str, "~/", 4); |
| + if (g_strv_length(tokens) == 3) { |
| + is_valid = (validate_token(tokens[1]) == TRUE && |
| + validate_token(tokens[2]) == TRUE); |
| + if (is_valid) { |
| + ident->user = g_strdup(tokens[1]); |
| + ident->ident = g_strdup(tokens[2]); |
| + } |
| + } else |
| + is_valid = FALSE; |
| + g_strfreev(tokens); |
| + } else { |
| + is_valid = validate_token(str); |
| + if (is_valid) { |
| + ident->user = NULL; |
| + ident->ident = g_strdup(str); |
| + } |
| + } |
| + return is_valid; |
| +} |
| + |
| +/* |
| + * Lookup a profile on the stack by object path. |
| + */ |
| +static struct connman_profile *lookup_stack_by_path(const char *path) |
| +{ |
| + int i; |
| + |
| + for (i = cur_profile; i >= 0; i--) |
| + if (g_strcmp0(profile_stack[i]->path, path) == 0) |
| + return profile_stack[i]; |
| + return NULL; |
| +} |
| + |
| +/* |
| + * Push a profile on the stack and make it the ``active profile''. |
| + * The profile may be currently registered in memory or previously |
| + * created by CreateProfile. The profile may not already be on |
| + * the stack. |
| + */ |
| +int __connman_profile_push(const char *ident, const char *name, |
| + const char **path) |
| { |
| struct connman_profile *profile; |
| + struct connman_storage_ident sid; |
| + char *tmp_path; |
| int err; |
| - _DBG_PROFILE("name %s", name); |
| + _DBG_PROFILE("ident %s name %s", ident, name); |
| - if (validate_ident(name) == FALSE) |
| + if (parse_ident(ident, &sid) == FALSE) { |
| + connman_error("%s: invalid profile name %s", __func__, ident); |
| return -EINVAL; |
| + } |
| - err = create_profile(name, NULL, path); |
| - if (err < 0) |
| - return err; |
| + if (cur_profile+1 >= PROFILE_MAX) { |
| + connman_error("%s: too many profiles (max %d)", __func__, |
| + PROFILE_MAX); |
| + free_ident(&sid); |
| + return -EMFILE; |
| + } |
| - profile = g_hash_table_lookup(profile_hash, *path); |
| - if (profile == NULL) |
| - return -EIO; |
| + /* |
| + * Check for in-memory profile by way of a CreateProfile request. |
| + */ |
| + tmp_path = getpath(&sid); |
| + if (tmp_path == NULL) { |
| + connman_error("%s: no memory for %s", __func__, ident); |
| + free_ident(&sid); |
| + return -ENOMEM; |
| + } |
| - __connman_storage_save_profile(profile); |
| + profile = g_hash_table_lookup(profile_hash, tmp_path); |
| + g_free(tmp_path); |
| + if (profile == NULL) { |
| + /* |
| + * Not in memory; accept an existing file. |
| + */ |
| + if (__connman_storage_exists(&sid) == FALSE) { |
| + connman_error("%s: profile %s does not exist", |
| + __func__, ident); |
| + free_ident(&sid); |
| + return -ENOENT; |
| + } |
| + |
| + err = create_profile(&sid, name, path, &profile); |
| + if (err < 0) { |
| + connman_error("%s: cannot open, error %d", |
| + __func__, err); |
| + /* NB: create_profile reclaims sid */ |
| + return err; |
| + } |
| + } else { |
| + free_ident(&sid); /* NB: not needed below */ |
| + /* |
| + * Check this profile is not already on the stack. |
| + */ |
| + if (lookup_stack_by_path(profile->path) != NULL) { |
| + connman_error("%s: already pushed", __func__); |
| + return -EEXIST; |
| + } |
| + if (path != NULL) |
| + *path = profile->path; |
| + } |
| + |
| + profile_stack[++cur_profile] = profile; |
| profiles_changed(); |
| + /* NB: this is a noop if we are online (or trying to get online) */ |
| + __connman_service_auto_connect_any(); |
| + |
| return 0; |
| } |
| -int __connman_profile_remove(const char *path) |
| +/* |
| + * Pop the profile from the top of the stack and remove it from |
| + * the in-memory table. Any associated services are invalidated |
| + * (credentials revoked and connections closed). After a pop we |
| + * generate a PropertyChanged signal for Manager.Profiles. |
| + * |
| + * An optional identifier may be specified. If specified it is |
| + * checked against the active profile and if not the same then |
| + * the request is rejected. This is useful to ensure the right |
| + * profile is pop'd (as might happen on logout if an associated |
| + * push failed for some reason). |
| + */ |
| +int __connman_profile_pop(const char *ident) |
| { |
| struct connman_profile *profile; |
| - _DBG_PROFILE("path %s", path); |
| + _DBG_PROFILE("ident %s", ident); |
| - if (default_profile != NULL && |
| - g_strcmp0(path, default_profile->path) == 0) |
| + if (cur_profile < 0) { |
| + connman_error("%s: profile stack empty", __func__); |
| return -EINVAL; |
| + } |
| + |
| + profile = profile_stack[cur_profile]; |
| - profile = g_hash_table_lookup(profile_hash, path); |
| - if (profile == NULL) |
| - return -ENXIO; |
| + if (ident != NULL) { |
| + struct connman_storage_ident sid; |
| + |
| + if (parse_ident(ident, &sid) == FALSE) { |
| + connman_error("%s: invalid profile name %s", |
| + __func__, ident); |
| + return -EINVAL; |
| + } |
| + if (ident_equal(&profile->ident, &sid) == FALSE) { |
| + connman_error("%s: %s is not the active profile", |
| + __func__, ident); |
| + free_ident(&sid); |
| + return -ENXIO; |
| + } |
| + free_ident(&sid); |
| + } |
| - __connman_storage_delete(profile->ident); |
| + __connman_service_invalidate_profile(&profile->ident); |
| - g_hash_table_remove(profile_hash, path); |
| + 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; |
| } |
| -static int profile_init(void) |
| +/* |
| + * Create a profile file and register it in memory. The |
| + * file is created with minimal contents. |
| + * TODO(sleffler) disallow overwriting an existing file? |
| + */ |
| +int __connman_profile_create(const char *name, const char **path) |
| { |
| - GDir *dir; |
| - const gchar *file; |
| + struct connman_profile *profile; |
| + struct connman_storage_ident sid; |
| + int err; |
| - _DBG_PROFILE(""); |
| + _DBG_PROFILE("name %s", name); |
| - dir = g_dir_open(STORAGEDIR, 0, NULL); |
| - if (dir != NULL) { |
| - while ((file = g_dir_read_name(dir)) != NULL) { |
| - GString *str; |
| - gchar *ident; |
| + if (parse_ident(name, &sid) == FALSE) { |
| + connman_error("%s: invalid profile name %s)", __func__, name); |
| + return -EINVAL; |
| + } |
| - if (g_str_has_suffix(file, ".profile") == FALSE) |
| - continue; |
| + err = create_profile(&sid, NULL, path, &profile); |
| + if (err < 0) { |
| + connman_error("%s: cannot open, error %d", __func__, err); |
| + /* NB: create_profile reclaims sid */ |
| + return err; |
| + } |
| - ident = g_strrstr(file, ".profile"); |
| - if (ident == NULL) |
| - continue; |
| + __connman_storage_save_profile(profile); |
| + |
| + profiles_changed(); |
| - str = g_string_new_len(file, ident - file); |
| - if (str == NULL) |
| - continue; |
| + return 0; |
| +} |
| - ident = g_string_free(str, FALSE); |
| +/* |
| + * Delete a profile and remove from memory. The default |
| + * profile may not be removed/deleted. Likewise, one |
| + * cannot remove a profile pushed on the stack. |
| + */ |
| +int __connman_profile_remove(const char *ident) |
| +{ |
| + struct connman_profile *profile; |
| + struct connman_storage_ident sid; |
| + char *tmp_path; |
| + int err = 0; |
| - if (validate_ident(ident) == TRUE) |
| - create_profile(ident, NULL, NULL); |
| + _DBG_PROFILE("ident %s", ident); |
| - g_free(ident); |
| + if (parse_ident(ident, &sid) == FALSE) { |
| + connman_error("%s: invalid profile name %s)", __func__, ident); |
| + return -EINVAL; |
| + } |
| + |
| + if (ident_equal(&sid, &default_ident)) { |
| + /* TODO(sleffler) should this be permitted? */ |
| + connman_error("%s: cannot delete default profile", __func__); |
| + err = -EINVAL; |
| + goto done; |
| + } |
| + |
| + /* |
| + * Check for in-memory profile by way of a CreateProfile request. |
| + */ |
| + tmp_path = getpath(&sid); |
| + if (tmp_path == NULL) { |
| + connman_error("%s: no memory for %s", __func__, ident); |
| + err = -ENOMEM; |
| + goto done; |
| + } |
| + profile = g_hash_table_lookup(profile_hash, tmp_path); |
| + g_free(tmp_path); |
| + |
| + if (profile != NULL) { |
| + /* |
| + * Check this profile is not already on the stack. |
|
Eric Shienbrood
2011/03/23 21:14:21
I don't think "already" is right in this context.
Sam Leffler
2011/03/29 18:25:35
Right, cut&pasto fixed.
|
| + */ |
| + if (lookup_stack_by_path(profile->path) != NULL) { |
| + connman_error("%s: already pushed", __func__); |
| + err = -EEXIST; |
| + goto done; |
| } |
| + g_hash_table_remove(profile_hash, profile->path); |
| - g_dir_close(dir); |
| + profiles_changed(); |
| } |
| - if (g_hash_table_size(profile_hash) == 0) |
| - create_profile(PROFILE_DEFAULT_IDENT, "Default", NULL); |
| + __connman_storage_delete(&sid); |
| +done: |
| + free_ident(&sid); |
| + return err; |
| +} |
| - profiles_changed(); |
| +/* |
| + * Initialize the profile stack by pushing the default |
| + * (global) profile. Unlike connman we do not read in |
| + * all available .profile's in the global dir. |
| + */ |
| +static int profile_init(void) |
| +{ |
| + struct connman_profile *profile; |
| + int err; |
| - return 0; |
| + err = create_profile(&default_ident, "Default", NULL, &profile); |
| + if (err == 0) |
| + profile_stack[++cur_profile] = profile; |
| + return err; |
| } |
| static int profile_load(struct connman_profile *profile) |
| @@ -762,7 +1125,7 @@ static int profile_load(struct connman_profile *profile) |
| _DBG_PROFILE("profile %p", profile); |
| - keyfile = __connman_storage_open(profile->ident); |
| + keyfile = __connman_storage_open(&profile->ident); |
| if (keyfile == NULL) |
| return -EIO; |
| @@ -778,7 +1141,7 @@ static int profile_load(struct connman_profile *profile) |
| profile->offlinemode = offlinemode; |
| g_clear_error(&error); |
| - __connman_storage_close(profile->ident, keyfile, FALSE); |
| + __connman_storage_close(&profile->ident, keyfile, FALSE); |
| return 0; |
| } |
| @@ -789,7 +1152,7 @@ static int profile_save(struct connman_profile *profile) |
| _DBG_PROFILE("profile %p", profile); |
| - keyfile = __connman_storage_open(profile->ident); |
| + keyfile = __connman_storage_open(&profile->ident); |
| if (keyfile == NULL) |
| return -EIO; |
| @@ -800,7 +1163,7 @@ static int profile_save(struct connman_profile *profile) |
| g_key_file_set_boolean(keyfile, "global", |
| "OfflineMode", profile->offlinemode); |
| - __connman_storage_close(profile->ident, keyfile, TRUE); |
| + __connman_storage_close(&profile->ident, keyfile, TRUE); |
| return 0; |
| } |
| @@ -830,6 +1193,21 @@ int __connman_profile_init(void) |
| return 0; |
| } |
| +int __connman_profile_push_batch(char **profiles) |
| +{ |
| + int i; |
| + |
| + if (profiles == NULL) |
| + return 0; |
| + |
| + for (i = 0; profiles[i] != NULL; i++) { |
| + int err = __connman_profile_push(profiles[i], NULL, NULL); |
| + if (err != 0) |
| + return err; |
| + } |
| + return 0; |
| +} |
| + |
| void __connman_profile_cleanup(void) |
| { |
| _DBG_PROFILE(""); |
| @@ -837,9 +1215,14 @@ void __connman_profile_cleanup(void) |
| if (connection == NULL) |
| return; |
| + while (cur_profile >= 0) |
| + __connman_profile_pop(NULL); |
| + |
| g_hash_table_destroy(profile_hash); |
| profile_hash = NULL; |
| + clear_timeout(); /* cancel any pending timer */ |
| + |
| connman_storage_unregister(&profile_storage); |
| dbus_connection_unref(connection); |