diff --git a/js/top/jamdos.js b/js/top/jamdos.js new file mode 100644 index 0000000..199bc19 --- /dev/null +++ b/js/top/jamdos.js @@ -0,0 +1,749 @@ +/** + ** ============================== + ** 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(); +}