func (rpcs *rpcRex) HandleCall(message *etf.Term, from *etf.Tuple) (reply *etf.Term) { nLog("REX: HandleCall: %#v, From: %#v", *message, *from) var replyTerm etf.Term valid := false switch req := (*message).(type) { case etf.Tuple: if len(req) > 0 { switch act := req[0].(type) { case etf.Atom: if string(act) == "call" { valid = true if fun, ok := rpcs.callMap[modFun{string(req[1].(etf.Atom)), string(req[2].(etf.Atom))}]; ok { replyTerm = fun(req[3].(etf.List)) } else { replyTerm = etf.Term(etf.Tuple{etf.Atom("badrpc"), etf.Tuple{etf.Atom("EXIT"), etf.Tuple{etf.Atom("undef"), etf.List{etf.Tuple{req[1], req[2], req[3], etf.List{}}}}}}) } } } } } if !valid { replyTerm = etf.Term(etf.Tuple{etf.Atom("badrpc"), etf.Atom("unknown")}) } reply = &replyTerm return }
// shutdown request // takes no arguments func (r *Relay) rpcShutdown(terms etf.List) etf.Term { if len(terms) != 0 { return etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("badarith")}) } r.shutdown() r.shutdownChan <- struct{}{} return etf.Term(etf.Atom("ok")) }
// HandleCall handles incoming messages from `gen_server:call/2`, if returns non-nil term, // then calling process have reply // Call `gen_server:call({go_srv, gonode@localhost}, Message)` at Erlang node func (gs *gonodeSrv) HandleCall(message *etf.Term, from *etf.Tuple) (reply *etf.Term) { log.Printf("GO_SRV: HandleCall: %#v, From: %#v", *message, *from) // Just create new term tuple where first element is atom 'ok', second 'go_reply' and third is original message replyTerm := etf.Term(etf.Tuple{etf.Atom("ok"), etf.Atom("go_reply"), *message}) reply = &replyTerm return }
func main() { // Parse CLI flags flag.Parse() setup_logging() write_pid() log.Println("node started") // Initialize new node with given name and cookie enode := node.NewNode(NodeName, Cookie) // Allow node be available on EpmdPort port err = enode.Publish(EpmdPort) if err != nil { log.Fatalf("Cannot publish: %s", err) } // Create channel to receive message when main process should be stopped completeChan := make(chan bool) // Initialize new instance of srv structure which implements Process behaviour eSrv := new(srv) // Spawn process with one arguments enode.Spawn(eSrv, completeChan) // RPC if EnableRPC { // Create closure eClos := func(terms etf.List) (r etf.Term) { r = etf.Term(etf.Tuple{etf.Atom("gonode"), etf.Atom("reply"), len(terms)}) return } // Provide it to call via RPC with `rpc:call(gonode@localhost, go_rpc, call, [as, qwe])` err = enode.RpcProvide("go_rpc", "call", eClos) if err != nil { log.Printf("Cannot provide function to RPC: %s", err) } } // Wait to stop <-completeChan log.Println("node finished") return }
func (r *Relay) createDevice(fsName, deviceName string, isSensor, isAffector bool) (etf.Atom, error) { fs, ok := r.fss[fsName] if !ok { return etf.Atom(""), errors.New("no such fs") } device, err := fs.CreateDevice(deviceName, isSensor, isAffector) if err != nil { return etf.Atom(""), err } dp := newDeviceProc(device) fullName := deviceName + "@" + fsName r.node.Spawn(dp, fullName) return etf.Atom(fullName), nil }
func (n *Node) registrator() { for { select { case req := <-n.registry.storeChan: // FIXME: make proper allocation, now it just stub var id uint32 = 0 for k, _ := range n.channels { if k.Id >= id { id = k.Id + 1 } } var pid etf.Pid pid.Node = etf.Atom(n.FullName) pid.Id = id pid.Serial = 0 // FIXME pid.Creation = byte(n.Creation) n.channels[pid] = req.channels req.replyTo <- pid case req := <-n.registry.regNameChan: n.registered[req.name] = req.pid case req := <-n.registry.unregNameChan: delete(n.registered, req.name) } } }
func (currNode *Node) handleTerms(c net.Conn, wchan chan []etf.Term, terms []etf.Term) { nLog("Node terms: %#v", terms) if len(terms) == 0 { return } switch t := terms[0].(type) { case etf.Tuple: if len(t) > 0 { switch act := t.Element(1).(type) { case int: switch act { case REG_SEND: if len(terms) == 2 { currNode.RegSend(t.Element(2), t.Element(4), terms[1]) } else { nLog("*** ERROR: bad REG_SEND: %#v", terms) } default: nLog("Unhandled node message (act %d): %#v", act, t) } case etf.Atom: switch act { case etf.Atom("$go_set_node"): nLog("SET NODE %#v", t) currNode.neighbors[t[1].(etf.Atom)] = nodeConn{conn: c, wchan: wchan} } default: nLog("UNHANDLED ACT: %#v", t.Element(1)) } } } }
// Init initializes process state using arbitrary arguments func (gs *gonodeSrv) Init(args ...interface{}) { log.Printf("GO_SRV: Init: %#v", args) // Self-registration with name go_srv gs.Node.Register(etf.Atom("go_srv"), gs.Self) // Store first argument as channel gs.completeChan = args[0].(chan bool) }
func (rpcs *rpcRex) HandleCall(message *etf.Term, from *etf.Tuple) (reply *etf.Term) { nLog("REX: HandleCall: %#v, From: %#v", *message, *from) switch req := (*message).(type) { case etf.Tuple: if len(req) > 0 { switch act := req[0].(type) { case etf.Atom: if string(act) == "call" { nLog("RPC CALL: Module: %#v, Function: %#v, Args: %#v, GroupLeader: %#v", req[1], req[2], req[3], req[4]) replyTerm := etf.Term(etf.Tuple{req[1], req[2]}) reply = &replyTerm } } } } if reply == nil { replyTerm := etf.Term(etf.Tuple{etf.Atom("badrpc"), etf.Atom("unknown")}) reply = &replyTerm } return }
// Send sends message to destination process withoud source func (currNode *Node) Send(to etf.Pid, message etf.Term) { nLog("Send: %#v, %#v", to, message) if string(to.Node) == currNode.FullName { nLog("Send to local node") pcs := currNode.channels[to] pcs.in <- message } else { nLog("Send to remote node: %#v, %#v", to, currNode.neighbors[to.Node]) msg := []etf.Term{etf.Tuple{SEND, etf.Atom(""), to}, message} currNode.neighbors[to.Node].wchan <- msg } }
func main() { // Initialize new node with given name and cookie enode := node.NewNode("gonode@localhost", "123") // Allow node be available on 5588 port err := enode.Publish(5588) if err != nil { log.Fatalf("Cannot publish: %s", err) } // Create channel to receive message when main process should be stopped completeChan := make(chan bool) // Initialize new instance of gonodeSrv structure which implements Process behaviour eSrv := new(gonodeSrv) // Spawn process with one arguments enode.Spawn(eSrv, completeChan) // RPC // Create closure eClos := func(terms etf.List) (r etf.Term) { r = etf.Term(etf.Tuple{etf.Atom("gonode"), etf.Atom("reply"), len(terms)}) return } // Provide it to call via RPC with `rpc:call(gonode@localhost, go_rpc, call, [as, qwe])` err = enode.RpcProvide("go_rpc", "call", eClos) if err != nil { log.Printf("Cannot provide function to RPC: %s", err) } // Wait to stop <-completeChan return }
func (nk *netKernel) HandleCall(message *etf.Term, from *etf.Tuple) (reply *etf.Term) { nLog("NET_KERNEL: HandleCall: %#v, From: %#v", *message, *from) switch t := (*message).(type) { case etf.Tuple: if len(t) == 2 { switch tag := t[0].(type) { case etf.Atom: if string(tag) == "is_auth" { nLog("NET_KERNEL: is_auth: %#v", t[1]) replyTerm := etf.Term(etf.Atom("yes")) reply = &replyTerm } } } } return }
// rpc funcs for erlang nodes // args must be in form `[Name:string]` // returns either `ok` or `{error, Reason}` func (r *Relay) rpcCreateFS(terms etf.List) etf.Term { if len(terms) != 1 { return etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("badarith")}) } t := terms[0] if _, ok := t.(string); !ok { return etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("badarg")}) } name := t.(string) err := r.createFS(name) if err != nil { println("cannot create fs: ", err) return etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("failed-to-create-fs")}) } return etf.Term(etf.Atom("ok")) }
func runNode() (enode *node.Node) { enode = node.NewNode(nodeName, nodeCookie) err := enode.Publish(nodePort) if err != nil { log.Printf("Cannot publish: %s", err) enode = nil } eSrv := new(eclusSrv) enode.Spawn(eSrv) eClos := func(terms etf.List) (r etf.Term) { r = etf.Term(etf.Tuple{etf.Atom("enode"), len(terms)}) return } err = enode.RpcProvide("enode", "lambda", eClos) if err != nil { log.Printf("Cannot provide function to RPC: %s", err) } return }
// affecter data request // msg must be atom `req` func (d *deviceProc) HandleCall(msg *etf.Term, from *etf.Tuple) *etf.Term { println("handling call") if atom, ok := (*msg).(etf.Atom); !ok { t := etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("badarg")}) return &t } else { if string(atom) != "req" { t := etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("badarg")}) return &t } buf := d.device.Affect() if buf == nil { t := etf.Term(etf.Tuple{etf.Atom("ok"), etf.Atom("nil")}) return &t } else { t := etf.Term(etf.Tuple{etf.Atom("ok"), buf}) return &t } } }
// HandleCast // Call `gen_server:cast({go_srv, gonode@localhost}, stop)` at Erlang node to stop this Go-node func (gs *srv) HandleCast(message *etf.Term) { log.Printf("HandleCast: %#v", *message) // Check type of message switch req := (*message).(type) { case etf.Tuple: if len(req) > 0 { switch act := req[0].(type) { case etf.Atom: if string(act) == "ping" { var self_pid etf.Pid = gs.Self gs.Node.Send(req[1].(etf.Pid), etf.Tuple{etf.Atom("pong"), etf.Pid(self_pid)}) } } } case etf.Atom: // If message is atom 'stop', we should say it to main process if string(req) == "stop" { gs.completeChan <- true } } }
func (currNd *NodeDesc) ReadMessage(c net.Conn) (ts []etf.Term, err error) { sendData := func(headerLen int, data []byte) (int, error) { reply := make([]byte, len(data)+headerLen) if headerLen == 2 { binary.BigEndian.PutUint16(reply[0:headerLen], uint16(len(data))) } else { binary.BigEndian.PutUint32(reply[0:headerLen], uint32(len(data))) } copy(reply[headerLen:], data) dLog("Write to enode: %v", reply) return c.Write(reply) } switch currNd.state { case HANDSHAKE: var length uint16 if err = binary.Read(c, binary.BigEndian, &length); err != nil { return } msg := make([]byte, length) if _, err = io.ReadFull(c, msg); err != nil { return } dLog("Read from enode %d: %v", length, msg) switch msg[0] { case 'n': sn := currNd.read_SEND_NAME(msg) // Statuses: ok, nok, ok_simultaneous, alive, not_allowed sok := currNd.compose_SEND_STATUS(sn, true) _, err = sendData(2, sok) if err != nil { return } rand.Seed(time.Now().UTC().UnixNano()) currNd.challenge = rand.Uint32() // Now send challenge challenge := currNd.compose_SEND_CHALLENGE(sn) sendData(2, challenge) if err != nil { return } case 'r': sn := currNd.remote ok := currNd.read_SEND_CHALLENGE_REPLY(sn, msg) if ok { challengeAck := currNd.compose_SEND_CHALLENGE_ACK(sn) sendData(2, challengeAck) if err != nil { return } dLog("Remote: %#v", sn) ts = []etf.Term{etf.Term(etf.Tuple{etf.Atom("$go_set_node"), etf.Atom(sn.Name)})} } else { err = errors.New("bad handshake") return } } case CONNECTED: var length uint32 if err = binary.Read(c, binary.BigEndian, &length); err != nil { return } if length == 0 { dLog("Keepalive") sendData(4, []byte{}) return } r := &io.LimitedReader{c, int64(length)} if currNd.flag.isSet(DIST_HDR_ATOM_CACHE) { var ctl, message etf.Term if err = currNd.readDist(r); err != nil { break } if ctl, err = currNd.readCtl(r); err != nil { break } dLog("READ CTL: %#v", ctl) if message, err = currNd.readMessage(r); err != nil { break } dLog("READ MESSAGE: %#v", message) ts = append(ts, ctl, message) } else { msg := make([]byte, 1) if _, err = io.ReadFull(r, msg); err != nil { return } dLog("Read from enode %d: %#v", length, msg) switch msg[0] { case 'p': ts = make([]etf.Term, 0) for { var res etf.Term if res, err = currNd.readTerm(r); err != nil { break } ts = append(ts, res) dLog("READ TERM: %#v", res) } if err == io.EOF { err = nil } default: _, err = ioutil.ReadAll(r) } } } return }
func (gns *globalNameServer) HandleCall(message *etf.Term, from *etf.Tuple) (reply *etf.Term) { nLog("GLOBAL_NAME_SERVER: HandleCall: %#v, From: %#v", *message, *from) replyTerm := etf.Term(etf.Atom("reply")) reply = &replyTerm return }
// ProcessLoop executes during whole time of process life. // It receives incoming messages from channels and handle it using methods of behaviour implementation func (gs *GenServerImpl) ProcessLoop(pcs procChannels, pd Process, args ...interface{}) { pd.(GenServer).Init(args...) //pcs.ctl <- etf.Tuple{etf.Atom("$go_ctl"), etf.Tuple{etf.Atom("control-message"), etf.Atom("example")}} defer func() { if r := recover(); r != nil { // TODO: send message to parent process log.Printf("GenServer recovered: %#v", r) } }() for { var message etf.Term var fromPid etf.Pid select { case msg := <-pcs.in: message = msg case msgFrom := <-pcs.inFrom: message = msgFrom[1] fromPid = msgFrom[0].(etf.Pid) case ctlMsg := <-pcs.ctl: switch m := ctlMsg.(type) { case etf.Tuple: switch mtag := m[0].(type) { case etf.Atom: switch mtag { case etf.Atom("$go_ctl"): nLog("Control message: %#v", m) default: nLog("Unknown message: %#v", m) } default: nLog("Unknown message: %#v", m) } default: nLog("Unknown message: %#v", m) } continue } nLog("Message from %#v", fromPid) switch m := message.(type) { case etf.Tuple: switch mtag := m[0].(type) { case etf.Atom: switch mtag { case etf.Atom("$go_ctl"): nLog("Control message: %#v", message) case etf.Atom("$gen_call"): fromTuple := m[1].(etf.Tuple) reply := pd.(GenServer).HandleCall(&m[2], &fromTuple) if reply != nil { gs.Reply(&fromTuple, reply) } case etf.Atom("$gen_cast"): pd.(GenServer).HandleCast(&m[1]) default: pd.(GenServer).HandleInfo(&message) } default: nLog("mtag: %#v", mtag) pd.(GenServer).HandleInfo(&message) } default: nLog("m: %#v", m) pd.(GenServer).HandleInfo(&message) } } }
func (es *eclusSrv) Init(args ...interface{}) { log.Printf("ECLUS_SRV: Init: %#v", args) es.Node.Register(etf.Atom("eclus"), es.Self) }
func (rpcs *rpcRex) Init(args ...interface{}) { nLog("REX: Init: %#v", args) rpcs.Node.Register(etf.Atom("rex"), rpcs.Self) }
func (es *eclusSrv) HandleCall(message *etf.Term, from *etf.Tuple) (reply *etf.Term) { log.Printf("ECLUS_SRV: HandleCall: %#v, From: %#v", *message, *from) replyTerm := etf.Term(etf.Tuple{etf.Atom("ok"), etf.Atom("eclus_reply"), *message}) reply = &replyTerm return }
// gen_server behavior func (d *deviceProc) Init(args ...interface{}) { println("handling init") d.Node.Register(etf.Atom(args[0].(string)), d.Self) // initialization done before spawn }
func (nk *netKernel) Init(args ...interface{}) { nLog("NET_KERNEL: Init: %#v", args) nk.Node.Register(etf.Atom("net_kernel"), nk.Self) }
func (rpcs *rpcRex) Init(args ...interface{}) { nLog("REX: Init: %#v", args) rpcs.Node.Register(etf.Atom("rex"), rpcs.Self) rpcs.callMap = make(map[modFun]rpcFunction, 0) }
func (gns *globalNameServer) Init(args ...interface{}) { nLog("GLOBAL_NAME_SERVER: Init: %#v", args) gns.Node.Register(etf.Atom("global_name_server"), gns.Self) }
// args must be in form `[FSName:string, DevName:string, isSensor/optional, // isAffector/optional] // return `{ok, Pid}` or `{error, Reason}` func (r *Relay) rpcCreateDevice(terms etf.List) etf.Term { if len(terms) != 3 { return etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("badarith")}) } tFSName := terms[0] if _, ok := tFSName.(string); !ok { return etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("badarg")}) } fsName := tFSName.(string) tDeviceName := terms[1] if _, ok := tDeviceName.(string); !ok { return etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("badarg")}) } deviceName := tDeviceName.(string) if _, ok := terms[2].(etf.List); !ok { return etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("badarg")}) } isSensor, isAffector, err := parseCreateFlags(terms[2].(etf.List)) if err != nil { return etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("badarg")}) } fullName, err := r.createDevice(fsName, deviceName, isSensor, isAffector) if err != nil { println("failed to create device: ", err) return etf.Term(etf.Tuple{etf.Atom("error"), etf.Atom("failed-to-create-device")}) } return etf.Term(etf.Tuple{etf.Atom("ok"), fullName}) }