diff options
author | Eric Wong <normalperson@yhbt.net> | 2008-10-11 23:39:50 -0700 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2008-10-11 23:39:50 -0700 |
commit | 0df62a2c3cb7af88347d40a17cc336b5d1740f62 (patch) | |
tree | 25040833144c3e24f6b4702d9b745cd068b5371a /src/database.c | |
parent | 3456e2de5bf90207d8149a842bb12c3f9bdd218f (diff) | |
parent | 6e2b0ca9edaed200f250ef487701ad161aa4a168 (diff) | |
download | mpd-0df62a2c3cb7af88347d40a17cc336b5d1740f62.tar.gz mpd-0df62a2c3cb7af88347d40a17cc336b5d1740f62.tar.xz mpd-0df62a2c3cb7af88347d40a17cc336b5d1740f62.zip |
Merge branch 'mk/directory'
* mk/directory: (59 commits)
directory: don't use identical struct and variable names
update: replaced update_return with global "modified" flag
update: make the variable "progress" static
update: don't print debug message when song was not modified
update: fix memory leak in directory_update_init()
update: make the job id unsigned
update: job ID must be positive
update: check progress!=IDLE in reap_update_task()
update: fixed stack corruption due to pthread_join() call
updated: always call removeDeletedFromDirectory()
update: eliminated addSubDirectoryToDirectory()
update: make the "song" variable more local
update: do the recursive directory check only once
update: copy stat to new directory
update: avoid duplicate stat() calls
update: rewrote updatePath() using updateInDirectory()
update: don't export updateDirectory()
update: pass const pointer to addSubDirectoryToDirectory()
update: never pass root path to updatePath()
update: merged addDirectoryPathToDB() into addParentPathToDB()
...
Conflicts:
src/song.c
Diffstat (limited to '')
-rw-r--r-- | src/database.c | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/src/database.c b/src/database.c new file mode 100644 index 000000000..dde57ce6a --- /dev/null +++ b/src/database.c @@ -0,0 +1,314 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com) + * Copyright (C) 2008 Max Kellermann <max@duempel.org> + * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "database.h" +#include "directory.h" +#include "directory_save.h" +#include "song.h" +#include "conf.h" +#include "log.h" +#include "ls.h" +#include "path.h" +#include "stats.h" +#include "utils.h" +#include "dbUtils.h" +#include "update.h" +#include "os_compat.h" +#include "myfprintf.h" + +static struct directory *music_root; + +static time_t directory_dbModTime; + +void db_init(void) +{ + music_root = directory_new("", NULL); + + if (!directory_update_init(NULL)) + FATAL("directory update failed\n"); + + do { + my_usleep(100000); + reap_update_task(); + } while (isUpdatingDB()); + + stats.numberOfSongs = countSongsIn(NULL); + stats.dbPlayTime = sumSongTimesIn(NULL); +} + +void db_finish(void) +{ + directory_free(music_root); +} + +struct directory * db_get_root(void) +{ + assert(music_root != NULL); + + return music_root; +} + +struct directory * db_get_directory(const char *name) +{ + if (name == NULL) + return music_root; + + return directory_get_subdir(music_root, name); +} + +struct mpd_song *db_get_song(const char *file) +{ + struct mpd_song *song = NULL; + struct directory *dir; + char *dirpath = NULL; + char *duplicated = xstrdup(file); + char *shortname = strrchr(duplicated, '/'); + + DEBUG("get song: %s\n", file); + + if (!shortname) { + shortname = duplicated; + } else { + *shortname = '\0'; + ++shortname; + dirpath = duplicated; + } + + if (!(dir = db_get_directory(dirpath))) + goto out; + if (!(song = songvec_find(&dir->songs, shortname))) + goto out; + assert(song->parent == dir); + +out: + free(duplicated); + return song; +} + +int db_walk(const char *name, + int (*forEachSong) (struct mpd_song *, void *), + int (*forEachDir) (struct directory *, void *), void *data) +{ + struct directory *dir; + + if ((dir = db_get_directory(name)) == NULL) { + struct mpd_song *song; + if ((song = db_get_song(name)) && forEachSong) { + return forEachSong(song, data); + } + return -1; + } + + return directory_walk(dir, forEachSong, forEachDir, data); +} + +static char *db_get_file(void) +{ + ConfigParam *param = parseConfigFilePath(CONF_DB_FILE, 1); + + assert(param); + assert(param->value); + + return param->value; +} + +int db_check(void) +{ + struct stat st; + char *dbFile = db_get_file(); + + /* Check if the file exists */ + if (access(dbFile, 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[MPD_PATH_MAX]; + parent_path(dirPath, dbFile); + if (*dirPath == '\0') + strcpy(dirPath, "/"); + + /* Check that the parent part of the path is a directory */ + if (stat(dirPath, &st) < 0) { + ERROR("Couldn't stat parent directory of db file " + "\"%s\": %s\n", dbFile, strerror(errno)); + return -1; + } + + if (!S_ISDIR(st.st_mode)) { + ERROR("Couldn't create db file \"%s\" because the " + "parent path is not a directory\n", dbFile); + return -1; + } + + /* Check if we can write to the directory */ + if (access(dirPath, R_OK | W_OK)) { + ERROR("Can't create db file in \"%s\": %s\n", dirPath, + strerror(errno)); + return -1; + } + + return 0; + } + + /* Path exists, now check if it's a regular file */ + if (stat(dbFile, &st) < 0) { + ERROR("Couldn't stat db file \"%s\": %s\n", dbFile, + strerror(errno)); + return -1; + } + + if (!S_ISREG(st.st_mode)) { + ERROR("db file \"%s\" is not a regular file\n", dbFile); + return -1; + } + + /* And check that we can write to it */ + if (access(dbFile, R_OK | W_OK)) { + ERROR("Can't open db file \"%s\" for reading/writing: %s\n", + dbFile, strerror(errno)); + return -1; + } + + return 0; +} + +int db_save(void) +{ + int fd; + char *dbFile = db_get_file(); + struct stat st; + + DEBUG("removing empty directories from DB\n"); + directory_prune_empty(music_root); + + DEBUG("sorting DB\n"); + + directory_sort(music_root); + + DEBUG("writing DB\n"); + + fd = open(dbFile, O_WRONLY|O_TRUNC|O_CREAT, 0666); + if (fd < 0) { + ERROR("unable to write to db file \"%s\": %s\n", + dbFile, strerror(errno)); + return -1; + } + + /* + * TODO: block signals when writing the db so we don't get a corrupted + * db (or unexpected failures). fdprintf() needs better error handling + */ + fdprintf(fd, + DIRECTORY_INFO_BEGIN "\n" + DIRECTORY_MPD_VERSION VERSION "\n" + DIRECTORY_FS_CHARSET "%s\n" + DIRECTORY_INFO_END "\n", getFsCharset()); + + if (directory_save(fd, music_root) < 0) { + ERROR("Failed to write to database file: %s\n", + strerror(errno)); + xclose(fd); + return -1; + } + xclose(fd); + + if (stat(dbFile, &st) == 0) + directory_dbModTime = st.st_mtime; + + return 0; +} + +int db_load(void) +{ + FILE *fp = NULL; + char *dbFile = db_get_file(); + struct stat st; + char buffer[100]; + int foundFsCharset = 0; + int foundVersion = 0; + + if (!music_root) + music_root = directory_new("", NULL); + while (!(fp = fopen(dbFile, "r")) && errno == EINTR) ; + if (fp == NULL) { + ERROR("unable to open db file \"%s\": %s\n", + dbFile, strerror(errno)); + return -1; + } + + /* get initial info */ + if (!myFgets(buffer, sizeof(buffer), fp)) + FATAL("Error reading db, fgets\n"); + + if (0 != strcmp(DIRECTORY_INFO_BEGIN, buffer)) { + ERROR("db info not found in db file\n"); + ERROR("you should recreate the db using --create-db\n"); + while (fclose(fp) && errno == EINTR) ; + return -1; + } + + while (myFgets(buffer, sizeof(buffer), fp) && + 0 != strcmp(DIRECTORY_INFO_END, buffer)) { + if (!prefixcmp(buffer, DIRECTORY_MPD_VERSION)) { + if (foundVersion) + FATAL("already found version in db\n"); + foundVersion = 1; + } else if (!prefixcmp(buffer, DIRECTORY_FS_CHARSET)) { + char *fsCharset; + char *tempCharset; + + if (foundFsCharset) + FATAL("already found fs charset in db\n"); + + foundFsCharset = 1; + + fsCharset = &(buffer[strlen(DIRECTORY_FS_CHARSET)]); + if ((tempCharset = getConfigParamValue(CONF_FS_CHARSET)) + && strcmp(fsCharset, tempCharset)) { + WARNING("Using \"%s\" for the " + "filesystem charset " + "instead of \"%s\"\n", + fsCharset, tempCharset); + WARNING("maybe you need to " + "recreate the db?\n"); + setFsCharset(fsCharset); + } + } else + FATAL("directory: unknown line in db info: %s\n", + buffer); + } + + DEBUG("reading DB\n"); + + directory_load(fp, music_root); + while (fclose(fp) && errno == EINTR) ; + + stats.numberOfSongs = countSongsIn(NULL); + stats.dbPlayTime = sumSongTimesIn(NULL); + + if (stat(dbFile, &st) == 0) + directory_dbModTime = st.st_mtime; + + return 0; +} + +time_t db_get_mtime(void) +{ + return directory_dbModTime; +} |