diff options
Diffstat (limited to 'src/Main.cxx')
-rw-r--r-- | src/Main.cxx | 227 |
1 files changed, 140 insertions, 87 deletions
diff --git a/src/Main.cxx b/src/Main.cxx index b45e2c3ae..65993ea8e 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,67 +23,76 @@ #include "CommandLine.hxx" #include "PlaylistFile.hxx" #include "PlaylistGlobal.hxx" -#include "UpdateGlue.hxx" #include "MusicChunk.hxx" #include "StateFile.hxx" #include "PlayerThread.hxx" #include "Mapper.hxx" -#include "DatabaseGlue.hxx" -#include "DatabaseSimple.hxx" #include "Permission.hxx" #include "Listen.hxx" -#include "Client.hxx" -#include "ClientList.hxx" +#include "client/Client.hxx" +#include "client/ClientList.hxx" #include "command/AllCommands.hxx" #include "Partition.hxx" -#include "Volume.hxx" -#include "OutputAll.hxx" +#include "mixer/Volume.hxx" #include "tag/TagConfig.hxx" #include "ReplayGainConfig.hxx" #include "Idle.hxx" -#include "SignalHandlers.hxx" #include "Log.hxx" #include "LogInit.hxx" #include "GlobalEvents.hxx" -#include "InputInit.hxx" +#include "input/Init.hxx" #include "event/Loop.hxx" #include "IOThread.hxx" #include "fs/AllocatedPath.hxx" #include "fs/Config.hxx" -#include "PlaylistRegistry.hxx" -#include "ZeroconfGlue.hxx" -#include "DecoderList.hxx" +#include "fs/StandardDirectory.hxx" +#include "playlist/PlaylistRegistry.hxx" +#include "zeroconf/ZeroconfGlue.hxx" +#include "decoder/DecoderList.hxx" #include "AudioConfig.hxx" -#include "pcm/PcmResample.hxx" -#include "Daemon.hxx" +#include "pcm/PcmConvert.hxx" +#include "unix/SignalHandlers.hxx" +#include "unix/Daemon.hxx" #include "system/FatalError.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "thread/Id.hxx" -#include "ConfigGlobal.hxx" -#include "ConfigData.hxx" -#include "ConfigDefaults.hxx" -#include "ConfigOption.hxx" +#include "thread/Slack.hxx" +#include "config/ConfigGlobal.hxx" +#include "config/ConfigData.hxx" +#include "config/ConfigDefaults.hxx" +#include "config/ConfigOption.hxx" #include "Stats.hxx" +#ifdef ENABLE_DATABASE +#include "db/update/Service.hxx" +#include "db/DatabaseGlue.hxx" +#include "db/DatabaseSimple.hxx" +#include "db/plugins/SimpleDatabasePlugin.hxx" +#include "storage/plugins/LocalStorage.hxx" +#endif + +#ifdef ENABLE_NEIGHBOR_PLUGINS +#include "neighbor/Glue.hxx" +#endif + #ifdef ENABLE_INOTIFY -#include "InotifyUpdate.hxx" +#include "db/update/InotifyUpdate.hxx" #endif #ifdef ENABLE_SQLITE -#include "StickerDatabase.hxx" +#include "sticker/StickerDatabase.hxx" #endif #ifdef ENABLE_ARCHIVE -#include "ArchiveList.hxx" +#include "archive/ArchiveList.hxx" #endif +#ifdef HAVE_GLIB #include <glib.h> +#endif -#include <unistd.h> #include <stdlib.h> -#include <errno.h> -#include <string.h> #ifdef HAVE_LOCALE_H #include <locale.h> @@ -94,14 +103,13 @@ #include <ws2tcpip.h> #endif +#include <limits.h> + static constexpr unsigned DEFAULT_BUFFER_SIZE = 4096; static constexpr unsigned DEFAULT_BUFFER_BEFORE_PLAY = 10; static constexpr Domain main_domain("main"); -ThreadId main_thread; -EventLoop *main_loop; - Instance *instance; static StateFile *state_file; @@ -135,19 +143,17 @@ glue_mapper_init(Error &error) return false; if (music_dir.IsNull()) { - const char *path = - g_get_user_special_dir(G_USER_DIRECTORY_MUSIC); - if (path != nullptr) { - music_dir = AllocatedPath::FromUTF8(path, error); - if (music_dir.IsNull()) - return false; - } + music_dir = GetUserMusicDir(); + if (music_dir.IsNull()) + return true; } mapper_init(std::move(music_dir), std::move(playlist_dir)); return true; } +#ifdef ENABLE_DATABASE + /** * Returns the database. If this function returns false, this has not * succeeded, and the caller should create the database after the @@ -187,19 +193,35 @@ glue_db_init_and_load(void) if (param == nullptr) return true; + bool is_simple; Error error; - if (!DatabaseGlobalInit(*param, error)) + instance->database = DatabaseGlobalInit(*instance->event_loop, + *instance, *param, + is_simple, error); + if (instance->database == nullptr) FatalError(error); delete allocated; - if (!DatabaseGlobalOpen(error)) + if (!instance->database->Open(error)) FatalError(error); + if (!is_simple) + return true; + + SimpleDatabase &db = *(SimpleDatabase *)instance->database; + instance->storage = new LocalStorage(mapper_get_music_directory_utf8(), + mapper_get_music_directory_fs()); + instance->update = new UpdateService(*instance->event_loop, db, + *instance->storage, + *instance); + /* run database update after daemonization? */ - return !db_is_simple() || db_exists(); + return db.FileExists(); } +#endif + /** * Configure and initialize the sticker subsystem. */ @@ -228,7 +250,8 @@ glue_state_file_init(Error &error) return !error.IsDefined(); state_file = new StateFile(std::move(path_fs), - *instance->partition, *main_loop); + *instance->partition, + *instance->event_loop); state_file->Read(); return true; } @@ -240,9 +263,8 @@ static void winsock_init(void) { #ifdef WIN32 WSADATA sockinfo; - int retval; - retval = WSAStartup(MAKEWORD(2, 2), &sockinfo); + int retval = WSAStartup(MAKEWORD(2, 2), &sockinfo); if(retval != 0) FormatFatalError("Attempt to open Winsock2 failed; error code %d", retval); @@ -260,14 +282,11 @@ static void initialize_decoder_and_player(void) { const struct config_param *param; - char *test; - size_t buffer_size; - float perc; - unsigned buffered_chunks; - unsigned buffered_before_play; + size_t buffer_size; param = config_get_param(CONF_AUDIO_BUFFER_SIZE); if (param != nullptr) { + char *test; long tmp = strtol(param->value.c_str(), &test, 10); if (*test != '\0' || tmp <= 0 || tmp == LONG_MAX) FormatFatalError("buffer size \"%s\" is not a " @@ -279,14 +298,16 @@ initialize_decoder_and_player(void) buffer_size *= 1024; - buffered_chunks = buffer_size / CHUNK_SIZE; + const unsigned buffered_chunks = buffer_size / CHUNK_SIZE; if (buffered_chunks >= 1 << 15) FormatFatalError("buffer size \"%lu\" is too big", (unsigned long)buffer_size); + float perc; param = config_get_param(CONF_BUFFER_BEFORE_PLAY); if (param != nullptr) { + char *test; perc = strtod(param->value.c_str(), &test); if (*test != '%' || perc < 0 || perc > 100) { FormatFatalError("buffered before play \"%s\" is not " @@ -297,7 +318,7 @@ initialize_decoder_and_player(void) } else perc = DEFAULT_BUFFER_BEFORE_PLAY; - buffered_before_play = (perc / 100) * buffered_chunks; + unsigned buffered_before_play = (perc / 100) * buffered_chunks; if (buffered_before_play > buffered_chunks) buffered_before_play = buffered_chunks; @@ -336,7 +357,7 @@ idle_event_emitted(void) static void shutdown_event_emitted(void) { - main_loop->Break(); + instance->event_loop->Break(); } #endif @@ -353,10 +374,7 @@ int main(int argc, char *argv[]) int mpd_main(int argc, char *argv[]) { struct options options; - clock_t start; - bool create_db; Error error; - bool success; daemonize_close_stdin(); @@ -365,19 +383,20 @@ int mpd_main(int argc, char *argv[]) setlocale(LC_CTYPE,""); #endif +#ifdef HAVE_GLIB g_set_application_name("Music Player Daemon"); #if !GLIB_CHECK_VERSION(2,32,0) /* enable GLib's thread safety code */ g_thread_init(nullptr); #endif +#endif - io_thread_init(); winsock_init(); + io_thread_init(); config_global_init(); - success = parse_cmdline(argc, argv, &options, error); - if (!success) { + if (!parse_cmdline(argc, argv, &options, error)) { LogError(error); return EXIT_FAILURE; } @@ -395,23 +414,34 @@ int mpd_main(int argc, char *argv[]) return EXIT_FAILURE; } - main_thread = ThreadId::GetCurrent(); - main_loop = new EventLoop(EventLoop::Default()); - instance = new Instance(); + instance->event_loop = new EventLoop(); + +#ifdef ENABLE_NEIGHBOR_PLUGINS + instance->neighbors = new NeighborGlue(); + if (!instance->neighbors->Init(io_thread_get(), *instance, error)) { + LogError(error); + return EXIT_FAILURE; + } + + if (instance->neighbors->IsEmpty()) { + delete instance->neighbors; + instance->neighbors = nullptr; + } +#endif const unsigned max_clients = config_get_positive(CONF_MAX_CONN, 10); instance->client_list = new ClientList(max_clients); - success = listen_global_init(error); - if (!success) { + if (!listen_global_init(*instance->event_loop, error)) { LogError(error); return EXIT_FAILURE; } daemonize_set_user(); + daemonize_begin(options.daemon); - GlobalEvents::Initialize(*main_loop); + GlobalEvents::Initialize(*instance->event_loop); GlobalEvents::Register(GlobalEvents::IDLE, idle_event_emitted); #ifdef WIN32 GlobalEvents::Register(GlobalEvents::SHUTDOWN, shutdown_event_emitted); @@ -431,15 +461,16 @@ int mpd_main(int argc, char *argv[]) archive_plugin_init_all(); #endif - if (!pcm_resample_global_init(error)) { + if (!pcm_convert_global_init(error)) { LogError(error); return EXIT_FAILURE; } decoder_plugin_init_all(); - update_global_init(); - create_db = !glue_db_init_and_load(); +#ifdef ENABLE_DATABASE + const bool create_db = !glue_db_init_and_load(); +#endif glue_sticker_init(); @@ -447,7 +478,8 @@ int mpd_main(int argc, char *argv[]) initialize_decoder_and_player(); volume_init(); initAudioConfig(); - audio_output_all_init(instance->partition->pc); + instance->partition->outputs.Configure(*instance->event_loop, + instance->partition->pc); client_manager_init(); replay_gain_global_init(); @@ -458,43 +490,53 @@ int mpd_main(int argc, char *argv[]) playlist_list_global_init(); - daemonize(options.daemon); + daemonize_commit(); setup_log_output(options.log_stderr); - SignalHandlersInit(*main_loop); + SignalHandlersInit(*instance->event_loop); io_thread_start(); - ZeroconfInit(*main_loop); +#ifdef ENABLE_NEIGHBOR_PLUGINS + if (instance->neighbors != nullptr && + !instance->neighbors->Open(error)) + FatalError(error); +#endif + + ZeroconfInit(*instance->event_loop); player_create(instance->partition->pc); +#ifdef ENABLE_DATABASE if (create_db) { /* the database failed to load: recreate the database */ - unsigned job = update_enqueue("", true); + unsigned job = instance->update->Enqueue("", true); if (job == 0) FatalError("directory update failed"); } +#endif if (!glue_state_file_init(error)) { - g_printerr("%s\n", error.GetMessage()); + LogError(error); return EXIT_FAILURE; } - audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(instance->partition->playlist.queue.random)); + instance->partition->outputs.SetReplayGainMode(replay_gain_get_real_mode(instance->partition->playlist.queue.random)); - success = config_get_bool(CONF_AUTO_UPDATE, false); + if (config_get_bool(CONF_AUTO_UPDATE, false)) { #ifdef ENABLE_INOTIFY - if (success && mapper_has_music_directory()) - mpd_inotify_init(config_get_unsigned(CONF_AUTO_UPDATE_DEPTH, - G_MAXUINT)); + if (mapper_has_music_directory() && + instance->update != nullptr) + mpd_inotify_init(*instance->event_loop, *instance->update, + config_get_unsigned(CONF_AUTO_UPDATE_DEPTH, + G_MAXUINT)); #else - if (success) FormatWarning(main_domain, "inotify: auto_update was disabled. enable during compilation phase"); #endif + } config_global_check(); @@ -506,8 +548,12 @@ int mpd_main(int argc, char *argv[]) win32_app_started(); #endif + /* the MPD frontend does not care about timer slack; set it to + a huge value to allow the kernel to reduce CPU wakeups */ + SetThreadTimerSlackMS(100); + /* run the main loop */ - main_loop->Run(); + instance->event_loop->Run(); #ifdef WIN32 win32_app_stopping(); @@ -529,11 +575,18 @@ int mpd_main(int argc, char *argv[]) listen_global_finish(); delete instance->client_list; - start = clock(); - DatabaseGlobalDeinit(); - FormatDebug(main_domain, - "db_finish took %f seconds", - ((float)(clock()-start))/CLOCKS_PER_SEC); +#ifdef ENABLE_NEIGHBOR_PLUGINS + if (instance->neighbors != nullptr) { + instance->neighbors->Close(); + delete instance->neighbors; + } +#endif + +#ifdef ENABLE_DATABASE + delete instance->update; + instance->database->Close(); + delete instance->database; +#endif #ifdef ENABLE_SQLITE sticker_global_finish(); @@ -543,22 +596,22 @@ int mpd_main(int argc, char *argv[]) playlist_list_global_finish(); input_stream_global_finish(); - audio_output_all_finish(); - volume_finish(); + +#ifdef ENABLE_DATABASE mapper_finish(); +#endif + delete instance->partition; command_finish(); - update_global_finish(); decoder_plugin_deinit_all(); #ifdef ENABLE_ARCHIVE archive_plugin_deinit_all(); #endif config_global_finish(); - stats_global_finish(); io_thread_deinit(); SignalHandlersFinish(); + delete instance->event_loop; delete instance; - delete main_loop; daemonize_finish(); #ifdef WIN32 WSACleanup(); |