#ifdef EV_ERROR #undef EV_ERROR #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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(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()); }