| /* |
| * |
| * Connection Manager |
| * |
| * Copyright (C) 2007-2009 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 <sys/types.h> |
| #include <sys/stat.h> |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "connman.h" |
| |
| #define _DBG_STORAGE(fmt, arg...) DBG(DBG_STORAGE, fmt, ## arg) |
| |
| static GSList *storage_list = NULL; |
| |
| static gint compare_priority(gconstpointer a, gconstpointer b) |
| { |
| const struct connman_storage *storage1 = a; |
| const struct connman_storage *storage2 = b; |
| |
| return storage2->priority - storage1->priority; |
| } |
| |
| /** |
| * connman_storage_register: |
| * @storage: storage module |
| * |
| * Register a new storage module |
| * |
| * Returns: %0 on success |
| */ |
| int connman_storage_register(struct connman_storage *storage) |
| { |
| _DBG_STORAGE("storage %p name %s", storage, storage->name); |
| |
| storage_list = g_slist_insert_sorted(storage_list, storage, |
| compare_priority); |
| |
| return 0; |
| } |
| |
| /** |
| * connman_storage_unregister: |
| * @storage: storage module |
| * |
| * Remove a previously registered storage module |
| */ |
| void connman_storage_unregister(struct connman_storage *storage) |
| { |
| _DBG_STORAGE("storage %p name %s", storage, storage->name); |
| |
| storage_list = g_slist_remove(storage_list, storage); |
| } |
| |
| void __connman_storage_set_encrypted_value(GKeyFile *keyfile, |
| const char *section, const char *key, const char *value) |
| { |
| char *cryptvalue = __connman_crypto_encrypt_keyvalue(key, value); |
| g_key_file_set_string(keyfile, section, key, cryptvalue); |
| g_free(cryptvalue); |
| } |
| |
| char *__connman_storage_get_encrypted_value(GKeyFile *keyfile, |
| const char *section, const char *key) |
| { |
| char *ciphertext, *plaintext; |
| |
| ciphertext = g_key_file_get_string(keyfile, section, key, NULL); |
| if (ciphertext == NULL) |
| return NULL; |
| |
| plaintext = __connman_crypto_decrypt_keyvalue(key, ciphertext); |
| g_free(ciphertext); |
| |
| return plaintext; |
| } |
| |
| static const char *__getpath(const struct connman_storage_ident *ident) |
| { |
| static char path[80]; |
| |
| if (ident->user != NULL) { |
| /* TODO(sleffler) worth using getpwnam & co? */ |
| /* TODO(sleffler) at least use #define */ |
| snprintf(path, sizeof(path), STORAGE_HOMEDIR "/%s.profile", |
| ident->user, ident->ident); |
| } else { |
| snprintf(path, sizeof(path), STORAGEDIR "/%s.profile", |
| ident->ident); |
| } |
| return path; |
| } |
| |
| static char *getpath(const struct connman_storage_ident *ident) |
| { |
| char *dir, *path; |
| struct stat sb; |
| |
| if (ident->user != NULL) |
| dir = g_strdup_printf(STORAGE_HOMEDIR, ident->user); |
| else |
| dir = g_strdup(STORAGEDIR); |
| /* container directory must be root-owned and unwritable */ |
| if (lstat(dir, &sb) < 0) { |
| connman_error("%s: cannot lstat: %s", dir, strerror(errno)); |
| goto bad; |
| } |
| if (!S_ISDIR(sb.st_mode)) { |
| connman_error("%s: not a directory", dir); |
| goto bad; |
| } |
| if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) { |
| connman_error("%s: bad permissions 0%o", dir, sb.st_mode); |
| goto bad; |
| } |
| if (sb.st_uid != geteuid()) { |
| connman_error("%s: wrong owner, uid %d", dir, sb.st_uid); |
| goto bad; |
| } |
| |
| path = g_strdup_printf("%s/%s.profile", dir, ident->ident); |
| g_free(dir); |
| |
| return path; |
| bad: |
| g_free(dir); |
| return NULL; |
| } |
| |
| gboolean __connman_storage_exists(const struct connman_storage_ident *ident) |
| { |
| gchar *pathname, *data = NULL; |
| gboolean result; |
| gsize length; |
| |
| _DBG_STORAGE("ident %s", __getpath(ident)); |
| |
| pathname = getpath(ident); |
| if (pathname == NULL) |
| return FALSE; |
| |
| result = g_file_get_contents(pathname, &data, &length, NULL); |
| |
| g_free(pathname); |
| g_free(data); |
| |
| return result; |
| } |
| |
| GKeyFile *__connman_storage_open(const struct connman_storage_ident *ident) |
| { |
| GKeyFile *keyfile; |
| gchar *pathname, *data = NULL; |
| gboolean result; |
| gsize length; |
| |
| _DBG_STORAGE("ident %s", __getpath(ident)); |
| |
| pathname = getpath(ident); |
| if (pathname == NULL) |
| return NULL; |
| |
| result = g_file_get_contents(pathname, &data, &length, NULL); |
| |
| g_free(pathname); |
| |
| keyfile = g_key_file_new(); |
| |
| if (result == FALSE) |
| goto done; |
| |
| if (length > 0) |
| g_key_file_load_from_data(keyfile, data, length, 0, NULL); |
| |
| g_free(data); |
| |
| done: |
| _DBG_STORAGE("keyfile %p", keyfile); |
| |
| return keyfile; |
| } |
| |
| void __connman_storage_close(const struct connman_storage_ident *ident, |
| GKeyFile *keyfile, gboolean save) |
| { |
| gchar *pathname, *data = NULL; |
| gsize length = 0; |
| |
| _DBG_STORAGE("ident %s keyfile %p save %d", __getpath(ident), |
| keyfile, save); |
| |
| if (save == FALSE) { |
| g_key_file_free(keyfile); |
| return; |
| } |
| |
| pathname = getpath(ident); |
| if (pathname == NULL) |
| return; |
| |
| data = g_key_file_to_data(keyfile, &length, NULL); |
| |
| if (g_file_set_contents(pathname, data, length, NULL) == FALSE) |
| connman_error("%s: failed to store data for %s:%s", |
| __func__, ident->user, ident->ident); |
| |
| g_free(data); |
| |
| g_free(pathname); |
| |
| g_key_file_free(keyfile); |
| } |
| |
| void __connman_storage_delete(const struct connman_storage_ident *ident) |
| { |
| gchar *pathname; |
| |
| _DBG_STORAGE("ident %s", __getpath(ident)); |
| |
| pathname = getpath(ident); |
| if (pathname == NULL) |
| return; |
| |
| if (unlink(pathname) < 0) |
| connman_error("Failed to remove %s", pathname); |
| |
| g_free(pathname); |
| } |
| |
| int __connman_storage_init_profile(void) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE(""); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->profile_init) { |
| if (storage->profile_init() == 0) |
| return 0; |
| } |
| } |
| |
| return -ENOENT; |
| } |
| |
| int __connman_storage_load_profile(struct connman_profile *profile) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE("profile %p", profile); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->profile_load) { |
| if (storage->profile_load(profile) == 0) |
| return 0; |
| } |
| } |
| |
| return -ENOENT; |
| } |
| |
| int __connman_storage_save_profile(struct connman_profile *profile) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE("profile %p", profile); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->profile_save) { |
| if (storage->profile_save(profile) == 0) |
| return 0; |
| } |
| } |
| |
| return -ENOENT; |
| } |
| |
| /* |
| * Load/Save object support. Walk the list of registered |
| * storage implementors until we find one that has a handler |
| * for the object type. The storage implementor template |
| * has per-object types (apparently) but these are not used |
| * at the moment. |
| */ |
| #define DOLOAD(func, arg, profile_ident) do { \ |
| int err; \ |
| GKeyFile *keyfile = __connman_storage_open(profile_ident); \ |
| if (keyfile == NULL) \ |
| return -ENXIO; \ |
| err = func(arg, keyfile); \ |
| __connman_storage_close(profile_ident, keyfile, FALSE); \ |
| return err; \ |
| } while (0) |
| |
| #define DOSAVE(func, arg, profile_ident) do { \ |
| int err; \ |
| GKeyFile *keyfile = __connman_storage_open(profile_ident); \ |
| if (keyfile == NULL) \ |
| return -ENXIO; \ |
| err = func(arg, keyfile); \ |
| /* NB: always write-back for compatibility */ \ |
| __connman_storage_close(profile_ident, keyfile, TRUE); \ |
| return err; \ |
| } while (0) |
| |
| int __connman_storage_load_service(struct connman_service *service, |
| const struct connman_storage_ident *profile_ident) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE("service %p", service); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->service_load) |
| DOLOAD(storage->service_load, service, profile_ident); |
| } |
| return -ENOENT; |
| } |
| |
| int __connman_storage_save_service(struct connman_service *service, |
| const struct connman_storage_ident *profile_ident) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE("service %p", service); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->service_save) |
| DOSAVE(storage->service_save, service, profile_ident); |
| } |
| return -ENOENT; |
| } |
| |
| char *__connman_storage_find_guid(const char *guid, |
| const struct connman_storage_ident *profile_ident) |
| { |
| char *identifier = NULL; |
| GKeyFile *keyfile; |
| |
| _DBG_STORAGE("guid %s profile %p", guid, profile_ident); |
| |
| keyfile = __connman_storage_open(profile_ident); |
| if (keyfile != NULL) { |
| gsize ngroups; |
| gchar **groups = g_key_file_get_groups(keyfile, &ngroups); |
| int i; |
| |
| for (i = 0; i < ngroups && identifier == NULL; i++) { |
| char *str = g_key_file_get_string(keyfile, groups[i], |
| "GUID", NULL); |
| if (g_strcmp0(guid, str) == 0) |
| identifier = g_strdup(groups[i]); |
| g_free(str); |
| } |
| g_strfreev(groups); |
| __connman_storage_close(profile_ident, keyfile, FALSE); |
| } |
| return identifier; |
| } |
| |
| int __connman_storage_load_provider(struct connman_provider *provider, |
| const struct connman_storage_ident *profile_ident) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE("provider %p", provider); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->provider_load) |
| DOLOAD(storage->provider_load, provider, profile_ident); |
| } |
| return -ENOENT; |
| } |
| |
| int __connman_storage_save_provider(struct connman_provider *provider, |
| const struct connman_storage_ident *profile_ident) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE("provider %p", provider); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->provider_save) |
| DOSAVE(storage->provider_save, provider, profile_ident); |
| } |
| return -ENOENT; |
| } |
| |
| int __connman_storage_load_device(struct connman_device *device, |
| const struct connman_storage_ident *profile_ident) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE("device %p", device); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->device_load) |
| DOLOAD(storage->device_load, device, profile_ident); |
| } |
| return -ENOENT; |
| } |
| |
| int __connman_storage_save_device(struct connman_device *device, |
| const struct connman_storage_ident *profile_ident) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE("device %p", device); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->device_save) |
| DOSAVE(storage->device_save, device, profile_ident); |
| } |
| return -ENOENT; |
| } |
| |
| int __connman_storage_load_ipconfig(struct connman_ipconfig *ipconfig, |
| const struct connman_storage_ident *profile_ident) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE("ipconfig %p", ipconfig); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->ipconfig_load) |
| DOLOAD(storage->ipconfig_load, ipconfig, profile_ident); |
| } |
| return -ENOENT; |
| } |
| |
| int __connman_storage_save_ipconfig(const struct connman_ipconfig *ipconfig, |
| const struct connman_storage_ident *profile_ident) |
| { |
| GSList *list; |
| |
| _DBG_STORAGE("ipconfig %p", ipconfig); |
| |
| for (list = storage_list; list; list = list->next) { |
| struct connman_storage *storage = list->data; |
| |
| if (storage->ipconfig_save) |
| DOSAVE(storage->ipconfig_save, ipconfig, profile_ident); |
| } |
| return -ENOENT; |
| } |
| |
| int __connman_storage_init(void) |
| { |
| _DBG_STORAGE(""); |
| |
| return 0; |
| } |
| |
| void __connman_storage_cleanup(void) |
| { |
| _DBG_STORAGE(""); |
| } |