Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Unified Diff: src/profile.c

Issue 6659006: flimflam: add support for multiple profiles (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/flimflam.git@master
Patch Set: more ers comments Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/manager.c ('k') | src/service.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/profile.c
diff --git a/src/profile.c b/src/profile.c
index 2bc14c781dd3f73ee03caf0dbf0c613d08927480..30fb5cec27866ff5844c5f24e2094c3664dc31f3 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;
+ }
+ 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,292 @@ 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 on the stack.
+ */
+ if (lookup_stack_by_path(profile->path) != NULL) {
+ connman_error("%s: cannot delete (on the stack)",
+ __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 +1126,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 +1142,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 +1153,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 +1164,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 +1194,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 +1216,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);
« no previous file with comments | « src/manager.c ('k') | src/service.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698