blob: d65554c4cfcb697dea51e2c9187bf4c7e17bc630 [file] [log] [blame]
/*
*
* 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("");
}