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

View file

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

View file

@ -12,6 +12,9 @@
#include <memory>
#include <signal.h>
#include <QDir>
#include <QStandardPaths>
#include <AddressManager.h>
#include <LogHandler.h>
#include <udt/PacketHeaders.h>
@ -29,7 +32,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
const unsigned int maxAssignmentClientForks,
Assignment::Type requestAssignmentType, QString assignmentPool,
quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
quint16 assignmentServerPort) :
quint16 assignmentServerPort, QDir logDirectory) :
_numAssignmentClientForks(numAssignmentClientForks),
_minAssignmentClientForks(minAssignmentClientForks),
_maxAssignmentClientForks(maxAssignmentClientForks),
@ -37,7 +40,8 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
_assignmentPool(assignmentPool),
_walletUUID(walletUUID),
_assignmentServerHostname(assignmentServerHostname),
_assignmentServerPort(assignmentServerPort)
_assignmentServerPort(assignmentServerPort),
_logDirectory(logDirectory)
{
qDebug() << "_requestAssignmentType =" << _requestAssignmentType;
@ -155,11 +159,50 @@ void AssignmentClientMonitor::spawnChildClient() {
_childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION);
_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
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
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) {
// make sure we hear that this process has finished when it does
connect(assignmentClient, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(childProcessFinished()));

View file

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

View file

@ -80,11 +80,13 @@
<div id="manage-separator">
<img src="images/manage-button-sep.svg"/>
</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" />
<p>Settings</p>
</a>
<a id="logs" href="#" class="manage-button">
<a id="open-logs" href="#" class="manage-button">
<img src="images/logs.svg" />
<p>Logs</p>
</a>

View file

@ -4,18 +4,17 @@ $(function() {
function onProcessUpdate(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;
// $('#process-interface .status').text(state);
// var on = state != 'stopped';
// if (on) {
// $('#process-interface .power-on').hide();
// $('#process-interface .power-off').show();
// } else {
// $('#process-interface .power-on').show();
// $('#process-interface .power-off').hide();
// }
var serverState = arg.home.state;
$('#server .status').text(serverState);
var serverOn = serverState != 'stopped';
$('#server .power-on').prop('disabled', serverOn);
$('#server .power-off').prop('disabled', !serverOn);
}
$('#process-interface .power-on').click(function() {
@ -24,6 +23,15 @@ $(function() {
$('#process-interface .power-off').click(function() {
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);

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 Menu = require('menu');
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 Process = hfprocess.Process;
@ -12,9 +16,6 @@ var ProcessGroup = hfprocess.ProcessGroup;
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
// be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null;
@ -47,6 +48,18 @@ if (argv.localDebugBuilds || 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
// TODO: show an error for the binaries that couldn't be found
@ -89,16 +102,20 @@ app.on('ready', function() {
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) {
var pInterface = new Process('interface', interfacePath);
var homeServer = new ProcessGroup('home', [
new Process('Domain Server', dsPath),
new Process('AC - Audio', acPath, ['-t0']),
new Process('AC - Avatar', acPath, ['-t1']),
new Process('AC - Asset', acPath, ['-t3']),
new Process('AC - Messages', acPath, ['-t4']),
new Process('AC - Entity', acPath, ['-t6'])
new Process('domain_server', dsPath),
new Process('ac_monitor', acPath, ['-n6', '--log-directory', logPath])
]);
homeServer.start();
@ -106,7 +123,7 @@ app.on('ready', function() {
app.on('quit', function(){
pInterface.stop();
homeServer.stop();
})
});
var processes = {
interface: pInterface,
@ -119,6 +136,7 @@ app.on('ready', function() {
};
pInterface.on('state-update', sendProcessUpdate);
homeServer.on('state-update', sendProcessUpdate);
ipcMain.on('start-process', function(event, arg) {
pInterface.start();
@ -128,9 +146,18 @@ app.on('ready', function() {
pInterface.stop();
sendProcessUpdate();
});
ipcMain.on('update', function(event, arg) {
ipcMain.on('start-server', function(event, arg) {
homeServer.start();
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();
}

View file

@ -4,6 +4,13 @@ var extend = require('extend');
var util = require('util');
var events = require('events');
var childProcess = require('child_process');
var fs = require('fs');
const ProcessGroupStates = {
STOPPED: 'stopped',
STARTED: 'started',
STOPPING: 'stopping'
};
const ProcessStates = {
STOPPED: 'stopped',
@ -12,35 +19,72 @@ const ProcessStates = {
};
function ProcessGroup(name, processes) {
events.EventEmitter.call(this);
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);
ProcessGroup.prototype = extend(ProcessGroup.prototype, {
addProcess: function(process) {
this.processes.push(process);
process.on('state-update', this.onProcessStateUpdate.bind(this));
},
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) {
process.start();
}
this.state = ProcessGroupStates.STARTED;
},
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) {
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;
function Process(name, command, commandArgs) {
function Process(name, command, commandArgs, logDirectory) {
events.EventEmitter.call(this);
this.blah = 'adsf';
this.id = ++ID;
this.name = name;
this.command = command;
this.commandArgs = commandArgs ? commandArgs : [];
this.child = null;
this.logDirectory = logDirectory;
this.state = ProcessStates.STOPPED;
};
@ -52,23 +96,80 @@ Process.prototype = extend(Process.prototype, {
return;
}
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 {
this.child = childProcess.spawn(this.command, this.commandArgs, {
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) {
console.log("Got error starting child process for " + this.name, e);
this.child = null;
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() {
if (this.state != ProcessStates.STARTED) {
@ -83,12 +184,12 @@ Process.prototype = extend(Process.prototype, {
onChildStartError: function(error) {
console.log("Child process error ", error);
this.state = ProcessStates.STOPPED;
this.emit('state-update');
this.emit('state-update', this);
},
onChildClose: function(code) {
console.log("Child process closed with code ", code);
this.state = ProcessStates.STOPPED;
this.emit('state-update');
this.emit('state-update', this);
}
});