resolve conflicts on merge with birarda/console

This commit is contained in:
Stephen Birarda 2015-12-21 10:55:05 -07:00
commit 7632a8af42
8 changed files with 236 additions and 39 deletions

View file

@ -22,6 +22,8 @@
#include "AssignmentClient.h" #include "AssignmentClient.h"
#include "AssignmentClientMonitor.h" #include "AssignmentClientMonitor.h"
#include "AssignmentClientApp.h" #include "AssignmentClientApp.h"
#include <QtCore/QDir>
#include <QtCore/QStandardPaths>
AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
@ -92,6 +94,9 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
const QCommandLineOption monitorPortOption(ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION, "assignment-client monitor port", "port"); const QCommandLineOption monitorPortOption(ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION, "assignment-client monitor port", "port");
parser.addOption(monitorPortOption); parser.addOption(monitorPortOption);
const QCommandLineOption logDirectoryOption(ASSIGNMENT_LOG_DIRECTORY, "directory to store logs", "log-directory");
parser.addOption(logDirectoryOption);
if (!parser.parse(QCoreApplication::arguments())) { if (!parser.parse(QCoreApplication::arguments())) {
qCritical() << parser.errorText() << endl; qCritical() << parser.errorText() << endl;
parser.showHelp(); parser.showHelp();
@ -130,6 +135,13 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
numForks = minForks; numForks = minForks;
} }
QDir logDirectory { "." };
if (parser.isSet(logDirectoryOption)) {
logDirectory = parser.value(logDirectoryOption);
} else {
logDirectory = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
}
Assignment::Type requestAssignmentType = Assignment::AllTypes; Assignment::Type requestAssignmentType = Assignment::AllTypes;
if (argumentVariantMap.contains(ASSIGNMENT_TYPE_OVERRIDE_OPTION)) { if (argumentVariantMap.contains(ASSIGNMENT_TYPE_OVERRIDE_OPTION)) {
requestAssignmentType = (Assignment::Type) argumentVariantMap.value(ASSIGNMENT_TYPE_OVERRIDE_OPTION).toInt(); requestAssignmentType = (Assignment::Type) argumentVariantMap.value(ASSIGNMENT_TYPE_OVERRIDE_OPTION).toInt();
@ -200,7 +212,7 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks, AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks,
requestAssignmentType, assignmentPool, requestAssignmentType, assignmentPool,
listenPort, walletUUID, assignmentServerHostname, listenPort, walletUUID, assignmentServerHostname,
assignmentServerPort); assignmentServerPort, logDirectory);
monitor->setParent(this); monitor->setParent(this);
connect(this, &QCoreApplication::aboutToQuit, monitor, &AssignmentClientMonitor::aboutToQuit); connect(this, &QCoreApplication::aboutToQuit, monitor, &AssignmentClientMonitor::aboutToQuit);
} else { } else {

View file

@ -25,6 +25,7 @@ const QString ASSIGNMENT_NUM_FORKS_OPTION = "n";
const QString ASSIGNMENT_MIN_FORKS_OPTION = "min"; const QString ASSIGNMENT_MIN_FORKS_OPTION = "min";
const QString ASSIGNMENT_MAX_FORKS_OPTION = "max"; const QString ASSIGNMENT_MAX_FORKS_OPTION = "max";
const QString ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION = "monitor-port"; const QString ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION = "monitor-port";
const QString ASSIGNMENT_LOG_DIRECTORY = "log-directory";
class AssignmentClientApp : public QCoreApplication { class AssignmentClientApp : public QCoreApplication {
Q_OBJECT Q_OBJECT

View file

@ -12,6 +12,9 @@
#include <memory> #include <memory>
#include <signal.h> #include <signal.h>
#include <QDir>
#include <QStandardPaths>
#include <AddressManager.h> #include <AddressManager.h>
#include <LogHandler.h> #include <LogHandler.h>
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
@ -29,7 +32,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
const unsigned int maxAssignmentClientForks, const unsigned int maxAssignmentClientForks,
Assignment::Type requestAssignmentType, QString assignmentPool, Assignment::Type requestAssignmentType, QString assignmentPool,
quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname, quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
quint16 assignmentServerPort) : quint16 assignmentServerPort, QDir logDirectory) :
_numAssignmentClientForks(numAssignmentClientForks), _numAssignmentClientForks(numAssignmentClientForks),
_minAssignmentClientForks(minAssignmentClientForks), _minAssignmentClientForks(minAssignmentClientForks),
_maxAssignmentClientForks(maxAssignmentClientForks), _maxAssignmentClientForks(maxAssignmentClientForks),
@ -37,7 +40,8 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
_assignmentPool(assignmentPool), _assignmentPool(assignmentPool),
_walletUUID(walletUUID), _walletUUID(walletUUID),
_assignmentServerHostname(assignmentServerHostname), _assignmentServerHostname(assignmentServerHostname),
_assignmentServerPort(assignmentServerPort) _assignmentServerPort(assignmentServerPort),
_logDirectory(logDirectory)
{ {
qDebug() << "_requestAssignmentType =" << _requestAssignmentType; qDebug() << "_requestAssignmentType =" << _requestAssignmentType;
@ -155,11 +159,50 @@ void AssignmentClientMonitor::spawnChildClient() {
_childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION); _childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION);
_childArguments.append(QString::number(DependencyManager::get<NodeList>()->getLocalSockAddr().getPort())); _childArguments.append(QString::number(DependencyManager::get<NodeList>()->getLocalSockAddr().getPort()));
// Setup log files
const QString DATETIME_FORMAT = "yyyyMMdd.hh.mm.ss.zzz";
if (!_logDirectory.exists()) {
qDebug() << "Log directory (" << _logDirectory.absolutePath() << ") does not exist, creating.";
_logDirectory.mkpath(_logDirectory.absolutePath());
}
auto nowString = QDateTime::currentDateTime().toString(DATETIME_FORMAT);
auto stdoutFilenameTemp = QString("ac_stdout_%1.txt").arg(nowString);
auto stderrFilenameTemp = QString("ac_stderr_%1.txt").arg(nowString);
QString stdoutPathTemp = _logDirectory.absoluteFilePath(stdoutFilenameTemp);
QString stderrPathTemp = _logDirectory.absoluteFilePath(stderrFilenameTemp);
// reset our output and error files
assignmentClient->setStandardOutputFile(stdoutPathTemp);
assignmentClient->setStandardErrorFile(stderrPathTemp);
// make sure that the output from the child process appears in our output // make sure that the output from the child process appears in our output
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels); assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments); assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments);
// Update log path to use PID in filename
auto stdoutFilename = QString("ac_stdout_%1_%2.txt").arg(nowString).arg(assignmentClient->processId());
auto stderrFilename = QString("ac_stderr_%1_%2.txt").arg(nowString).arg(assignmentClient->processId());
QString stdoutPath = _logDirectory.absoluteFilePath(stdoutFilename);
QString stderrPath = _logDirectory.absoluteFilePath(stderrFilename);
qDebug() << "Renaming " << stdoutPathTemp << " to " << stdoutPath;
if (!_logDirectory.rename(stdoutFilenameTemp, stdoutFilename)) {
qDebug() << "Failed to rename " << stdoutFilenameTemp;
stdoutFilename = stdoutFilenameTemp;
}
qDebug() << "Renaming " << stderrPathTemp << " to " << stderrPath;
if (!QFile::rename(stderrPathTemp, stderrPath)) {
qDebug() << "Failed to rename " << stderrFilenameTemp;
stderrFilename = stderrFilenameTemp;
}
qDebug() << "Child stdout being written to: " << stdoutPathTemp;
qDebug() << "Child stderr being written to: " << stderrPathTemp;
if (assignmentClient->processId() > 0) { if (assignmentClient->processId() > 0) {
// make sure we hear that this process has finished when it does // make sure we hear that this process has finished when it does
connect(assignmentClient, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(childProcessFinished())); connect(assignmentClient, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(childProcessFinished()));

View file

@ -16,6 +16,7 @@
#include <QtCore/qpointer.h> #include <QtCore/qpointer.h>
#include <QtCore/QProcess> #include <QtCore/QProcess>
#include <QtCore/QDateTime> #include <QtCore/QDateTime>
#include <QDir>
#include <Assignment.h> #include <Assignment.h>
@ -29,7 +30,7 @@ public:
AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks, AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks,
const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType, const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType,
QString assignmentPool, quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname, QString assignmentPool, quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
quint16 assignmentServerPort); quint16 assignmentServerPort, QDir logDirectory);
~AssignmentClientMonitor(); ~AssignmentClientMonitor();
void stopChildProcesses(); void stopChildProcesses();
@ -47,6 +48,8 @@ private:
QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children
QDir _logDirectory;
const unsigned int _numAssignmentClientForks; const unsigned int _numAssignmentClientForks;
const unsigned int _minAssignmentClientForks; const unsigned int _minAssignmentClientForks;
const unsigned int _maxAssignmentClientForks; const unsigned int _maxAssignmentClientForks;

View file

@ -80,11 +80,13 @@
<div id="manage-separator"> <div id="manage-separator">
<img src="images/manage-button-sep.svg"/> <img src="images/manage-button-sep.svg"/>
</div> </div>
<a id="settings" href="#" class="manage-button">
<!-- TODO: handle the case where the DS HTTP port is non-standard -->
<a id="settings" href="http://localhost:40100/settings" target="_blank" class="manage-button">
<img src="images/settings.svg" /> <img src="images/settings.svg" />
<p>Settings</p> <p>Settings</p>
</a> </a>
<a id="logs" href="#" class="manage-button"> <a id="open-logs" href="#" class="manage-button">
<img src="images/logs.svg" /> <img src="images/logs.svg" />
<p>Logs</p> <p>Logs</p>
</a> </a>

View file

@ -4,18 +4,17 @@ $(function() {
function onProcessUpdate(event, arg) { function onProcessUpdate(event, arg) {
console.log("update", event, arg); console.log("update", event, arg);
// Update interface // var interfaceState = arg.interface.state;
// $('#process-interface .status').text(interfaceState);
// var interfaceOn = interfaceState != 'stopped';
// $('#process-interface .power-on').prop('disabled', interfaceOn);
// $('#process-interface .power-off').prop('disabled', !interfaceOn);
// var state = arg.interface.state; var serverState = arg.home.state;
// $('#process-interface .status').text(state); $('#server .status').text(serverState);
// var on = state != 'stopped'; var serverOn = serverState != 'stopped';
// if (on) { $('#server .power-on').prop('disabled', serverOn);
// $('#process-interface .power-on').hide(); $('#server .power-off').prop('disabled', !serverOn);
// $('#process-interface .power-off').show();
// } else {
// $('#process-interface .power-on').show();
// $('#process-interface .power-off').hide();
// }
} }
$('#process-interface .power-on').click(function() { $('#process-interface .power-on').click(function() {
@ -24,6 +23,15 @@ $(function() {
$('#process-interface .power-off').click(function() { $('#process-interface .power-off').click(function() {
ipcRenderer.send('stop-process', { name: 'interface' }); ipcRenderer.send('stop-process', { name: 'interface' });
}); });
$('#server .power-on').click(function() {
ipcRenderer.send('start-server', { name: 'home' });
});
$('#server .power-off').click(function() {
ipcRenderer.send('stop-server', { name: 'home' });
});
$('#open-logs').click(function() {
ipcRenderer.send('open-logs');
});
ipcRenderer.on('process-update', onProcessUpdate); ipcRenderer.on('process-update', onProcessUpdate);

View file

@ -5,6 +5,10 @@ var app = electron.app; // Module to control application life.
var BrowserWindow = require('browser-window'); // Module to create native browser window. var BrowserWindow = require('browser-window'); // Module to create native browser window.
var Menu = require('menu'); var Menu = require('menu');
var Tray = require('tray'); var Tray = require('tray');
var shell = require('shell');
var os = require('os');
var childProcess = require('child_process');
var path = require('path');
var hfprocess = require('./modules/hf-process.js'); var hfprocess = require('./modules/hf-process.js');
var Process = hfprocess.Process; var Process = hfprocess.Process;
@ -12,9 +16,6 @@ var ProcessGroup = hfprocess.ProcessGroup;
const ipcMain = electron.ipcMain; const ipcMain = electron.ipcMain;
// Report crashes to our server.
require('crash-reporter').start();
// Keep a global reference of the window object, if you don't, the window will // Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected. // be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null; var mainWindow = null;
@ -47,6 +48,18 @@ if (argv.localDebugBuilds || argv.localReleaseBuilds) {
acPath = pathFinder.discoveredPath("assignment-client", argv.localReleaseBuilds); acPath = pathFinder.discoveredPath("assignment-client", argv.localReleaseBuilds);
} }
function openFileBrowser(path) {
var type = os.type();
console.log(type);
if (type == "Windows_NT") {
childProcess.exec('start ' + path);
} else if (type == "Darwin") {
childProcess.exec('open ' + path);
} else if (type == "Linux") {
childProcess.exec('xdg-open ' + path);
}
}
// if at this point any of the paths are null, we're missing something we wanted to find // if at this point any of the paths are null, we're missing something we wanted to find
// TODO: show an error for the binaries that couldn't be found // TODO: show an error for the binaries that couldn't be found
@ -89,16 +102,20 @@ app.on('ready', function() {
mainWindow = null; mainWindow = null;
}); });
// When a link is clicked that has `_target="_blank"`, open it in the user's native browser
mainWindow.webContents.on('new-window', function(e, url) {
e.preventDefault();
shell.openExternal(url);
});
var logPath = path.join(app.getAppPath(), 'logs');
if (interfacePath && dsPath && acPath) { if (interfacePath && dsPath && acPath) {
var pInterface = new Process('interface', interfacePath); var pInterface = new Process('interface', interfacePath);
var homeServer = new ProcessGroup('home', [ var homeServer = new ProcessGroup('home', [
new Process('Domain Server', dsPath), new Process('domain_server', dsPath),
new Process('AC - Audio', acPath, ['-t0']), new Process('ac_monitor', acPath, ['-n6', '--log-directory', logPath])
new Process('AC - Avatar', acPath, ['-t1']),
new Process('AC - Asset', acPath, ['-t3']),
new Process('AC - Messages', acPath, ['-t4']),
new Process('AC - Entity', acPath, ['-t6'])
]); ]);
homeServer.start(); homeServer.start();
@ -106,7 +123,7 @@ app.on('ready', function() {
app.on('quit', function(){ app.on('quit', function(){
pInterface.stop(); pInterface.stop();
homeServer.stop(); homeServer.stop();
}) });
var processes = { var processes = {
interface: pInterface, interface: pInterface,
@ -119,6 +136,7 @@ app.on('ready', function() {
}; };
pInterface.on('state-update', sendProcessUpdate); pInterface.on('state-update', sendProcessUpdate);
homeServer.on('state-update', sendProcessUpdate);
ipcMain.on('start-process', function(event, arg) { ipcMain.on('start-process', function(event, arg) {
pInterface.start(); pInterface.start();
@ -128,9 +146,18 @@ app.on('ready', function() {
pInterface.stop(); pInterface.stop();
sendProcessUpdate(); sendProcessUpdate();
}); });
ipcMain.on('update', function(event, arg) { ipcMain.on('start-server', function(event, arg) {
homeServer.start();
sendProcessUpdate(); sendProcessUpdate();
}); });
ipcMain.on('stop-server', function(event, arg) {
homeServer.stop();
sendProcessUpdate();
});
ipcMain.on('open-logs', function(event, arg) {
openFileBrowser(logPath);
});
ipcMain.on('update', sendProcessUpdate);
sendProcessUpdate(); sendProcessUpdate();
} }

View file

@ -4,6 +4,13 @@ var extend = require('extend');
var util = require('util'); var util = require('util');
var events = require('events'); var events = require('events');
var childProcess = require('child_process'); var childProcess = require('child_process');
var fs = require('fs');
const ProcessGroupStates = {
STOPPED: 'stopped',
STARTED: 'started',
STOPPING: 'stopping'
};
const ProcessStates = { const ProcessStates = {
STOPPED: 'stopped', STOPPED: 'stopped',
@ -12,35 +19,72 @@ const ProcessStates = {
}; };
function ProcessGroup(name, processes) { function ProcessGroup(name, processes) {
events.EventEmitter.call(this);
this.name = name; this.name = name;
this.processes = processes; this.state = ProcessGroupStates.STOPPED;
this.processes = [];
for (let process of processes) {
this.addProcess(process);
}
}; };
util.inherits(ProcessGroup, events.EventEmitter); util.inherits(ProcessGroup, events.EventEmitter);
ProcessGroup.prototype = extend(ProcessGroup.prototype, { ProcessGroup.prototype = extend(ProcessGroup.prototype, {
addProcess: function(process) { addProcess: function(process) {
this.processes.push(process); this.processes.push(process);
process.on('state-update', this.onProcessStateUpdate.bind(this));
}, },
start: function() { start: function() {
if (this.state != ProcessGroupStates.STOPPED) {
console.warn("Can't start process group that is not stopped.");
return;
}
for (let process of this.processes) { for (let process of this.processes) {
process.start(); process.start();
} }
this.state = ProcessGroupStates.STARTED;
}, },
stop: function() { stop: function() {
if (this.state != ProcessGroupStates.STARTED) {
console.warn("Can't stop process group that is not started.");
return;
}
for (let process of this.processes) { for (let process of this.processes) {
process.stop(); process.stop();
} }
this.state = ProcessGroupStates.STOPPING;
},
// Event handlers
onProcessStateUpdate: function(process) {
var processesStillRunning = false;
for (let process of this.processes) {
if (process.state != ProcessStates.STOPPED) {
processesStillRunning = true;
break;
}
}
if (!processesStillRunning) {
this.state = ProcessGroupStates.STOPPED;
}
this.emit('state-update', this, process);
} }
}); });
var ID = 0; var ID = 0;
function Process(name, command, commandArgs) { function Process(name, command, commandArgs, logDirectory) {
events.EventEmitter.call(this); events.EventEmitter.call(this);
this.blah = 'adsf';
this.id = ++ID; this.id = ++ID;
this.name = name; this.name = name;
this.command = command; this.command = command;
this.commandArgs = commandArgs ? commandArgs : []; this.commandArgs = commandArgs ? commandArgs : [];
this.child = null; this.child = null;
this.logDirectory = logDirectory;
this.state = ProcessStates.STOPPED; this.state = ProcessStates.STOPPED;
}; };
@ -52,23 +96,80 @@ Process.prototype = extend(Process.prototype, {
return; return;
} }
console.log("Starting " + this.command); console.log("Starting " + this.command);
var logStdout = 'ignore',
logStderr = 'ignore';
if (this.logDirectory) {
var logDirectoryCreated = false;
try {
fs.mkdirSync('logs');
logDirectoryCreated = true;
} catch (e) {
if (e.code == 'EEXIST') {
logDirectoryCreated = true;
} else {
console.error("Error creating log directory");
}
}
if (logDirectoryCreated) {
// Create a temporary file with the current time
var time = (new Date).getTime();
var tmpLogStdout = this.logDirectory + '/' + this.name + '-' + time + '-stdout.txt';
var tmpLogStderr = this.logDirectory + '/' + this.name + '-' + time + '-stderr.txt';
try {
logStdout = fs.openSync(tmpLogStdout, 'ax');
} catch(e) {
console.log("Error creating stdout log file", e);
logStdout = 'ignore';
}
try {
logStderr = fs.openSync(tmpLogStderr, 'ax');
} catch(e) {
console.log("Error creating stderr log file", e);
logStderr = 'ignore';
}
}
}
try { try {
this.child = childProcess.spawn(this.command, this.commandArgs, { this.child = childProcess.spawn(this.command, this.commandArgs, {
detached: false, detached: false,
stdio: 'ignore' stdio: ['ignore', logStdout, logStderr]
}); });
//console.log("started ", this.child);
this.child.on('error', this.onChildStartError.bind(this));
this.child.on('close', this.onChildClose.bind(this));
this.state = ProcessStates.STARTED;
console.log("Child process started");
} catch (e) { } catch (e) {
console.log("Got error starting child process for " + this.name, e); console.log("Got error starting child process for " + this.name, e);
this.child = null; this.child = null;
this.state = ProcessStates.STOPPED; this.state = ProcessStates.STOPPED;
} }
this.emit('state-update'); if (logStdout != 'ignore') {
var pidLogStdout = './logs/' + this.name + "-" + this.child.pid + "-" + time + "-stdout.txt";
fs.rename(tmpLogStdout, pidLogStdout, function(e) {
if (e !== null) {
console.log("Error renaming log file from " + tmpLogStdout + " to " + pidLogStdout, e);
}
});
}
if (logStderr != 'ignore') {
var pidLogStderr = './logs/' + this.name + "-" + this.child.pid + "-" + time + "-stderr.txt";
fs.rename(tmpLogStderr, pidLogStderr, function(e) {
if (e !== null) {
console.log("Error renaming log file from " + tmpLogStdout + " to " + pidLogStdout, e);
}
});
}
this.child.on('error', this.onChildStartError.bind(this));
this.child.on('close', this.onChildClose.bind(this));
this.state = ProcessStates.STARTED;
console.log("Child process started");
this.emit('state-update', this);
}, },
stop: function() { stop: function() {
if (this.state != ProcessStates.STARTED) { if (this.state != ProcessStates.STARTED) {
@ -83,12 +184,12 @@ Process.prototype = extend(Process.prototype, {
onChildStartError: function(error) { onChildStartError: function(error) {
console.log("Child process error ", error); console.log("Child process error ", error);
this.state = ProcessStates.STOPPED; this.state = ProcessStates.STOPPED;
this.emit('state-update'); this.emit('state-update', this);
}, },
onChildClose: function(code) { onChildClose: function(code) {
console.log("Child process closed with code ", code); console.log("Child process closed with code ", code);
this.state = ProcessStates.STOPPED; this.state = ProcessStates.STOPPED;
this.emit('state-update'); this.emit('state-update', this);
} }
}); });