connect: function(){ var client = this; var opt = client.options client.socket = (opt.secure?tls:net).connect(opt.connection, function() { debug('Client connected'); client.connected = true; client.write = this.write.bind(this); client.send('NICK ', opt.nick); client.send('USER ', opt.ident, ' 0 * :', opt.realname); client.emit('connect'); }) .on('close', function (data) { client.connected = false; debug('Disconnected from server'); client.emit('close'); }) .on('error', function (err) { debug(err); client.emit('error', err); }); client.once('RPL_WELCOME', function(event) { this.emit('welcome', event.params[1]); }); eachline(client.socket, client.ondata.bind(client)); return client; },
cordovaProject.runCommands(`tailing logs for ${this.displayName}`, async () => { await this.checkPlatformRequirementsAndSetEnv(cordovaProject); // XXX This only works if we have at most one device or one emulator // connected. We should find a way to get the target ID from run and use // it instead of -d or -e. const target = this.isDevice ? "-d" : "-e"; // Clear logs await execFileAsync('adb', [target, 'logcat', '-c']); const filterExpressions = ['CordovaLog:D', 'chromium:I', 'SystemWebViewClient:I', '*:S']; const { Log } = isopackets.load('cordova-support')['logging']; const logStream = eachline((line) => { const logEntry = logFromAndroidLogcatLine(Log, line); if (logEntry) { return `${logEntry}\n`; } }); logStream.pipe(process.stdout); // Asynchronously start tailing logs to stdout execFileAsync('adb', [target, 'logcat', ...filterExpressions], { destination: logStream }); });
exports.execFileAsync = function (file, args, opts) { opts = opts || {}; var child_process = require('child_process'); var eachline = require('eachline'); var p = child_process.spawn(file, args, opts); var mapper = opts.lineMapper || _.identity; var logOutput = fiberHelpers.bindEnvironment(function (line) { if (opts.verbose) { line = mapper(line); if (line) console.log(line); } }); eachline(p.stdout, logOutput); eachline(p.stderr, logOutput); return p; };
exports.execFileSync = function (file, args, opts) { var child_process = require('child_process'); var eachline = require('eachline'); opts = opts || {}; if (! _.has(opts, 'maxBuffer')) { opts.maxBuffer = 1024 * 1024 * 10; } if (opts && opts.pipeOutput) { var p = child_process.spawn(file, args, opts); eachline(p.stdout, fiberHelpers.bindEnvironment(function (line) { process.stdout.write(line + '\n'); })); eachline(p.stderr, fiberHelpers.bindEnvironment(function (line) { process.stderr.write(line + '\n'); })); return { success: ! new Promise(function (resolve) { p.on('exit', resolve); }).await(), stdout: "", stderr: "" }; } return new Promise(function (resolve) { child_process.execFile(file, args, opts, function (err, stdout, stderr) { resolve({ success: ! err, stdout: stdout, stderr: stderr }); }); }).await(); };
exports.execFileSync = function (file, args, opts) { var future = new Future; var child_process = require('child_process'); var eachline = require('eachline'); if (opts && opts.pipeOutput) { var p = child_process.spawn(file, args, opts); eachline(p.stdout, fiberHelpers.bindEnvironment(function (line) { process.stdout.write(line + '\n'); })); eachline(p.stderr, fiberHelpers.bindEnvironment(function (line) { process.stderr.write(line + '\n'); })); p.on('exit', function (code) { future.return(code); }); return { success: !future.wait(), stdout: "", stderr: "" }; } child_process.execFile(file, args, opts, function (err, stdout, stderr) { future.return({ success: ! err, stdout: stdout, stderr: stderr }); }); return future.wait(); };
var startServer = function (options) { // environment options = _.extend({ nodeOptions: [] }, options); var env = {}; for (var k in process.env) env[k] = process.env[k]; env.PORT = options.innerPort; env.MONGO_URL = options.mongoUrl; if (options.oplogUrl) env.MONGO_OPLOG_URL = options.oplogUrl; env.ROOT_URL = options.rootUrl; if (options.settings) env.METEOR_SETTINGS = options.settings; else delete env.METEOR_SETTINGS; // Display errors from (eg) the NPM connect module over the network. env.NODE_ENV = 'development'; if (! options.program) { var nodeOptions = _.clone(options.nodeOptions); nodeOptions.push(path.join(options.bundlePath, 'main.js')); nodeOptions.push('--keepalive'); var child_process = require('child_process'); var proc = child_process.spawn(process.execPath, nodeOptions, {env: env}); } else { var starJson = JSON.parse( fs.readFileSync(path.join(options.bundlePath, 'star.json'), 'utf8')); var programPath = null; var archinfo = require('./archinfo.js'); _.each(starJson.programs, function (p) { // XXX should actually use archinfo.mostSpecificMatch instead of // taking the first match if (p.name !== options.program) return; if (! archinfo.matches(archinfo.host(), p.arch)) return; // can't run here programPath = path.join(options.bundlePath, p.path); }); if (! programPath) { // XXX probably not the correct error handling process.stderr.write("Program '" + options.program + "' not found.\n"); process.exit(1); } var child_process = require('child_process'); var proc = child_process.spawn(programPath, [], {env: env}); } // XXX deal with test server logging differently?! var Log = unipackage.load({ library: options.library, packages: ['logging'] }).logging.Log; // Since no other process will be listening to stdout and parsing it, // print directly in the same format as log messages from other apps Log.outputFormat = 'colored-text'; var eachline = require('eachline'); eachline(proc.stdout, 'utf8', function (line) { if (!line) return; // string must match server.js if (line.match(/^LISTENING\s*$/)) { options.onListen && options.onListen(); return; } if (options.rawLogs) { console.log(line); saveLog({stdout: line}); } else { var obj = Log.parse(line) || Log.objFromText(line); console.log(Log.format(obj, { color:true })); saveLog({stdout: Log.format(obj)}); } }); eachline(proc.stderr, 'utf8', function (line) { if (!line) return; if (options.rawLogs) { console.error(line); saveLog({stderr: line}); } else { var obj = Log.objFromText(line, { level: 'warn', stderr: true }); console.log(Log.format(obj, { color: true })); saveLog({stderr: Log.format(obj)}); } }); proc.on('close', function (code, signal) { if (signal) { logToClients({'exit': '=> Exited from signal: ' + signal}); } else { logToClients({'exit': '=> Exited with code: ' + code}); } options.onExit(code); }); // this happens sometimes when we write a keepalive after the app is // dead. If we don't register a handler, we get a top level exception // and the whole app dies. // http://stackoverflow.com/questions/2893458/uncatchable-errors-in-node-js proc.stdin.on('error', function () {}); // Keepalive so server can detect when we die var timer = setInterval(function () { try { if (proc && proc.pid && proc.stdin && proc.stdin.write) proc.stdin.write('k'); } catch (e) { // do nothing. this fails when the process dies. } }, 2000); return { proc: proc, timer: timer }; };
start: function () { var self = this; if (self.proc) throw new Error("already started?"); // Start the app! self.proc = self._spawn(); // Send stdout and stderr to the runLog var eachline = require('eachline'); eachline(self.proc.stdout, 'utf8', fiberHelpers.inBareFiber(function (line) { if (line.match(/^LISTENING\s*$/)) { // This is the child process telling us that it's ready to receive // connections. (It does this because we told it to with // $METEOR_PRINT_ON_LISTEN.) self.onListen && self.onListen(); } else { runLog.logAppOutput(line); } })); eachline(self.proc.stderr, 'utf8', fiberHelpers.inBareFiber(function (line) { if (self.debugPort && line.indexOf("debugger listening on port ") >= 0) { Console.enableProgressDisplay(false); return; } runLog.logAppOutput(line, true); })); // Watch for exit and for stdio to be fully closed (so that we don't miss // log lines). self.proc.on('close', fiberHelpers.inBareFiber(function (code, signal) { self._maybeCallOnExit(code, signal); })); self.proc.on('error', fiberHelpers.inBareFiber(function (err) { // if the error is the result of .send command over ipc pipe, ignore it if (self._refreshing) { return; } runLog.log("Couldn't spawn process: " + err.message, { arrow: true }); // node docs say that it might make both an 'error' and a // 'close' callback, so we use a guard to make sure we only call // onExit once. self._maybeCallOnExit(); })); // This happens sometimes when we write a keepalive after the app // is dead. If we don't register a handler, we get a top level // exception and the whole app dies. // http://stackoverflow.com/questions/2893458/uncatchable-errors-in-node-js self.proc.stdin.on('error', function () {}); // When the parent process exits (i.e. the server is shutting down and // not merely restarting), make sure to disconnect any still-connected // shell clients. require('../tool-env/cleanup.js').onExit(function() { require('../static-assets/server/shell-server.js').disable( self.projectContext.getMeteorShellDirectory() ); }); },
function connect() { if (connected) { return; } var sock = net.connect(socketFile); process.stdin.on("data", onData); sock.pipe(process.stdout); sock.on("connect", onConnect); sock.on("close", onClose); sock.on("error", onError); function onConnect() { firstTimeConnecting = false; reconnect.count = 0; connected = true; // Sending a JSON-stringified options object (even just an empty // object) over the socket is required to start the REPL session. sock.write(JSON.stringify({ terminal: !process.env.EMACS })); process.stderr.write(shellBanner()); process.stdin.pipe(sock); process.stdin.setRawMode(true); } eachline(sock, "utf8", function(line) { exitOnClose = line.indexOf(EXITING_MESSAGE) >= 0; }); function onClose() { tearDown(); // If we received the special EXITING_MESSAGE just before the socket // closed, then exit the shell instead of reconnecting. if (exitOnClose) { process.exit(0); } else { reconnect(); } } function onError(err) { tearDown(); if (err.errno === "ENOENT" || err.errno === "ECONNREFUSED") { // If the shell.sock file is missing or looks like a socket but is // not accepting connections, keep trying to connect. reconnect(); } else if (err.errno === "ENOTSOCK") { // When the server shuts down completely, it replaces the // shell.sock file with a regular file to force connected shell // clients to disconnect and exit. If this shell client is // connecting for the first time, however, assume the user intends // to start the server again soon, and wait to reconnect. if (firstTimeConnecting) { reconnect(); } else { process.exit(0); } } } function tearDown() { connected = false; process.stdin.unpipe(sock); process.stdin.removeListener("data", onData); process.stdin.setRawMode(false); sock.unpipe(process.stdout); sock.removeListener("connect", onConnect); sock.removeListener("close", onClose); sock.removeListener("error", onError); sock.end(); } }
Cp.setUpSocket = function setUpSocket(sock, key) { const self = this; if (! process.stdin.isTTY) { return self.setUpSocketForSingleUse(sock, key); } // Put STDIN into "flowing mode": // http://nodejs.org/api/stream.html#stream_compatibility_with_older_node_versions process.stdin.resume(); function onConnect() { self.firstTimeConnecting = false; self.reconnectCount = 0; self.connected = true; // Sending a JSON-stringified options object (even just an empty // object) over the socket is required to start the REPL session. sock.write(JSON.stringify({ terminal: ! process.env.EMACS, key: key }) + "\n"); process.stderr.write(shellBanner()); process.stdin.pipe(sock); if (process.stdin.setRawMode) { // https://github.com/joyent/node/issues/8204 process.stdin.setRawMode(true); } } function onClose() { tearDown(); // If we received the special EXITING_MESSAGE just before the socket // closed, then exit the shell instead of reconnecting. if (self.exitOnClose) { process.exit(0); } else { self.reconnect(); } } function onError(err) { tearDown(); self.reconnect(); } function tearDown() { self.connected = false; if (process.stdin.setRawMode) { // https://github.com/joyent/node/issues/8204 process.stdin.setRawMode(false); } process.stdin.unpipe(sock); sock.unpipe(process.stdout); sock.removeListener("connect", onConnect); sock.removeListener("close", onClose); sock.removeListener("error", onError); sock.end(); } sock.pipe(process.stdout); eachline(sock, "utf8", function(line) { self.exitOnClose = line.indexOf(EXITING_MESSAGE) >= 0; }); sock.on("connect", onConnect); sock.on("close", onClose); sock.on("error", onError); };
start: function () { var self = this; if (self.proc) throw new Error("already started?"); // Start the app! self.proc = self._spawn(); if (self.proc === null) { runLog.log("Program '" + self.program + "' not found."); self._maybeCallOnExit(); return; } // Send stdout and stderr to the runLog var eachline = require('eachline'); eachline(self.proc.stdout, 'utf8', function (line) { if (line.match(/^LISTENING\s*$/)) { // This is the child process telling us that it's ready to // receive connections. self.onListen && self.onListen(); } else { runLog.logAppOutput(line); } }); eachline(self.proc.stderr, 'utf8', function (line) { runLog.logAppOutput(line, true); }); // Watch for exit and for stdio to be fully closed (so that we don't miss // log lines). self.proc.on('close', function (code, signal) { self._maybeCallOnExit(code, signal); }); self.proc.on('error', function (err) { runLog.log("=> Couldn't spawn process: " + err.message); // node docs say that it might make both an 'error' and a // 'close' callback, so we use a guard to make sure we only call // onExit once. self._maybeCallOnExit(); }); // This happens sometimes when we write a keepalive after the app // is dead. If we don't register a handler, we get a top level // exception and the whole app dies. // http://stackoverflow.com/questions/2893458/uncatchable-errors-in-node-js self.proc.stdin.on('error', function () {}); // Keepalive so child process can detect when we die self.keepaliveTimer = setInterval(function () { try { if (self.proc && self.proc.pid && self.proc.stdin && self.proc.stdin.write) self.proc.stdin.write('k'); } catch (e) { // do nothing. this fails when the process dies. } }, 2000); },
function eachline(stream, encoding, callback) { realEachline(stream, encoding, (...args) => void(callback(...args))); }