###############################################################################1#2# CoCalc: Collaborative Calculation in the Cloud3#4# Copyright (C) 2016, Sagemath Inc.5#6# This program is free software: you can redistribute it and/or modify7# it under the terms of the GNU General Public License as published by8# the Free Software Foundation, either version 3 of the License, or9# (at your option) any later version.10#11# This program is distributed in the hope that it will be useful,12# but WITHOUT ANY WARRANTY; without even the implied warranty of13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14# GNU General Public License for more details.15#16# You should have received a copy of the GNU General Public License17# along with this program. If not, see <http://www.gnu.org/licenses/>.18#19###############################################################################202122pty = require('pty.js')23{setrlimit} = require('posix')2425message = require('smc-util/message')26misc = require('smc-util/misc')27{defaults, required} = misc2829process.on 'message', (opts, socket) ->30opts = defaults opts,31rows : required32cols : required33command : required34args : required35path : undefined36filename : undefined3738# I noticed that LATELY sometimes we don't see output until hitting input from the client side.39# Setting the socket to immediately send when written to might help.40# See https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay41socket.setNoDelay()4243fn = misc.path_split(opts.filename).tail44env = misc.merge({SMC_FILENAME: fn}, process.env)4546term_opts =47name : 'xterm'48rows : opts.rows49cols : opts.cols50env : env5152if opts.path?53term_opts.cwd = opts.path54if opts.home?55term_opts.home = opts.home5657#console.log("about to pty.fork with: opts.command=#{opts.command}, opts.args=#{misc.to_json(opts.args)}, term_opts=#{misc.to_json(term_opts)}")58term = pty.fork(opts.command, opts.args, term_opts)5960# See http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt61# CSI Ps ; Ps ; Ps t62# CSI[4];[height];[width]t63CSI = String.fromCharCode(0x9b)64resize_sequence = undefined6566parse_resize = (data) ->67i = data.indexOf('t')68if i == -169resize_sequence += data70return data.length71else72# Got complete sequence73s = (resize_sequence + data.slice(0,i)).slice(3)74resize_sequence = undefined75j = s.indexOf(';')76if j != -177rows = parseInt(s.slice(0,j))78cols = parseInt(s.slice(j+1))79term.resize(cols, rows)80return i+18182CSI_code = (data) ->83s = data.toString('utf-8')84if resize_sequence?85start = 086end = parse_resize(s)87else88i = s.lastIndexOf(CSI)89if i != -190resize_sequence = ''91start = i92end = start + parse_resize(s.slice(i))9394if start?95# skip data consumed in CSI96data = data.slice(0,start) + data.slice(end+1)9798return data99100socket.on 'data', (data) ->101data = CSI_code(data)102term.write(data)103104term.on 'data', (data) ->105socket.write(data)106107term.on 'exit', () ->108socket.end()109110socket.on 'end', () ->111# If the hub connection dies, there is no point in112# letting this process continue running, since it can't send113# its output anywhere. So we terminate.114process.exit(1)115116