Main.cxx 15.8 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2021 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/Config.hxx"
31
#include "client/List.hxx"
32
#include "command/AllCommands.hxx"
33
#include "Partition.hxx"
34
#include "tag/Config.hxx"
35
#include "ReplayGainGlobal.hxx"
36
#include "IdleFlags.hxx"
37
#include "Log.hxx"
38
#include "LogInit.hxx"
Max Kellermann's avatar
Max Kellermann committed
39
#include "input/Init.hxx"
40 41
#include "input/cache/Config.hxx"
#include "input/cache/Manager.hxx"
42
#include "event/Loop.hxx"
43
#include "event/Call.hxx"
44
#include "fs/AllocatedPath.hxx"
45
#include "fs/Config.hxx"
46
#include "playlist/PlaylistRegistry.hxx"
47
#include "zeroconf/Glue.hxx"
48
#include "decoder/DecoderList.hxx"
49
#include "pcm/AudioParser.hxx"
50
#include "pcm/Convert.hxx"
51
#include "unix/SignalHandlers.hxx"
52
#include "thread/Slack.hxx"
53
#include "net/Init.hxx"
54
#include "lib/icu/Init.hxx"
55
#include "config/Check.hxx"
56
#include "config/Data.hxx"
57
#include "config/Param.hxx"
58
#include "config/Path.hxx"
59 60 61
#include "config/Defaults.hxx"
#include "config/Option.hxx"
#include "config/Domain.hxx"
62
#include "config/Parser.hxx"
63
#include "util/RuntimeError.hxx"
64
#include "util/ScopeExit.hxx"
Max Kellermann's avatar
Max Kellermann committed
65

66 67 68 69
#ifdef ENABLE_DAEMON
#include "unix/Daemon.hxx"
#endif

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

82 83 84 85
#ifdef ENABLE_NEIGHBOR_PLUGINS
#include "neighbor/Glue.hxx"
#endif

86
#ifdef ENABLE_SQLITE
87
#include "sticker/Database.hxx"
88 89
#endif

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

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

105 106
#ifdef ENABLE_DBUS
#include "lib/dbus/Init.hxx"
107 108
#endif

109 110
#ifdef ENABLE_SYSTEMD_DAEMON
#include <systemd/sd-daemon.h>
111 112
#endif

113
#include <climits>
114

Rosen Penev's avatar
Rosen Penev committed
115
#ifndef ANDROID
116
#include <clocale>
117 118
#endif

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

static constexpr size_t DEFAULT_BUFFER_SIZE = 4 * MEGABYTE;
123

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

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

133
Instance *global_instance;
134

135
struct Config {
136
	ReplayGainConfig replay_gain;
137

138 139 140
	explicit Config(const ConfigData &raw)
		:replay_gain(LoadReplayGainConfig(raw)) {}
};
141

142
#ifdef ENABLE_DAEMON
143

144
static void
145
glue_daemonize_init(const CommandLineOptions &options,
146
		    const ConfigData &config)
147
{
148 149 150 151 152 153 154 155 156 157
	auto pid_file = config.GetPath(ConfigOption::PID_FILE);

#ifdef __linux__
	if (options.systemd && pid_file != nullptr) {
		pid_file = nullptr;
		fprintf(stderr,
			"Ignoring the 'pid_file' setting in systemd mode\n");
	}
#endif

158 159
	daemonize_init(config.GetString(ConfigOption::USER),
		       config.GetString(ConfigOption::GROUP),
160
		       std::move(pid_file));
161

162
	if (options.kill)
163 164 165
		daemonize_kill();
}

166 167
#endif

168
static void
169
glue_mapper_init(const ConfigData &config)
170
{
171 172 173 174 175 176 177 178 179 180 181
	auto playlist_directory = config.GetPath(ConfigOption::PLAYLIST_DIR);

#ifdef ANDROID
	/* if there is no explicit configuration, store playlists in
	   "/sdcard/Android/data/org.musicpd/files/playlists" */
	if (playlist_directory.IsNull())
		playlist_directory = context->GetExternalFilesDir(Java::GetEnv(),
								  "playlists");
#endif

	mapper_init(std::move(playlist_directory));
182
}
183

184 185
#ifdef ENABLE_DATABASE

186
static void
187 188
InitStorage(Instance &instance, EventLoop &event_loop,
	    const ConfigData &config)
189
{
190
	auto storage = CreateConfiguredStorage(config, event_loop);
191
	if (storage == nullptr)
192
		return;
193

Max Kellermann's avatar
Max Kellermann committed
194
	auto *composite = new CompositeStorage();
195
	instance.storage = composite;
196
	composite->Mount("", std::move(storage));
197 198
}

199 200 201 202 203 204
/**
 * 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
205
glue_db_init_and_load(Instance &instance, const ConfigData &config)
206
{
207 208 209
	auto db = CreateConfiguredDatabase(config, instance.event_loop,
					   instance.io_thread.GetEventLoop(),
					   instance);
210
	if (!db)
211
		return true;
212

213
	if (db->GetPlugin().RequireStorage()) {
214 215
		InitStorage(instance, instance.io_thread.GetEventLoop(),
			    config);
216

217
		if (instance.storage == nullptr) {
218 219 220
			LogNotice(config_domain,
				  "Found database setting without "
				  "music_directory - disabling database");
221 222 223
			return true;
		}
	} else {
224
		if (IsStorageConfigured(config))
225 226 227
			LogNotice(config_domain,
				  "Ignoring the storage configuration "
				  "because the database does not need it");
228 229
	}

230
	try {
231
		db->Open();
232 233 234
	} catch (...) {
		std::throw_with_nested(std::runtime_error("Failed to open database plugin"));
	}
235

236
	instance.database = std::move(db);
237

238
	auto *sdb = dynamic_cast<SimpleDatabase *>(instance.database.get());
239
	if (sdb == nullptr)
240 241
		return true;

242 243 244 245
	instance.update = new UpdateService(config,
					    instance.event_loop, *sdb,
					    static_cast<CompositeStorage &>(*instance.storage),
					    instance);
246

247
	/* run database update after daemonization? */
248
	return sdb->FileExists();
Warren Dukes's avatar
Warren Dukes committed
249
}
Warren Dukes's avatar
Warren Dukes committed
250

251
static bool
252
InitDatabaseAndStorage(Instance &instance, const ConfigData &config)
253
{
254
	const bool create_db = !glue_db_init_and_load(instance, config);
255 256 257
	return create_db;
}

258 259
#endif

260 261
#ifdef ENABLE_SQLITE

262 263 264
/**
 * Configure and initialize the sticker subsystem.
 */
265 266
static std::unique_ptr<StickerDatabase>
LoadStickerDatabase(const ConfigData &config)
267
{
268
	auto sticker_file = config.GetPath(ConfigOption::STICKER_FILE);
269
	if (sticker_file.IsNull())
270
		return nullptr;
271

272
	return std::make_unique<StickerDatabase>(std::move(sticker_file));
273 274
}

275 276
#endif

277
static void
278
glue_state_file_init(Instance &instance, const ConfigData &raw_config)
279
{
280 281
	StateFileConfig config(raw_config);
	if (!config.IsEnabled())
282
		return;
283

284 285 286
	instance.state_file = std::make_unique< StateFile>(std::move(config),
							   instance.partitions.front(),
							   instance.event_loop);
287
	instance.state_file->Read();
288 289
}

Max Kellermann's avatar
Max Kellermann committed
290 291 292 293
/**
 * Initialize the decoder and player core, including the music pipe.
 */
static void
294 295
initialize_decoder_and_player(Instance &instance,
			      const ConfigData &config,
296
			      const ReplayGainConfig &replay_gain_config)
Max Kellermann's avatar
Max Kellermann committed
297
{
298
	const ConfigParam *param;
Max Kellermann's avatar
Max Kellermann committed
299

300
	size_t buffer_size;
301
	param = config.GetParam(ConfigOption::AUDIO_BUFFER_SIZE);
302
	if (param != nullptr) {
303
		buffer_size = param->With([](const char *s){
304 305
			size_t result = ParseSize(s, KILOBYTE);
			if (result <= 0)
306 307 308 309
				throw FormatRuntimeError("buffer size \"%s\" is not a "
							 "positive integer", s);

			if (result < MIN_BUFFER_SIZE) {
310 311
				FmtWarning(config_domain, "buffer size {} is too small, using {} bytes instead",
					   result, MIN_BUFFER_SIZE);
312 313 314 315 316
				result = MIN_BUFFER_SIZE;
			}

			return result;
		});
Max Kellermann's avatar
Max Kellermann committed
317 318 319
	} else
		buffer_size = DEFAULT_BUFFER_SIZE;

320
	const unsigned buffered_chunks = buffer_size / CHUNK_SIZE;
Max Kellermann's avatar
Max Kellermann committed
321 322

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

326
	const unsigned max_length =
327 328
		config.GetPositive(ConfigOption::MAX_PLAYLIST_LENGTH,
				   DEFAULT_PLAYLIST_MAX_LENGTH);
329

330 331 332 333 334 335
	AudioFormat configured_audio_format = config.With(ConfigOption::AUDIO_OUTPUT_FORMAT, [](const char *s){
		if (s == nullptr)
			return AudioFormat::Undefined();

		return ParseAudioFormat(s, true);
	});
336

337 338 339 340 341 342 343
	instance.partitions.emplace_back(instance,
					 "default",
					 max_length,
					 buffered_chunks,
					 configured_audio_format,
					 replay_gain_config);
	auto &partition = instance.partitions.back();
344

345 346 347 348 349
	partition.replay_gain_mode = config.With(ConfigOption::REPLAYGAIN, [](const char *s){
		return s != nullptr
			? FromString(s)
			: ReplayGainMode::OFF;
	});
Max Kellermann's avatar
Max Kellermann committed
350 351
}

352 353 354 355 356
inline void
Instance::BeginShutdownUpdate() noexcept
{
#ifdef ENABLE_DATABASE
#ifdef ENABLE_INOTIFY
357
	inotify_update.reset();
358 359 360 361 362 363 364 365 366 367
#endif

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

inline void
Instance::BeginShutdownPartitions() noexcept
{
368
	for (auto &partition : partitions) {
369
		partition.BeginShutdown();
370
	}
371 372
}

373
static inline void
374 375
MainConfigured(const CommandLineOptions &options,
	       const ConfigData &raw_config)
376
{
377
#ifdef ENABLE_DAEMON
378
	daemonize_close_stdin();
379
#endif
380

381
#ifndef ANDROID
382
	/* initialize locale */
383 384
	std::setlocale(LC_CTYPE,"");
	std::setlocale(LC_COLLATE, "");
385
#endif
Max Kellermann's avatar
Max Kellermann committed
386

387
	const ScopeIcuInit icu_init;
388 389
	const ScopeNetInit net_init;

390 391 392 393
#ifdef ENABLE_DBUS
	const ODBus::ScopeInit dbus_init;
#endif

394
	InitPathParser(raw_config);
395
	const Config config(raw_config);
396

397
#ifdef ENABLE_DAEMON
398
	glue_daemonize_init(options, raw_config);
399
#endif
400

401
	TagLoadConfig(raw_config);
402

403
	log_init(raw_config, options.verbose, options.log_stderr);
404

405 406
	Instance instance;
	global_instance = &instance;
407

408
#ifdef ENABLE_NEIGHBOR_PLUGINS
409 410 411 412
	instance.neighbors = std::make_unique<NeighborGlue>();
	instance.neighbors->Init(raw_config,
				 instance.io_thread.GetEventLoop(),
				 instance);
413

414 415
	if (instance.neighbors->IsEmpty())
		instance.neighbors.reset();
416 417
#endif

418
	const unsigned max_clients =
419
		raw_config.GetPositive(ConfigOption::MAX_CONN, 100);
420
	instance.client_list = std::make_unique<ClientList>(max_clients);
421

422 423 424 425 426 427
	const auto *input_cache_config = raw_config.GetBlock(ConfigBlockOption::INPUT_CACHE);
	if (input_cache_config != nullptr) {
		const InputCacheConfig c(*input_cache_config);
		instance.input_cache = std::make_unique<InputCacheManager>(c);
	}

428 429
	initialize_decoder_and_player(instance,
				      raw_config, config.replay_gain);
430

431
	listen_global_init(raw_config, *instance.partitions.front().listener);
432

433
#ifdef ENABLE_DAEMON
434
	daemonize_set_user();
435
	daemonize_begin(options.daemon);
436
	AtScopeExit() { daemonize_finish(); };
437
#endif
Warren Dukes's avatar
Warren Dukes committed
438

439
	ConfigureFS(raw_config);
440
	AtScopeExit() { DeinitFS(); };
441

442
	glue_mapper_init(raw_config);
443

444
	initPermissions(raw_config);
445
	spl_global_init(raw_config);
446
#ifdef ENABLE_ARCHIVE
447
	const ScopeArchivePluginsInit archive_plugins_init;
448
#endif
449

450
	pcm_convert_global_init(raw_config);
451

452
	const ScopeDecoderPluginsInit decoder_plugins_init(raw_config);
453

454
#ifdef ENABLE_DATABASE
455
	const bool create_db = InitDatabaseAndStorage(instance, raw_config);
456
#endif
457

458
#ifdef ENABLE_SQLITE
459
	instance.sticker_database = LoadStickerDatabase(raw_config);
460
#endif
461

Max Kellermann's avatar
Max Kellermann committed
462
	command_init();
463

464
	for (auto &partition : instance.partitions) {
465 466
		partition.outputs.Configure(instance.io_thread.GetEventLoop(),
					    instance.rtio_thread.GetEventLoop(),
467
					    raw_config,
468
					    config.replay_gain);
469 470
		partition.UpdateEffectiveReplayGainMode();
	}
471

472
	client_manager_init(raw_config);
473
	const ScopeInputPluginsInit input_plugins_init(raw_config,
474
						       instance.io_thread.GetEventLoop());
475

476
	const ScopePlaylistPluginsInit playlist_plugins_init(raw_config);
477

478
#ifdef ENABLE_DAEMON
479
	daemonize_commit();
480
#endif
481

482
#ifndef ANDROID
483
	setup_log_output();
Warren Dukes's avatar
Warren Dukes committed
484

485
	const ScopeSignalHandlersInit signal_handlers_init(instance);
486
#endif
487

488 489
	instance.io_thread.Start();
	instance.rtio_thread.Start();
490

491
#ifdef ENABLE_NEIGHBOR_PLUGINS
492 493
	if (instance.neighbors != nullptr)
		instance.neighbors->Open();
494

495 496 497
	AtScopeExit(&instance) {
		if (instance.neighbors != nullptr)
			instance.neighbors->Close();
498
	};
499 500
#endif

501 502
#ifdef HAVE_ZEROCONF
	std::unique_ptr<ZeroconfHelper> zeroconf;
503
	try {
504 505 506 507
		auto &event_loop = instance.io_thread.GetEventLoop();
		BlockingCall(event_loop, [&](){
			zeroconf = ZeroconfInit(raw_config, event_loop);
		});
508 509 510 511
	} catch (...) {
		LogError(std::current_exception(),
			 "Zeroconf initialization failed");
	}
Max Kellermann's avatar
Max Kellermann committed
512 513 514 515 516 517 518 519 520

	AtScopeExit(&zeroconf, &instance) {
		if (zeroconf) {
			auto &event_loop = instance.io_thread.GetEventLoop();
			BlockingCall(event_loop, [&](){
				zeroconf.reset();
			});
		}
	};
521
#endif
522

523
#ifdef ENABLE_DATABASE
524
	if (create_db) {
525 526
		/* the database failed to load: recreate the
		   database */
527
		instance.update->Enqueue("", true);
528
	}
529
#endif
530

531
	glue_state_file_init(instance, raw_config);
532

533
#ifdef ENABLE_DATABASE
534
	if (raw_config.GetBool(ConfigOption::AUTO_UPDATE, false)) {
535
#ifdef ENABLE_INOTIFY
536
		if (instance.storage != nullptr &&
537 538 539 540 541 542 543 544 545 546 547 548
		    instance.update != nullptr) {
			try {
				instance.inotify_update =
					mpd_inotify_init(instance.event_loop,
							 *instance.storage,
							 *instance.update,
							 raw_config.GetUnsigned(ConfigOption::AUTO_UPDATE_DEPTH,
										INT_MAX));
			} catch (...) {
				LogError(std::current_exception());
			}
		}
549
#else
550 551
		LogWarning(config_domain,
			   "inotify: auto_update was disabled. enable during compilation phase");
552
#endif
553
	}
554
#endif
555

556
	Check(raw_config);
557

558 559
	/* enable all audio outputs (if not already done by
	   playlist_state_restore() */
560
	for (auto &partition : instance.partitions)
561
		partition.pc.LockUpdateAudio();
562

563
#ifdef _WIN32
564 565
	win32_app_started();
#endif
566

567 568
	/* the MPD frontend does not care about timer slack; set it to
	   a huge value to allow the kernel to reduce CPU wakeups */
569
	SetThreadTimerSlack(std::chrono::milliseconds(100));
570

571 572 573 574
#ifdef ENABLE_SYSTEMD_DAEMON
	sd_notify(0, "READY=1");
#endif

575
	/* run the main loop */
576
	instance.event_loop.Run();
577

578
#ifdef _WIN32
579 580 581
	win32_app_stopping();
#endif

582 583
	/* cleanup */

584 585 586
	if (instance.state_file)
		instance.state_file->Write();

587 588
	instance.BeginShutdownUpdate();
	instance.BeginShutdownPartitions();
Warren Dukes's avatar
Warren Dukes committed
589
}
Max Kellermann's avatar
Max Kellermann committed
590 591 592

#ifdef ANDROID

593 594 595
static void
AndroidMain()
{
596
	CommandLineOptions options;
597 598 599 600 601 602 603 604 605 606 607 608 609
	ConfigData raw_config;

	const auto sdcard = Environment::getExternalStorageDirectory();
	if (!sdcard.IsNull()) {
		const auto config_path =
			sdcard / Path::FromFS("mpd.conf");
		if (FileExists(config_path))
			ReadConfigFile(raw_config, config_path);
	}

	MainConfigured(options, raw_config);
}

Max Kellermann's avatar
Max Kellermann committed
610 611
gcc_visibility_default
JNIEXPORT void JNICALL
612
Java_org_musicpd_Bridge_run(JNIEnv *env, jclass, jobject _context, jobject _logListener)
Max Kellermann's avatar
Max Kellermann committed
613
{
614
	Java::Init(env);
615
	Java::Object::Initialise(env);
616
	Java::File::Initialise(env);
617
	Environment::Initialise(env);
618
	AtScopeExit(env) { Environment::Deinitialise(env); };
619

620
	context = new Context(env, _context);
621 622
	AtScopeExit() { delete context; };

623 624
	if (_logListener != nullptr)
		logListener = new LogListener(env, _logListener);
625
	AtScopeExit() { delete logListener; };
626

627 628 629 630 631
	try {
		AndroidMain();
	} catch (...) {
		LogError(std::current_exception());
	}
Max Kellermann's avatar
Max Kellermann committed
632 633
}

634 635 636 637
gcc_visibility_default
JNIEXPORT void JNICALL
Java_org_musicpd_Bridge_shutdown(JNIEnv *, jclass)
{
638 639
	if (global_instance != nullptr)
		global_instance->Break();
640 641
}

642 643 644 645 646 647 648 649 650
gcc_visibility_default
JNIEXPORT void JNICALL
Java_org_musicpd_Bridge_pause(JNIEnv *, jclass)
{
	if (global_instance != nullptr)
		for (auto &partition : global_instance->partitions)
			partition.pc.LockSetPause(true);
}

651 652
#else

653 654 655
static inline void
MainOrThrow(int argc, char *argv[])
{
656
	CommandLineOptions options;
657 658 659 660 661 662 663
	ConfigData raw_config;

	ParseCommandLine(argc, argv, options, raw_config);

	MainConfigured(options, raw_config);
}

664 665
int
mpd_main(int argc, char *argv[])
666
{
667 668
	MainOrThrow(argc, argv);
	return EXIT_SUCCESS;
669 670
}

671 672
int
main(int argc, char *argv[]) noexcept
673 674 675
try {
	AtScopeExit() { log_deinit(); };

676 677 678 679 680
#ifdef _WIN32
	return win32_main(argc, argv);
#else
	return mpd_main(argc, argv);
#endif
681 682 683
} catch (...) {
	LogError(std::current_exception());
	return EXIT_FAILURE;
684 685
}

Max Kellermann's avatar
Max Kellermann committed
686
#endif