/** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2017 bLAB ** $CREATED: 29-3-16 by sbosse. ** $VERSION: 1.4.24 ** ** $INFO: ** ** JAM Standalone Node VM with DOS networking ** ** $ENDOFINFO */ global.config={simulation:false,dos:true}; var Io = Require('com/io'); var Comp = Require('com/compat'); var Name = Require('com/pwgen'); var Net = Require('dos/network'); var Buf = Require('dos/buf'); var Sch = Require('dos/scheduler'); var Conn = Require('dos/connection'); var Rpc = Require('dos/rpc'); var Std = Require('dos/std'); var Router = Require('dos/router'); var util = require('util'); var assert = Comp.assert; var String = Comp.string; var Array = Comp.array; var Perv = Comp.pervasives; var Printf = Comp.printf; var Filename = Comp.filename; var Obj = Comp.obj; var Args = Comp.args; var Status = Net.Status; var Command = Net.Command; var Fs = require('fs'); var Dns = Require('dos/dns'); var Cs = Require('dos/capset'); var Getenv = Require('com/getenv'); var HostSrv = Require('dos/hostsrv'); var Run = Require('dos/run'); var RunSrv = Require('dos/runsrv'); var Esprima = Require('parser/esprima'); var Json = Require('jam/jsonfn'); var satelize = Require('dos/ext/satelize'); var Db = Require('db/db'); var Aios = Require('jam/aios'); var Dios = Require('dos/dios'); Run.current(Aios); var nameopts = {length:8, memorable:true, lowercase:true}; var options = { amp:false, aport:6000, bip:'localhost', bport:3001, broker:false, default:true, dip : 'localhost', domain: 'default', dports : [], env:{}, geo:undefined, hostname:Io.hostname(), hostport:undefined, http:false, keepalive:true, links:[], myip:'localhost', monitor:0, nodename:Name.generate(nameopts), // pre-guess onexit:false, scheduler:none, start:false, tcpnet:1, verbose:0, world:none }; var out = function (msg) { Io.out('[JAM] '+msg)}; var BROKER=Io.getenv('BROKER',''); if (BROKER!='') { var tokens=String.split(':',BROKER); options.broker=true; options.http=true; options.bip=tokens[0]; if (tokens.length==2) { options.bport=Perv.int_of_string(tokens[1]); } } var jam = function (options) { var main=this; this.options = options||{}; this.env=this.options.env; this.verbose=options.verbose||0; // Name of host platform (computer), e.g., DNS IP name or IP address this.hostname=options.hostname; // Name of the JAM node this.nodename=options.nodename; this.domain=options.domain; this.out=function (msg) { Io.out('[JAM '+main.nodename+'] '+msg)}; this.world = options.world||Aios.World.World([],{ id:this.hostname, classes:options.classes||[], verbose:options.verbose }); this.node = Aios.Node.Node({ id:this.nodename, out:this.out, position:{x:0,y:0}, verbose:options.verbose },true); this.world.addNode(this.node); this.run=false; this.looprun=none; this.broker=options.http||options.tcpnet; this.bport=options.bport||3001; this.bip=options.bip; this.options.privhostport=Net.uniqport(); this.options.pubhostport = Net.prv2pub(this.options.privhostport); this.scheduler = options.scheduler||Sch.TaskScheduler; this.network = options.network||Conn.setup(options,1); this.router = this.network.router; // this.router.log(2); // network.XX uses global scheduler this.rpc = this.network.rpc; this.std = this.network.std; this.dns = this.network.dns; this.cs = this.network.cs; this.dios = Dios.Dios(this.network.rpc,this.network.env); Aios.current.network=this.network; // Aios.options.verbose=1; // Register a DOS link-connection for agent and signal migration this.network.register(this.node); /* this.node.connections.dos = { // OLDCOMM send: function (text,dest,context) { send: function (msg) { // NEWCOMM var text=msg.agent||msg.signal; main.node.connections.dos.count += text.length; if (Obj.isObject(msg.to)) // cap { var stat; // This schedule block must by passed to the global (DOS) scheduler!! Sch.B([ function () { main.network.run.ps_migrate(msg.to,text,function (_stat) { stat=_stat; }); }, function () { if (stat!=Net.Status.STD_OK) { // context??? msg.context.error='Migration to server '+Net.Print.capability(msg.to)+' failed: '+Net.Print.status(stat); // We're still in the agent process context! Throw an error for this agent .. throw 'MOVE'; }; // kill ghost agent msg.context.process.finalize(); } ]); } else if (Obj.isString(msg.to)) { // path } }, status: function () { // TODO return main.network.status()==Net.Status.STD_OK; }, count:0 } */ this.hostsrv=none; // requires router init., created on initialization this.todo=[]; this.exit = []; this.amp=options.amp; this.aport=options.aport||6000; /* Install HOST/DNS tuple provider ** Expected patterns: ** DNS,path,? ** DOMAIN,? ** HOSTNAME,? */ this.node.ts.register(function (pat) { var stat,tuple; // Caching? if (pat.length<2) return none; switch (pat[0]) { case 'DNS': if (pat.length<3) return none; else Sch.B([ function () { main.dios.dir(pat[1],function (rows,_stat) { stat=_stat; if (stat==Status.STD_OK) tuple=[pat[0],pat[1],Comp.array.filtermap(rows,function (row) { if (row.stat==Status.STD_OK) return {name:row.name,cap:Net.Print.capability(row.cap)}; else return none; })]; }) }, function () { // console.log('>> '+Status.print(stat)); if (stat==Status.STD_OK) main.node.ts.checkwaiter(tuple); } ]); break; case 'HOSTNAME': return ['HOSTNAME',main.hostname]; case 'NODENAME': return ['NODENAME',main.nodename]; case 'DOMAIN': return ['DOMAIN','default']; } // console.log(Sch.GetCurrent()) return none; }); } // Import analyzer class... var JamAnal = Require('jam/analyzer'); JamAnal.current(Aios); jam.prototype.analyze=JamAnal.jamc.prototype.analyze; jam.prototype.syntax=JamAnal.jamc.prototype.syntax; // Run server extension RunSrv.current(Aios); jam.prototype.request=RunSrv.run.prototype.request; /** Add an agent class template {:} to the JAM world * */ jam.prototype.addClass = function (templates) { for (var p in templates) { if (this.options.verbose>0) this.out((this.world.classes[p]?'Updating':'Adding')+' agent class template '+p+'.'); this.world.classes[p]=[ Aios.Code.makeSandbox(templates[p],0), Aios.Code.makeSandbox(templates[p],1), Aios.Code.makeSandbox(templates[p],2), Aios.Code.makeSandbox(templates[p],3) ] } }; /** Create and start an agent from class ac with arguments. * */ jam.prototype.create = function (ac,args) { var node=this.node; var agent=none; if (this.world.classes[ac]) agent = Aios.Code.createOn(node,this.world.classes[ac],args); else this.out('create: Cannot find agent class '+ac); if (agent) { if (this.options.verbose>0) this.out('Created agent '+agent.agent.id+' from class template(s) '+ac); agent.agent.ac=ac; return agent.agent.id; } else return none; } /** Initialize JAMDOS and create/start host server * */ jam.prototype.init=function () { var self=this, stat, cs, csrow, csdir, i, names=[]; satelize.satelize({}, function(err, geoData) { // process err if (err != undefined) { self.out('GEO Location failed: '+err) } else if (geoData) { try { var obj = JSON.parse(geoData); self.out('GEO Location (lati=' + obj.lat + ', long=' + obj.lon + ')'); options.geo=obj; } catch (e) { if (options.verbose>1) self.out('GEO Location failed: '+e+',\n'+geoData); else self.out('GEO Location not available: '+e); } } }); for(i=0;i<10;i++) names.push(Name.generate(nameopts)); this.todo.push([ function () { // Append default DNS and root directory var dnscs; if (self.env.rootdir) { dnscs = self.env.rootdir; self.hostsrv.append('/dns/default', dnscs, function (_stat) {stat=_stat}); if (stat != Status.STD_OK) self.out('Cannot append ' + 'dns/default' + ': ' + Status.print(stat)); self.hostsrv.append('/root', dnscs, function (_stat) {stat=_stat}); if (stat != Status.STD_OK) self.out('Cannot append ' + 'root' + ': ' + Status.print(stat)); } }, function () { // Check for default domain ... self.hostsrv.lookup('/root/domains/'+self.domain, function (_stat,_cs) { if (self.options.verbose>0) self.out('lookup /root/domains/'+self.domain+': '+Status.print(_stat)) stat=_stat; csdir=_cs; }); }, function () { if (stat==Status.STD_OK) Sch.ScheduleLoop( function () { return names.length>0; }, [ function () { // Check for node in default domain ... self.hostsrv.lookup('/root/domains/'+self.domain+'/'+self.nodename, function (_stat,_cs) { if (self.options.verbose>0) self.out('lookup /root/domains/'+self.domain+'/'+self.nodename+': '+Status.print(_stat)) stat=_stat; cs=_cs; }); }, function () { // Try to remove remains of node in default domain (2) if (stat==Status.RPC_FAILURE) { self.hostsrv.delete('/root/domains/'+self.domain+'/'+self.nodename, function (_stat) { if (self.options.verbose>0) self.out('delete /root/domains/'+self.domain+'/'+self.nodename+': '+Status.print(_stat)) stat=_stat; } ); } }, function () { // Try to register node in default domain (3) if (stat==Status.STD_NOTFOUND || stat==Status.STD_OK) { self.hostsrv.append('/root/domains/'+self.domain+'/'+self.nodename, self.hostsrv.hostcap, function (_stat) { self.out('Published /domains/'+self.domain+'/'+self.nodename+': '+Status.print(_stat)) stat=_stat; } ); } }, function () { if (stat==Status.STD_OK) names=[]; // Done else { self.nodename=Array.head(names); names=Array.tail(names); } } ], [ function () { self.out('My final domain name is '+self.hostname+'.'+self.nodename); } ] ); } ]); B(Comp.array.flatten(this.todo)); // if (0) this.exit.push([ function () { self.hostsrv.delete('/root/domains/'+self.domain+'/'+self.nodename, function (_stat) { self.out('Unpublished /root/domains/'+self.domain+'/'+self.nodename+': '+Status.print(_stat)) stat=_stat; } ); } ]); this.hostsrv=HostSrv.HostServer(this.scheduler, this.rpc, this.options, 'JAM.'+this.hostname+'.'+this.nodename, this.env); // Register our request handler and make a link back to our class handler this.hostsrv.register(this.request); this.hostsrv.addClass=function (ac) { self.addClass(ac); }; this.hostsrv.getclass=function (classname) { return self.world.classes[classname]; }; this.hostsrv.receive=function (code,start) { self.node.receive(code,start); }; if (options.geo) myJam.hostsrv.set_geo(options.geo); if (this.amp) { this.startamp(); } } /** Read and compile agent class templates from file * */ jam.prototype.readclass = function (file) { var ac, text, modu, p, regex1, ast=null, all=null, off=null; if (this.options.verbose>0) this.out('Looking up agent class template(s) from '+file); modu=Require(file); if (Comp.obj.isEmpty(modu)) { if (this.options.verbose>0) this.out('Reading agent class template(s) from file '+file); if (Comp.string.get(file,0)!='/') file = './'+file; modu=require(file); all=Io.read_file(file); ast=Esprima.parse(all, { tolerant: true, loc:true }); } for (p in modu) { ac={}; ac[p]=modu[p]; if (all) off=this.syntax.find(ast,'VariableDeclarator',p); if (off && off.loc) this.syntax.offset=off.loc.start.line-1; content = 'var ac = '+modu[p]; syntax = Esprima.parse(content, { tolerant: true, loc:true }); this.analyze(syntax,{classname:p,level:2}); text=Json.stringify(ac); regex1= /this\.next=([a-zA-Z0-9_]+)/; text=text.replace(regex1,"this.next='$1'"); // console.log(text); ac=Json.parse(text,{}); this.addClass(ac); this.syntax.offset=0; } }; /** Start the JAM scheduler * */ jam.prototype.start=function () { var proc,self=this; this.run=true; // Start up the network .. this.network.init(function (stat) {self.network.start()}); } /** Start AMP service port */ jam.prototype.startamp = function () { var self=this; var ip = 'localhost'; this.out('Starting AMP server on port '+this.aport); this.amp = Aios.Chan.Amp({rcv:ip+':'+this.aport,snd_ip:ip,verbose:this.options.verbose}); this.amp.receiver(function (handler) { var code,agentid,stat,text; if (!handler) return; if (self.options.verbose>0) { self.out('AMP: got request:'); console.log(handler) }; switch (handler.cmd) { case Net.Command.PS_EXEC: code = Buf.buf_get_string(handler.buf); // console.log(code); // console.log(myJam.amp.url(handler.remote)) self.node.receive(code,true); break; case Net.Command.PS_MIGRATE: code = Buf.buf_get_string(handler.buf); // console.log(code); // console.log(myJam.amp.url(handler.remote)) self.node.receive(code,false); break; case Net.Command.PS_STUN: agentid = Buf.buf_get_string(handler.buf); stat=Aios.kill(agentid); if (stat) self.out('Agent '+agentid+' terminated.'); break; case Net.Command.PS_WRITE: text = Buf.buf_get_string(handler.buf); ac=Json.parse(text,{}); // console.log(ac) self.addClass(ac); break; } }); } /** Stop the JAM scheduler * */ jam.prototype.stop=function () { this.run=false; this.out('Stopping ..'); if (this.looprun) clearTimeout(this.looprun); } var Jam = function(options) { var obj = new jam(options); return obj; }; // --------------------------------- // function usage() { var msg=''; msg += ' jam [options]'+NL; msg += ' -c .js : Load an agent class template from file'+NL; msg += ' -r [,,..] : Create an agent from a class template'+NL; msg += ' -s : Start scheduler loop'+NL; msg += ' -d -default : Ask broker for default DNS'+NL; msg += ' -broker : Broker HTTP IP Address and optional Port (default: 3001)'+NL; msg += ' -A : Start AMP service on specified port'+NL; msg += ' [-H -T -T2] Enable HTTP or TCPNET Broker connection'+NL; msg += ' T: TCPIP, 1-ch H:HTTP T2: TCPIP, 2-ch'+NL; msg += ' H: bport, T: bport+100'+NL; msg += ' (Default: -T)'+NL; msg += ' [-broker ] Broker URL (Default: '+options.bip+': HTTP '+options.bport+' TCPNET '+(options.bport+100)+')'+NL; msg += ' [-D ] UDP Server port'+NL; msg += ' [-L > var scheduler = Sch.TaskScheduler(); options.scheduler = scheduler; var myJamWorld = Aios.World.World([],{ classes:options.classes||[], id:options.hostname, scheduler:scheduler, verbose:options.verbose }); options.world=myJamWorld; var myJam = Jam(options); Aios.current.scheduler=scheduler; // << Comp.args.parse(Io.getargs(),[ ['-c',1,function (file) { if (global.DEBUG) myJam.readclass(file); else try { myJam.readclass(file) } catch (e) { myJam.out('Compilation failed: '+e+'.'); Io.printstack(e) Io.exit(); } }], ['-r',2,function (ac,args) { try { if (args.length < 2) args='[]'; var _args = Comp.array.map(Comp.string.split(',',Comp.string.trim(args,1,1)),function (arg) { try {var num=Number(arg); if (isNaN(num)) return arg; else return num;} catch (e) {return arg } }); myJam.create(ac,_args) } catch (e) { myJam.out('Failed to start agent '+ac+' '+args); } }], [['-root'],1,function(val) { cap = Net.Parse.capability(val, 0); if (cap != undefined) { myJam.env.rootdir = this.cs.cs_singleton(cap.cap); if (myJam.env.workdir == undefined) myJam.env.workdir = myJam.env.rootdir; } else { /* ** Capabiliy file path? */ cap = Net.cap_of_file(val); myJam.env.rootdir = myJam.cs.cs_singleton(cap); if (myJam.env.workdir == undefined) myJam.env.workdir = myJam.env.rootdir; if (cap) myJam.out ('Got root capability from file '+val); } }], [['+root'],1,function(val) { var dnscap,dnscs; dnscap = Net.Parse.capability(val, 0); if (dnscap == undefined) { /* ** Capabiliy file path? */ dnscap = Net.cap_of_file(val); if (dnscap) myJam.out ('Got root capability from file '+val); } else dnscap=dnscap.cap; if (dnscap != undefined) { myJam.todo.push([ function () { var name = Net.Print.port(dnscap.cap_port); dnscs = myJam.cs.cs_singleton(dnscap); myJam.out('Append DNS ROOT: ' + Net.Print.capability(dnscap)); myJam.hostsrv.append('dns/'+name, dnscs, function (_stat){stat=_stat}); if (stat != Status.STD_OK) myJam.out('Cannot append dns/' + name + ': ' + Status.print(stat)); hostsrv.append('dns/default', dnscs, function (_stat){stat=_stat}); if (stat != Status.STD_OK) myJam.out('Cannot append dns/default: ' + Status.print(stat)); }, function () { if (dnscs != undefined && myJam.env.afs_def==undefined) { myJam.dns.dns_getdefafs(dnscs,function(_stat,_cap) { maJam.out('Default AFS: '+Status.print(_stat)+' is '+ Net.Print.capability(_cap)); if (_stat==Status.STD_OK) { options.env.afs_def=_cap; var name=Net.Print.port(_cap.cap_port); var afscs = myJam.cs.cs_singleton(_cap); myJam.out('Append AFS: ' + Net.Print.capability(_cap)); myJam.hostsrv.append('afs/'+name,afscs,function (stat) { if (stat!=Status.STD_OK) myJam.out('Cannot append afs/'+name+': '+Status.print(stat)); }); } }) } } ]) } else { myJam.out('DNS_ROOT: invalid capability (or file) ' + val); } }], [['-host'],1,function(val) { var port = Net.Parse.port(val, 0); if (port != undefined) { options.hostport = port.port; myJam.todo.push([ function () { myJam.dns.dns_getrootcap(options.hostport, function (_stat, _cap) { stat = _stat; if (stat==Status.STD_OK) { myJam.env.rootdir = myJam.cs.cs_singleton(_cap); } }) }, function () { if (stat != Status.STD_OK) { maJam.out('DNS_ROOT: DNS_GETROOT failed for host ' + Net.Print.port(options.hostport) + ': ' + Status.print(stat)); } else { myJam.out('DNS ROOT: '+Net.Print.capability(myJam.cs.cs_to_cap(myJam.env.rootdir))); if (env.workdir == undefined) myJam.env.workdir = myJam.env.rootdir; } } ]); } }] ]); var interrupt=0; //if (0) process.on('SIGINT', function () { myJam.out('Got SIGINT ..'); interrupt++; if (interrupt>2) process.exit(2); myJam.exit.push([ function () {process.exit(2);} ]); B(Comp.array.flatten(myJam.exit)); }); if (!options.onexit && options.start) { scheduler.Init(); scheduler.Run(); myJamWorld.init(); myJamWorld.start(); myJam.init(); myJam.start(); }