require('coffee-cache').setCacheDir("#{process.env.HOME}/.coffee")
BUG_COUNTER = 0
process.addListener "uncaughtException", (err) ->
winston.debug("BUG ****************************************************************************")
winston.debug("Uncaught exception: " + err)
winston.debug(err.stack)
winston.debug("BUG ****************************************************************************")
if console? and console.trace?
console.trace()
BUG_COUNTER += 1
exports.get_bugs_total = ->
return BUG_COUNTER
path = require('path')
async = require('async')
fs = require('fs')
os = require('os')
net = require('net')
uuid = require('uuid')
winston = require('winston')
request = require('request')
program = require('commander')
winston.remove(winston.transports.Console)
winston.add(winston.transports.Console, {level: 'debug', timestamp:true, colorize:true})
require('coffee-script/register')
message = require('smc-util/message')
misc = require('smc-util/misc')
smc_version = require('smc-util/smc-version')
misc_node = require('smc-util-node/misc_node')
{to_json, from_json, defaults, required} = require('smc-util/misc')
kucalc = require('./kucalc')
raw_server = require('./raw_server')
print_to_pdf = require('./print_to_pdf')
secret_token = require('./secret_token')
console_session_manager = require('./console_session_manager')
console_sessions = new console_session_manager.ConsoleSessions()
port_manager = require('./port_manager')
read_write_files = require('./read_write_files')
jupyter_manager = require('./jupyter_manager')
{exec_shell_code} = require('./exec_shell_code')
blobs = require('./blobs')
{Client} = require('./client')
if process.env.SMC_LOCAL_HUB_HOME?
process.env.HOME = process.env.SMC_LOCAL_HUB_HOME
if not process.env.SMC?
process.env.SMC = path.join(process.env.HOME, '.smc')
SMC = process.env.SMC
process.chdir(process.env.HOME)
DATA = path.join(SMC, 'local_hub')
SAGE = path.join(process.env.HOME, '.sage')
for directory in [SMC, DATA, SAGE]
if not fs.existsSync(directory)
fs.mkdirSync(directory)
CONFPATH = exports.CONFPATH = misc_node.abspath(DATA)
common = require('./common')
json = common.json
INFO = undefined
hub_client = undefined
init_info_json = (cb) ->
winston.debug("Writing 'info.json'")
filename = "#{SMC}/info.json"
if kucalc.IN_KUCALC and process.env.COCALC_PROJECT_ID? and process.env.COCALC_USERNAME?
project_id = process.env.COCALC_PROJECT_ID
username = process.env.COCALC_USERNAME
else
v = process.env.HOME.split('/')
project_id = v[v.length-1]
username = project_id.replace(/-/g,'')
if process.env.SMC_HOST?
host = process.env.SMC_HOST
else if os.hostname() == 'sagemathcloud'
host = 'localhost'
else
nics = require('os').networkInterfaces()
mynic = nics.eth0 ? nics.ens4
host = mynic?[0].address
base_url = process.env.SMC_BASE_URL ? ''
port = 22
INFO =
project_id : project_id
location : {host:host, username:username, port:port, path:'.'}
base_url : base_url
exports.client = hub_client = new Client(INFO.project_id)
fs.writeFile(filename, misc.to_json(INFO), cb)
connect_to_session = (socket, mesg) ->
winston.debug("connect_to_session -- type='#{mesg.type}'")
switch mesg.type
when 'console'
console_sessions.connect(socket, mesg)
else
err = message.error(id:mesg.id, error:"Unsupported session type '#{mesg.type}'")
socket.write_mesg('json', err)
terminate_session = (socket, mesg) ->
cb = (err) ->
if err
mesg = message.error(id:mesg.id, error:err)
socket.write_mesg('json', mesg)
sid = mesg.session_uuid
if console_sessions.session_exists(sid)
console_sessions.terminate_session(sid, cb)
else
cb()
handle_mesg = (socket, mesg, handler) ->
if hub_client.handle_mesg(mesg, socket)
return
switch mesg.event
when 'connect_to_session', 'start_session'
socket.removeListener('mesg', handler)
connect_to_session(socket, mesg)
when 'jupyter_port'
jupyter_manager.jupyter_port(socket, mesg)
when 'project_exec'
exec_shell_code(socket, mesg)
when 'read_file_from_project'
read_write_files.read_file_from_project(socket, mesg)
when 'write_file_to_project'
read_write_files.write_file_to_project(socket, mesg)
when 'print_to_pdf'
print_to_pdf.print_to_pdf(socket, mesg)
when 'send_signal'
misc_node.process_kill(mesg.pid, mesg.signal)
if mesg.id?
socket.write_mesg('json', message.signal_sent(id:mesg.id))
when 'terminate_session'
terminate_session(socket, mesg)
when 'save_blob'
blobs.handle_save_blob_message(mesg)
when 'error'
winston.debug("ERROR from hub: #{mesg.error}")
when 'hello'
winston.debug("hello from hub -- sending back our version = #{smc_version.version}")
socket.write_mesg('json', message.version(version:smc_version.version))
else
if mesg.id?
err = message.error(id:mesg.id, error:"Local hub failed to handle mesg of type '#{mesg.event}'")
socket.write_mesg('json', err)
else
winston.debug("Dropping unknown mesg type '#{mesg.event}'")
start_tcp_server = (secret_token, port, cb) ->
if not secret_token?
cb("secret token must be defined")
return
winston.info("starting tcp server: project <--> hub...")
server = net.createServer (socket) ->
winston.debug("received new connection")
socket.on 'error', (err) ->
winston.debug("socket error - #{err}")
misc_node.unlock_socket socket, secret_token, (err) ->
if err
winston.debug(err)
else
socket.id = uuid.v4()
misc_node.enable_mesg(socket)
handler = (type, mesg) ->
if mesg.event not in ['connect_to_session', 'start_session']
hub_client.active_socket(socket)
if type == "json"
handle_mesg(socket, mesg, handler)
socket.on('mesg', handler)
port_file = misc_node.abspath("#{DATA}/local_hub.port")
server.listen port, '0.0.0.0', (err) ->
if err
winston.info("tcp_server failed to start -- #{err}")
cb(err)
else
winston.info("tcp_server listening 0.0.0.0:#{server.address().port}")
fs.writeFile(port_file, server.address().port, cb)
start_server = (tcp_port, raw_port, cb) ->
the_secret_token = undefined
if program.console_port
console_sessions.set_port(program.console_port)
async.series([
(cb) ->
init_info_json(cb)
(cb) ->
fs.writeFile(misc_node.abspath("#{DATA}/local_hub.pid"), "#{process.pid}", cb)
(cb) ->
secret_token.init_secret_token (err, token) ->
if err
cb(err)
else
the_secret_token = token
console_sessions.set_secret_token(token)
cb()
(cb) ->
start_tcp_server(the_secret_token, tcp_port, cb)
(cb) ->
raw_server.start_raw_server
project_id : INFO.project_id
base_url : INFO.base_url
host : process.env.SMC_PROXY_HOST ? INFO.location.host ? 'localhost'
data_path : DATA
home : process.env.HOME
port : raw_port
logger : winston
cb : cb
], (err) ->
if err
winston.debug("ERROR starting server -- #{err}")
else
winston.debug("Successfully started servers.")
cb(err)
)
program.usage('[?] [options]')
.option('--tcp_port <n>', 'TCP server port to listen on (default: 0 = os assigned)', ((n)->parseInt(n)), 0)
.option('--raw_port <n>', 'RAW server port to listen on (default: 0 = os assigned)', ((n)->parseInt(n)), 0)
.option('--console_port <n>', 'port to find console server on (optional; uses port file if not given); if this is set we assume some other system is managing the console server and do not try to start it -- just assume it is listening on this port always', ((n)->parseInt(n)), 0)
.option('--kucalc', "Running in the kucalc environment")
.option('--test_firewall', 'Abort and exit w/ code 99 if internal GCE information is accessible')
.parse(process.argv)
if program.kucalc
kucalc.IN_KUCALC = true
if program.test_firewall
kucalc.init_gce_firewall_test(winston)
start_server program.tcp_port, program.raw_port, (err) ->
if err
process.exit(1)