| /* |
| * |
| * Connection Manager |
| * |
| * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <sys/vfs.h> |
| #include <sys/inotify.h> |
| #include <glib.h> |
| |
| #include <connman/provision.h> |
| #include "connman.h" |
| |
| struct connman_config_service { |
| char *ident; |
| char *name; |
| char *type; |
| void *ssid; |
| unsigned int ssid_len; |
| char *eap; |
| char *identity; |
| char *ca_cert_file; |
| char *client_cert_file; |
| char *private_key_file; |
| char *private_key_passphrase; |
| char *private_key_passphrase_type; |
| char *phase2; |
| char *passphrase; |
| GSList *service_identifiers; |
| char *config_ident; /* file prefix */ |
| char *config_entry; /* entry name */ |
| connman_bool_t hidden; |
| }; |
| |
| struct connman_config { |
| char *ident; |
| char *name; |
| char *description; |
| connman_bool_t protected; |
| GHashTable *service_table; |
| }; |
| |
| static GHashTable *config_table = NULL; |
| static GSList *protected_services = NULL; |
| |
| static connman_bool_t cleanup = FALSE; |
| |
| #define INTERNAL_CONFIG_PREFIX "__internal" |
| |
| /* Definition of possible strings in the .config files */ |
| #define CONFIG_KEY_NAME "Name" |
| #define CONFIG_KEY_DESC "Description" |
| #define CONFIG_KEY_PROT "Protected" |
| |
| #define SERVICE_KEY_TYPE "Type" |
| #define SERVICE_KEY_NAME "Name" |
| #define SERVICE_KEY_SSID "SSID" |
| #define SERVICE_KEY_EAP "EAP" |
| #define SERVICE_KEY_CA_CERT "CACertFile" |
| #define SERVICE_KEY_CL_CERT "ClientCertFile" |
| #define SERVICE_KEY_PRV_KEY "PrivateKeyFile" |
| #define SERVICE_KEY_PRV_KEY_PASS "PrivateKeyPassphrase" |
| #define SERVICE_KEY_PRV_KEY_PASS_TYPE "PrivateKeyPassphraseType" |
| #define SERVICE_KEY_IDENTITY "Identity" |
| #define SERVICE_KEY_PHASE2 "Phase2" |
| #define SERVICE_KEY_PASSPHRASE "Passphrase" |
| #define SERVICE_KEY_HIDDEN "Hidden" |
| |
| static const char *config_possible_keys[] = { |
| CONFIG_KEY_NAME, |
| CONFIG_KEY_DESC, |
| CONFIG_KEY_PROT, |
| NULL, |
| }; |
| |
| static const char *service_possible_keys[] = { |
| SERVICE_KEY_TYPE, |
| SERVICE_KEY_NAME, |
| SERVICE_KEY_SSID, |
| SERVICE_KEY_EAP, |
| SERVICE_KEY_CA_CERT, |
| SERVICE_KEY_CL_CERT, |
| SERVICE_KEY_PRV_KEY, |
| SERVICE_KEY_PRV_KEY_PASS, |
| SERVICE_KEY_PRV_KEY_PASS_TYPE, |
| SERVICE_KEY_IDENTITY, |
| SERVICE_KEY_PHASE2, |
| SERVICE_KEY_PASSPHRASE, |
| SERVICE_KEY_HIDDEN, |
| NULL, |
| }; |
| |
| static void unregister_config(gpointer data) |
| { |
| struct connman_config *config = data; |
| |
| connman_info("Removing configuration %s", config->ident); |
| |
| g_hash_table_destroy(config->service_table); |
| |
| g_free(config->description); |
| g_free(config->name); |
| g_free(config->ident); |
| g_free(config); |
| } |
| |
| static void unregister_service(gpointer data) |
| { |
| struct connman_config_service *config_service = data; |
| struct connman_service *service; |
| char *service_id; |
| GSList *list; |
| |
| if (cleanup == TRUE) |
| goto free_only; |
| |
| connman_info("Removing service configuration %s", |
| config_service->ident); |
| |
| protected_services = g_slist_remove(protected_services, |
| config_service); |
| |
| for (list = config_service->service_identifiers; list != NULL; |
| list = list->next) { |
| service_id = list->data; |
| |
| service = __connman_service_lookup_from_ident(service_id); |
| if (service != NULL) { |
| __connman_service_set_immutable(service, FALSE); |
| __connman_service_remove(service); |
| } |
| |
| if (__connman_storage_remove_service(service_id) == FALSE) |
| DBG("Could not remove all files for service %s", |
| service_id); |
| } |
| |
| free_only: |
| g_free(config_service->ident); |
| g_free(config_service->type); |
| g_free(config_service->name); |
| g_free(config_service->ssid); |
| g_free(config_service->eap); |
| g_free(config_service->identity); |
| g_free(config_service->ca_cert_file); |
| g_free(config_service->client_cert_file); |
| g_free(config_service->private_key_file); |
| g_free(config_service->private_key_passphrase); |
| g_free(config_service->private_key_passphrase_type); |
| g_free(config_service->phase2); |
| g_free(config_service->passphrase); |
| g_slist_free_full(config_service->service_identifiers, g_free); |
| g_free(config_service->config_ident); |
| g_free(config_service->config_entry); |
| g_free(config_service); |
| } |
| |
| static void check_keys(GKeyFile *keyfile, const char *group, |
| const char **possible_keys) |
| { |
| char **avail_keys; |
| gsize nb_avail_keys, i, j; |
| |
| avail_keys = g_key_file_get_keys(keyfile, group, &nb_avail_keys, NULL); |
| if (avail_keys == NULL) |
| return; |
| |
| /* |
| * For each key in the configuration file, |
| * verify it is understood by connman |
| */ |
| for (i = 0 ; i < nb_avail_keys; i++) { |
| for (j = 0; possible_keys[j] ; j++) |
| if (g_strcmp0(avail_keys[i], possible_keys[j]) == 0) |
| break; |
| |
| if (possible_keys[j] == NULL) |
| connman_warn("Unknown configuration key %s in [%s]", |
| avail_keys[i], group); |
| } |
| |
| g_strfreev(avail_keys); |
| } |
| |
| static connman_bool_t |
| is_protected_service(struct connman_config_service *service) |
| { |
| GSList *list; |
| |
| DBG("ident %s", service->ident); |
| |
| for (list = protected_services; list; list = list->next) { |
| struct connman_config_service *s = list->data; |
| |
| if (g_strcmp0(s->type, service->type) != 0) |
| continue; |
| |
| if (s->ssid == NULL || service->ssid == NULL) |
| continue; |
| |
| if (s->ssid_len != service->ssid_len) |
| continue; |
| |
| if (g_strcmp0(service->type, "wifi") == 0 && |
| strncmp(s->ssid, service->ssid, s->ssid_len) == 0) { |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| static int load_service(GKeyFile *keyfile, const char *group, |
| struct connman_config *config) |
| { |
| struct connman_config_service *service; |
| const char *ident; |
| char *str, *hex_ssid; |
| gboolean service_created = FALSE; |
| int err; |
| |
| /* Strip off "service_" prefix */ |
| ident = group + 8; |
| |
| if (strlen(ident) < 1) |
| return -EINVAL; |
| |
| /* Verify that provided keys are good */ |
| check_keys(keyfile, group, service_possible_keys); |
| |
| service = g_hash_table_lookup(config->service_table, ident); |
| if (service == NULL) { |
| service = g_try_new0(struct connman_config_service, 1); |
| if (service == NULL) |
| return -ENOMEM; |
| |
| service->ident = g_strdup(ident); |
| |
| service_created = TRUE; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, SERVICE_KEY_TYPE, NULL); |
| if (str != NULL) { |
| g_free(service->type); |
| service->type = str; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, SERVICE_KEY_NAME, NULL); |
| if (str != NULL) { |
| g_free(service->name); |
| service->name = str; |
| } |
| |
| hex_ssid = g_key_file_get_string(keyfile, group, SERVICE_KEY_SSID, |
| NULL); |
| if (hex_ssid != NULL) { |
| char *ssid; |
| unsigned int i, j = 0, hex; |
| size_t hex_ssid_len = strlen(hex_ssid); |
| |
| ssid = g_try_malloc0(hex_ssid_len / 2); |
| if (ssid == NULL) { |
| err = -ENOMEM; |
| g_free(hex_ssid); |
| goto err; |
| } |
| |
| for (i = 0; i < hex_ssid_len; i += 2) { |
| if (sscanf(hex_ssid + i, "%02x", &hex) <= 0) { |
| connman_warn("Invalid SSID %s", hex_ssid); |
| g_free(ssid); |
| g_free(hex_ssid); |
| err = -EILSEQ; |
| goto err; |
| } |
| ssid[j++] = hex; |
| } |
| |
| g_free(hex_ssid); |
| |
| g_free(service->ssid); |
| service->ssid = ssid; |
| service->ssid_len = hex_ssid_len / 2; |
| } else if (service->name != NULL) { |
| char *ssid; |
| unsigned int ssid_len; |
| |
| ssid_len = strlen(service->name); |
| ssid = g_try_malloc0(ssid_len); |
| if (ssid == NULL) { |
| err = -ENOMEM; |
| goto err; |
| } |
| |
| memcpy(ssid, service->name, ssid_len); |
| g_free(service->ssid); |
| service->ssid = ssid; |
| service->ssid_len = ssid_len; |
| } |
| |
| if (is_protected_service(service) == TRUE) { |
| connman_error("Trying to provision a protected service"); |
| err = -EACCES; |
| goto err; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, SERVICE_KEY_EAP, NULL); |
| if (str != NULL) { |
| g_free(service->eap); |
| service->eap = str; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, SERVICE_KEY_CA_CERT, NULL); |
| if (str != NULL) { |
| g_free(service->ca_cert_file); |
| service->ca_cert_file = str; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, SERVICE_KEY_CL_CERT, NULL); |
| if (str != NULL) { |
| g_free(service->client_cert_file); |
| service->client_cert_file = str; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, SERVICE_KEY_PRV_KEY, NULL); |
| if (str != NULL) { |
| g_free(service->private_key_file); |
| service->private_key_file = str; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, |
| SERVICE_KEY_PRV_KEY_PASS, NULL); |
| if (str != NULL) { |
| g_free(service->private_key_passphrase); |
| service->private_key_passphrase = str; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, |
| SERVICE_KEY_PRV_KEY_PASS_TYPE, NULL); |
| if (str != NULL) { |
| g_free(service->private_key_passphrase_type); |
| service->private_key_passphrase_type = str; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, SERVICE_KEY_IDENTITY, NULL); |
| if (str != NULL) { |
| g_free(service->identity); |
| service->identity = str; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, SERVICE_KEY_PHASE2, NULL); |
| if (str != NULL) { |
| g_free(service->phase2); |
| service->phase2 = str; |
| } |
| |
| str = g_key_file_get_string(keyfile, group, SERVICE_KEY_PASSPHRASE, |
| NULL); |
| if (str != NULL) { |
| g_free(service->passphrase); |
| service->passphrase = str; |
| } |
| |
| service->config_ident = g_strdup(config->ident); |
| service->config_entry = g_strdup_printf("service_%s", service->ident); |
| |
| service->hidden = g_key_file_get_boolean(keyfile, group, |
| SERVICE_KEY_HIDDEN, NULL); |
| |
| if (service_created) |
| g_hash_table_insert(config->service_table, service->ident, |
| service); |
| |
| if (config->protected == TRUE) |
| protected_services = |
| g_slist_prepend(protected_services, service); |
| |
| connman_info("Adding service configuration %s", service->ident); |
| |
| return 0; |
| |
| err: |
| if (service_created == TRUE) { |
| g_free(service->ident); |
| g_free(service->type); |
| g_free(service->name); |
| g_free(service->ssid); |
| g_free(service); |
| } |
| |
| return err; |
| } |
| |
| static int load_config(struct connman_config *config) |
| { |
| GKeyFile *keyfile; |
| GError *error = NULL; |
| gsize length; |
| char **groups; |
| char *str; |
| gboolean protected, found = FALSE; |
| int i; |
| |
| DBG("config %p", config); |
| |
| keyfile = __connman_storage_load_config(config->ident); |
| if (keyfile == NULL) |
| return -EIO; |
| |
| /* Verify keys validity of the global section */ |
| check_keys(keyfile, "global", config_possible_keys); |
| |
| str = g_key_file_get_string(keyfile, "global", CONFIG_KEY_NAME, NULL); |
| if (str != NULL) { |
| g_free(config->name); |
| config->name = str; |
| } |
| |
| str = g_key_file_get_string(keyfile, "global", CONFIG_KEY_DESC, NULL); |
| if (str != NULL) { |
| g_free(config->description); |
| config->description = str; |
| } |
| |
| protected = g_key_file_get_boolean(keyfile, "global", |
| CONFIG_KEY_PROT, &error); |
| if (error == NULL) |
| config->protected = protected; |
| else |
| config->protected = TRUE; |
| g_clear_error(&error); |
| |
| groups = g_key_file_get_groups(keyfile, &length); |
| |
| for (i = 0; groups[i] != NULL; i++) { |
| if (g_str_has_prefix(groups[i], "service_") == TRUE) { |
| if (load_service(keyfile, groups[i], config) == 0) |
| found = TRUE; |
| } |
| } |
| |
| if (found == FALSE) |
| connman_warn("Config file %s/%s.config does not contain any " |
| "configuration that can be provisioned!", |
| STORAGEDIR, config->ident); |
| |
| g_strfreev(groups); |
| |
| g_key_file_free(keyfile); |
| |
| return 0; |
| } |
| |
| static struct connman_config *create_config(const char *ident) |
| { |
| struct connman_config *config; |
| |
| DBG("ident %s", ident); |
| |
| if (g_hash_table_lookup(config_table, ident) != NULL) |
| return NULL; |
| |
| config = g_try_new0(struct connman_config, 1); |
| if (config == NULL) |
| return NULL; |
| |
| config->ident = g_strdup(ident); |
| |
| config->service_table = g_hash_table_new_full(g_str_hash, g_str_equal, |
| NULL, unregister_service); |
| |
| g_hash_table_insert(config_table, config->ident, config); |
| |
| connman_info("Adding configuration %s", config->ident); |
| |
| return config; |
| } |
| |
| static connman_bool_t validate_ident(const char *ident) |
| { |
| unsigned int i; |
| |
| if (ident == NULL) |
| return FALSE; |
| |
| for (i = 0; i < strlen(ident); i++) |
| if (g_ascii_isprint(ident[i]) == FALSE) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| static int read_configs(void) |
| { |
| GDir *dir; |
| |
| DBG(""); |
| |
| dir = g_dir_open(STORAGEDIR, 0, NULL); |
| if (dir != NULL) { |
| const gchar *file; |
| |
| while ((file = g_dir_read_name(dir)) != NULL) { |
| GString *str; |
| gchar *ident; |
| |
| if (g_str_has_suffix(file, ".config") == FALSE) |
| continue; |
| |
| ident = g_strrstr(file, ".config"); |
| if (ident == NULL) |
| continue; |
| |
| str = g_string_new_len(file, ident - file); |
| if (str == NULL) |
| continue; |
| |
| ident = g_string_free(str, FALSE); |
| |
| if (validate_ident(ident) == TRUE) { |
| struct connman_config *config; |
| |
| config = create_config(ident); |
| if (config != NULL) |
| load_config(config); |
| } else { |
| connman_error("Invalid config ident %s", ident); |
| } |
| g_free(ident); |
| } |
| |
| g_dir_close(dir); |
| } |
| |
| return 0; |
| } |
| |
| static void config_notify_handler(struct inotify_event *event, |
| const char *ident) |
| { |
| char *ext; |
| |
| if (ident == NULL) |
| return; |
| |
| if (g_str_has_suffix(ident, ".config") == FALSE) |
| return; |
| |
| ext = g_strrstr(ident, ".config"); |
| if (ext == NULL) |
| return; |
| |
| *ext = '\0'; |
| |
| if (validate_ident(ident) == FALSE) { |
| connman_error("Invalid config ident %s", ident); |
| return; |
| } |
| |
| if (event->mask & IN_CREATE) |
| create_config(ident); |
| |
| if (event->mask & IN_MODIFY) { |
| struct connman_config *config; |
| |
| config = g_hash_table_lookup(config_table, ident); |
| if (config != NULL) { |
| int ret; |
| |
| g_hash_table_remove_all(config->service_table); |
| load_config(config); |
| ret = __connman_service_provision_changed(ident); |
| if (ret > 0) { |
| /* |
| * Re-scan the config file for any |
| * changes |
| */ |
| g_hash_table_remove_all(config->service_table); |
| load_config(config); |
| __connman_service_provision_changed(ident); |
| } |
| } |
| } |
| |
| if (event->mask & IN_DELETE) |
| g_hash_table_remove(config_table, ident); |
| } |
| |
| int __connman_config_init(void) |
| { |
| DBG(""); |
| |
| config_table = g_hash_table_new_full(g_str_hash, g_str_equal, |
| NULL, unregister_config); |
| |
| connman_inotify_register(STORAGEDIR, config_notify_handler); |
| |
| return read_configs(); |
| } |
| |
| void __connman_config_cleanup(void) |
| { |
| DBG(""); |
| |
| cleanup = TRUE; |
| |
| connman_inotify_unregister(STORAGEDIR, config_notify_handler); |
| |
| g_hash_table_destroy(config_table); |
| config_table = NULL; |
| |
| cleanup = FALSE; |
| } |
| |
| static char *config_pem_fsid(const char *pem_file) |
| { |
| struct statfs buf; |
| unsigned *fsid = (unsigned *) &buf.f_fsid; |
| unsigned long long fsid64; |
| |
| if (pem_file == NULL) |
| return NULL; |
| |
| if (statfs(pem_file, &buf) < 0) { |
| connman_error("statfs error %s for %s", |
| strerror(errno), pem_file); |
| return NULL; |
| } |
| |
| fsid64 = ((unsigned long long) fsid[0] << 32) | fsid[1]; |
| |
| return g_strdup_printf("%llx", fsid64); |
| } |
| |
| static void provision_service(gpointer key, gpointer value, gpointer user_data) |
| { |
| struct connman_service *service = user_data; |
| struct connman_config_service *config = value; |
| struct connman_network *network; |
| const void *ssid, *service_id; |
| unsigned int ssid_len; |
| |
| /* For now only WiFi service entries are supported */ |
| if (g_strcmp0(config->type, "wifi") != 0) |
| return; |
| |
| network = __connman_service_get_network(service); |
| if (network == NULL) { |
| connman_error("Service has no network set"); |
| return; |
| } |
| |
| ssid = connman_network_get_blob(network, "WiFi.SSID", &ssid_len); |
| if (ssid == NULL) { |
| connman_error("Network SSID not set"); |
| return; |
| } |
| |
| if (config->ssid == NULL || ssid_len != config->ssid_len) |
| return; |
| |
| if (memcmp(config->ssid, ssid, ssid_len) != 0) |
| return; |
| |
| service_id = __connman_service_get_ident(service); |
| config->service_identifiers = |
| g_slist_prepend(config->service_identifiers, |
| g_strdup(service_id)); |
| |
| __connman_service_set_immutable(service, TRUE); |
| |
| __connman_service_set_favorite_delayed(service, TRUE, TRUE); |
| |
| __connman_service_set_config(service, config->config_ident, |
| config->config_entry); |
| |
| if (config->eap != NULL) |
| __connman_service_set_string(service, "EAP", config->eap); |
| |
| if (config->identity != NULL) |
| __connman_service_set_string(service, "Identity", |
| config->identity); |
| |
| if (config->ca_cert_file != NULL) |
| __connman_service_set_string(service, "CACertFile", |
| config->ca_cert_file); |
| |
| if (config->client_cert_file != NULL) |
| __connman_service_set_string(service, "ClientCertFile", |
| config->client_cert_file); |
| |
| if (config->private_key_file != NULL) |
| __connman_service_set_string(service, "PrivateKeyFile", |
| config->private_key_file); |
| |
| if (g_strcmp0(config->private_key_passphrase_type, "fsid") == 0 && |
| config->private_key_file != NULL) { |
| char *fsid; |
| |
| fsid = config_pem_fsid(config->private_key_file); |
| if (fsid == NULL) |
| return; |
| |
| g_free(config->private_key_passphrase); |
| config->private_key_passphrase = fsid; |
| } |
| |
| if (config->private_key_passphrase != NULL) { |
| __connman_service_set_string(service, "PrivateKeyPassphrase", |
| config->private_key_passphrase); |
| /* |
| * TODO: Support for PEAP with both identity and key passwd. |
| * In that case, we should check if both of them are found |
| * from the config file. If not, we should not set the |
| * service passphrase in order for the UI to request for an |
| * additional passphrase. |
| */ |
| } |
| |
| if (config->phase2 != NULL) |
| __connman_service_set_string(service, "Phase2", config->phase2); |
| |
| if (config->passphrase != NULL) |
| __connman_service_set_string(service, "Passphrase", config->passphrase); |
| |
| if (config->hidden == TRUE) |
| __connman_service_set_hidden(service); |
| |
| __connman_service_mark_dirty(); |
| |
| __connman_service_save(service); |
| } |
| |
| int __connman_config_provision_service(struct connman_service *service) |
| { |
| enum connman_service_type type; |
| GHashTableIter iter; |
| gpointer value, key; |
| |
| DBG("service %p", service); |
| |
| /* For now only WiFi services are supported */ |
| type = connman_service_get_type(service); |
| if (type != CONNMAN_SERVICE_TYPE_WIFI) |
| return -ENOSYS; |
| |
| g_hash_table_iter_init(&iter, config_table); |
| |
| while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { |
| struct connman_config *config = value; |
| |
| g_hash_table_foreach(config->service_table, |
| provision_service, service); |
| } |
| |
| return 0; |
| } |
| |
| int __connman_config_provision_service_ident(struct connman_service *service, |
| const char *ident, const char *file, const char *entry) |
| { |
| enum connman_service_type type; |
| struct connman_config *config; |
| int ret = 0; |
| |
| DBG("service %p", service); |
| |
| /* For now only WiFi services are supported */ |
| type = connman_service_get_type(service); |
| if (type != CONNMAN_SERVICE_TYPE_WIFI) |
| return -ENOSYS; |
| |
| config = g_hash_table_lookup(config_table, ident); |
| if (config != NULL) { |
| GHashTableIter iter; |
| gpointer value, key; |
| gboolean found = FALSE; |
| |
| g_hash_table_iter_init(&iter, config->service_table); |
| |
| /* |
| * Check if we need to remove individual service if it |
| * is missing from config file. |
| */ |
| if (file != NULL && entry != NULL) { |
| while (g_hash_table_iter_next(&iter, &key, |
| &value) == TRUE) { |
| struct connman_config_service *config_service; |
| |
| config_service = value; |
| |
| if (g_strcmp0(config_service->config_ident, |
| file) != 0) |
| continue; |
| |
| if (g_strcmp0(config_service->config_entry, |
| entry) != 0) |
| continue; |
| |
| found = TRUE; |
| break; |
| } |
| |
| DBG("found %d ident %s file %s entry %s", found, ident, |
| file, entry); |
| |
| if (found == FALSE) { |
| /* |
| * The entry+8 will skip "service_" prefix |
| */ |
| g_hash_table_remove(config->service_table, |
| entry + 8); |
| ret = 1; |
| } |
| } |
| |
| g_hash_table_foreach(config->service_table, |
| provision_service, service); |
| } |
| |
| return ret; |
| } |
| |
| struct connman_config_entry **connman_config_get_entries(void) |
| { |
| GHashTableIter iter_file, iter_config; |
| gpointer value, key; |
| struct connman_config_entry **entries = NULL; |
| int i = 0, count; |
| |
| g_hash_table_iter_init(&iter_file, config_table); |
| while (g_hash_table_iter_next(&iter_file, &key, &value) == TRUE) { |
| struct connman_config *config_file = value; |
| |
| count = g_hash_table_size(config_file->service_table); |
| |
| entries = g_try_realloc(entries, (i + count + 1) * |
| sizeof(struct connman_config_entry *)); |
| if (entries == NULL) |
| return NULL; |
| |
| g_hash_table_iter_init(&iter_config, |
| config_file->service_table); |
| while (g_hash_table_iter_next(&iter_config, &key, |
| &value) == TRUE) { |
| struct connman_config_service *config = value; |
| |
| entries[i] = g_try_new0(struct connman_config_entry, |
| 1); |
| if (entries[i] == NULL) |
| goto cleanup; |
| |
| entries[i]->ident = g_strdup(config->ident); |
| entries[i]->name = g_strdup(config->name); |
| entries[i]->ssid = g_try_malloc0(config->ssid_len + 1); |
| if (entries[i]->ssid == NULL) |
| goto cleanup; |
| |
| memcpy(entries[i]->ssid, config->ssid, |
| config->ssid_len); |
| entries[i]->ssid_len = config->ssid_len; |
| entries[i]->hidden = config->hidden; |
| |
| i++; |
| } |
| } |
| |
| if (entries != NULL) { |
| entries = g_try_realloc(entries, (i + 1) * |
| sizeof(struct connman_config_entry *)); |
| if (entries == NULL) |
| return NULL; |
| |
| entries[i] = NULL; |
| |
| DBG("%d provisioned AP found", i); |
| } |
| |
| return entries; |
| |
| cleanup: |
| connman_config_free_entries(entries); |
| return NULL; |
| } |
| |
| void connman_config_free_entries(struct connman_config_entry **entries) |
| { |
| int i; |
| |
| if (entries == NULL) |
| return; |
| |
| for (i = 0; entries[i]; i++) { |
| g_free(entries[i]->ident); |
| g_free(entries[i]->name); |
| g_free(entries[i]->ssid); |
| g_free(entries[i]); |
| } |
| |
| g_free(entries); |
| return; |
| } |