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.
329 lines
8.5 KiB
C++
329 lines
8.5 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 "booster.h"
|
|
#include "connection.h"
|
|
#include "logger.h"
|
|
|
|
#include <cstdlib>
|
|
#include <dlfcn.h>
|
|
#include <cerrno>
|
|
#include <unistd.h>
|
|
#include <sys/user.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/resource.h>
|
|
|
|
#ifdef HAVE_CREDS
|
|
#include <sys/creds.h>
|
|
#endif
|
|
|
|
Booster::Booster() :
|
|
m_conn(NULL),
|
|
m_argvArraySize(0),
|
|
m_oldPriority(0),
|
|
m_oldPriorityOk(false)
|
|
{}
|
|
|
|
Booster::~Booster()
|
|
{
|
|
if (m_conn != NULL)
|
|
{
|
|
delete m_conn;
|
|
m_conn = NULL;
|
|
}
|
|
}
|
|
|
|
bool Booster::preload()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void Booster::initialize(int initialArgc, char ** initialArgv, int pipefd[2])
|
|
{
|
|
m_pipefd[0] = pipefd[0];
|
|
m_pipefd[1] = pipefd[1];
|
|
|
|
// Drop priority (nice = 10)
|
|
pushPriority(10);
|
|
|
|
// Preload stuff
|
|
preload();
|
|
|
|
// Clean-up all the env variables
|
|
clearenv();
|
|
|
|
// Rename process to temporary booster process name, e.g. "booster-m"
|
|
renameProcess(initialArgc, initialArgv);
|
|
|
|
// Restore priority
|
|
popPriority();
|
|
|
|
// Wait and read commands from the invoker
|
|
Logger::logNotice("Daemon: Wait for message from invoker");
|
|
readCommand();
|
|
|
|
// Give the process the real application name now that it
|
|
// has been read from invoker in readCommand().
|
|
renameProcess(initialArgc, initialArgv);
|
|
|
|
// Signal the parent process that it can create a new
|
|
// waiting booster process and close write end
|
|
const char msg = boosterType();
|
|
ssize_t ret = write(m_pipefd[1], reinterpret_cast<const void *>(&msg), 1);
|
|
if (ret == -1) {
|
|
Logger::logError("Daemon: Can't send signal to launcher process' \n");
|
|
}
|
|
|
|
// Send to the parent process pid of invoker for tracking
|
|
pid_t pid = invokersPid();
|
|
ret = write(m_pipefd[1], reinterpret_cast<const void *>(&pid), sizeof(pid_t));
|
|
if (ret == -1) {
|
|
Logger::logError("Daemon: Can't send invoker's pid to launcher process' \n");
|
|
}
|
|
|
|
// close pipe
|
|
close(m_pipefd[1]);
|
|
}
|
|
|
|
bool Booster::readCommand()
|
|
{
|
|
// Setup the conversation channel with the invoker.
|
|
m_conn = new Connection(socketId());
|
|
|
|
// Accept a new invocation.
|
|
if (m_conn->acceptConn(m_app))
|
|
{
|
|
bool res = m_conn->receiveApplicationData(m_app);
|
|
if(!res)
|
|
{
|
|
m_conn->closeConn();
|
|
return false;
|
|
}
|
|
|
|
if (!m_conn->isReportAppExitStatusNeeded())
|
|
{
|
|
m_conn->closeConn();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Booster::run()
|
|
{
|
|
if (!m_app.fileName().empty())
|
|
{
|
|
//check if can close sockets here
|
|
if (!m_conn->isReportAppExitStatusNeeded())
|
|
{
|
|
Connection::closeAllSockets();
|
|
}
|
|
|
|
Logger::logInfo("Booster: invoking '%s' ", m_app.fileName().c_str());
|
|
int ret_val = launchProcess();
|
|
|
|
if (m_conn->isReportAppExitStatusNeeded())
|
|
{
|
|
m_conn->sendAppExitStatus(ret_val);
|
|
m_conn->closeConn();
|
|
Connection::closeAllSockets();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Logger::logError("Booster: nothing to invoke\n");
|
|
}
|
|
}
|
|
|
|
void Booster::renameProcess(int parentArgc, char** parentArgv)
|
|
{
|
|
if (m_argvArraySize == 0)
|
|
{
|
|
// rename process for the first time
|
|
// calculate and store size of parentArgv array
|
|
|
|
for (int i = 0; i < parentArgc; i++)
|
|
m_argvArraySize += strlen(parentArgv[i]) + 1;
|
|
|
|
m_argvArraySize--;
|
|
}
|
|
|
|
if (m_app.appName().empty())
|
|
{
|
|
// application name isn't known yet, let's give to the process
|
|
// temporary booster name
|
|
m_app.setAppName(boosterTemporaryProcessName());
|
|
}
|
|
|
|
const char* newProcessName = m_app.appName().c_str();
|
|
Logger::logNotice("Booster: set new name for process: %s", newProcessName);
|
|
|
|
// This code copies all the new arguments to the space reserved
|
|
// in the old argv array. If an argument won't fit then the algorithm
|
|
// leaves it fully out and terminates.
|
|
|
|
int spaceAvailable = m_argvArraySize;
|
|
if (spaceAvailable > 0)
|
|
{
|
|
memset(parentArgv[0], '\0', spaceAvailable);
|
|
strncat(parentArgv[0], newProcessName, spaceAvailable);
|
|
|
|
spaceAvailable -= strlen(parentArgv[0]);
|
|
|
|
for (int i = 1; i < m_app.argc(); i++)
|
|
{
|
|
if (spaceAvailable > static_cast<int>(strlen(m_app.argv()[i])) + 1)
|
|
{
|
|
strncat(parentArgv[0], " ", 1);
|
|
strncat(parentArgv[0], m_app.argv()[i], spaceAvailable);
|
|
spaceAvailable -= strlen(m_app.argv()[i] + 1);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the process name using prctl, killall and top use it
|
|
if ( prctl(PR_SET_NAME, basename(newProcessName)) == -1 )
|
|
Logger::logError("Booster: on set new process name: %s ", strerror(errno));
|
|
|
|
setenv("_", newProcessName, true);
|
|
}
|
|
|
|
int Booster::launchProcess()
|
|
{
|
|
// Possibly restore process priority
|
|
errno = 0;
|
|
const int cur_prio = getpriority(PRIO_PROCESS, 0);
|
|
if (!errno && cur_prio < m_app.priority())
|
|
setpriority(PRIO_PROCESS, 0, m_app.priority());
|
|
|
|
// Set user ID and group ID of calling process if differing
|
|
// from the ones we got from invoker
|
|
|
|
if (getuid() != m_app.userId())
|
|
setuid(m_app.userId());
|
|
|
|
if (getgid() != m_app.groupId())
|
|
setgid(m_app.groupId());
|
|
|
|
// Load the application and find out the address of main()
|
|
void* handle = loadMain();
|
|
|
|
// Duplicate I/O descriptors
|
|
for (unsigned int i = 0; i < m_app.ioDescriptors().size(); i++)
|
|
if (m_app.ioDescriptors()[i] > 0)
|
|
dup2(m_app.ioDescriptors()[i], i);
|
|
|
|
// Set PWD
|
|
const char * pwd = getenv("PWD");
|
|
if (pwd)
|
|
{
|
|
chdir(pwd);
|
|
}
|
|
|
|
Logger::logNotice("Booster: launching process: '%s' ", m_app.fileName().c_str());
|
|
|
|
// Close logger
|
|
Logger::closeLog();
|
|
|
|
// Jump to main()
|
|
const int retVal = m_app.entry()(m_app.argc(), const_cast<char **>(m_app.argv()));
|
|
m_app.deleteArgv();
|
|
dlclose(handle);
|
|
return retVal;
|
|
}
|
|
|
|
void* Booster::loadMain()
|
|
{
|
|
#ifdef HAVE_CREDS
|
|
// Set application's platform security credentials
|
|
int err = creds_confine2(m_app.fileName().c_str(), credp_str2flags("set", NULL), m_app.peerCreds());
|
|
m_app.deletePeerCreds();
|
|
|
|
if (err < 0)
|
|
{
|
|
// Credential setup has failed, abort.
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Booster: Failed to setup credentials for launching application: %d\n", err);
|
|
}
|
|
#endif
|
|
|
|
// Load the application as a library
|
|
void * module = dlopen(m_app.fileName().c_str(), RTLD_LAZY | RTLD_GLOBAL);
|
|
|
|
if (!module)
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Booster: Loading invoked application failed: '%s'\n", dlerror());
|
|
|
|
// Find out the address for symbol "main". dlerror() is first used to clear any old error conditions,
|
|
// then dlsym() is called, and then dlerror() is checked again. This procedure is documented
|
|
// in dlsym()'s man page.
|
|
|
|
dlerror();
|
|
m_app.setEntry(reinterpret_cast<entry_t>(dlsym(module, "main")));
|
|
|
|
const char * error_s = dlerror();
|
|
if (error_s != NULL)
|
|
Logger::logErrorAndDie(EXIT_FAILURE, "Booster: Loading symbol 'main' failed: '%s'\n", error_s);
|
|
|
|
return module;
|
|
}
|
|
|
|
bool Booster::pushPriority(int nice)
|
|
{
|
|
errno = 0;
|
|
m_oldPriorityOk = true;
|
|
m_oldPriority = getpriority(PRIO_PROCESS, getpid());
|
|
|
|
if (errno)
|
|
{
|
|
m_oldPriorityOk = false;
|
|
}
|
|
else
|
|
{
|
|
return setpriority(PRIO_PROCESS, getpid(), nice) != -1;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Booster::popPriority()
|
|
{
|
|
if (m_oldPriorityOk)
|
|
{
|
|
return setpriority(PRIO_PROCESS, getpid(), m_oldPriority) != -1;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
pid_t Booster::invokersPid()
|
|
{
|
|
if (m_conn->isReportAppExitStatusNeeded())
|
|
{
|
|
return m_conn->peerPid();
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|