Ensure no ubuntu-server process is running at startup. Fixes #6584

Signed-off-by: Pedro Pombeiro <pombeirp@users.noreply.github.com>
This commit is contained in:
Pedro Pombeiro 2018-11-16 17:34:39 +01:00
parent 73ccb44663
commit 50200404ce
No known key found for this signature in database
GPG Key ID: A65DEB11E4BBC647
4 changed files with 131 additions and 45 deletions

1
.gitignore vendored
View File

@ -130,6 +130,7 @@ CMakeCache.txt
**/CMakeFiles/ **/CMakeFiles/
/StatusImPackage/* /StatusImPackage/*
*.AppImage *.AppImage
Status-Windows-x86_64.zip
/desktop/bin/* /desktop/bin/*
/desktop/lib/* /desktop/lib/*
/desktop/modules/* /desktop/modules/*

View File

@ -38,8 +38,9 @@ static QStringList consoleOutputStrings;
static QMutex consoleOutputMutex; static QMutex consoleOutputMutex;
#ifdef BUILD_FOR_BUNDLE #ifdef BUILD_FOR_BUNDLE
bool ubuntuServerStarted = false; bool nodeJsServerStarted = false;
QProcess *g_ubuntuServerProcess = nullptr; QProcess *g_nodeJsServerProcess = nullptr;
#define NODEJS_SERVER_NAME "ubuntu-server"
#endif #endif
const int MAIN_WINDOW_WIDTH = 1024; const int MAIN_WINDOW_WIDTH = 1024;
@ -54,12 +55,12 @@ const char *LOG_FILE_PATH_ENV_VAR_NAME = "STATUS_LOG_PATH";
// TODO: some way to change while running // TODO: some way to change while running
class ReactNativeProperties : public QObject { class ReactNativeProperties : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool liveReload READ liveReload WRITE setLiveReload NOTIFY Q_PROPERTY(
liveReloadChanged) bool liveReload READ liveReload WRITE setLiveReload NOTIFY liveReloadChanged)
Q_PROPERTY(QUrl codeLocation READ codeLocation WRITE setCodeLocation NOTIFY Q_PROPERTY(
codeLocationChanged) QUrl codeLocation READ codeLocation WRITE setCodeLocation NOTIFY codeLocationChanged)
Q_PROPERTY(QString pluginsPath READ pluginsPath WRITE setPluginsPath NOTIFY Q_PROPERTY(
pluginsPathChanged) QString pluginsPath READ pluginsPath WRITE setPluginsPath NOTIFY pluginsPathChanged)
Q_PROPERTY( Q_PROPERTY(
QString executor READ executor WRITE setExecutor NOTIFY executorChanged) QString executor READ executor WRITE setExecutor NOTIFY executorChanged)
public: public:
@ -155,26 +156,29 @@ void writeSingleLineLogFromJSServer(const QString &msg);
#ifdef BUILD_FOR_BUNDLE #ifdef BUILD_FOR_BUNDLE
void runUbuntuServer(); void killZombieJsServer();
bool runNodeJsServer();
#endif #endif
void loadFontsFromResources() { void loadFontsFromResources() {
QDirIterator it(":", QDirIterator::Subdirectories); QDirIterator it(":", QDirIterator::Subdirectories);
while (it.hasNext()) { while (it.hasNext()) {
QString resourceFile = it.next(); QString resourceFile = it.next();
if (resourceFile.endsWith(".otf", Qt::CaseInsensitive) || if (resourceFile.endsWith(".otf", Qt::CaseInsensitive) ||
resourceFile.endsWith(".ttf", Qt::CaseInsensitive)) { resourceFile.endsWith(".ttf", Qt::CaseInsensitive)) {
QFontDatabase::addApplicationFont(resourceFile); qint32 fontId = QFontDatabase::addApplicationFont(resourceFile);
if (Q_UNLIKELY(fontId == -1)) {
qCDebug(STATUS) << "Unable to install font" << resourceFile;
}
} }
} }
} }
void exceptionPostHandledCallback() { void exceptionPostHandledCallback() {
#ifdef BUILD_FOR_BUNDLE #ifdef BUILD_FOR_BUNDLE
if (g_ubuntuServerProcess) { if (g_nodeJsServerProcess) {
g_ubuntuServerProcess->kill(); g_nodeJsServerProcess->kill();
} }
#endif #endif
} }
@ -199,7 +203,6 @@ QString getDataStoragePath() {
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
@ -207,7 +210,9 @@ int main(int argc, char **argv) {
QString appPath = QCoreApplication::applicationDirPath(); QString appPath = QCoreApplication::applicationDirPath();
QString dataStoragePath = getDataStoragePath(); QString dataStoragePath = getDataStoragePath();
#ifndef BUILD_FOR_BUNDLE #ifdef BUILD_FOR_BUNDLE
killZombieJsServer();
#else
appPath.append(CRASH_REPORT_EXECUTABLE_RELATIVE_PATH); appPath.append(CRASH_REPORT_EXECUTABLE_RELATIVE_PATH);
dataStoragePath = ""; dataStoragePath = "";
#endif #endif
@ -227,7 +232,15 @@ int main(int argc, char **argv) {
} }
#ifdef BUILD_FOR_BUNDLE #ifdef BUILD_FOR_BUNDLE
runUbuntuServer(); if (!runNodeJsServer()) {
if (g_nodeJsServerProcess->state() == QProcess::NotRunning) {
// If we failed to start the Node.js server (happens on Windows if the Node.js server process was previously running), let's do a final attempt
delete g_nodeJsServerProcess;
if (!runNodeJsServer()) {
return 1;
}
}
}
app.setWindowIcon(QIcon(":/icon.png")); app.setWindowIcon(QIcon(":/icon.png"));
#endif #endif
@ -285,11 +298,11 @@ int main(int argc, char **argv) {
QString getLogFilePath() { QString getLogFilePath() {
QString logFilePath; QString logFilePath;
#ifdef BUILD_FOR_BUNDLE #ifdef BUILD_FOR_BUNDLE
logFilePath = getDataStoragePath() + "/Status.log"; logFilePath = getDataStoragePath() + QDir::separator() + "Status.log";
#else #else
logFilePath = qEnvironmentVariable(LOG_FILE_PATH_ENV_VAR_NAME, ""); logFilePath = qEnvironmentVariable(LOG_FILE_PATH_ENV_VAR_NAME, "");
if (logFilePath.isEmpty()) { if (logFilePath.isEmpty()) {
logFilePath = getDataStoragePath() + "/StatusDev.log"; logFilePath = getDataStoragePath() + QDir::separator() + "StatusDev.log";
} }
#endif #endif
return logFilePath; return logFilePath;
@ -310,49 +323,112 @@ void writeLogsToFile() {
} }
#ifdef BUILD_FOR_BUNDLE #ifdef BUILD_FOR_BUNDLE
void runUbuntuServer() {
g_ubuntuServerProcess = new QProcess(); #ifdef Q_OS_WIN
g_ubuntuServerProcess->setWorkingDirectory(getDataStoragePath());
g_ubuntuServerProcess->setProgram(QGuiApplication::applicationDirPath() + #include <windows.h>
"/ubuntu-server"); #include <tlhelp32.h>
QObject::connect(g_ubuntuServerProcess, &QProcess::errorOccurred,
bool IsProcessRunning(const wchar_t *processName) {
bool exists = false;
PROCESSENTRY32 entry = { sizeof(PROCESSENTRY32) };
HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot != NULL) {
if (::Process32First(snapshot, &entry)) {
do {
if (!wcsicmp(entry.szExeFile, processName)) {
exists = true;
break;
}
} while (::Process32Next(snapshot, &entry));
}
::CloseHandle(snapshot);
}
return exists;
}
#endif
void killZombieJsServer() {
// Ensure that a zombie Node.js server process is not still running in the background before we spawn a new one
QString cmd;
#ifdef Q_OS_LINUX
cmd = QString("pkill -f %1").arg(NODEJS_SERVER_NAME);
#elif defined(Q_OS_MAC)
cmd = QString("killall -9 %1").arg(NODEJS_SERVER_NAME);
#elif defined(Q_OS_WIN)
#define _CAT(A, B) A##B
#define _W(A) _CAT(L, #A)
WCHAR exeName[_MAX_PATH];
wsprintf(exeName, L"%s.exe", _W(NODEJS_SERVER_NAME));
if (IsProcessRunning(exeName)) {
qCDebug(STATUS) << NODEJS_SERVER_NAME << "is running, killing it";
::ShellExecuteW(NULL, NULL, L"tskill", _W(NODEJS_SERVER_NAME), NULL, SW_HIDE);
} else {
qCDebug(STATUS) << NODEJS_SERVER_NAME << "is not running";
}
#endif
if (!cmd.isEmpty()) {
qCDebug(STATUS) << "Running " << cmd;
QByteArray cmdArray = cmd.toLocal8Bit();
system(cmdArray.data());
}
}
bool runNodeJsServer() {
g_nodeJsServerProcess = new QProcess();
g_nodeJsServerProcess->setWorkingDirectory(getDataStoragePath());
g_nodeJsServerProcess->setProgram(QGuiApplication::applicationDirPath() + QDir::separator() + NODEJS_SERVER_NAME);
QObject::connect(g_nodeJsServerProcess, &QProcess::errorOccurred,
[=](QProcess::ProcessError) { [=](QProcess::ProcessError) {
qCWarning(JSSERVER) << "process name: " qCWarning(JSSERVER) << "process name: "
<< qUtf8Printable(g_ubuntuServerProcess->program()); << qUtf8Printable(g_nodeJsServerProcess->program());
qCWarning(JSSERVER) << "process error: " qCWarning(JSSERVER) << "process error: "
<< qUtf8Printable(g_ubuntuServerProcess->errorString()); << qUtf8Printable(g_nodeJsServerProcess->errorString());
}); });
QObject::connect( QObject::connect(
g_ubuntuServerProcess, &QProcess::readyReadStandardOutput, [=] { g_nodeJsServerProcess, &QProcess::readyReadStandardOutput, [=] {
writeLogFromJSServer(g_ubuntuServerProcess->readAllStandardOutput().trimmed()); writeLogFromJSServer(g_nodeJsServerProcess->readAllStandardOutput().trimmed());
}); });
QObject::connect( QObject::connect(
g_ubuntuServerProcess, &QProcess::readyReadStandardError, [=] { g_nodeJsServerProcess, &QProcess::readyReadStandardError, [=] {
QString output = QString output =
g_ubuntuServerProcess->readAllStandardError().trimmed(); g_nodeJsServerProcess->readAllStandardError().trimmed();
writeLogFromJSServer(output); writeLogFromJSServer(output);
if (output.contains("Server starting")) { if (output.contains("Server starting")) {
ubuntuServerStarted = true; nodeJsServerStarted = true;
} }
}); });
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit,
[=]() { [=]() {
qCDebug(STATUS) << "Kill ubuntu server"; qCDebug(STATUS) << "Kill node.js server process";
g_ubuntuServerProcess->kill(); g_nodeJsServerProcess->kill();
}); });
qCDebug(STATUS) << "starting ubuntu server..."; qCDebug(STATUS) << "starting node.js server process...";
g_ubuntuServerProcess->start(); g_nodeJsServerProcess->start();
qCDebug(STATUS) << "wait for started..."; qCDebug(STATUS) << "wait for started...";
while (!ubuntuServerStarted) { if (g_nodeJsServerProcess->waitForReadyRead(10000)) {
QGuiApplication::processEvents(); // We know that the process started, now wait until it communicates that it has started
while (!nodeJsServerStarted) {
QGuiApplication::processEvents();
}
qCDebug(STATUS) << "waiting finished";
return true;
} else {
qCDebug(STATUS) << "failed to start process";
} }
qCDebug(STATUS) << "waiting finished"; return false;
} }
#endif #endif
void writeLogFromJSServer(const QString &msg) { void writeLogFromJSServer(const QString &msg) {

View File

@ -1,6 +1,6 @@
# Build # Build
``` ``` shell
docker-compose -f docker-build/docker-compose.yml build docker-compose -f docker-build/docker-compose.yml build
``` ```
@ -8,7 +8,7 @@ This will install all the required sdks, depending on the connection will take s
# Run # Run
``` ``` shell
docker-compose -f docker-build/docker-compose.yml up docker-compose -f docker-build/docker-compose.yml up
``` ```
@ -18,7 +18,7 @@ You need to connect your device and accept the key.
After the figwheel prompt you can install the application running: After the figwheel prompt you can install the application running:
``` ``` shell
docker-compose -f docker-build/docker-compose.yml exec adbd make run-android docker-compose -f docker-build/docker-compose.yml exec adbd make run-android
``` ```

View File

@ -207,11 +207,12 @@ function compile() {
-DJS_BUNDLE_PATH="$JS_BUNDLE_PATH" \ -DJS_BUNDLE_PATH="$JS_BUNDLE_PATH" \
-DCMAKE_CXX_FLAGS:='-DBUILD_FOR_BUNDLE=1' || exit 1 -DCMAKE_CXX_FLAGS:='-DBUILD_FOR_BUNDLE=1' || exit 1
fi fi
make -j5 || exit 1 make -S -j5 || exit 1
popd popd
} }
function bundleWindows() { function bundleWindows() {
local buildType="$1"
# TODO: Produce a setup program instead of a ZIP # TODO: Produce a setup program instead of a ZIP
pushd $WORKFOLDER pushd $WORKFOLDER
rm -rf Windows rm -rf Windows
@ -227,9 +228,10 @@ function bundleWindows() {
pushd Windows pushd Windows
cp $STATUSREACTPATH/.env . cp $STATUSREACTPATH/.env .
mkdir -p assets/resources notifier mkdir -p assets/resources notifier
cp $STATUSREACTPATH/node_modules/node-notifier/vendor/snoreToast/SnoreToast.exe \ cp $STATUSREACTPATH/node_modules/node-notifier/vendor/notifu/*.exe \
$STATUSREACTPATH/node_modules/node-notifier/vendor/notifu/*.exe \
notifier/ notifier/
cp $STATUSREACTPATH/node_modules/node-notifier/vendor/snoreToast/SnoreToast.exe \
.
cp -r $STATUSREACTPATH/resources/fonts \ cp -r $STATUSREACTPATH/resources/fonts \
$STATUSREACTPATH/resources/icons \ $STATUSREACTPATH/resources/icons \
$STATUSREACTPATH/resources/images \ $STATUSREACTPATH/resources/images \
@ -237,7 +239,14 @@ function bundleWindows() {
local _bin=$STATUSREACTPATH/desktop/bin local _bin=$STATUSREACTPATH/desktop/bin
rm -rf $_bin/cmake_install.cmake $_bin/Makefile $_bin/CMakeFiles $_bin/Status_autogen && \ rm -rf $_bin/cmake_install.cmake $_bin/Makefile $_bin/CMakeFiles $_bin/Status_autogen && \
cp -r $_bin/* . cp -r $_bin/* .
zip -mr9 ../../Status-Windows-x86_64.zip .
local zipOptions="-mr9"
if [ -z $buildType ]; then
zipOptions="-mr1"
elif [ "$buildType" = "pr" ]; then
zipOptions="-mr2"
fi
zip $zipOptions ../../Status-Windows-x86_64.zip .
popd popd
rm -rf Windows rm -rf Windows
popd popd