main.c 11.5 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2011 The Music Player Daemon Project
3
 * http://www.musicpd.org
Warren Dukes's avatar
Warren Dukes committed
4 5 6 7 8 9 10 11 12 13
 *
 * 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.
14 15 16 17
 *
 * 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.
Warren Dukes's avatar
Warren Dukes committed
18 19
 */

20
#include "config.h"
21
#include "main.h"
22
#include "daemon.h"
23
#include "io_thread.h"
24
#include "client.h"
25
#include "client_idle.h"
26
#include "idle.h"
Warren Dukes's avatar
Warren Dukes committed
27 28
#include "command.h"
#include "playlist.h"
29
#include "stored_playlist.h"
30
#include "database.h"
31
#include "update.h"
32
#include "player_thread.h"
Warren Dukes's avatar
Warren Dukes committed
33
#include "listen.h"
34
#include "cmdline.h"
Warren Dukes's avatar
Warren Dukes committed
35 36
#include "conf.h"
#include "path.h"
37
#include "mapper.h"
38
#include "chunk.h"
39
#include "player_control.h"
Warren Dukes's avatar
Warren Dukes committed
40 41
#include "stats.h"
#include "sig_handlers.h"
42
#include "audio_config.h"
43
#include "output_all.h"
Warren Dukes's avatar
Warren Dukes committed
44 45 46
#include "volume.h"
#include "log.h"
#include "permission.h"
47
#include "pcm_resample.h"
48
#include "replay_gain_config.h"
49
#include "decoder_list.h"
50
#include "input_init.h"
51
#include "playlist_list.h"
52
#include "state_file.h"
53
#include "tag.h"
54
#include "zeroconf.h"
55
#include "event_pipe.h"
56
#include "tag_pool.h"
57
#include "mpd_error.h"
58 59

#ifdef ENABLE_INOTIFY
60
#include "inotify_update.h"
61
#endif
Warren Dukes's avatar
Warren Dukes committed
62

63 64 65 66
#ifdef ENABLE_SQLITE
#include "sticker.h"
#endif

67 68 69 70
#ifdef ENABLE_ARCHIVE
#include "archive_list.h"
#endif

71 72
#include <glib.h>

Max Kellermann's avatar
Max Kellermann committed
73 74 75 76
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
77

78
#ifdef HAVE_LOCALE_H
79 80 81
#include <locale.h>
#endif

82 83 84 85 86
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif

Max Kellermann's avatar
Max Kellermann committed
87 88 89 90 91
enum {
	DEFAULT_BUFFER_SIZE = 2048,
	DEFAULT_BUFFER_BEFORE_PLAY = 10,
};

92
GThread *main_task;
93 94
GMainLoop *main_loop;

95
GCond *main_cond;
96

97 98
struct player_control *global_player_control;

99 100
static bool
glue_daemonize_init(const struct options *options, GError **error_r)
101
{
102 103 104 105 106 107 108 109
	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;
	}

110
	daemonize_init(config_get_string(CONF_USER, NULL),
111
		       config_get_string(CONF_GROUP, NULL),
112 113
		       pid_file);
	g_free(pid_file);
114 115 116

	if (options->kill)
		daemonize_kill();
117 118

	return true;
119 120
}

121 122
static bool
glue_mapper_init(GError **error_r)
123
{
124 125 126 127 128 129 130 131 132 133 134 135
	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;
	}
136

137
#if GLIB_CHECK_VERSION(2,14,0)
138
	if (music_dir == NULL)
139
		music_dir = g_strdup(g_get_user_special_dir(G_USER_DIRECTORY_MUSIC));
140
#endif
141 142

	mapper_init(music_dir, playlist_dir);
143 144 145 146

	g_free(music_dir);
	g_free(playlist_dir);
	return true;
147 148
}

149 150 151 152 153 154
/**
 * 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
155
glue_db_init_and_load(void)
156
{
157
	const struct config_param *path = config_get_param(CONF_DB_FILE);
158

159
	GError *error = NULL;
160
	bool ret;
161

162
	if (!mapper_has_music_directory()) {
163
		if (path != NULL)
164 165
			g_message("Found " CONF_DB_FILE " setting without "
				  CONF_MUSIC_DIR " - disabling database");
166
		return true;
167 168
	}

169
	if (path == NULL)
170
		MPD_ERROR(CONF_DB_FILE " setting missing");
171

172 173 174 175
	struct config_param *param = config_new_param("database", path->line);
	config_add_block_param(param, "path", path->value, path->line);

	if (!db_init(param, &error))
176
		MPD_ERROR("%s", error->message);
177

178 179
	config_param_free(param);

180
	ret = db_load(&error);
181 182
	if (!ret)
		MPD_ERROR("%s", error->message);
183

184 185
	/* run database update after daemonization? */
	return db_exists();
Warren Dukes's avatar
Warren Dukes committed
186
}
Warren Dukes's avatar
Warren Dukes committed
187

188 189 190 191 192 193 194 195
/**
 * Configure and initialize the sticker subsystem.
 */
static void
glue_sticker_init(void)
{
#ifdef ENABLE_SQLITE
	GError *error = NULL;
196 197 198
	char *sticker_file = config_dup_path(CONF_STICKER_FILE, &error);
	if (sticker_file == NULL && error != NULL)
		MPD_ERROR("%s", error->message);
199

200
	if (!sticker_global_init(sticker_file, &error))
201
		MPD_ERROR("%s", error->message);
202 203

	g_free(sticker_file);
204 205 206
#endif
}

207 208
static bool
glue_state_file_init(GError **error_r)
209
{
210 211 212 213 214 215 216 217 218 219 220 221
	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;
222 223
}

224 225 226 227 228
/**
 * Windows-only initialization of the Winsock2 library.
 */
static void winsock_init(void)
{
229
#ifdef WIN32
230 231 232 233 234 235
	WSADATA sockinfo;
	int retval;

	retval = WSAStartup(MAKEWORD(2, 2), &sockinfo);
	if(retval != 0)
	{
236
		MPD_ERROR("Attempt to open Winsock2 failed; error code %d\n",
237 238 239 240 241
			retval);
	}

	if (LOBYTE(sockinfo.wVersion) != 2)
	{
242 243
		MPD_ERROR("We use Winsock2 but your version is either too new "
			  "or old; please install Winsock 2.x\n");
244 245
	}
#endif
246
}
247

Max Kellermann's avatar
Max Kellermann committed
248 249 250 251 252 253
/**
 * Initialize the decoder and player core, including the music pipe.
 */
static void
initialize_decoder_and_player(void)
{
254
	const struct config_param *param;
Max Kellermann's avatar
Max Kellermann committed
255 256 257 258 259 260 261 262
	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) {
263 264
		long tmp = strtol(param->value, &test, 10);
		if (*test != '\0' || tmp <= 0 || tmp == LONG_MAX)
265 266
			MPD_ERROR("buffer size \"%s\" is not a positive integer, "
				  "line %i\n", param->value, param->line);
267
		buffer_size = tmp;
Max Kellermann's avatar
Max Kellermann committed
268 269 270 271 272 273 274 275
	} else
		buffer_size = DEFAULT_BUFFER_SIZE;

	buffer_size *= 1024;

	buffered_chunks = buffer_size / CHUNK_SIZE;

	if (buffered_chunks >= 1 << 15)
276
		MPD_ERROR("buffer size \"%li\" is too big\n", (long)buffer_size);
Max Kellermann's avatar
Max Kellermann committed
277 278 279 280 281

	param = config_get_param(CONF_BUFFER_BEFORE_PLAY);
	if (param != NULL) {
		perc = strtod(param->value, &test);
		if (*test != '%' || perc < 0 || perc > 100) {
282 283 284
			MPD_ERROR("buffered before play \"%s\" is not a positive "
				  "percentage and less than 100 percent, line %i",
				  param->value, param->line);
Max Kellermann's avatar
Max Kellermann committed
285 286 287 288 289 290 291 292
		}
	} else
		perc = DEFAULT_BUFFER_BEFORE_PLAY;

	buffered_before_play = (perc / 100) * buffered_chunks;
	if (buffered_before_play > buffered_chunks)
		buffered_before_play = buffered_chunks;

293
	global_player_control = pc_new(buffered_chunks, buffered_before_play);
Max Kellermann's avatar
Max Kellermann committed
294 295
}

296 297 298 299 300
/**
 * event_pipe callback function for PIPE_EVENT_IDLE
 */
static void
idle_event_emitted(void)
301 302 303
{
	/* send "idle" notificaions to all subscribed
	   clients */
304
	unsigned flags = idle_get();
305 306 307 308
	if (flags != 0)
		client_manager_idle_add(flags);
}

309 310 311 312 313 314 315 316 317
/**
 * event_pipe callback function for PIPE_EVENT_SHUTDOWN
 */
static void
shutdown_event_emitted(void)
{
	g_main_loop_quit(main_loop);
}

318
int main(int argc, char *argv[])
319 320 321 322 323 324 325 326 327
{
#ifdef WIN32
	return win32_main(argc, argv);
#else
	return mpd_main(argc, argv);
#endif
}

int mpd_main(int argc, char *argv[])
328
{
Max Kellermann's avatar
Max Kellermann committed
329
	struct options options;
330
	clock_t start;
331
	bool create_db;
332 333
	GError *error = NULL;
	bool success;
Warren Dukes's avatar
Warren Dukes committed
334

335 336
	daemonize_close_stdin();

337
#ifdef HAVE_LOCALE_H
338 339 340 341
	/* initialize locale */
	setlocale(LC_CTYPE,"");
#endif

342 343
	g_set_application_name("Music Player Daemon");

Max Kellermann's avatar
Max Kellermann committed
344 345 346
	/* enable GLib's thread safety code */
	g_thread_init(NULL);

347
	io_thread_init();
348
	winsock_init();
349
	idle_init();
350
	tag_pool_init();
351
	config_global_init();
Warren Dukes's avatar
Warren Dukes committed
352

353 354 355 356 357 358
	success = parse_cmdline(argc, argv, &options, &error);
	if (!success) {
		g_warning("%s", error->message);
		g_error_free(error);
		return EXIT_FAILURE;
	}
Warren Dukes's avatar
Warren Dukes committed
359

360 361 362 363 364
	if (!glue_daemonize_init(&options, &error)) {
		g_warning("%s", error->message);
		g_error_free(error);
		return EXIT_FAILURE;
	}
365

Max Kellermann's avatar
Max Kellermann committed
366
	stats_global_init();
367
	tag_lib_init();
368 369 370 371 372 373

	if (!log_init(options.verbose, options.log_stderr, &error)) {
		g_warning("%s", error->message);
		g_error_free(error);
		return EXIT_FAILURE;
	}
374

375 376 377 378 379 380
	success = listen_global_init(&error);
	if (!success) {
		g_warning("%s", error->message);
		g_error_free(error);
		return EXIT_FAILURE;
	}
381

382
	daemonize_set_user();
Warren Dukes's avatar
Warren Dukes committed
383

384
	main_task = g_thread_self();
385
	main_loop = g_main_loop_new(NULL, FALSE);
386
	main_cond = g_cond_new();
387

388 389
	event_pipe_init();
	event_pipe_register(PIPE_EVENT_IDLE, idle_event_emitted);
390
	event_pipe_register(PIPE_EVENT_SHUTDOWN, shutdown_event_emitted);
391

Max Kellermann's avatar
Max Kellermann committed
392
	path_global_init();
393 394 395 396 397 398 399

	if (!glue_mapper_init(&error)) {
		g_warning("%s", error->message);
		g_error_free(error);
		return EXIT_FAILURE;
	}

400
	initPermissions();
Max Kellermann's avatar
Max Kellermann committed
401
	playlist_global_init();
402
	spl_global_init();
403
#ifdef ENABLE_ARCHIVE
404
	archive_plugin_init_all();
405
#endif
406 407 408 409 410 411 412

	if (!pcm_resample_global_init(&error)) {
		g_warning("%s", error->message);
		g_error_free(error);
		return EXIT_FAILURE;
	}

413
	decoder_plugin_init_all();
414
	update_global_init();
415

416
	create_db = !glue_db_init_and_load();
417

418
	glue_sticker_init();
419

Max Kellermann's avatar
Max Kellermann committed
420
	command_init();
Max Kellermann's avatar
Max Kellermann committed
421
	initialize_decoder_and_player();
422
	volume_init();
423
	initAudioConfig();
424
	audio_output_all_init(global_player_control);
425
	client_manager_init();
426
	replay_gain_global_init();
427 428 429 430 431 432 433

	if (!input_stream_global_init(&error)) {
		g_warning("%s", error->message);
		g_error_free(error);
		return EXIT_FAILURE;
	}

434
	playlist_list_global_init();
435

436
	daemonize(options.daemon);
437

438
	setup_log_output(options.log_stderr);
Warren Dukes's avatar
Warren Dukes committed
439

440
	initSigHandlers();
441

442 443 444 445 446 447
	if (!io_thread_start(&error)) {
		g_warning("%s", error->message);
		g_error_free(error);
		return EXIT_FAILURE;
	}

448 449
	initZeroconf();

450
	player_create(global_player_control);
451

452
	if (create_db) {
453 454
		/* the database failed to load: recreate the
		   database */
455
		unsigned job = update_enqueue(NULL, true);
456
		if (job == 0)
457
			MPD_ERROR("directory update failed");
458 459
	}

460 461 462 463 464
	if (!glue_state_file_init(&error)) {
		g_warning("%s", error->message);
		g_error_free(error);
		return EXIT_FAILURE;
	}
465

466
	success = config_get_bool(CONF_AUTO_UPDATE, false);
467
#ifdef ENABLE_INOTIFY
468
	if (success && mapper_has_music_directory())
469 470
		mpd_inotify_init(config_get_unsigned(CONF_AUTO_UPDATE_DEPTH,
						     G_MAXUINT));
471 472 473
#else
	if (success)
		g_warning("inotify: auto_update was disabled. enable during compilation phase");
474
#endif
475

476 477
	config_global_check();

478 479
	/* enable all audio outputs (if not already done by
	   playlist_state_restore() */
480
	pc_update_audio(global_player_control);
481

482 483 484
#ifdef WIN32
	win32_app_started();
#endif
485

486
	/* run the main loop */
487
	g_main_loop_run(main_loop);
488

489 490 491 492
#ifdef WIN32
	win32_app_stopping();
#endif

493 494 495
	/* cleanup */

	g_main_loop_unref(main_loop);
Warren Dukes's avatar
Warren Dukes committed
496

497
#ifdef ENABLE_INOTIFY
498
	mpd_inotify_finish();
499
#endif
500

501 502
	state_file_finish(global_player_control);
	pc_kill(global_player_control);
503
	finishZeroconf();
504
	client_manager_deinit();
Max Kellermann's avatar
Max Kellermann committed
505
	listen_global_finish();
Max Kellermann's avatar
Max Kellermann committed
506
	playlist_global_finish();
507

508
	start = clock();
509
	db_finish();
Max Kellermann's avatar
Max Kellermann committed
510 511
	g_debug("db_finish took %f seconds",
		((float)(clock()-start))/CLOCKS_PER_SEC);
512

513 514 515 516
#ifdef ENABLE_SQLITE
	sticker_global_finish();
#endif

517
	g_cond_free(main_cond);
518
	event_pipe_deinit();
519

520
	playlist_list_global_finish();
521
	input_stream_global_finish();
522
	audio_output_all_finish();
523
	volume_finish();
524
	mapper_finish();
Max Kellermann's avatar
Max Kellermann committed
525
	path_global_finish();
Warren Dukes's avatar
Warren Dukes committed
526
	finishPermissions();
527
	pc_free(global_player_control);
Max Kellermann's avatar
Max Kellermann committed
528
	command_finish();
529
	update_global_finish();
530
	decoder_plugin_deinit_all();
531
#ifdef ENABLE_ARCHIVE
532
	archive_plugin_deinit_all();
533
#endif
534
	config_global_finish();
535
	tag_pool_deinit();
536
	idle_deinit();
537
	stats_global_finish();
538
	io_thread_deinit();
539
	daemonize_finish();
540 541 542
#ifdef WIN32
	WSACleanup();
#endif
543

Eric Wong's avatar
Eric Wong committed
544
	close_log_files();
545
	return EXIT_SUCCESS;
Warren Dukes's avatar
Warren Dukes committed
546
}