UNPKG

9.95 kBJavaScriptView Raw
1"use strict";
2var __extends = (this && this.__extends) || (function () {
3 var extendStatics = function (d, b) {
4 extendStatics = Object.setPrototypeOf ||
5 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
7 return extendStatics(d, b);
8 };
9 return function (d, b) {
10 extendStatics(d, b);
11 function __() { this.constructor = d; }
12 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13 };
14})();
15Object.defineProperty(exports, "__esModule", { value: true });
16/**
17 * Copyright (c) 2012-2015, Christopher Jeffrey (MIT License)
18 * Copyright (c) 2016, Daniel Imms (MIT License).
19 * Copyright (c) 2018, Microsoft Corporation (MIT License).
20 */
21var net = require("net");
22var terminal_1 = require("./terminal");
23var utils_1 = require("./utils");
24var pty;
25try {
26 pty = require('../build/Release/pty.node');
27}
28catch (outerError) {
29 try {
30 pty = require('../build/Debug/pty.node');
31 }
32 catch (innerError) {
33 console.error('innerError', innerError);
34 // Re-throw the exception from the Release require if the Debug require fails as well
35 throw outerError;
36 }
37}
38var DEFAULT_FILE = 'sh';
39var DEFAULT_NAME = 'xterm';
40var DESTROY_SOCKET_TIMEOUT_MS = 200;
41var UnixTerminal = /** @class */ (function (_super) {
42 __extends(UnixTerminal, _super);
43 function UnixTerminal(file, args, opt) {
44 var _this = _super.call(this, opt) || this;
45 if (typeof args === 'string') {
46 throw new Error('args as a string is not supported on unix.');
47 }
48 // Initialize arguments
49 args = args || [];
50 file = file || DEFAULT_FILE;
51 opt = opt || {};
52 opt.env = opt.env || process.env;
53 _this._cols = opt.cols || terminal_1.DEFAULT_COLS;
54 _this._rows = opt.rows || terminal_1.DEFAULT_ROWS;
55 var uid = opt.uid || -1;
56 var gid = opt.gid || -1;
57 var env = utils_1.assign({}, opt.env);
58 if (opt.env === process.env) {
59 _this._sanitizeEnv(env);
60 }
61 var cwd = opt.cwd || process.cwd();
62 env.PWD = cwd;
63 var name = opt.name || env.TERM || DEFAULT_NAME;
64 env.TERM = name;
65 var parsedEnv = _this._parseEnv(env);
66 var encoding = (opt.encoding === undefined ? 'utf8' : opt.encoding);
67 var onexit = function (code, signal) {
68 // XXX Sometimes a data event is emitted after exit. Wait til socket is
69 // destroyed.
70 if (!_this._emittedClose) {
71 if (_this._boundClose) {
72 return;
73 }
74 _this._boundClose = true;
75 // From macOS High Sierra 10.13.2 sometimes the socket never gets
76 // closed. A timeout is applied here to avoid the terminal never being
77 // destroyed when this occurs.
78 var timeout_1 = setTimeout(function () {
79 timeout_1 = null;
80 // Destroying the socket now will cause the close event to fire
81 _this._socket.destroy();
82 }, DESTROY_SOCKET_TIMEOUT_MS);
83 _this.once('close', function () {
84 if (timeout_1 !== null) {
85 clearTimeout(timeout_1);
86 }
87 _this.emit('exit', code, signal);
88 });
89 return;
90 }
91 _this.emit('exit', code, signal);
92 };
93 // fork
94 var term = pty.fork(file, args, parsedEnv, cwd, _this._cols, _this._rows, uid, gid, (encoding === 'utf8'), onexit);
95 _this._socket = new PipeSocket(term.fd);
96 if (encoding !== null) {
97 _this._socket.setEncoding(encoding);
98 }
99 // setup
100 _this._socket.on('error', function (err) {
101 // NOTE: fs.ReadStream gets EAGAIN twice at first:
102 if (err.code) {
103 if (~err.code.indexOf('EAGAIN')) {
104 return;
105 }
106 }
107 // close
108 _this._close();
109 // EIO on exit from fs.ReadStream:
110 if (!_this._emittedClose) {
111 _this._emittedClose = true;
112 _this.emit('close');
113 }
114 // EIO, happens when someone closes our child process: the only process in
115 // the terminal.
116 // node < 0.6.14: errno 5
117 // node >= 0.6.14: read EIO
118 if (err.code) {
119 if (~err.code.indexOf('errno 5') || ~err.code.indexOf('EIO')) {
120 return;
121 }
122 }
123 // throw anything else
124 if (_this.listeners('error').length < 2) {
125 throw err;
126 }
127 });
128 _this._pid = term.pid;
129 _this._fd = term.fd;
130 _this._pty = term.pty;
131 _this._file = file;
132 _this._name = name;
133 _this._readable = true;
134 _this._writable = true;
135 _this._socket.on('close', function () {
136 if (_this._emittedClose) {
137 return;
138 }
139 _this._emittedClose = true;
140 _this._close();
141 _this.emit('close');
142 });
143 _this._forwardEvents();
144 return _this;
145 }
146 Object.defineProperty(UnixTerminal.prototype, "master", {
147 get: function () { return this._master; },
148 enumerable: true,
149 configurable: true
150 });
151 Object.defineProperty(UnixTerminal.prototype, "slave", {
152 get: function () { return this._slave; },
153 enumerable: true,
154 configurable: true
155 });
156 UnixTerminal.prototype._write = function (data) {
157 this._socket.write(data);
158 };
159 /**
160 * openpty
161 */
162 UnixTerminal.open = function (opt) {
163 var self = Object.create(UnixTerminal.prototype);
164 opt = opt || {};
165 if (arguments.length > 1) {
166 opt = {
167 cols: arguments[1],
168 rows: arguments[2]
169 };
170 }
171 var cols = opt.cols || terminal_1.DEFAULT_COLS;
172 var rows = opt.rows || terminal_1.DEFAULT_ROWS;
173 var encoding = (opt.encoding === undefined ? 'utf8' : opt.encoding);
174 // open
175 var term = pty.open(cols, rows);
176 self._master = new PipeSocket(term.master);
177 if (encoding !== null) {
178 self._master.setEncoding(encoding);
179 }
180 self._master.resume();
181 self._slave = new PipeSocket(term.slave);
182 if (encoding !== null) {
183 self._slave.setEncoding(encoding);
184 }
185 self._slave.resume();
186 self._socket = self._master;
187 self._pid = null;
188 self._fd = term.master;
189 self._pty = term.pty;
190 self._file = process.argv[0] || 'node';
191 self._name = process.env.TERM || '';
192 self._readable = true;
193 self._writable = true;
194 self._socket.on('error', function (err) {
195 self._close();
196 if (self.listeners('error').length < 2) {
197 throw err;
198 }
199 });
200 self._socket.on('close', function () {
201 self._close();
202 });
203 return self;
204 };
205 UnixTerminal.prototype.destroy = function () {
206 var _this = this;
207 this._close();
208 // Need to close the read stream so node stops reading a dead file
209 // descriptor. Then we can safely SIGHUP the shell.
210 this._socket.once('close', function () {
211 _this.kill('SIGHUP');
212 });
213 this._socket.destroy();
214 };
215 UnixTerminal.prototype.kill = function (signal) {
216 try {
217 process.kill(this.pid, signal || 'SIGHUP');
218 }
219 catch (e) { /* swallow */ }
220 };
221 Object.defineProperty(UnixTerminal.prototype, "process", {
222 /**
223 * Gets the name of the process.
224 */
225 get: function () {
226 return pty.process(this._fd, this._pty) || this._file;
227 },
228 enumerable: true,
229 configurable: true
230 });
231 /**
232 * TTY
233 */
234 UnixTerminal.prototype.resize = function (cols, rows) {
235 if (cols <= 0 || rows <= 0 || isNaN(cols) || isNaN(rows) || cols === Infinity || rows === Infinity) {
236 throw new Error('resizing must be done using positive cols and rows');
237 }
238 pty.resize(this._fd, cols, rows);
239 this._cols = cols;
240 this._rows = rows;
241 };
242 UnixTerminal.prototype._sanitizeEnv = function (env) {
243 // Make sure we didn't start our server from inside tmux.
244 delete env['TMUX'];
245 delete env['TMUX_PANE'];
246 // Make sure we didn't start our server from inside screen.
247 // http://web.mit.edu/gnu/doc/html/screen_20.html
248 delete env['STY'];
249 delete env['WINDOW'];
250 // Delete some variables that might confuse our terminal.
251 delete env['WINDOWID'];
252 delete env['TERMCAP'];
253 delete env['COLUMNS'];
254 delete env['LINES'];
255 };
256 return UnixTerminal;
257}(terminal_1.Terminal));
258exports.UnixTerminal = UnixTerminal;
259/**
260 * Wraps net.Socket to force the handle type "PIPE" by temporarily overwriting
261 * tty_wrap.guessHandleType.
262 * See: https://github.com/chjj/pty.js/issues/103
263 */
264var PipeSocket = /** @class */ (function (_super) {
265 __extends(PipeSocket, _super);
266 function PipeSocket(fd) {
267 var _this = this;
268 var pipeWrap = process.binding('pipe_wrap'); // tslint:disable-line
269 // @types/node has fd as string? https://github.com/DefinitelyTyped/DefinitelyTyped/pull/18275
270 var handle = new pipeWrap.Pipe(pipeWrap.constants.SOCKET);
271 handle.open(fd);
272 _this = _super.call(this, { handle: handle }) || this;
273 return _this;
274 }
275 return PipeSocket;
276}(net.Socket));
277//# sourceMappingURL=unixTerminal.js.map
\No newline at end of file