/** * 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 *path = config_get_param(CONF_DB_FILE); GError *error = NULL; bool ret; if (!mapper_has_music_directory()) { if (path != NULL) g_message("Found " CONF_DB_FILE " setting without " CONF_MUSIC_DIR " - disabling database"); db_init(NULL, NULL); return true; } if (path == NULL) MPD_ERROR(CONF_DB_FILE " setting missing"); if (!db_init(path, &error)) MPD_ERROR("%s", error->message); ret = db_load(&error); if (!ret) MPD_ERROR("%s", error->message); /* run database update after daemonization? */ return db_exists(); }
static void redirect_logs(int fd) { assert(fd >= 0); if (dup2(fd, STDOUT_FILENO) < 0) MPD_ERROR("problems dup2 stdout : %s\n", strerror(errno)); if (dup2(fd, STDERR_FILENO) < 0) MPD_ERROR("problems dup2 stderr : %s\n", strerror(errno)); }
void replay_gain_global_init(void) { const struct config_param *param = config_get_param(CONF_REPLAYGAIN); if (param != NULL && !replay_gain_set_mode_string(param->value)) { MPD_ERROR("replaygain value \"%s\" at line %i is invalid\n", param->value, param->line); } param = config_get_param(CONF_REPLAYGAIN_PREAMP); if (param) { char *test; float f = strtod(param->value, &test); if (*test != '\0') { MPD_ERROR("Replaygain preamp \"%s\" is not a number at " "line %i\n", param->value, param->line); } if (f < -15 || f > 15) { MPD_ERROR("Replaygain preamp \"%s\" is not between -15 and" "15 at line %i\n", param->value, param->line); } replay_gain_preamp = pow(10, f / 20.0); } param = config_get_param(CONF_REPLAYGAIN_MISSING_PREAMP); if (param) { char *test; float f = strtod(param->value, &test); if (*test != '\0') { MPD_ERROR("Replaygain missing preamp \"%s\" is not a number at " "line %i\n", param->value, param->line); } if (f < -15 || f > 15) { MPD_ERROR("Replaygain missing preamp \"%s\" is not between -15 and" "15 at line %i\n", param->value, param->line); } replay_gain_missing_preamp = pow(10, f / 20.0); } replay_gain_limit = config_get_bool(CONF_REPLAYGAIN_LIMIT, DEFAULT_REPLAYGAIN_LIMIT); }
/** * 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 size_t vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length) { struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; ogg_page page; int ret; unsigned char *dest = _dest; size_t nbytes; ret = ogg_stream_pageout(&encoder->os, &page); if (ret == 0 && encoder->flush) { encoder->flush = false; ret = ogg_stream_flush(&encoder->os, &page); } if (ret == 0) return 0; assert(page.header_len > 0 || page.body_len > 0); nbytes = (size_t)page.header_len + (size_t)page.body_len; if (nbytes > length) /* XXX better error handling */ MPD_ERROR("buffer too small"); memcpy(dest, page.header, page.header_len); memcpy(dest + page.header_len, page.body, page.body_len); return nbytes; }
static gboolean main_notify_event(G_GNUC_UNUSED GIOChannel *source, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer data) { char buffer[256]; gsize bytes_read; GError *error = NULL; GIOStatus status = g_io_channel_read_chars(event_channel, buffer, sizeof(buffer), &bytes_read, &error); if (status == G_IO_STATUS_ERROR) MPD_ERROR("error reading from pipe: %s", error->message); bool events[PIPE_EVENT_MAX]; g_mutex_lock(event_pipe_mutex); memcpy(events, pipe_events, sizeof(events)); memset(pipe_events, 0, sizeof(pipe_events)); g_mutex_unlock(event_pipe_mutex); for (unsigned i = 0; i < PIPE_EVENT_MAX; ++i) if (events[i]) /* invoke the event handler */ event_pipe_invoke(i); return true; }
static unsigned parsePermissions(const char *string) { unsigned permission = 0; gchar **tokens; if (!string) return 0; tokens = g_strsplit(string, PERMISSION_SEPERATOR, 0); for (unsigned i = 0; tokens[i] != NULL; ++i) { char *temp = tokens[i]; if (strcmp(temp, PERMISSION_READ_STRING) == 0) { permission |= PERMISSION_READ; } else if (strcmp(temp, PERMISSION_ADD_STRING) == 0) { permission |= PERMISSION_ADD; } else if (strcmp(temp, PERMISSION_CONTROL_STRING) == 0) { permission |= PERMISSION_CONTROL; } else if (strcmp(temp, PERMISSION_ADMIN_STRING) == 0) { permission |= PERMISSION_ADMIN; } else { MPD_ERROR("unknown permission \"%s\"", temp); } } g_strfreev(tokens); return permission; }
static gboolean mpd_inotify_in_event(G_GNUC_UNUSED GIOChannel *_source, G_GNUC_UNUSED GIOCondition condition, gpointer data) { struct mpd_inotify_source *source = data; void *dest; size_t length; ssize_t nbytes; const struct inotify_event *event; dest = fifo_buffer_write(source->buffer, &length); if (dest == NULL) MPD_ERROR("buffer full"); nbytes = read(source->fd, dest, length); if (nbytes < 0) MPD_ERROR("failed to read from inotify: %s", g_strerror(errno)); if (nbytes == 0) MPD_ERROR("end of file from inotify"); fifo_buffer_append(source->buffer, nbytes); while (true) { const char *name; event = fifo_buffer_read(source->buffer, &length); if (event == NULL || length < sizeof(*event) || length < sizeof(*event) + event->len) break; if (event->len > 0 && event->name[event->len - 1] == 0) name = event->name; else name = NULL; source->callback(event->wd, event->mask, name, source->callback_ctx); fifo_buffer_consume(source->buffer, sizeof(*event) + event->len); } return true; }
void audio_output_thread_start(struct audio_output *ao) { GError *e = NULL; assert(ao->command == AO_COMMAND_NONE); if (!(ao->thread = g_thread_create(audio_output_task, ao, true, &e))) MPD_ERROR("Failed to spawn output task: %s\n", e->message); }
/** * 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); }
/** * 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 }
void decoder_thread_start(struct decoder_control *dc) { GError *e = NULL; assert(dc->thread == NULL); dc->quit = false; dc->thread = g_thread_create(decoder_task, dc, true, &e); if (dc->thread == NULL) MPD_ERROR("Failed to spawn decoder task: %s", e->message); }
void tag_lib_init(void) { const char *value; int quit = 0; char *temp; char *s; char *c; enum tag_type type; /* parse the "metadata_to_use" config parameter below */ /* ignore comments by default */ ignore_tag_items[TAG_COMMENT] = true; value = config_get_string(CONF_METADATA_TO_USE, NULL); if (value == NULL) return; memset(ignore_tag_items, true, TAG_NUM_OF_ITEM_TYPES); if (0 == g_ascii_strcasecmp(value, "none")) return; temp = c = s = g_strdup(value); while (!quit) { if (*s == ',' || *s == '\0') { if (*s == '\0') quit = 1; *s = '\0'; c = g_strstrip(c); if (*c == 0) continue; type = tag_name_parse_i(c); if (type == TAG_NUM_OF_ITEM_TYPES) MPD_ERROR("error parsing metadata item \"%s\"", c); ignore_tag_items[type] = false; s++; c = s; } s++; } g_free(temp); }
static inline GLogLevelFlags parse_log_level(const char *value, unsigned line) { if (0 == strcmp(value, "default")) return G_LOG_LEVEL_MESSAGE; if (0 == strcmp(value, "secure")) return LOG_LEVEL_SECURE; else if (0 == strcmp(value, "verbose")) return G_LOG_LEVEL_DEBUG; else { MPD_ERROR("unknown log level \"%s\" at line %u\n", value, line); return G_LOG_LEVEL_MESSAGE; } }
static void spawn_update_task(const char *path) { GError *e = NULL; assert(g_thread_self() == main_task); progress = UPDATE_PROGRESS_RUNNING; modified = false; update_thr = g_thread_create(update_task, g_strdup(path), TRUE, &e); if (update_thr == NULL) MPD_ERROR("Failed to spawn update task: %s", e->message); if (++update_task_id > update_task_id_max) update_task_id = 1; g_debug("spawned thread for update job id %i", update_task_id); }
static void path_set_fs_charset(const char *charset) { char *test; assert(charset != NULL); /* convert a space to ensure that the charset is valid */ test = g_convert(" ", 1, charset, "UTF-8", NULL, NULL, NULL); if (test == NULL) MPD_ERROR("invalid filesystem charset: %s", charset); g_free(test); g_free(fs_charset); fs_charset = g_strdup(charset); g_debug("path_set_fs_charset: fs charset is: %s", fs_charset); }
void initPermissions(void) { char *password; unsigned permission; const struct config_param *param; permission_passwords = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); permission_default = PERMISSION_READ | PERMISSION_ADD | PERMISSION_CONTROL | PERMISSION_ADMIN; param = config_get_next_param(CONF_PASSWORD, NULL); if (param) { permission_default = 0; do { const char *separator = strchr(param->value, PERMISSION_PASSWORD_CHAR); if (separator == NULL) MPD_ERROR("\"%c\" not found in password string " "\"%s\", line %i", PERMISSION_PASSWORD_CHAR, param->value, param->line); password = g_strndup(param->value, separator - param->value); permission = parsePermissions(separator + 1); g_hash_table_replace(permission_passwords, password, GINT_TO_POINTER(permission)); } while ((param = config_get_next_param(CONF_PASSWORD, param))); } param = config_get_param(CONF_DEFAULT_PERMS); if (param) permission_default = parsePermissions(param->value); }
void event_pipe_init(void) { GIOChannel *channel; int ret; ret = pipe_cloexec_nonblock(event_pipe); if (ret < 0) MPD_ERROR("Couldn't open pipe: %s", strerror(errno)); channel = g_io_channel_new_socket(event_pipe[0]); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, false); event_pipe_source_id = g_io_add_watch(channel, G_IO_IN, main_notify_event, NULL); event_channel = channel; event_pipe_mutex = g_mutex_new(); }
void event_pipe_emit(enum pipe_event event) { ssize_t w; assert((unsigned)event < PIPE_EVENT_MAX); g_mutex_lock(event_pipe_mutex); if (pipe_events[event]) { /* already set: don't write */ g_mutex_unlock(event_pipe_mutex); return; } pipe_events[event] = true; g_mutex_unlock(event_pipe_mutex); w = write(event_pipe[1], "", 1); if (w < 0 && errno != EAGAIN && errno != EINTR) MPD_ERROR("error writing to pipe: %s", strerror(errno)); }
static void WINAPI service_main(G_GNUC_UNUSED DWORD argc, G_GNUC_UNUSED CHAR *argv[]) { DWORD error_code; gchar* error_message; service_handle = RegisterServiceCtrlHandlerEx(service_name, service_dispatcher, NULL); if (service_handle == 0) { error_code = GetLastError(); error_message = g_win32_error_message(error_code); MPD_ERROR("RegisterServiceCtrlHandlerEx() failed: %s", error_message); } service_notify_status(SERVICE_START_PENDING); mpd_main(service_argc, service_argv); service_notify_status(SERVICE_STOPPED); }
int win32_main(int argc, char *argv[]) { DWORD error_code; gchar* error_message; service_argc = argc; service_argv = argv; if (StartServiceCtrlDispatcher(service_registry)) return 0; /* run as service successefully */ error_code = GetLastError(); if (error_code == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { /* running as console app */ SetConsoleTitle("Music Player Daemon"); ignore_console_events = TRUE; SetConsoleCtrlHandler(console_handler, TRUE); return mpd_main(argc, argv); } error_message = g_win32_error_message(error_code); MPD_ERROR("StartServiceCtrlDispatcher() failed: %s", error_message); }
static void x_sigaction(int signum, const struct sigaction *act) { if (sigaction(signum, act, NULL) < 0) MPD_ERROR("sigaction() failed: %s", strerror(errno)); }
static void decoder_run_song(struct decoder_control *dc, const struct song *song, const char *uri) { struct decoder decoder = { .dc = dc, .initial_seek_pending = dc->start_ms > 0, .initial_seek_running = false, }; int ret; decoder.timestamp = 0.0; decoder.seeking = false; decoder.song_tag = song->tag != NULL && song_is_file(song) ? tag_dup(song->tag) : NULL; decoder.stream_tag = NULL; decoder.decoder_tag = NULL; decoder.chunk = NULL; dc->state = DECODE_STATE_START; decoder_command_finished_locked(dc); pcm_convert_init(&decoder.conv_state); ret = song_is_file(song) ? decoder_run_file(&decoder, uri) : decoder_run_stream(&decoder, uri); decoder_unlock(dc); pcm_convert_deinit(&decoder.conv_state); /* flush the last chunk */ if (decoder.chunk != NULL) decoder_flush_chunk(&decoder); if (decoder.song_tag != NULL) tag_free(decoder.song_tag); if (decoder.stream_tag != NULL) tag_free(decoder.stream_tag); if (decoder.decoder_tag != NULL) tag_free(decoder.decoder_tag); decoder_lock(dc); dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR; } static void decoder_run(struct decoder_control *dc) { const struct song *song = dc->song; char *uri; assert(song != NULL); if (song_is_file(song)) uri = map_song_fs(song); else uri = song_get_uri(song); if (uri == NULL) { dc->state = DECODE_STATE_ERROR; decoder_command_finished_locked(dc); return; } decoder_run_song(dc, song, uri); g_free(uri); } static gpointer decoder_task(gpointer arg) { struct decoder_control *dc = arg; decoder_lock(dc); do { assert(dc->state == DECODE_STATE_STOP || dc->state == DECODE_STATE_ERROR); switch (dc->command) { case DECODE_COMMAND_START: dc_mixramp_start(dc, NULL); dc_mixramp_prev_end(dc, dc->mixramp_end); dc->mixramp_end = NULL; /* Don't free, it's copied above. */ dc->replay_gain_prev_db = dc->replay_gain_db; dc->replay_gain_db = 0; /* fall through */ case DECODE_COMMAND_SEEK: decoder_run(dc); break; case DECODE_COMMAND_STOP: decoder_command_finished_locked(dc); break; case DECODE_COMMAND_NONE: decoder_wait(dc); break; } } while (dc->command != DECODE_COMMAND_NONE || !dc->quit); decoder_unlock(dc); return NULL; } void decoder_thread_start(struct decoder_control *dc) { GError *e = NULL; assert(dc->thread == NULL); dc->quit = false; dc->thread = g_thread_create(decoder_task, dc, true, &e); if (dc->thread == NULL) MPD_ERROR("Failed to spawn decoder task: %s", e->message); }
/* * The main loop of the player thread, during playback. This is * basically a state machine, which multiplexes data between the * decoder thread and the output threads. */ static void do_play(struct player_control *pc, struct decoder_control *dc) { struct player player = { .pc = pc, .dc = dc, .buffering = true, .decoder_starting = false, .paused = false, .queued = true, .song = NULL, .xfade = XFADE_UNKNOWN, .cross_fading = false, .cross_fade_chunks = 0, .cross_fade_tag = NULL, .elapsed_time = 0.0, }; player_unlock(pc); player.pipe = music_pipe_new(); player_dc_start(&player, player.pipe); if (!player_wait_for_decoder(&player)) { player_dc_stop(&player); player_command_finished(pc); music_pipe_free(player.pipe); event_pipe_emit(PIPE_EVENT_PLAYLIST); player_lock(pc); return; } player_lock(pc); pc->state = PLAYER_STATE_PLAY; player_command_finished_locked(pc); while (true) { player_process_command(&player); if (pc->command == PLAYER_COMMAND_STOP || pc->command == PLAYER_COMMAND_EXIT || pc->command == PLAYER_COMMAND_CLOSE_AUDIO) { player_unlock(pc); audio_output_all_cancel(); break; } player_unlock(pc); if (player.buffering) { /* buffering at the start of the song - wait until the buffer is large enough, to prevent stuttering on slow machines */ if (music_pipe_size(player.pipe) < pc->buffered_before_play && !decoder_lock_is_idle(dc)) { /* not enough decoded buffer space yet */ if (!player.paused && audio_format_defined(&player.play_audio_format) && audio_output_all_check() < 4 && !player_send_silence(&player)) break; decoder_lock(dc); /* XXX race condition: check decoder again */ player_wait_decoder(pc, dc); decoder_unlock(dc); player_lock(pc); continue; } else { /* buffering is complete */ player.buffering = false; } } if (player.decoder_starting) { /* wait until the decoder is initialized completely */ if (!player_check_decoder_startup(&player)) break; player_lock(pc); continue; } #ifndef NDEBUG /* music_pipe_check_format(&play_audio_format, player.next_song_chunk, &dc->out_audio_format); */ #endif if (decoder_lock_is_idle(dc) && player.queued && dc->pipe == player.pipe) { /* the decoder has finished the current song; make it decode the next song */ assert(dc->pipe == NULL || dc->pipe == player.pipe); player_dc_start(&player, music_pipe_new()); } if (player_dc_at_next_song(&player) && player.xfade == XFADE_UNKNOWN && !decoder_lock_is_starting(dc)) { /* enable cross fading in this song? if yes, calculate how many chunks will be required for it */ player.cross_fade_chunks = cross_fade_calc(pc->cross_fade_seconds, dc->total_time, pc->mixramp_db, pc->mixramp_delay_seconds, dc->replay_gain_db, dc->replay_gain_prev_db, dc->mixramp_start, dc->mixramp_prev_end, &dc->out_audio_format, &player.play_audio_format, music_buffer_size(player_buffer) - pc->buffered_before_play); if (player.cross_fade_chunks > 0) { player.xfade = XFADE_ENABLED; player.cross_fading = false; } else /* cross fading is disabled or the next song is too short */ player.xfade = XFADE_DISABLED; } if (player.paused) { player_lock(pc); if (pc->command == PLAYER_COMMAND_NONE) player_wait(pc); continue; } else if (!music_pipe_empty(player.pipe)) { /* at least one music chunk is ready - send it to the audio output */ play_next_chunk(&player); } else if (audio_output_all_check() > 0) { /* not enough data from decoder, but the output thread is still busy, so it's okay */ /* XXX synchronize in a better way */ g_usleep(10000); } else if (player_dc_at_next_song(&player)) { /* at the beginning of a new song */ if (!player_song_border(&player)) break; } else if (decoder_lock_is_idle(dc)) { /* check the size of the pipe again, because the decoder thread may have added something since we last checked */ if (music_pipe_empty(player.pipe)) { /* wait for the hardware to finish playback */ audio_output_all_drain(); break; } } else { /* the decoder is too busy and hasn't provided new PCM data in time: send silence (if the output pipe is empty) */ if (!player_send_silence(&player)) break; } player_lock(pc); } player_dc_stop(&player); music_pipe_clear(player.pipe, player_buffer); music_pipe_free(player.pipe); if (player.cross_fade_tag != NULL) tag_free(player.cross_fade_tag); player_lock(pc); if (player.queued) { assert(pc->next_song != NULL); pc->next_song = NULL; } pc->state = PLAYER_STATE_STOP; player_unlock(pc); event_pipe_emit(PIPE_EVENT_PLAYLIST); player_lock(pc); } static gpointer player_task(gpointer arg) { struct player_control *pc = arg; struct decoder_control *dc = dc_new(pc->cond); decoder_thread_start(dc); player_buffer = music_buffer_new(pc->buffer_chunks); player_lock(pc); while (1) { switch (pc->command) { case PLAYER_COMMAND_QUEUE: assert(pc->next_song != NULL); do_play(pc, dc); break; case PLAYER_COMMAND_STOP: player_unlock(pc); audio_output_all_cancel(); player_lock(pc); /* fall through */ case PLAYER_COMMAND_SEEK: case PLAYER_COMMAND_PAUSE: pc->next_song = NULL; player_command_finished_locked(pc); break; case PLAYER_COMMAND_CLOSE_AUDIO: player_unlock(pc); audio_output_all_release(); player_lock(pc); player_command_finished_locked(pc); #ifndef NDEBUG /* in the DEBUG build, check for leaked music_chunk objects by freeing the music_buffer */ music_buffer_free(player_buffer); player_buffer = music_buffer_new(pc->buffer_chunks); #endif break; case PLAYER_COMMAND_UPDATE_AUDIO: player_unlock(pc); audio_output_all_enable_disable(); player_lock(pc); player_command_finished_locked(pc); break; case PLAYER_COMMAND_EXIT: player_unlock(pc); dc_quit(dc); dc_free(dc); audio_output_all_close(); music_buffer_free(player_buffer); player_command_finished(pc); return NULL; case PLAYER_COMMAND_CANCEL: pc->next_song = NULL; player_command_finished_locked(pc); break; case PLAYER_COMMAND_REFRESH: /* no-op when not playing */ player_command_finished_locked(pc); break; case PLAYER_COMMAND_NONE: player_wait(pc); break; } } } void player_create(struct player_control *pc) { assert(pc->thread == NULL); GError *e = NULL; pc->thread = g_thread_create(player_task, pc, true, &e); if (pc->thread == NULL) MPD_ERROR("Failed to spawn player task: %s", e->message); }
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(); tag_pool_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(); tag_pool_deinit(); idle_deinit(); stats_global_finish(); io_thread_deinit(); daemonize_finish(); #ifdef WIN32 WSACleanup(); #endif close_log_files(); return EXIT_SUCCESS; }