pax_global_header00006660000000000000000000000064130247111230014504gustar00rootroot0000000000000052 comment=4243914e257079bf041574d84c0381cfce269ccc foreground-child-1.5.6/000077500000000000000000000000001302471112300147505ustar00rootroot00000000000000foreground-child-1.5.6/.gitignore000066400000000000000000000000541302471112300167370ustar00rootroot00000000000000node_modules .DS_Store .nyc_output coverage foreground-child-1.5.6/.travis.yml000066400000000000000000000001151302471112300170560ustar00rootroot00000000000000sudo: false language: node_js node_js: - '0.10' - '0.12' - '4' - '5' foreground-child-1.5.6/CHANGELOG.md000066400000000000000000000032201302471112300165560ustar00rootroot00000000000000# Changes ## v1.5.5 * add files list to package.json * neveragain.tech pledge request ## v1.5.4 * update tap to v8 * Let the child decide if signals should be fatal ## v1.5.3 * bump deps ## v1.5.2 * add an automatic changelog script * replace cross-spawn-async with cross-spawn * test: stay alive long enough to be signaled ## v1.5.1 * avoid race condition in test * Use fd numbers instead of 'inherit' for Node v0.10 compatibility ## v1.5.0 * add caveats re IPC and arbitrary FDs * Forward IPC messages to foregrounded child process ## v1.4.0 * Set `process.exitCode` based on the child’s exit code ## v1.3.5 * Better testing for when cross-spawn-async needed * appveyor: node v0.10 on windows is too flaky ## v1.3.4 * Only use cross-spawn-async for shebangs * update vanity badges and package.json for repo move * appveyor ## v1.3.3 * Skip signals in tests on Windows * update to tap@4 * use cross-spawn-async on windows ## v1.3.2 * Revert "switch to win-spawn" * Revert "Transparently translate high-order exit code to appropriate signal" * update travis versions * Transparently translate high-order exit code to appropriate signal * ignore coverage folder ## v1.3.1 * switch to win-spawn ## v1.3.0 * note skipped test in test output * left an unused var c in * slice arguments, added documentation * added a unit test, because I strive to be a good open-source-citizen * make travis also work on 0.12 and iojs again * added badge * patch for travis exit weirdness * fix typo in .gitignore * beforeExit hook ## v1.2.0 * Use signal-exit, fix kill(process.pid) race ## v1.1.0 * Enforce that parent always gets a 'exit' event ## v1.0.0 * first foreground-child-1.5.6/CONTRIBUTING.md000066400000000000000000000001161302471112300171770ustar00rootroot00000000000000Please consider signing [the neveragain.tech pledge](http://neveragain.tech/) foreground-child-1.5.6/LICENSE000066400000000000000000000013751302471112300157630ustar00rootroot00000000000000The ISC License Copyright (c) Isaac Z. Schlueter and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. foreground-child-1.5.6/README.md000066400000000000000000000036061302471112300162340ustar00rootroot00000000000000# foreground-child [![Build Status](https://travis-ci.org/tapjs/foreground-child.svg)](https://travis-ci.org/tapjs/foreground-child) [![Build status](https://ci.appveyor.com/api/projects/status/kq9ylvx9fyr9khx0?svg=true)](https://ci.appveyor.com/project/isaacs/foreground-child) Run a child as if it's the foreground process. Give it stdio. Exit when it exits. Mostly this module is here to support some use cases around wrapping child processes for test coverage and such. ## USAGE ```js var foreground = require('foreground-child') // cats out this file var child = foreground('cat', [__filename]) // At this point, it's best to just do nothing else. // return or whatever. // If the child gets a signal, or just exits, then this // parent process will exit in the same way. ``` A callback can optionally be provided, if you want to perform an action before your foreground-child exits: ```js var child = foreground('cat', [__filename], function (done) { // perform an action. return done() }) ``` ## Caveats The "normal" standard IO file descriptors (0, 1, and 2 for stdin, stdout, and stderr respectively) are shared with the child process. Additionally, if there is an IPC channel set up in the parent, then messages are proxied to the child on file descriptor 3. However, in Node, it's possible to also map arbitrary file descriptors into a child process. In these cases, foreground-child will not map the file descriptors into the child. If file descriptors 0, 1, or 2 are used for the IPC channel, then strange behavior may happen (like printing IPC messages to stderr, for example). Note that a SIGKILL will always kill the parent process, _and never the child process_, because SIGKILL cannot be caught or proxied. The only way to do this would be if Node provided a way to truly exec a process as the new foreground program in the same process space, without forking a separate child process. foreground-child-1.5.6/appveyor.yml000066400000000000000000000005711302471112300173430ustar00rootroot00000000000000environment: matrix: - nodejs_version: '5' - nodejs_version: '4' - nodejs_version: '0.12' install: - ps: Install-Product node $env:nodejs_version - set CI=true - npm -g install npm@latest - set PATH=%APPDATA%\npm;%PATH% - npm install matrix: fast_finish: true build: off version: '{build}' shallow_clone: true clone_depth: 1 test_script: - npm test foreground-child-1.5.6/changelog.sh000066400000000000000000000004061302471112300172330ustar00rootroot00000000000000#!/bin/bash ( echo '# Changes' echo '' git log --first-parent --pretty=format:'%s' \ | grep -v '^update changelog' \ | grep -v 'beta' \ | perl -p -e 's/^((v?[0-9]+\.?)+)?$/\n## \1\n/g' \ | perl -p -e 's/^([^#\s].*)$/* \1/g' )> CHANGELOG.md foreground-child-1.5.6/index.js000066400000000000000000000047551302471112300164300ustar00rootroot00000000000000var signalExit = require('signal-exit') var spawn = require('child_process').spawn if (process.platform === 'win32') { spawn = require('cross-spawn') } module.exports = function (program, args, cb) { var arrayIndex = arguments.length if (typeof args === 'function') { cb = args args = undefined } else { cb = Array.prototype.slice.call(arguments).filter(function (arg, i) { if (typeof arg === 'function') { arrayIndex = i return true } })[0] } cb = cb || function (done) { return done() } if (Array.isArray(program)) { args = program.slice(1) program = program[0] } else if (!Array.isArray(args)) { args = [].slice.call(arguments, 1, arrayIndex) } var spawnOpts = { stdio: [0, 1, 2] } if (process.send) { spawnOpts.stdio.push('ipc') } var child = spawn(program, args, spawnOpts) var childExited = false var unproxySignals = proxySignals(child) process.on('exit', childHangup) function childHangup () { child.kill('SIGHUP') } child.on('close', function (code, signal) { // Allow the callback to inspect the child’s exit code and/or modify it. process.exitCode = signal ? 128 + signal : code cb(function () { unproxySignals() process.removeListener('exit', childHangup) childExited = true if (signal) { // If there is nothing else keeping the event loop alive, // then there's a race between a graceful exit and getting // the signal to this process. Put this timeout here to // make sure we're still alive to get the signal, and thus // exit with the intended signal code. setTimeout(function () {}, 200) process.kill(process.pid, signal) } else { // Equivalent to process.exit() on Node.js >= 0.11.8 process.exit(process.exitCode) } }) }) if (process.send) { process.removeAllListeners('message') child.on('message', function (message, sendHandle) { process.send(message, sendHandle) }) process.on('message', function (message, sendHandle) { child.send(message, sendHandle) }) } return child } function proxySignals (child) { var listeners = {} signalExit.signals().forEach(function (sig) { listeners[sig] = function () { child.kill(sig) } process.on(sig, listeners[sig]) }) return unproxySignals function unproxySignals () { for (var sig in listeners) { process.removeListener(sig, listeners[sig]) } } } foreground-child-1.5.6/package.json000066400000000000000000000016561302471112300172460ustar00rootroot00000000000000{ "name": "foreground-child", "version": "1.5.6", "description": "Run a child as if it's the foreground process. Give it stdio. Exit when it exits.", "main": "index.js", "directories": { "test": "test" }, "dependencies": { "cross-spawn": "^4", "signal-exit": "^3.0.0" }, "devDependencies": { "tap": "^8.0.1" }, "scripts": { "test": "tap --coverage test/*.js", "changelog": "bash changelog.sh", "postversion": "npm run changelog && git add CHANGELOG.md && git commit -m 'update changelog - '${npm_package_version}" }, "repository": { "type": "git", "url": "git+https://github.com/tapjs/foreground-child.git" }, "author": "Isaac Z. Schlueter (http://blog.izs.me/)", "license": "ISC", "bugs": { "url": "https://github.com/tapjs/foreground-child/issues" }, "homepage": "https://github.com/tapjs/foreground-child#readme", "files": [ "index.js" ] } foreground-child-1.5.6/test/000077500000000000000000000000001302471112300157275ustar00rootroot00000000000000foreground-child-1.5.6/test/basic.js000066400000000000000000000113111302471112300173430ustar00rootroot00000000000000var fg = require('../index.js') var spawn = require('child_process').spawn var signalExit = require('signal-exit') if (process.argv[2] === 'child') { setTimeout(function(){}, 1000); console.log('stdout') setTimeout(function () {}, 1000) switch (process.argv[3]) { case 'SIGTERM': case 'SIGHUP': case 'SIGKILL': process.kill(process.pid, process.argv[3]) break case '0': case '1': case '2': process.exit(+process.argv[3]) break case 'ipc': process.on('message', function(m) { console.log('message received') process.send(m) process.exit(0) }) break } return } if (process.argv[2] === 'parent') { var cb = undefined // we can optionally assign a beforeExit handler // to the foreground-child process; we should test it. if (process.argv[4] === 'beforeExitHandler') { cb = function (done) { var expectedExitCode = +process.argv[3] if (expectedExitCode !== process.exitCode) { console.log('unexpected exit code', expectedExitCode, process.exitCode); } console.log('beforeExitHandler') return done() } } var program = process.execPath var args = [__filename, 'child'].concat(process.argv.slice(3)) var child = fg(program, args, cb) if (process.argv[3] === 'signalexit') { signalExit(function (code, signal) { console.log('parent exit') }) switch (process.argv[4]) { case 'parent': process.kill(process.pid, 'SIGTERM') break case 'child': process.kill(child.pid, 'SIGTERM') break default: process.exit() break } } return } var t = require('tap') t.test('signals', { skip: winSignals() }, function (t) { var signals = [ 'SIGTERM', 'SIGHUP', 'SIGKILL' ] signals.forEach(function (sig) { t.test(sig, function (t) { t.plan(3) var prog = process.execPath var args = [__filename, 'parent', sig] var child = spawn(prog, args) var out = '' child.stdout.on('data', function (c) { out += c }) child.on('close', function (code, signal) { t.equal(signal, sig) t.equal(code, null) t.equal(out, 'stdout\n') }) }) }) t.end() }) t.test('exit codes', function (t) { var codes = [0, 1, 2] codes.forEach(function (c) { t.test(c, function (t) { t.plan(3) var prog = process.execPath var args = [__filename, 'parent', c] var child = spawn(prog, args) var out = '' child.stdout.on('data', function (c) { out += c }) child.on('close', function (code, signal) { t.equal(signal, null) t.equal(code, c) t.equal(out, 'stdout\n') }) }) }) t.end() }) t.test('parent emits exit when SIGTERMed', { skip: isZero10OnTravis() || winSignals() }, function (t) { var which = ['parent', 'child', 'nobody'] which.forEach(function (who) { t.test('SIGTERM ' + who, function (t) { var prog = process.execPath var args = [__filename, 'parent', 'signalexit', who] var child = spawn(prog, args) var out = '' child.stdout.on('data', function (c) { out += c }) child.on('close', function (code, signal) { if (who === 'nobody') t.equal(signal, null) else t.equal(signal, 'SIGTERM') t.equal(out, 'parent exit\n') t.end() }) }) }) t.end() }) t.test('beforeExitHandler', function (t) { var codes = [0, 1, 2] codes.forEach(function (c) { t.test(c, function (t) { t.plan(3) var prog = process.execPath var args = [__filename, 'parent', c, 'beforeExitHandler'] var child = spawn(prog, args) var out = '' child.stdout.on('data', function (c) { out += c }) child.on('close', function (code, signal) { t.equal(signal, null) t.equal(code, c) t.equal(out, 'stdout\nbeforeExitHandler\n') }) }) }) t.end() }) t.test('IPC forwarding', function (t) { t.plan(5) var prog = process.execPath var args = [__filename, 'parent', 'ipc'] child = spawn(prog, args, { stdio: ['ipc', 'pipe', 'pipe'] }) var out = '' var messages = [] child.on('message', function (m) { messages.push(m) }) child.stdout.on('data', function (c) { out += c }) child.send({ data: 'foobar' }) child.on('close', function (code, signal) { t.equal(signal, null) t.equal(code, 0) t.equal(out, 'stdout\nmessage received\n') t.equal(messages.length, 1) t.equal(messages[0].data, 'foobar') }) }) function isZero10OnTravis () { return process.env.TRAVIS && /^v0\.10\.[0-9]+$/.test(process.version) ? 'skip on 0.10 on Travis' : false } function winSignals () { return process.platform === 'win32' ? 'windows does not support unix signals' : false } foreground-child-1.5.6/test/immortal-child.js000066400000000000000000000031671302471112300212010ustar00rootroot00000000000000var node = process.execPath if (process.argv[2] === 'child') child() else if (process.argv[2] === 'parent') parent() else test() function test () { var t = require('tap') var spawn = require('child_process').spawn var proc = spawn(node, [__filename, 'parent']) var out = '' proc.stdout.on('data', function (c) { out += c }) var err = '' proc.stderr.on('data', function (c) { err += c }) proc.on('exit', function (code, signal) { clearTimeout(timer) }) proc.on('close', function (code, signal) { var actual = { out: out, err: err, code: code, signal: signal } var expect = { out: /^(child alive\n)*child SIGINT received\n(child alive\n)*child exit null SIGTERM\n$/, err: /^parent \d+\nchild \d+\n$/, code: null, signal: 'SIGTERM' } t.match(actual, expect) t.end() }) var time = 250 // coverage slows things down a bit if (process.env._TAP_COVERAGE_) time = 1000 var timer = setTimeout(function () { proc.kill('SIGINT') timer = setTimeout(function () { proc.kill('SIGTERM') }, time) }, time) } function parent () { console.error('parent', process.pid) var fg = require('../') fg(node, [ __filename, 'child' ]) } function child () { console.error('child', process.pid) setInterval(function () { console.log('child alive') }, 200) process.on('SIGINT', function () { console.log('child SIGINT received') }) process.on('SIGHUP', function () { console.log('child SIGHUP received') }) require('signal-exit')(function (code, signal) { console.log('child exit', code, signal) }) }