Main.cxx 14.8 KB
Newer Older
1
/*
2
 * Copyright 2003-2018 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"
Max Kellermann's avatar
Max Kellermann committed
21
#include "Main.hxx"
22
#include "Instance.hxx"
Max Kellermann's avatar
Max Kellermann committed
23
#include "CommandLine.hxx"
Max Kellermann's avatar
Max Kellermann committed
24
#include "PlaylistFile.hxx"
25
#include "MusicChunk.hxx"
26
#include "StateFile.hxx"
Max Kellermann's avatar
Max Kellermann committed
27
#include "Mapper.hxx"
28
#include "Permission.hxx"
Max Kellermann's avatar
Max Kellermann committed
29
#include "Listen.hxx"
30
#include "client/Listener.hxx"
31 32
#include "client/Client.hxx"
#include "client/ClientList.hxx"
33
#include "command/AllCommands.hxx"
34
#include "Partition.hxx"
35
#include "tag/Config.hxx"
36
#include "ReplayGainGlobal.hxx"
Max Kellermann's avatar
Max Kellermann committed
37
#include "Idle.hxx"
38
#include "Log.hxx"
39
#include "LogInit.hxx"
Max Kellermann's avatar
Max Kellermann committed
40
#include "input/Init.hxx"
41
#include "event/Loop.hxx"
42
#include "fs/AllocatedPath.hxx"
43
#include "fs/Config.hxx"
44
#include "playlist/PlaylistRegistry.hxx"
45
#include "zeroconf/ZeroconfGlue.hxx"
46
#include "decoder/DecoderList.hxx"
47
#include "AudioParser.hxx"
48
#include "pcm/PcmConvert.hxx"
49
#include "unix/SignalHandlers.hxx"
50
#include "thread/Slack.hxx"
51
#include "net/Init.hxx"
52
#include "lib/icu/Init.hxx"
53
#include "config/File.hxx"
54
#include "config/Check.hxx"
55
#include "config/Data.hxx"
56
#include "config/Param.hxx"
57
#include "config/Path.hxx"
58 59 60
#include "config/Defaults.hxx"
#include "config/Option.hxx"
#include "config/Domain.hxx"
61
#include "util/RuntimeError.hxx"
62
#include "util/ScopeExit.hxx"
Max Kellermann's avatar
Max Kellermann committed
63

64 65 66 67
#ifdef ENABLE_DAEMON
#include "unix/Daemon.hxx"
#endif

68 69
#ifdef ENABLE_DATABASE
#include "db/update/Service.hxx"
70
#include "db/Configured.hxx"
71
#include "db/DatabasePlugin.hxx"
72
#include "db/plugins/simple/SimpleDatabasePlugin.hxx"
73
#include "storage/Configured.hxx"
74
#include "storage/CompositeStorage.hxx"
75 76 77
#ifdef ENABLE_INOTIFY
#include "db/update/InotifyUpdate.hxx"
#endif
78 79
#endif

80 81 82 83
#ifdef ENABLE_NEIGHBOR_PLUGINS
#include "neighbor/Glue.hxx"
#endif

84
#ifdef ENABLE_SQLITE
85
#include "sticker/StickerDatabase.hxx"
86 87
#endif

88
#ifdef ENABLE_ARCHIVE
89
#include "archive/ArchiveList.hxx"
90
#endif
Max Kellermann's avatar
Max Kellermann committed
91

Max Kellermann's avatar
Max Kellermann committed
92
#ifdef ANDROID
93 94
#include "java/Global.hxx"
#include "java/File.hxx"
95
#include "android/Environment.hxx"
96
#include "android/Context.hxx"
97
#include "android/LogListener.hxx"
98
#include "fs/FileSystem.hxx"
Max Kellermann's avatar
Max Kellermann committed
99 100 101
#include "org_musicpd_Bridge.h"
#endif

102 103
#ifdef ENABLE_DBUS
#include "lib/dbus/Init.hxx"
104 105
#endif

106 107
#ifdef ENABLE_SYSTEMD_DAEMON
#include <systemd/sd-daemon.h>
108 109
#endif

Max Kellermann's avatar
Max Kellermann committed
110
#include <stdlib.h>
111

112
#ifdef HAVE_LOCALE_H
113 114 115
#include <locale.h>
#endif

116 117
#include <limits.h>

118 119 120 121
static constexpr size_t KILOBYTE = 1024;
static constexpr size_t MEGABYTE = 1024 * KILOBYTE;

static constexpr size_t DEFAULT_BUFFER_SIZE = 4 * MEGABYTE;
122

123
static constexpr
124 125
size_t MIN_BUFFER_SIZE = std::max(CHUNK_SIZE * 32,
				  64 * KILOBYTE);
126

127 128
#ifdef ANDROID
Context *context;
129
LogListener *logListener;
130 131
#endif

132
Instance *instance;
133

134
struct Config {
135
	ReplayGainConfig replay_gain;
136 137 138
};

static Config
139
LoadConfig(const ConfigData &config)
140
{
141
	return {LoadReplayGainConfig(config)};
142 143
}

144
#ifdef ENABLE_DAEMON
145

146
static void
147 148
glue_daemonize_init(const struct options *options,
		    const ConfigData &config)
149
{
150 151 152
	daemonize_init(config.GetString(ConfigOption::USER),
		       config.GetString(ConfigOption::GROUP),
		       config.GetPath(ConfigOption::PID_FILE));
153 154 155 156 157

	if (options->kill)
		daemonize_kill();
}

158 159
#endif

160
static void
161
glue_mapper_init(const ConfigData &config)
162
{
163
	mapper_init(config.GetPath(ConfigOption::PLAYLIST_DIR));
164
}
165

166 167
#ifdef ENABLE_DATABASE

168
static void
169
InitStorage(const ConfigData &config, EventLoop &event_loop)
170
{
171
	auto storage = CreateConfiguredStorage(config, event_loop);
172
	if (storage == nullptr)
173
		return;
174 175 176

	CompositeStorage *composite = new CompositeStorage();
	instance->storage = composite;
177
	composite->Mount("", std::move(storage));
178 179
}

180 181 182 183 184 185
/**
 * 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
186
glue_db_init_and_load(const ConfigData &config)
187
{
188 189 190 191
	auto db = CreateConfiguredDatabase(config, instance->event_loop,
					   instance->io_thread.GetEventLoop(),
					   *instance);
	if (!db)
192
		return true;
193

194
	if (db->GetPlugin().RequireStorage()) {
195
		InitStorage(config, instance->io_thread.GetEventLoop());
196

197 198 199 200 201 202 203
		if (instance->storage == nullptr) {
			LogDefault(config_domain,
				   "Found database setting without "
				   "music_directory - disabling database");
			return true;
		}
	} else {
204
		if (IsStorageConfigured(config))
205 206 207
			LogDefault(config_domain,
				   "Ignoring the storage configuration "
				   "because the database does not need it");
208 209
	}

210
	try {
211
		db->Open();
212 213 214
	} catch (...) {
		std::throw_with_nested(std::runtime_error("Failed to open database plugin"));
	}
215

216 217 218 219
	instance->database = std::move(db);

	auto *sdb = dynamic_cast<SimpleDatabase *>(instance->database.get());
	if (sdb == nullptr)
220 221
		return true;

222
	instance->update = new UpdateService(config,
223
					     instance->event_loop, *sdb,
Max Kellermann's avatar
Max Kellermann committed
224
					     static_cast<CompositeStorage &>(*instance->storage),
225
					     *instance);
226

227
	/* run database update after daemonization? */
228
	return sdb->FileExists();
Warren Dukes's avatar
Warren Dukes committed
229
}
Warren Dukes's avatar
Warren Dukes committed
230

231
static bool
232
InitDatabaseAndStorage(const ConfigData &config)
233
{
234
	const bool create_db = !glue_db_init_and_load(config);
235 236 237
	return create_db;
}

238 239
#endif

240 241 242 243
/**
 * Configure and initialize the sticker subsystem.
 */
static void
244
glue_sticker_init(const ConfigData &config)
245 246
{
#ifdef ENABLE_SQLITE
247
	auto sticker_file = config.GetPath(ConfigOption::STICKER_FILE);
248
	if (sticker_file.IsNull())
249
		return;
250

251
	sticker_global_init(std::move(sticker_file));
252 253
#else
	(void)config;
254 255 256
#endif
}

257
static void
258
glue_state_file_init(const ConfigData &raw_config)
259
{
260 261
	StateFileConfig config(raw_config);
	if (!config.IsEnabled())
262
		return;
263

264
	instance->state_file = new StateFile(std::move(config),
265
					     instance->partitions.front(),
266 267
					     instance->event_loop);
	instance->state_file->Read();
268 269
}

Max Kellermann's avatar
Max Kellermann committed
270 271 272 273
/**
 * Initialize the decoder and player core, including the music pipe.
 */
static void
274 275
initialize_decoder_and_player(const ConfigData &config,
			      const ReplayGainConfig &replay_gain_config)
Max Kellermann's avatar
Max Kellermann committed
276
{
277
	const ConfigParam *param;
Max Kellermann's avatar
Max Kellermann committed
278

279
	size_t buffer_size;
280
	param = config.GetParam(ConfigOption::AUDIO_BUFFER_SIZE);
281
	if (param != nullptr) {
282
		char *test;
283
		long tmp = strtol(param->value.c_str(), &test, 10);
284
		if (*test != '\0' || tmp <= 0 || tmp == LONG_MAX)
285 286 287
			throw FormatRuntimeError("buffer size \"%s\" is not a "
						 "positive integer, line %i",
						 param->value.c_str(), param->line);
288
		buffer_size = tmp * KILOBYTE;
289 290 291 292 293 294 295

		if (buffer_size < MIN_BUFFER_SIZE) {
			FormatWarning(config_domain, "buffer size %lu is too small, using %lu bytes instead",
				      (unsigned long)buffer_size,
				      (unsigned long)MIN_BUFFER_SIZE);
			buffer_size = MIN_BUFFER_SIZE;
		}
Max Kellermann's avatar
Max Kellermann committed
296 297 298
	} else
		buffer_size = DEFAULT_BUFFER_SIZE;

299
	const unsigned buffered_chunks = buffer_size / CHUNK_SIZE;
Max Kellermann's avatar
Max Kellermann committed
300 301

	if (buffered_chunks >= 1 << 15)
302 303
		throw FormatRuntimeError("buffer size \"%lu\" is too big",
					 (unsigned long)buffer_size);
Max Kellermann's avatar
Max Kellermann committed
304

305
	const unsigned max_length =
306 307
		config.GetPositive(ConfigOption::MAX_PLAYLIST_LENGTH,
				   DEFAULT_PLAYLIST_MAX_LENGTH);
308

309
	AudioFormat configured_audio_format = AudioFormat::Undefined();
310
	param = config.GetParam(ConfigOption::AUDIO_OUTPUT_FORMAT);
311 312 313 314
	if (param != nullptr) {
		try {
			configured_audio_format = ParseAudioFormat(param->value.c_str(),
								   true);
315
		} catch (...) {
316 317 318 319 320
			std::throw_with_nested(FormatRuntimeError("error parsing line %i",
								  param->line));
		}
	}

321 322 323 324 325 326 327
	instance->partitions.emplace_back(*instance,
					  "default",
					  max_length,
					  buffered_chunks,
					  configured_audio_format,
					  replay_gain_config);
	auto &partition = instance->partitions.back();
328 329

	try {
330
		param = config.GetParam(ConfigOption::REPLAYGAIN);
331
		if (param != nullptr)
332
			partition.replay_gain_mode =
333 334 335 336 337
				FromString(param->value.c_str());
	} catch (...) {
		std::throw_with_nested(FormatRuntimeError("Failed to parse line %i",
							  param->line));
	}
Max Kellermann's avatar
Max Kellermann committed
338 339
}

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
inline void
Instance::BeginShutdownUpdate() noexcept
{
#ifdef ENABLE_DATABASE
#ifdef ENABLE_INOTIFY
	mpd_inotify_finish();
#endif

	if (update != nullptr)
		update->CancelAllAsync();
#endif
}

inline void
Instance::BeginShutdownPartitions() noexcept
{
356
	for (auto &partition : partitions) {
357
		partition.pc.Kill();
358 359
		partition.listener.reset();
	}
360 361
}

362 363
void
Instance::OnIdle(unsigned flags)
364
{
Max Kellermann's avatar
Max Kellermann committed
365
	/* send "idle" notifications to all subscribed
366
	   clients */
367
	client_list->IdleAdd(flags);
368

369
	if (flags & (IDLE_PLAYLIST|IDLE_PLAYER|IDLE_MIXER|IDLE_OUTPUT) &&
370 371
	    state_file != nullptr)
		state_file->CheckModified();
372 373
}

Max Kellermann's avatar
Max Kellermann committed
374 375
#ifndef ANDROID

Max Kellermann's avatar
Max Kellermann committed
376 377
int
main(int argc, char *argv[]) noexcept
378
{
379
#ifdef _WIN32
380 381 382 383 384 385
	return win32_main(argc, argv);
#else
	return mpd_main(argc, argv);
#endif
}

Max Kellermann's avatar
Max Kellermann committed
386 387
#endif

388
static int
Max Kellermann's avatar
Max Kellermann committed
389 390
mpd_main_after_fork(const ConfigData &raw_config,
		    const Config &config);
391

392 393 394
static inline int
MainOrThrow(int argc, char *argv[])
{
Max Kellermann's avatar
Max Kellermann committed
395
	struct options options;
Warren Dukes's avatar
Warren Dukes committed
396

397
#ifdef ENABLE_DAEMON
398
	daemonize_close_stdin();
399
#endif
400

401
#ifndef ANDROID
402
#ifdef HAVE_LOCALE_H
403 404
	/* initialize locale */
	setlocale(LC_CTYPE,"");
405
	setlocale(LC_COLLATE, "");
406
#endif
407
#endif
Max Kellermann's avatar
Max Kellermann committed
408

409
	const ScopeIcuInit icu_init;
410 411
	const ScopeNetInit net_init;

412 413 414 415
#ifdef ENABLE_DBUS
	const ODBus::ScopeInit dbus_init;
#endif

416
	ConfigData raw_config;
Warren Dukes's avatar
Warren Dukes committed
417

418
#ifdef ANDROID
419 420 421 422 423 424
	(void)argc;
	(void)argv;

	const auto sdcard = Environment::getExternalStorageDirectory();
	if (!sdcard.IsNull()) {
		const auto config_path =
425
			sdcard / Path::FromFS("mpd.conf");
426
		if (FileExists(config_path))
427
			ReadConfigFile(raw_config, config_path);
428
	}
429
#else
430
	ParseCommandLine(argc, argv, options, raw_config);
431
#endif
Warren Dukes's avatar
Warren Dukes committed
432

433
	InitPathParser(raw_config);
434
	const auto config = LoadConfig(raw_config);
435

436
#ifdef ENABLE_DAEMON
437
	glue_daemonize_init(&options, raw_config);
438
#endif
439

440
	TagLoadConfig(raw_config);
441

442
	log_init(raw_config, options.verbose, options.log_stderr);
443

444
	instance = new Instance();
445 446 447 448
	AtScopeExit() {
		delete instance;
		instance = nullptr;
	};
449

450 451
#ifdef ENABLE_NEIGHBOR_PLUGINS
	instance->neighbors = new NeighborGlue();
452 453
	instance->neighbors->Init(raw_config,
				  instance->io_thread.GetEventLoop(),
454
				  *instance);
455 456 457 458 459 460 461

	if (instance->neighbors->IsEmpty()) {
		delete instance->neighbors;
		instance->neighbors = nullptr;
	}
#endif

462
	const unsigned max_clients =
463
		raw_config.GetPositive(ConfigOption::MAX_CONN, 10);
464
	instance->client_list = new ClientList(max_clients);
465

466
	initialize_decoder_and_player(raw_config, config.replay_gain);
467

468
	listen_global_init(raw_config, *instance->partitions.front().listener);
469

470
#ifdef ENABLE_DAEMON
471
	daemonize_set_user();
472
	daemonize_begin(options.daemon);
473
	AtScopeExit() { daemonize_finish(); };
474
#endif
Warren Dukes's avatar
Warren Dukes committed
475

476
	return mpd_main_after_fork(raw_config, config);
477
}
478

479 480 481
#ifdef ANDROID
static inline
#endif
Max Kellermann's avatar
Max Kellermann committed
482
int mpd_main(int argc, char *argv[]) noexcept
483
{
484 485
	AtScopeExit() { log_deinit(); };

486 487
	try {
		return MainOrThrow(argc, argv);
488 489
	} catch (...) {
		LogError(std::current_exception());
490 491
		return EXIT_FAILURE;
	}
492 493
}

494
static int
495
mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
496
{
497
	ConfigureFS(raw_config);
498
	AtScopeExit() { DeinitFS(); };
499

500
	glue_mapper_init(raw_config);
501

502
	initPermissions(raw_config);
503
	spl_global_init(raw_config);
504
#ifdef ENABLE_ARCHIVE
505
	const ScopeArchivePluginsInit archive_plugins_init;
506
#endif
507

508
	pcm_convert_global_init(raw_config);
509

510
	const ScopeDecoderPluginsInit decoder_plugins_init(raw_config);
511

512
#ifdef ENABLE_DATABASE
513
	const bool create_db = InitDatabaseAndStorage(raw_config);
514
#endif
515

516
	glue_sticker_init(raw_config);
517

Max Kellermann's avatar
Max Kellermann committed
518
	command_init();
519

520
	for (auto &partition : instance->partitions) {
521
		partition.outputs.Configure(instance->rtio_thread.GetEventLoop(),
522
					    raw_config,
523 524 525 526
					    config.replay_gain,
					    partition.pc);
		partition.UpdateEffectiveReplayGainMode();
	}
527

528
	client_manager_init(raw_config);
529 530
	const ScopeInputPluginsInit input_plugins_init(raw_config,
						       instance->io_thread.GetEventLoop());
531
	const ScopePlaylistPluginsInit playlist_plugins_init(raw_config);
532

533
#ifdef ENABLE_DAEMON
534
	daemonize_commit();
535
#endif
536

537
#ifndef ANDROID
538
	setup_log_output();
Warren Dukes's avatar
Warren Dukes committed
539

540
	const ScopeSignalHandlersInit signal_handlers_init(instance->event_loop);
541
#endif
542

543
	instance->io_thread.Start();
544
	instance->rtio_thread.Start();
545

546
#ifdef ENABLE_NEIGHBOR_PLUGINS
547 548
	if (instance->neighbors != nullptr)
		instance->neighbors->Open();
549 550
#endif

551
	ZeroconfInit(raw_config, instance->event_loop);
552

553
#ifdef ENABLE_DATABASE
554
	if (create_db) {
555 556
		/* the database failed to load: recreate the
		   database */
557
		instance->update->Enqueue("", true);
558
	}
559
#endif
560

561
	glue_state_file_init(raw_config);
562

563
#ifdef ENABLE_DATABASE
564
	if (raw_config.GetBool(ConfigOption::AUTO_UPDATE, false)) {
565
#ifdef ENABLE_INOTIFY
566
		if (instance->storage != nullptr &&
567
		    instance->update != nullptr)
568
			mpd_inotify_init(instance->event_loop,
569 570
					 *instance->storage,
					 *instance->update,
571 572
					 raw_config.GetUnsigned(ConfigOption::AUTO_UPDATE_DEPTH,
								INT_MAX));
573
#else
574
		FormatWarning(config_domain,
575
			      "inotify: auto_update was disabled. enable during compilation phase");
576
#endif
577
	}
578
#endif
579

580
	Check(raw_config);
581

582 583
	/* enable all audio outputs (if not already done by
	   playlist_state_restore() */
584 585
	for (auto &partition : instance->partitions)
		partition.pc.LockUpdateAudio();
586

587
#ifdef _WIN32
588 589
	win32_app_started();
#endif
590

591 592 593 594
	/* 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);

595 596 597 598
#ifdef ENABLE_SYSTEMD_DAEMON
	sd_notify(0, "READY=1");
#endif

599
	/* run the main loop */
600
	instance->event_loop.Run();
601

602
#ifdef _WIN32
603 604 605
	win32_app_stopping();
#endif

606 607
	/* cleanup */

608
	instance->BeginShutdownUpdate();
609

610 611 612
	if (instance->state_file != nullptr) {
		instance->state_file->Write();
		delete instance->state_file;
613 614
	}

615 616
	ZeroconfDeinit();

617
	instance->BeginShutdownPartitions();
618

619
	delete instance->client_list;
620

621 622 623 624 625 626 627
#ifdef ENABLE_NEIGHBOR_PLUGINS
	if (instance->neighbors != nullptr) {
		instance->neighbors->Close();
		delete instance->neighbors;
	}
#endif

628 629 630 631
#ifdef ENABLE_SQLITE
	sticker_global_finish();
#endif

632
	return EXIT_SUCCESS;
Warren Dukes's avatar
Warren Dukes committed
633
}
Max Kellermann's avatar
Max Kellermann committed
634 635 636 637 638

#ifdef ANDROID

gcc_visibility_default
JNIEXPORT void JNICALL
639
Java_org_musicpd_Bridge_run(JNIEnv *env, jclass, jobject _context, jobject _logListener)
Max Kellermann's avatar
Max Kellermann committed
640
{
641 642
	Java::Init(env);
	Java::File::Initialise(env);
643
	Environment::Initialise(env);
644

645
	context = new Context(env, _context);
646 647
	if (_logListener != nullptr)
		logListener = new LogListener(env, _logListener);
648

Max Kellermann's avatar
Max Kellermann committed
649
	mpd_main(0, nullptr);
650

651
	delete logListener;
652
	delete context;
653
	Environment::Deinitialise(env);
Max Kellermann's avatar
Max Kellermann committed
654 655
}

656 657 658 659 660
gcc_visibility_default
JNIEXPORT void JNICALL
Java_org_musicpd_Bridge_shutdown(JNIEnv *, jclass)
{
	if (instance != nullptr)
661
		instance->Break();
662 663
}

Max Kellermann's avatar
Max Kellermann committed
664
#endif