mirror of
https://github.com/clangen/musikcube.git
synced 2025-01-15 19:52:19 +00:00
308 lines
8.1 KiB
C++
308 lines
8.1 KiB
C++
#ifdef EV_ERROR
|
|
#undef EV_ERROR
|
|
#endif
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <csignal>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <signal.h>
|
|
#include <ev++.h>
|
|
|
|
#include <musikcore/audio/PlaybackService.h>
|
|
#include <musikcore/audio/MasterTransport.h>
|
|
#include <musikcore/debug.h>
|
|
#include <musikcore/library/LibraryFactory.h>
|
|
#include <musikcore/plugin/Plugins.h>
|
|
#include <musikcore/runtime/MessageQueue.h>
|
|
#include <musikcore/runtime/Message.h>
|
|
#include <musikcore/support/PreferenceKeys.h>
|
|
#include <musikcore/support/Common.h>
|
|
|
|
#include "../musikcore/sdk/version.h"
|
|
|
|
using namespace musik;
|
|
using namespace musik::core;
|
|
using namespace musik::core::audio;
|
|
using namespace musik::core::runtime;
|
|
|
|
static const char* DEFAULT_LOCKFILE = "/tmp/musikcubed.lock";
|
|
static const char* LOCKFILE_OVERRIDE = "MUSIKCUBED_LOCKFILE_OVERRIDE";
|
|
static const short EVENT_DISPATCH = 1;
|
|
static const short EVENT_QUIT = 2;
|
|
static const pid_t NOT_RUNNING = (pid_t) -1;
|
|
static int pipeFd[2] = { 0 };
|
|
static bool foreground = false;
|
|
|
|
static void printHelp();
|
|
static void handleCommandLine(int argc, char** argv);
|
|
static void exitIfRunning();
|
|
static pid_t getDaemonPid();
|
|
static void initForeground();
|
|
static void initDaemon();
|
|
static void stopDaemon();
|
|
static void initUtf8();
|
|
static void run();
|
|
|
|
class EvMessageQueue: public MessageQueue {
|
|
public:
|
|
void Post(IMessagePtr message, int64_t delayMs) {
|
|
MessageQueue::Post(message, delayMs);
|
|
|
|
if (delayMs <= 0) {
|
|
write(pipeFd[1], &EVENT_DISPATCH, sizeof(EVENT_DISPATCH));
|
|
}
|
|
else {
|
|
double delayTs = (double) delayMs / 1000.0;
|
|
loop.once<
|
|
EvMessageQueue,
|
|
&EvMessageQueue::DelayedDispatch
|
|
>(-1, ev::TIMER, (ev::tstamp) delayTs, this);
|
|
}
|
|
}
|
|
|
|
void DelayedDispatch(int revents) {
|
|
this->Dispatch();
|
|
}
|
|
|
|
static void SignalQuit(ev::sig& signal, int revents) {
|
|
write(pipeFd[1], &EVENT_QUIT, sizeof(EVENT_QUIT));
|
|
}
|
|
|
|
void ReadCallback(ev::io& watcher, int revents) {
|
|
short type;
|
|
if (read(pipeFd[0], &type, sizeof(type)) == 0) {
|
|
std::cerr << "read() failed.\n";
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
switch (type) {
|
|
case EVENT_DISPATCH: this->Dispatch(); break;
|
|
case EVENT_QUIT: loop.break_loop(ev::ALL); break;
|
|
}
|
|
}
|
|
|
|
void Run() {
|
|
io.set(loop);
|
|
io.set(pipeFd[0], ev::READ);
|
|
io.set<EvMessageQueue, &EvMessageQueue::ReadCallback>(this);
|
|
io.start();
|
|
|
|
sio.set(loop);
|
|
sio.set<&EvMessageQueue::SignalQuit>();
|
|
sio.start(SIGTERM);
|
|
|
|
write(pipeFd[1], &EVENT_DISPATCH, sizeof(EVENT_DISPATCH));
|
|
|
|
loop.run(0);
|
|
}
|
|
|
|
private:
|
|
ev::dynamic_loop loop;
|
|
ev::io io;
|
|
ev::sig sio;
|
|
};
|
|
|
|
static void printHelp() {
|
|
std::cout << "\n musikcubed:\n";
|
|
std::cout << " --start: start the daemon\n";
|
|
std::cout << " --foreground: start the in the foreground\n";
|
|
std::cout << " --stop: shut down the daemon\n";
|
|
std::cout << " --running: check if the daemon is running\n";
|
|
std::cout << " --version: print the version\n";
|
|
std::cout << " --help: show this message\n\n";
|
|
}
|
|
|
|
static void handleCommandLine(int argc, char** argv) {
|
|
if (argc >= 2) {
|
|
const std::string command = std::string(argv[1]);
|
|
if (command == "--start") {
|
|
return;
|
|
}
|
|
else if (command == "--foreground") {
|
|
std::cout << "\n musikcubed starting in the foreground...\n\n";
|
|
::foreground = true;
|
|
return;
|
|
}
|
|
else if (command == "--stop") {
|
|
stopDaemon();
|
|
}
|
|
else if (command == "--version") {
|
|
std::cout << "\n musikcubed version: " << MUSIKCUBE_VERSION << " " << MUSIKCUBE_VERSION_COMMIT_HASH << "\n\n";
|
|
}
|
|
else if (command == "--running") {
|
|
pid_t pid = getDaemonPid();
|
|
if (pid == NOT_RUNNING) {
|
|
std::cout << "\n musikcubed is NOT running\n\n";
|
|
}
|
|
else {
|
|
std::cout << "\n musikcubed is running with pid " << pid << "\n\n";
|
|
}
|
|
}
|
|
else {
|
|
printHelp();
|
|
}
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
static std::string getLockfileFn() {
|
|
std::string result = DEFAULT_LOCKFILE;
|
|
const char* userLock = std::getenv(LOCKFILE_OVERRIDE);
|
|
if (userLock && strlen(userLock)) {
|
|
result = userLock;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void stopDaemon() {
|
|
pid_t pid = getDaemonPid();
|
|
if (pid == NOT_RUNNING) {
|
|
std::cout << "\n musikcubed is not running\n\n";
|
|
}
|
|
else {
|
|
std::cout << "\n stopping musikcubed...";
|
|
kill(pid, SIGTERM);
|
|
int count = 0;
|
|
bool dead = false;
|
|
while (!dead && count++ < 7) { /* try for 7 seconds */
|
|
if (kill(pid, 0) == 0) {
|
|
std::cout << ".";
|
|
std::cout.flush();
|
|
usleep(500000);
|
|
}
|
|
else {
|
|
dead = true;
|
|
}
|
|
}
|
|
std::cout << (dead ? " success" : " failed") << "\n\n";
|
|
if (!dead) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static pid_t getDaemonPid() {
|
|
std::ifstream lock(getLockfileFn());
|
|
if (lock.good()) {
|
|
int pid;
|
|
lock >> pid;
|
|
if (kill((pid_t) pid, 0) == 0) {
|
|
return pid;
|
|
}
|
|
}
|
|
return NOT_RUNNING;
|
|
}
|
|
|
|
static void exitIfRunning() {
|
|
if (getDaemonPid() != NOT_RUNNING) {
|
|
std::cerr << "\n musikcubed is already running!\n\n";
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
std::cerr << "\n musikcubed is starting...\n\n";
|
|
}
|
|
|
|
static void initDaemon() {
|
|
pid_t pid = fork();
|
|
|
|
if (pid < 0) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (pid > 0) {
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
umask(0);
|
|
|
|
pid_t sid = setsid();
|
|
if (sid < 0) {
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
if (chdir("/") < 0) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (pipe(pipeFd) != 0) {
|
|
std::cerr << "\n ERROR! couldn't create pipe\n\n";
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
close(STDIN_FILENO);
|
|
close(STDOUT_FILENO);
|
|
freopen("/tmp/musikcube.log", "w", stderr);
|
|
|
|
std::ofstream lock(getLockfileFn());
|
|
if (lock.good()) {
|
|
lock << std::to_string((int) getpid());
|
|
}
|
|
|
|
debug::Start({
|
|
new debug::SimpleFileBackend()
|
|
});
|
|
}
|
|
|
|
static void initForeground() {
|
|
if (pipe(pipeFd) != 0) {
|
|
std::cerr << "\n ERROR! couldn't create pipe\n\n";
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
std::ofstream lock(getLockfileFn());
|
|
if (lock.good()) {
|
|
lock << std::to_string((int) getpid());
|
|
}
|
|
|
|
debug::Start({
|
|
new debug::ConsoleBackend(),
|
|
new debug::SimpleFileBackend()
|
|
});
|
|
}
|
|
|
|
static void rescanHandler(int signal) {
|
|
debug::info("daemon", "received SIGUSR1, rescanning the library...");
|
|
auto library = LibraryFactory::Instance().DefaultLocalLibrary();
|
|
library->Indexer()->Schedule(IIndexer::SyncType::All);
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
std::cout << "\n using lockfile at: " << getLockfileFn();
|
|
handleCommandLine(argc, argv);
|
|
exitIfRunning();
|
|
|
|
::foreground ? initForeground() : initDaemon();
|
|
|
|
srand((unsigned int) time(0));
|
|
|
|
std::signal(SIGUSR1, rescanHandler);
|
|
|
|
plugin::Init();
|
|
|
|
EvMessageQueue messageQueue;
|
|
LibraryFactory::Initialize(messageQueue);
|
|
auto library = LibraryFactory::Instance().DefaultLocalLibrary();
|
|
|
|
{
|
|
PlaybackService playback(messageQueue, library);
|
|
|
|
plugin::Start(&messageQueue, &playback, library);
|
|
|
|
auto prefs = Preferences::ForComponent(prefs::components::Settings);
|
|
if (prefs->GetBool(prefs::keys::SyncOnStartup, true)) {
|
|
library->Indexer()->Schedule(IIndexer::SyncType::All);
|
|
}
|
|
|
|
messageQueue.Run();
|
|
|
|
library->Indexer()->Shutdown();
|
|
}
|
|
|
|
LibraryFactory::Instance().Shutdown();
|
|
plugin::Shutdown();
|
|
debug::Shutdown();
|
|
|
|
remove(getLockfileFn().c_str());
|
|
}
|