mirror of https://github.com/cutefishos/appmotor
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
429 lines
12 KiB
C++
429 lines
12 KiB
C++
/***************************************************************************
|
|
**
|
|
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
|
** All rights reserved.
|
|
** Contact: Nokia Corporation (directui@nokia.com)
|
|
**
|
|
** This file is part of applauncherd
|
|
**
|
|
** If you have questions regarding the use of this file, please contact
|
|
** Nokia at directui@nokia.com.
|
|
**
|
|
** This library is free software; you can redistribute it and/or
|
|
** modify it under the terms of the GNU Lesser General Public
|
|
** License version 2.1 as published by the Free Software Foundation
|
|
** and appearing in the file LICENSE.LGPL included in the packaging
|
|
** of this file.
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "daemon.h"
|
|
#include "logger.h"
|
|
#include "connection.h"
|
|
#include "booster.h"
|
|
#include "mbooster.h"
|
|
#include "qtbooster.h"
|
|
#include "wrtbooster.h"
|
|
#include "boosterfactory.h"
|
|
|
|
#include <cstdlib>
|
|
#include <cerrno>
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/prctl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <iostream>
|
|
#include <dlfcn.h>
|
|
|
|
Daemon * Daemon::m_instance = NULL;
|
|
int Daemon::m_lockFd = -1;
|
|
const int Daemon::m_boosterSleepTime = 2;
|
|
|
|
Daemon::Daemon(int & argc, char * argv[]) :
|
|
m_daemon(false),
|
|
m_quiet(false)
|
|
{
|
|
if (!Daemon::m_instance)
|
|
{
|
|
Daemon::m_instance = this;
|
|
}
|
|
else
|
|
{
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Daemon already created!\n");
|
|
}
|
|
|
|
// Parse arguments
|
|
parseArgs(ArgVect(argv, argv + argc));
|
|
|
|
// Disable console output
|
|
if (m_quiet)
|
|
consoleQuiet();
|
|
|
|
// Store arguments list
|
|
m_initialArgv = argv;
|
|
m_initialArgc = argc;
|
|
|
|
if (pipe(m_pipefd) == -1)
|
|
{
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Creating a pipe failed!\n");
|
|
}
|
|
|
|
// Daemonize if desired
|
|
if (m_daemon)
|
|
{
|
|
daemonize();
|
|
}
|
|
}
|
|
|
|
void Daemon::consoleQuiet()
|
|
{
|
|
close(0);
|
|
close(1);
|
|
close(2);
|
|
|
|
if (open("/dev/null", O_RDONLY) < 0)
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: opening /dev/null readonly");
|
|
|
|
int fd = open("/dev/null", O_WRONLY);
|
|
if ((fd == -1) || (dup(fd) < 0))
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: opening /dev/null writeonly");
|
|
}
|
|
|
|
Daemon * Daemon::instance()
|
|
{
|
|
return Daemon::m_instance;
|
|
}
|
|
|
|
bool Daemon::lock()
|
|
{
|
|
struct flock fl;
|
|
|
|
fl.l_type = F_WRLCK;
|
|
fl.l_whence = SEEK_SET;
|
|
fl.l_start = 0;
|
|
fl.l_len = 1;
|
|
|
|
if((m_lockFd = open("/tmp/applauncherd.lock", O_WRONLY | O_CREAT, 0666)) == -1)
|
|
return false;
|
|
|
|
if(fcntl(m_lockFd, F_SETLK, &fl) == -1)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Daemon::unlock()
|
|
{
|
|
if (m_lockFd != -1)
|
|
{
|
|
close(m_lockFd);
|
|
m_lockFd = -1;
|
|
}
|
|
}
|
|
|
|
void Daemon::run()
|
|
{
|
|
// Make sure that LD_BIND_NOW does not prevent dynamic linker to
|
|
// use lazy binding in later dlopen() calls.
|
|
unsetenv("LD_BIND_NOW");
|
|
|
|
// Create sockets for each of the boosters
|
|
Connection::initSocket(MBooster::socketName());
|
|
Connection::initSocket(QtBooster::socketName());
|
|
Connection::initSocket(WRTBooster::socketName());
|
|
|
|
#ifdef HAVE_CREDS
|
|
// initialize credentials to be filtered out from boosted applications
|
|
Booster::initExtraCreds();
|
|
#endif
|
|
|
|
// Fork each booster for the first time
|
|
forkBooster(MBooster::type());
|
|
forkBooster(QtBooster::type());
|
|
forkBooster(WRTBooster::type());
|
|
|
|
// Main loop
|
|
while (true)
|
|
{
|
|
// Wait for something appearing in the pipe. There should first
|
|
// appear a character representing the type of a booster and then
|
|
// pid of the invoker process that communicated with that booster.
|
|
char msg;
|
|
ssize_t count = read(m_pipefd[0], reinterpret_cast<void *>(&msg), 1);
|
|
if (count)
|
|
{
|
|
// Read pid of peer invoker
|
|
pid_t invokerPid;
|
|
count = read(m_pipefd[0], reinterpret_cast<void *>(&invokerPid), sizeof(pid_t));
|
|
|
|
if (count < static_cast<ssize_t>(sizeof(pid_t)))
|
|
{
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: pipe connection with booster failed");
|
|
}
|
|
else
|
|
{
|
|
Logger::logInfo("Daemon: invoker's pid: %d \n", invokerPid);
|
|
}
|
|
|
|
if (invokerPid != 0)
|
|
{
|
|
// Store booster - invoker pid pair
|
|
m_boosterPidToInvokerPid[BoosterFactory::getBoosterPidForType(msg)] = invokerPid;
|
|
}
|
|
|
|
// Read booster respawn delay
|
|
int delay;
|
|
count = read(m_pipefd[0], reinterpret_cast<void *>(&delay), sizeof(int));
|
|
|
|
if (count < static_cast<ssize_t>(sizeof(int)))
|
|
{
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: pipe connection with booster failed");
|
|
}
|
|
else
|
|
{
|
|
Logger::logInfo("Daemon: respawn delay: %d \n", delay);
|
|
}
|
|
|
|
// Fork a new booster of the given type
|
|
|
|
// 2nd param guarantees some time for the just launched application
|
|
// to start up before forking new booster. Not doing this would
|
|
// slow down the start-up significantly on single core CPUs.
|
|
|
|
forkBooster(msg, delay);
|
|
}
|
|
else
|
|
{
|
|
Logger::logWarning("Daemon: Nothing read from the pipe\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void Daemon::killProcess(pid_t pid) const
|
|
{
|
|
if (pid)
|
|
{
|
|
if (kill(pid, SIGKILL) != 0)
|
|
{
|
|
Logger::logError("Daemon: Failed to kill %d: %s\n", pid, strerror(errno));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Daemon::forkBooster(char type, int sleepTime)
|
|
{
|
|
// Fork a new process
|
|
pid_t newPid = fork();
|
|
|
|
if (newPid == -1)
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Forking while invoking");
|
|
|
|
if (newPid == 0) /* Child process */
|
|
{
|
|
// Reset used signal handlers
|
|
signal(SIGCHLD, SIG_DFL);
|
|
|
|
// Will get this signal if applauncherd dies
|
|
prctl(PR_SET_PDEATHSIG, SIGHUP);
|
|
|
|
// Close unused read end
|
|
close(m_pipefd[0]);
|
|
|
|
// Close lock file, it's not needed in the booster
|
|
Daemon::unlock();
|
|
|
|
// Set session id
|
|
if (setsid() < 0)
|
|
{
|
|
Logger::logError("Daemon: Setting session id\n");
|
|
}
|
|
|
|
// Guarantee some time for the just launched application to
|
|
// start up before forking new booster if needed.
|
|
if (sleepTime)
|
|
sleep(sleepTime);
|
|
|
|
Logger::logNotice("Daemon: Running a new Booster of %c type...", type);
|
|
|
|
// Create a new booster, initialize and run it
|
|
Booster * booster = BoosterFactory::create(type);
|
|
if (booster)
|
|
{
|
|
booster->initialize(m_initialArgc, m_initialArgv, m_pipefd);
|
|
|
|
// Run the current Booster
|
|
booster->run();
|
|
|
|
// Finish
|
|
delete booster;
|
|
|
|
// avoid situation when destructors for static objects may be run incorrectly
|
|
_exit(EXIT_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Unknown booster type \n");
|
|
}
|
|
}
|
|
else /* Parent process */
|
|
{
|
|
// Store the pid so that we can reap it later
|
|
m_children.push_back(newPid);
|
|
|
|
// Set current process ID globally to the given booster type
|
|
// so that we now which booster to restart if on exits
|
|
BoosterFactory::setProcessIdToBooster(type, newPid);
|
|
}
|
|
}
|
|
|
|
void Daemon::reapZombies()
|
|
{
|
|
// Loop through all child pid's and wait for them with WNOHANG.
|
|
PidVect::iterator i(m_children.begin());
|
|
while (i != m_children.end())
|
|
{
|
|
// Check if the pid had exited and become a zombie
|
|
int status;
|
|
pid_t pid = waitpid(*i, &status, WNOHANG);
|
|
if (pid)
|
|
{
|
|
// The pid had exited. Remove it from the pid vector.
|
|
i = m_children.erase(i);
|
|
|
|
// Find out if the exited process has a mapping with an invoker process.
|
|
// If this is the case, then kill the invoker process with the same signal
|
|
// that killed the exited process.
|
|
PidMap::iterator it = m_boosterPidToInvokerPid.find(pid);
|
|
if (it != m_boosterPidToInvokerPid.end())
|
|
{
|
|
Logger::logInfo("Daemon: Terminated process had a mapping to an invoker pid");
|
|
|
|
if (WIFSIGNALED(status))
|
|
{
|
|
int signal = WTERMSIG(status);
|
|
pid_t invokerPid = (*it).second;
|
|
|
|
Logger::logInfo("Daemon: Booster (pid=%d) was terminated due to signal %d\n", pid, signal);
|
|
Logger::logInfo("Daemon: Killing invoker process (pid=%d) by signal %d..\n", invokerPid, signal);
|
|
|
|
if (kill(invokerPid, signal) != 0)
|
|
{
|
|
Logger::logError("Daemon: Failed to send signal %d to invoker: %s\n", signal, strerror(errno));
|
|
}
|
|
}
|
|
|
|
// Remove a dead booster
|
|
m_boosterPidToInvokerPid.erase(it);
|
|
}
|
|
|
|
// Check if pid belongs to a booster and restart the dead booster if needed
|
|
char type = BoosterFactory::getBoosterTypeForPid(pid);
|
|
if (type != 0)
|
|
{
|
|
forkBooster(type, m_boosterSleepTime);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Daemon::daemonize()
|
|
{
|
|
// Our process ID and Session ID
|
|
pid_t pid, sid;
|
|
|
|
// Fork off the parent process: first fork
|
|
pid = fork();
|
|
if (pid < 0)
|
|
{
|
|
Logger::logError("Daemon: Unable to fork daemon, code %d (%s)", errno, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// If we got a good PID, then we can exit the parent process.
|
|
if (pid > 0)
|
|
{
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
// Fork off the parent process: second fork
|
|
pid = fork();
|
|
if (pid < 0)
|
|
{
|
|
Logger::logError("Daemon: Unable to fork daemon, code %d (%s)", errno, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// If we got a good PID, then we can exit the parent process.
|
|
if (pid > 0)
|
|
{
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
// Change the file mode mask
|
|
umask(0);
|
|
|
|
// Open any logs here
|
|
|
|
// Create a new SID for the child process
|
|
sid = setsid();
|
|
if (sid < 0)
|
|
{
|
|
Logger::logError("Daemon: Unable to create a new session, code %d (%s)", errno, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Change the current working directory
|
|
if ((chdir("/")) < 0)
|
|
{
|
|
Logger::logError("Daemon: Unable to change directory to %s, code %d (%s)", "/", errno, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Open file descriptors pointing to /dev/null
|
|
// Redirect standard file descriptors to /dev/null
|
|
// Close new file descriptors
|
|
|
|
const int new_stdin = open("/dev/null", O_RDONLY);
|
|
if (new_stdin != -1) {
|
|
dup2(new_stdin, STDIN_FILENO);
|
|
close(new_stdin);
|
|
}
|
|
|
|
const int new_stdout = open("/dev/null", O_WRONLY);
|
|
if (new_stdout != -1) {
|
|
dup2(new_stdout, STDOUT_FILENO);
|
|
close(new_stdout);
|
|
}
|
|
|
|
const int new_stderr = open("/dev/null", O_WRONLY);
|
|
if (new_stderr != -1) {
|
|
dup2(new_stderr, STDERR_FILENO);
|
|
close(new_stderr);
|
|
}
|
|
}
|
|
|
|
void Daemon::parseArgs(const ArgVect & args)
|
|
{
|
|
for (ArgVect::const_iterator i(args.begin()); i != args.end(); i++)
|
|
{
|
|
if ((*i) == "--daemon")
|
|
{
|
|
m_daemon = true;
|
|
}
|
|
else if ((*i) == "--quiet")
|
|
{
|
|
m_quiet = true;
|
|
}
|
|
}
|
|
}
|