path = require('path')
async = require('async')
fs = require('fs')
net = require('net')
child_process = require('child_process')
winston = require('winston')
assert = require('assert')
program = require('commander')
message = require('smc-util/message')
misc_node = require('smc-util-node/misc_node')
{secret_token_filename} = require('./common')
port_manager = require('./port_manager')
misc = require('smc-util/misc')
{to_json, from_json, defaults, required} = misc
abspath = (path) ->
if path.length == 0
return process.env.HOME
if path[0] == '/'
return path
return process.env.HOME + '/' + path
if not process.env.SMC?
process.env.SMC = path.join(process.env.HOME, '.smc')
DATA = path.join(process.env['SMC'], 'console_server')
if not fs.existsSync(process.env['SMC'])
fs.mkdirSync(process.env['SMC'])
if not fs.existsSync(DATA)
fs.mkdirSync(DATA)
misc = require('smc-util/misc')
secret_token = undefined
read_token = (cb) ->
f = (cb) ->
fs.exists secret_token_filename(), (exists) ->
if not exists
cb("secret token file does not exist")
else
secret_token = fs.readFileSync(secret_token_filename()).toString()
cb()
misc.retry_until_success
f : f
max_time : 30000
cb : cb
start_session = (socket, mesg) ->
winston.info "start_session #{to_json(mesg)}"
if not mesg.params?
mesg.params = {}
opts = defaults mesg.params,
rows : 24
cols : 80
command : 'bash'
args : []
path : required
filename : required
opts.path = abspath(opts.path)
opts.filename = abspath(opts.filename)
init_fn = misc.console_init_filename(opts.filename)
if fs.existsSync(init_fn) and opts.command == 'bash'
opts.args = ['--init-file', "#{init_fn}"]
if process.env['USER'] == 'root'
if not mesg.project_id? or mesg.project_id.length != 36
winston.debug("suspicious project_id (=#{mesg.project_id}) -- bailing")
return
winston.debug "start_session opts = #{to_json(opts)}"
async.series([
(cb) ->
module = __dirname + '/console_server_child'
if fs.existsSync(module + '.js')
module += '.js'
else
module += '.coffee'
child = child_process.fork(module, [])
socket.write_mesg('json', message.session_description(pid:child.pid))
misc_node.disable_mesg(socket)
child.send(opts, socket)
cb()
], (err) ->
if err
winston.debug("ERROR - #{err}")
)
handle_client = (socket, mesg) ->
try
switch mesg.event
when 'start_session', 'connect_to_session'
start_session(socket, mesg)
when 'send_signal'
switch mesg.signal
when 2
signal = 'SIGINT'
when 3
signal = 'SIGQUIT'
when 9
signal = 'SIGKILL'
else
throw("only signals 2 (SIGINT), 3 (SIGQUIT), and 9 (SIGKILL) are supported")
process.kill(mesg.pid, signal)
if mesg.id?
socket.write_mesg('json', message.signal_sent(id:mesg.id))
else
if mesg.id?
err = message.error(id:mesg.id, error:"Console server received an invalid mesg type '#{mesg.event}'")
socket.write_mesg('json', err)
catch e
winston.error "ERROR: '#{e}' handling message '#{to_json(mesg)}'"
server = net.createServer (socket) ->
winston.debug "PARENT: received connection"
misc_node.unlock_socket socket, secret_token, (err) ->
if not err
misc_node.enable_mesg(socket)
socket.on 'mesg', (type, mesg) ->
winston.debug "received control mesg #{to_json(mesg)}"
handle_client(socket, mesg)
start_server = (cb) ->
async.series([
(cb) ->
read_token(cb)
(cb) ->
server.listen(program.port ? 0, '127.0.0.1', cb)
(cb) ->
fs.writeFile(port_manager.port_file('console'), server.address().port, cb)
], (err) ->
if err
cb(err)
else
winston.info("listening on port #{server.address().port}")
cb()
)
program.usage('[?] [options]')
.option('--port <n>', 'TCP server port to listen on (default: 0 = os assigned)', ((n)->parseInt(n)), 0)
.option('--kucalc', "Running in the kucalc environment")
.parse(process.argv)
if program.kucalc
require('./kucalc').IN_KUCALC = true
start_server (err) ->
if err
winston.debug("failed to start console server")