diff --git a/Makefile.am b/Makefile.am index ae4c4a7c3..fd34b74e4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -272,6 +272,8 @@ src_mpd_SOURCES = \ src/database.c \ src/db_save.c src/db_save.h \ src/db_print.c src/db_print.h \ + src/db_plugin.h \ + src/db/simple_db_plugin.c src/db/simple_db_plugin.h \ src/dirvec.c \ src/exclude.c \ src/fd_util.c \ diff --git a/src/database.c b/src/database.c index a892452ed..ea7f01eb1 100644 --- a/src/database.c +++ b/src/database.c @@ -20,8 +20,11 @@ #include "config.h" #include "database.h" #include "db_save.h" +#include "db_plugin.h" +#include "db/simple_db_plugin.h" #include "directory.h" #include "stats.h" +#include "conf.h" #include @@ -35,11 +38,8 @@ #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "database" -static char *database_path; - -static struct directory *music_root; - -static time_t database_mtime; +static struct db *db; +static bool db_is_open; /** * The quark used for GError.domain. @@ -50,49 +50,50 @@ db_quark(void) return g_quark_from_static_string("database"); } -void -db_init(const char *path) +bool +db_init(const struct config_param *path, GError **error_r) { - database_path = g_strdup(path); + assert(db == NULL); + assert(!db_is_open); - if (path != NULL) - music_root = directory_new("", NULL); + if (path == NULL) + return true; + + struct config_param *param = config_new_param("database", path->line); + config_add_block_param(param, "path", path->value, path->line); + + db = db_plugin_new(&simple_db_plugin, param, error_r); + + config_param_free(param); + + return db != NULL; } void db_finish(void) { - assert((database_path == NULL) == (music_root == NULL)); + if (db_is_open) + db_plugin_close(db); - if (music_root != NULL) - directory_free(music_root); - - g_free(database_path); -} - -void -db_clear(void) -{ - assert(music_root != NULL); - - directory_free(music_root); - music_root = directory_new("", NULL); + if (db != NULL) + db_plugin_free(db); } struct directory * db_get_root(void) { - assert(music_root != NULL); + assert(db != NULL); - return music_root; + return simple_db_get_root(db); } struct directory * db_get_directory(const char *name) { - if (music_root == NULL) + if (db == NULL) return NULL; + struct directory *music_root = db_get_root(); if (name == NULL) return music_root; @@ -106,9 +107,10 @@ db_get_song(const char *file) g_debug("get song: %s", file); - if (music_root == NULL) + if (db == NULL) return NULL; + struct directory *music_root = db_get_root(); return directory_lookup_song(music_root, file); } @@ -119,7 +121,7 @@ db_walk(const char *name, { struct directory *directory; - if (music_root == NULL) + if (db == NULL) return -1; if ((directory = db_get_directory(name)) == NULL) { @@ -133,157 +135,36 @@ db_walk(const char *name, return directory_walk(directory, forEachSong, forEachDir, data); } -bool -db_check(GError **error_r) -{ - struct stat st; - - assert(database_path != NULL); - - /* Check if the file exists */ - if (access(database_path, F_OK)) { - /* If the file doesn't exist, we can't check if we can write - * it, so we are going to try to get the directory path, and - * see if we can write a file in that */ - char *dirPath = g_path_get_dirname(database_path); - - /* Check that the parent part of the path is a directory */ - if (stat(dirPath, &st) < 0) { - g_free(dirPath); - g_set_error(error_r, db_quark(), errno, - "Couldn't stat parent directory of db file " - "\"%s\": %s", - database_path, g_strerror(errno)); - return false; - } - - if (!S_ISDIR(st.st_mode)) { - g_free(dirPath); - g_set_error(error_r, db_quark(), 0, - "Couldn't create db file \"%s\" because the " - "parent path is not a directory", - database_path); - return false; - } - - /* Check if we can write to the directory */ - if (access(dirPath, X_OK | W_OK)) { - g_set_error(error_r, db_quark(), errno, - "Can't create db file in \"%s\": %s", - dirPath, g_strerror(errno)); - g_free(dirPath); - return false; - } - - g_free(dirPath); - - return true; - } - - /* Path exists, now check if it's a regular file */ - if (stat(database_path, &st) < 0) { - g_set_error(error_r, db_quark(), errno, - "Couldn't stat db file \"%s\": %s", - database_path, g_strerror(errno)); - return false; - } - - if (!S_ISREG(st.st_mode)) { - g_set_error(error_r, db_quark(), 0, - "db file \"%s\" is not a regular file", - database_path); - return false; - } - - /* And check that we can write to it */ - if (access(database_path, R_OK | W_OK)) { - g_set_error(error_r, db_quark(), errno, - "Can't open db file \"%s\" for reading/writing: %s", - database_path, g_strerror(errno)); - return false; - } - - return true; -} - bool db_save(GError **error_r) { - FILE *fp; - struct stat st; + assert(db != NULL); + assert(db_is_open); - assert(database_path != NULL); - assert(music_root != NULL); - - g_debug("removing empty directories from DB"); - directory_prune_empty(music_root); - - g_debug("sorting DB"); - - directory_sort(music_root); - - g_debug("writing DB"); - - fp = fopen(database_path, "w"); - if (!fp) { - g_set_error(error_r, db_quark(), errno, - "unable to write to db file \"%s\": %s", - database_path, g_strerror(errno)); - return false; - } - - db_save_internal(fp, music_root); - - if (ferror(fp)) { - g_set_error(error_r, db_quark(), errno, - "Failed to write to database file: %s", - g_strerror(errno)); - fclose(fp); - return false; - } - - fclose(fp); - - if (stat(database_path, &st) == 0) - database_mtime = st.st_mtime; - - return true; + return simple_db_save(db, error_r); } bool db_load(GError **error) { - FILE *fp = NULL; - struct stat st; + assert(db != NULL); + assert(!db_is_open); - assert(database_path != NULL); - assert(music_root != NULL); - - fp = fopen(database_path, "r"); - if (fp == NULL) { - g_set_error(error, db_quark(), errno, - "Failed to open database file \"%s\": %s", - database_path, strerror(errno)); + if (!db_plugin_open(db, error)) return false; - } - if (!db_load_internal(fp, music_root, error)) { - fclose(fp); - return false; - } - - fclose(fp); + db_is_open = true; stats_update(); - if (stat(database_path, &st) == 0) - database_mtime = st.st_mtime; - return true; } time_t db_get_mtime(void) { - return database_mtime; + assert(db != NULL); + assert(db_is_open); + + return simple_db_get_mtime(db); } diff --git a/src/database.h b/src/database.h index 66423f46f..3e834ab84 100644 --- a/src/database.h +++ b/src/database.h @@ -25,6 +25,7 @@ #include #include +struct config_param; struct directory; /** @@ -32,18 +33,12 @@ struct directory; * * @param path the absolute path of the database file */ -void -db_init(const char *path); +bool +db_init(const struct config_param *path, GError **error_r); void db_finish(void); -/** - * Clear the database. - */ -void -db_clear(void); - /** * Returns the root directory object. Returns NULL if there is no * configured music directory. diff --git a/src/db/simple_db_plugin.c b/src/db/simple_db_plugin.c new file mode 100644 index 000000000..0154cb223 --- /dev/null +++ b/src/db/simple_db_plugin.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "simple_db_plugin.h" +#include "db_internal.h" +#include "db_save.h" +#include "conf.h" +#include "glib_compat.h" +#include "directory.h" + +#include +#include +#include +#include + +struct simple_db { + struct db base; + + char *path; + + struct directory *root; + + time_t mtime; +}; + +G_GNUC_CONST +static inline GQuark +simple_db_quark(void) +{ + return g_quark_from_static_string("simple_db"); +} + +static struct db * +simple_db_init(const struct config_param *param, GError **error_r) +{ + struct simple_db *db = g_malloc(sizeof(*db)); + db_base_init(&db->base, &simple_db_plugin); + + GError *error = NULL; + db->path = config_dup_block_path(param, "path", error_r); + if (db->path == NULL) { + g_free(db); + if (error != NULL) + g_propagate_error(error_r, error); + else + g_set_error(error_r, simple_db_quark(), 0, + "No \"path\" parameter specified"); + return NULL; + } + + return &db->base; +} + +static void +simple_db_finish(struct db *_db) +{ + struct simple_db *db = (struct simple_db *)_db; + + g_free(db->path); + g_free(db); +} + +static bool +simple_db_check(struct simple_db *db, GError **error_r) +{ + assert(db != NULL); + assert(db->path != NULL); + + /* Check if the file exists */ + if (access(db->path, F_OK)) { + /* If the file doesn't exist, we can't check if we can write + * it, so we are going to try to get the directory path, and + * see if we can write a file in that */ + char *dirPath = g_path_get_dirname(db->path); + + /* Check that the parent part of the path is a directory */ + struct stat st; + if (stat(dirPath, &st) < 0) { + g_free(dirPath); + g_set_error(error_r, simple_db_quark(), errno, + "Couldn't stat parent directory of db file " + "\"%s\": %s", + db->path, g_strerror(errno)); + return false; + } + + if (!S_ISDIR(st.st_mode)) { + g_free(dirPath); + g_set_error(error_r, simple_db_quark(), 0, + "Couldn't create db file \"%s\" because the " + "parent path is not a directory", + db->path); + return false; + } + + /* Check if we can write to the directory */ + if (access(dirPath, X_OK | W_OK)) { + g_set_error(error_r, simple_db_quark(), errno, + "Can't create db file in \"%s\": %s", + dirPath, g_strerror(errno)); + g_free(dirPath); + return false; + } + + g_free(dirPath); + + return true; + } + + /* Path exists, now check if it's a regular file */ + struct stat st; + if (stat(db->path, &st) < 0) { + g_set_error(error_r, simple_db_quark(), errno, + "Couldn't stat db file \"%s\": %s", + db->path, g_strerror(errno)); + return false; + } + + if (!S_ISREG(st.st_mode)) { + g_set_error(error_r, simple_db_quark(), 0, + "db file \"%s\" is not a regular file", + db->path); + return false; + } + + /* And check that we can write to it */ + if (access(db->path, R_OK | W_OK)) { + g_set_error(error_r, simple_db_quark(), errno, + "Can't open db file \"%s\" for reading/writing: %s", + db->path, g_strerror(errno)); + return false; + } + + return true; +} + +static bool +simple_db_load(struct simple_db *db, GError **error_r) +{ + assert(db != NULL); + assert(db->path != NULL); + assert(db->root != NULL); + + FILE *fp = fopen(db->path, "r"); + if (fp == NULL) { + g_set_error(error_r, simple_db_quark(), errno, + "Failed to open database file \"%s\": %s", + db->path, g_strerror(errno)); + return false; + } + + if (!db_load_internal(fp, db->root, error_r)) { + fclose(fp); + return false; + } + + fclose(fp); + + struct stat st; + if (stat(db->path, &st) == 0) + db->mtime = st.st_mtime; + + return true; +} + +static bool +simple_db_open(struct db *_db, G_GNUC_UNUSED GError **error_r) +{ + struct simple_db *db = (struct simple_db *)_db; + + db->root = directory_new("", NULL); + db->mtime = 0; + + GError *error = NULL; + if (!simple_db_load(db, &error)) { + directory_free(db->root); + + g_warning("Failed to load database: %s", error->message); + g_error_free(error); + + if (!simple_db_check(db, error_r)) + return false; + + db->root = directory_new("", NULL); + } + + return true; +} + +static void +simple_db_close(struct db *_db) +{ + struct simple_db *db = (struct simple_db *)_db; + + assert(db->root != NULL); + + directory_free(db->root); +} + +const struct db_plugin simple_db_plugin = { + .name = "simple", + .init = simple_db_init, + .finish = simple_db_finish, + .open = simple_db_open, + .close = simple_db_close, +}; + +struct directory * +simple_db_get_root(struct db *_db) +{ + struct simple_db *db = (struct simple_db *)_db; + + assert(db != NULL); + assert(db->root != NULL); + + return db->root; +} + +bool +simple_db_save(struct db *_db, GError **error_r) +{ + struct simple_db *db = (struct simple_db *)_db; + struct directory *music_root = db->root; + + g_debug("removing empty directories from DB"); + directory_prune_empty(music_root); + + g_debug("sorting DB"); + + directory_sort(music_root); + + g_debug("writing DB"); + + FILE *fp = fopen(db->path, "w"); + if (!fp) { + g_set_error(error_r, simple_db_quark(), errno, + "unable to write to db file \"%s\": %s", + db->path, g_strerror(errno)); + return false; + } + + db_save_internal(fp, music_root); + + if (ferror(fp)) { + g_set_error(error_r, simple_db_quark(), errno, + "Failed to write to database file: %s", + g_strerror(errno)); + fclose(fp); + return false; + } + + fclose(fp); + + struct stat st; + if (stat(db->path, &st) == 0) + db->mtime = st.st_mtime; + + return true; +} + +time_t +simple_db_get_mtime(const struct db *_db) +{ + const struct simple_db *db = (const struct simple_db *)_db; + + assert(db != NULL); + assert(db->root != NULL); + + return db->mtime; +} diff --git a/src/db/simple_db_plugin.h b/src/db/simple_db_plugin.h new file mode 100644 index 000000000..511505846 --- /dev/null +++ b/src/db/simple_db_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_SIMPLE_DB_PLUGIN_H +#define MPD_SIMPLE_DB_PLUGIN_H + +#include +#include +#include + +extern const struct db_plugin simple_db_plugin; + +struct db; + +G_GNUC_PURE +struct directory * +simple_db_get_root(struct db *db); + +bool +simple_db_save(struct db *db, GError **error_r); + +G_GNUC_PURE +time_t +simple_db_get_mtime(const struct db *db); + +#endif diff --git a/src/db_internal.h b/src/db_internal.h new file mode 100644 index 000000000..a33351524 --- /dev/null +++ b/src/db_internal.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_DB_INTERNAL_H +#define MPD_DB_INTERNAL_H + +#include "db_plugin.h" + +#include + +static inline void +db_base_init(struct db *db, const struct db_plugin *plugin) +{ + assert(plugin != NULL); + + db->plugin = plugin; +} + +#endif diff --git a/src/db_plugin.h b/src/db_plugin.h new file mode 100644 index 000000000..a5c57bbad --- /dev/null +++ b/src/db_plugin.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** \file + * + * This header declares the db_plugin class. It describes a + * plugin API for databases of song metadata. + */ + +#ifndef MPD_DB_PLUGIN_H +#define MPD_DB_PLUGIN_H + +#include +#include +#include + +struct config_param; + +struct db { + const struct db_plugin *plugin; +}; + +struct db_plugin { + const char *name; + + /** + * Allocates and configures a database. + */ + struct db *(*init)(const struct config_param *param, GError **error_r); + + /** + * Free instance data. + */ + void (*finish)(struct db *db); + + /** + * Open the database. Read it into memory if applicable. + */ + bool (*open)(struct db *db, GError **error_r); + + /** + * Close the database, free allocated memory. + */ + void (*close)(struct db *db); +}; + +G_GNUC_MALLOC +static inline struct db * +db_plugin_new(const struct db_plugin *plugin, const struct config_param *param, + GError **error_r) +{ + assert(plugin != NULL); + assert(plugin->init != NULL); + assert(plugin->finish != NULL); + assert(error_r == NULL || *error_r == NULL); + + struct db *db = plugin->init(param, error_r); + assert(db == NULL || db->plugin == plugin); + assert(db != NULL || error_r == NULL || *error_r != NULL); + + return db; +} + +static inline void +db_plugin_free(struct db *db) +{ + assert(db != NULL); + assert(db->plugin != NULL); + assert(db->plugin->finish != NULL); + + db->plugin->finish(db); +} + +static inline bool +db_plugin_open(struct db *db, GError **error_r) +{ + assert(db != NULL); + assert(db->plugin != NULL); + + return db->plugin->open != NULL + ? db->plugin->open(db, error_r) + : true; +} + +static inline void +db_plugin_close(struct db *db) +{ + assert(db != NULL); + assert(db->plugin != NULL); + + if (db->plugin->close != NULL) + db->plugin->close(db); +} + +#endif diff --git a/src/main.c b/src/main.c index e73f5ade0..49ce6e125 100644 --- a/src/main.c +++ b/src/main.c @@ -156,44 +156,31 @@ glue_mapper_init(GError **error_r) static bool glue_db_init_and_load(void) { - GError *error = NULL; - char *path = config_dup_path(CONF_DB_FILE, &error); - if (path == NULL && error != NULL) - MPD_ERROR("%s", error->message); + const struct config_param *path = config_get_param(CONF_DB_FILE); + GError *error = NULL; bool ret; if (!mapper_has_music_directory()) { - g_free(path); if (path != NULL) g_message("Found " CONF_DB_FILE " setting without " CONF_MUSIC_DIR " - disabling database"); - db_init(NULL); + db_init(NULL, NULL); return true; } if (path == NULL) MPD_ERROR(CONF_DB_FILE " setting missing"); - db_init(path); - g_free(path); + if (!db_init(path, &error)) + MPD_ERROR("%s", error->message); ret = db_load(&error); - if (!ret) { - g_warning("Failed to load database: %s", error->message); - g_error_free(error); - error = NULL; + if (!ret) + MPD_ERROR("%s", error->message); - if (!db_check(&error)) - MPD_ERROR("%s", error->message); - - db_clear(); - - /* run database update after daemonization */ - return false; - } - - return true; + /* run database update after daemonization? */ + return db_exists(); } /**