aboutsummaryrefslogtreecommitdiffstats
path: root/src/Main.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/Main.cxx')
-rw-r--r--src/Main.cxx561
1 files changed, 561 insertions, 0 deletions
diff --git a/src/Main.cxx b/src/Main.cxx
new file mode 100644
index 000000000..057551391
--- /dev/null
+++ b/src/Main.cxx
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2003-2012 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 "Main.hxx"
+#include "PlaylistFile.hxx"
+#include "chunk.h"
+
+extern "C" {
+#include "daemon.h"
+#include "io_thread.h"
+#include "client.h"
+#include "client_idle.h"
+#include "idle.h"
+#include "AllCommands.h"
+#include "playlist.h"
+#include "database.h"
+#include "update.h"
+#include "player_thread.h"
+#include "listen.h"
+#include "cmdline.h"
+#include "conf.h"
+#include "path.h"
+#include "mapper.h"
+#include "player_control.h"
+#include "stats.h"
+#include "sig_handlers.h"
+#include "audio_config.h"
+#include "output_all.h"
+#include "volume.h"
+#include "log.h"
+#include "permission.h"
+#include "pcm_resample.h"
+#include "replay_gain_config.h"
+#include "decoder_list.h"
+#include "input_init.h"
+#include "playlist_list.h"
+#include "state_file.h"
+#include "tag.h"
+#include "zeroconf.h"
+#include "event_pipe.h"
+}
+
+#include "mpd_error.h"
+
+extern "C" {
+
+#ifdef ENABLE_INOTIFY
+#include "inotify_update.h"
+#endif
+
+#ifdef ENABLE_SQLITE
+#include "sticker.h"
+#endif
+
+#ifdef ENABLE_ARCHIVE
+#include "archive_list.h"
+#endif
+
+}
+
+#include <glib.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+
+enum {
+ DEFAULT_BUFFER_SIZE = 2048,
+ DEFAULT_BUFFER_BEFORE_PLAY = 10,
+};
+
+GThread *main_task;
+GMainLoop *main_loop;
+
+GCond *main_cond;
+
+struct player_control *global_player_control;
+
+static bool
+glue_daemonize_init(const struct options *options, GError **error_r)
+{
+ GError *error = NULL;
+
+ char *pid_file = config_dup_path(CONF_PID_FILE, &error);
+ if (pid_file == NULL && error != NULL) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
+
+ daemonize_init(config_get_string(CONF_USER, NULL),
+ config_get_string(CONF_GROUP, NULL),
+ pid_file);
+ g_free(pid_file);
+
+ if (options->kill)
+ daemonize_kill();
+
+ return true;
+}
+
+static bool
+glue_mapper_init(GError **error_r)
+{
+ GError *error = NULL;
+ char *music_dir = config_dup_path(CONF_MUSIC_DIR, &error);
+ if (music_dir == NULL && error != NULL) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
+
+ char *playlist_dir = config_dup_path(CONF_PLAYLIST_DIR, &error);
+ if (playlist_dir == NULL && error != NULL) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
+
+ if (music_dir == NULL)
+ music_dir = g_strdup(g_get_user_special_dir(G_USER_DIRECTORY_MUSIC));
+
+ mapper_init(music_dir, playlist_dir);
+
+ g_free(music_dir);
+ g_free(playlist_dir);
+ return true;
+}
+
+/**
+ * Returns the database. If this function returns false, this has not
+ * succeeded, and the caller should create the database after the
+ * process has been daemonized.
+ */
+static bool
+glue_db_init_and_load(void)
+{
+ const struct config_param *param = config_get_param("database");
+ const struct config_param *path = config_get_param(CONF_DB_FILE);
+
+ if (param != NULL && path != NULL)
+ g_message("Found both 'database' and '" CONF_DB_FILE
+ "' setting - ignoring the latter");
+
+ GError *error = NULL;
+ bool ret;
+
+ if (!mapper_has_music_directory()) {
+ if (param != NULL)
+ g_message("Found database setting without "
+ CONF_MUSIC_DIR " - disabling database");
+ if (path != NULL)
+ g_message("Found " CONF_DB_FILE " setting without "
+ CONF_MUSIC_DIR " - disabling database");
+ return true;
+ }
+
+ struct config_param *allocated = NULL;
+
+ if (param == NULL && path != NULL) {
+ allocated = config_new_param("database", path->line);
+ config_add_block_param(allocated, "path",
+ path->value, path->line);
+ param = allocated;
+ }
+
+ if (!db_init(param, &error))
+ MPD_ERROR("%s", error->message);
+
+ if (allocated != NULL)
+ config_param_free(allocated);
+
+ ret = db_load(&error);
+ if (!ret)
+ MPD_ERROR("%s", error->message);
+
+ /* run database update after daemonization? */
+ return !db_is_simple() || db_exists();
+}
+
+/**
+ * Configure and initialize the sticker subsystem.
+ */
+static void
+glue_sticker_init(void)
+{
+#ifdef ENABLE_SQLITE
+ GError *error = NULL;
+ char *sticker_file = config_dup_path(CONF_STICKER_FILE, &error);
+ if (sticker_file == NULL && error != NULL)
+ MPD_ERROR("%s", error->message);
+
+ if (!sticker_global_init(sticker_file, &error))
+ MPD_ERROR("%s", error->message);
+
+ g_free(sticker_file);
+#endif
+}
+
+static bool
+glue_state_file_init(GError **error_r)
+{
+ GError *error = NULL;
+
+ char *path = config_dup_path(CONF_STATE_FILE, &error);
+ if (path == NULL && error != NULL) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
+
+ state_file_init(path, global_player_control);
+ g_free(path);
+
+ return true;
+}
+
+/**
+ * Windows-only initialization of the Winsock2 library.
+ */
+static void winsock_init(void)
+{
+#ifdef WIN32
+ WSADATA sockinfo;
+ int retval;
+
+ retval = WSAStartup(MAKEWORD(2, 2), &sockinfo);
+ if(retval != 0)
+ {
+ MPD_ERROR("Attempt to open Winsock2 failed; error code %d\n",
+ retval);
+ }
+
+ if (LOBYTE(sockinfo.wVersion) != 2)
+ {
+ MPD_ERROR("We use Winsock2 but your version is either too new "
+ "or old; please install Winsock 2.x\n");
+ }
+#endif
+}
+
+/**
+ * Initialize the decoder and player core, including the music pipe.
+ */
+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;
+
+ param = config_get_param(CONF_AUDIO_BUFFER_SIZE);
+ if (param != NULL) {
+ long tmp = strtol(param->value, &test, 10);
+ if (*test != '\0' || tmp <= 0 || tmp == LONG_MAX)
+ MPD_ERROR("buffer size \"%s\" is not a positive integer, "
+ "line %i\n", param->value, param->line);
+ buffer_size = tmp;
+ } else
+ buffer_size = DEFAULT_BUFFER_SIZE;
+
+ buffer_size *= 1024;
+
+ buffered_chunks = buffer_size / CHUNK_SIZE;
+
+ if (buffered_chunks >= 1 << 15)
+ MPD_ERROR("buffer size \"%li\" is too big\n", (long)buffer_size);
+
+ param = config_get_param(CONF_BUFFER_BEFORE_PLAY);
+ if (param != NULL) {
+ perc = strtod(param->value, &test);
+ if (*test != '%' || perc < 0 || perc > 100) {
+ MPD_ERROR("buffered before play \"%s\" is not a positive "
+ "percentage and less than 100 percent, line %i",
+ param->value, param->line);
+ }
+ } else
+ perc = DEFAULT_BUFFER_BEFORE_PLAY;
+
+ buffered_before_play = (perc / 100) * buffered_chunks;
+ if (buffered_before_play > buffered_chunks)
+ buffered_before_play = buffered_chunks;
+
+ global_player_control = pc_new(buffered_chunks, buffered_before_play);
+}
+
+/**
+ * event_pipe callback function for PIPE_EVENT_IDLE
+ */
+static void
+idle_event_emitted(void)
+{
+ /* send "idle" notificaions to all subscribed
+ clients */
+ unsigned flags = idle_get();
+ if (flags != 0)
+ client_manager_idle_add(flags);
+}
+
+/**
+ * event_pipe callback function for PIPE_EVENT_SHUTDOWN
+ */
+static void
+shutdown_event_emitted(void)
+{
+ g_main_loop_quit(main_loop);
+}
+
+int main(int argc, char *argv[])
+{
+#ifdef WIN32
+ return win32_main(argc, argv);
+#else
+ return mpd_main(argc, argv);
+#endif
+}
+
+int mpd_main(int argc, char *argv[])
+{
+ struct options options;
+ clock_t start;
+ bool create_db;
+ GError *error = NULL;
+ bool success;
+
+ daemonize_close_stdin();
+
+#ifdef HAVE_LOCALE_H
+ /* initialize locale */
+ setlocale(LC_CTYPE,"");
+#endif
+
+ g_set_application_name("Music Player Daemon");
+
+ /* enable GLib's thread safety code */
+ g_thread_init(NULL);
+
+ io_thread_init();
+ winsock_init();
+ idle_init();
+ config_global_init();
+
+ success = parse_cmdline(argc, argv, &options, &error);
+ if (!success) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ if (!glue_daemonize_init(&options, &error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ stats_global_init();
+ tag_lib_init();
+
+ if (!log_init(options.verbose, options.log_stderr, &error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ success = listen_global_init(&error);
+ if (!success) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ daemonize_set_user();
+
+ main_task = g_thread_self();
+ main_loop = g_main_loop_new(NULL, FALSE);
+ main_cond = g_cond_new();
+
+ event_pipe_init();
+ event_pipe_register(PIPE_EVENT_IDLE, idle_event_emitted);
+ event_pipe_register(PIPE_EVENT_SHUTDOWN, shutdown_event_emitted);
+
+ path_global_init();
+
+ if (!glue_mapper_init(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ initPermissions();
+ playlist_global_init();
+ spl_global_init();
+#ifdef ENABLE_ARCHIVE
+ archive_plugin_init_all();
+#endif
+
+ if (!pcm_resample_global_init(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ decoder_plugin_init_all();
+ update_global_init();
+
+ create_db = !glue_db_init_and_load();
+
+ glue_sticker_init();
+
+ command_init();
+ initialize_decoder_and_player();
+ volume_init();
+ initAudioConfig();
+ audio_output_all_init(global_player_control);
+ client_manager_init();
+ replay_gain_global_init();
+
+ if (!input_stream_global_init(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ playlist_list_global_init();
+
+ daemonize(options.daemon);
+
+ setup_log_output(options.log_stderr);
+
+ initSigHandlers();
+
+ if (!io_thread_start(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ initZeroconf();
+
+ player_create(global_player_control);
+
+ if (create_db) {
+ /* the database failed to load: recreate the
+ database */
+ unsigned job = update_enqueue(NULL, true);
+ if (job == 0)
+ MPD_ERROR("directory update failed");
+ }
+
+ if (!glue_state_file_init(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ success = 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));
+#else
+ if (success)
+ g_warning("inotify: auto_update was disabled. enable during compilation phase");
+#endif
+
+ config_global_check();
+
+ /* enable all audio outputs (if not already done by
+ playlist_state_restore() */
+ pc_update_audio(global_player_control);
+
+#ifdef WIN32
+ win32_app_started();
+#endif
+
+ /* run the main loop */
+ g_main_loop_run(main_loop);
+
+#ifdef WIN32
+ win32_app_stopping();
+#endif
+
+ /* cleanup */
+
+ g_main_loop_unref(main_loop);
+
+#ifdef ENABLE_INOTIFY
+ mpd_inotify_finish();
+#endif
+
+ state_file_finish(global_player_control);
+ pc_kill(global_player_control);
+ finishZeroconf();
+ client_manager_deinit();
+ listen_global_finish();
+ playlist_global_finish();
+
+ start = clock();
+ db_finish();
+ g_debug("db_finish took %f seconds",
+ ((float)(clock()-start))/CLOCKS_PER_SEC);
+
+#ifdef ENABLE_SQLITE
+ sticker_global_finish();
+#endif
+
+ g_cond_free(main_cond);
+ event_pipe_deinit();
+
+ playlist_list_global_finish();
+ input_stream_global_finish();
+ audio_output_all_finish();
+ volume_finish();
+ mapper_finish();
+ path_global_finish();
+ finishPermissions();
+ pc_free(global_player_control);
+ command_finish();
+ update_global_finish();
+ decoder_plugin_deinit_all();
+#ifdef ENABLE_ARCHIVE
+ archive_plugin_deinit_all();
+#endif
+ config_global_finish();
+ idle_deinit();
+ stats_global_finish();
+ io_thread_deinit();
+ daemonize_finish();
+#ifdef WIN32
+ WSACleanup();
+#endif
+
+ log_deinit();
+ return EXIT_SUCCESS;
+}