// used to break all known links to this mbox public void BreakLinks(IOtpErlangObject reason) { foreach (var link in links.ClearLinks()) { Exit(1, link.Remote, reason); } }
// exit (etc) has from, to, reason internal OtpMsg(int tag, OtpErlangPid from, OtpErlangPid to, IOtpErlangObject reason) { Type = tag; FromPid = from; ToPid = to; payload = reason; }
/** * Send an exit signal to a remote process. * Used internally when "processes" terminate */ private void SendExit(int tag, OtpErlangPid from, OtpErlangPid dest, IOtpErlangObject reason) { if (!Connected) { throw new IOException("Not connected"); } OtpOutputStream header = new OtpOutputStream(headerLen); // preamble: 4 byte length + "passthrough" tag header.Write4BE(0); // reserve space for length header.Write1(passThrough); header.Write1(version); // header header.WriteTupleHead(4); header.WriteLong(tag); header.WriteAny(from); header.WriteAny(dest); header.WriteAny(reason); // fix up length in preamble header.Poke4BE(0, header.Length - 4); DoSend(header); }
public void WriteCompressed(IOtpErlangObject o, CompressionLevel level) { try { OtpOutputStream oos = new OtpOutputStream(o); if (oos.Length < 5) { oos.WriteTo(this); } else { Write1(OtpExternal.compressedTag); Write4BE(oos.Length); DeflateStream dos = new DeflateStream(this, level, true); oos.WriteTo(dos); dos.Close(); } } catch (ObjectDisposedException) { throw new ArgumentException("Intermediate stream failed for Erlang object " + o); } catch (IOException) { throw new ArgumentException("Intermediate stream failed for Erlang object " + o); } }
// used by send and send_reg (message types with payload) protected void DoSend(OtpOutputStream header, OtpOutputStream payload) { lock (objWrite) { try { if (TraceLevel >= TraceSend) { // Need to decode header and output buffer to show trace // message! // First make OtpInputStream, then decode. try { IOtpErlangObject h = header.Slice(5).ReadAny(); Logger.Debug("-> " + HeaderType(h) + " " + h); IOtpErlangObject o = payload.Slice(0).ReadAny(); Logger.Debug(" " + o); } catch (OtpDecodeException e) { Logger.Debug(" " + "can't decode output buffer:" + e); } } header.WriteTo(socket.OutputStream); payload.WriteTo(socket.OutputStream); } catch (IOException) { Close(); throw; } } }
// used by the other message types protected void DoSend(OtpOutputStream header) { lock (objWrite) { try { if (TraceLevel >= TraceCTRL) { try { IOtpErlangObject h = header.Slice(5).ReadAny(); Logger.Debug("-> " + HeaderType(h) + " " + h); } catch (OtpDecodeException e) { Logger.Debug(" " + "can't decode output buffer: " + e); } } header.WriteTo(socket.OutputStream); } catch (IOException) { Close(); throw; } } }
// special case when reason is an atom (i.e. most of the time) internal OtpMsg(int tag, OtpErlangPid from, OtpErlangPid to, string reason) { Type = tag; FromPid = from; ToPid = to; payload = new OtpErlangAtom(reason); }
// this function used internally when "process" dies // since Erlang discerns between exit and exit/2. private void Exit(int arity, OtpErlangPid to, IOtpErlangObject reason) { try { string node = to.Node; if (node.Equals(home.Node)) { home.Deliver(new OtpMsg(OtpMsg.exitTag, Self, to, reason)); return; } OtpCookedConnection conn = home.GetConnection(node); if (conn == null) { return; } switch (arity) { case 1: conn.Exit(Self, to, reason); break; case 2: conn.Exit2(Self, to, reason); break; } } catch (Exception) { } }
// send_reg has sender pid and receiver name internal OtpMsg(OtpErlangPid from, string toName, IOtpErlangObject payload) { Type = regSendTag; FromPid = from; ToName = toName; this.payload = payload; }
public OtpErlangTuple CallTuple(IOtpErlangObject msg) { // { $gen_call, { self, ref }, <msg> } return(new OtpErlangTuple( new OtpErlangAtom("$gen_call"), new OtpErlangTuple( Self, home.CreateRef()), msg)); }
public IOtpErlangObject Call(string node, string module, IOtpErlangObject msg, long timeout) { Send(node, module, CallTuple(msg)); var reply = (OtpErlangTuple)Receive(timeout); if (reply != null && reply.Arity >= 2) { return(reply.ElementAt(1)); } return(null); }
/** * Send an auth error to peer because he sent a bad cookie. The auth error * uses his cookie (not revealing ours). This is just like send_reg * otherwise */ protected void CookieError(OtpLocalNode local, OtpErlangAtom cookie) { try { OtpOutputStream header = new OtpOutputStream(headerLen); // preamble: 4 byte length + "passthrough" tag + version header.Write4BE(0); // reserve space for length header.Write1(passThrough); header.Write1(version); header.WriteTupleHead(4); header.WriteLong(regSendTag); header.WriteAny(local.CreatePid()); // disposable pid header.WriteAtom(cookie.Value); // important: his cookie, // not mine... header.WriteAtom("auth"); // version for payload header.Write1(version); // the payload // the no_auth message (copied from Erlang) Don't change this // (Erlang will crash) // {$gen_cast, {print, "~n** Unauthorized cookie ~w **~n", // [foo@aule]}} IOtpErlangObject[] msg = new IOtpErlangObject[2]; IOtpErlangObject[] msgbody = new IOtpErlangObject[3]; msgbody[0] = new OtpErlangAtom("print"); msgbody[1] = new OtpErlangString("~n** Bad cookie sent to " + local + " **~n"); // Erlang will crash and burn if there is no third argument here... msgbody[2] = new OtpErlangList(); // empty list msg[0] = new OtpErlangAtom("$gen_cast"); msg[1] = new OtpErlangTuple(msgbody); OtpOutputStream payload = new OtpOutputStream(new OtpErlangTuple(msg)); // fix up length in preamble header.Poke4BE(0, header.Length + payload.Length - 4); try { DoSend(header, payload); } catch (IOException) { } // ignore } finally { Close(); } throw new OtpAuthException("Remote cookie not authorized: " + cookie.Value); }
/** * Send a message to a remote {@link OtpErlangPid pid}, representing either * another {@link OtpMbox mailbox} or an Erlang process. */ public void Send(OtpErlangPid to, IOtpErlangObject msg) { string node = to.Node; if (node.Equals(home.Node)) { home.Deliver(new OtpMsg(to, (IOtpErlangObject)msg.Clone())); return; } OtpCookedConnection conn = home.GetConnection(node); if (conn == null) { return; } conn.Send(Self, to, msg); }
public OtpErlangFun ReadFun() { int tag = Read1SkipVersion(); if (tag == OtpExternal.funTag) { int nFreeVars = Read4BE(); OtpErlangPid pid = ReadPid(); string module = ReadAtom(); long index = ReadLong(); long uniq = ReadLong(); IOtpErlangObject[] freeVars = new IOtpErlangObject[nFreeVars]; for (int i = 0; i < nFreeVars; ++i) { freeVars[i] = ReadAny(); } return(new OtpErlangFun(pid, module, index, uniq, freeVars)); } else if (tag == OtpExternal.newFunTag) { Read4BE(); int arity = Read1(); byte[] md5 = ReadN(16); int index = Read4BE(); int nFreeVars = Read4BE(); string module = ReadAtom(); long oldIndex = ReadLong(); long uniq = ReadLong(); OtpErlangPid pid = ReadPid(); IOtpErlangObject[] freeVars = new IOtpErlangObject[nFreeVars]; for (int i = 0; i < nFreeVars; ++i) { freeVars[i] = ReadAny(); } return(new OtpErlangFun(pid, module, arity, md5, index, oldIndex, uniq, freeVars)); } else { throw new OtpDecodeException("Wrong tag encountered, expected fun, got " + tag); } }
/** * Send a message to a named mailbox created from another node. */ public void Send(string node, string name, IOtpErlangObject msg) { // this node? if (node.Equals(home.Node) || node.Equals(home.Alive)) { Send(name, msg); return; } // other node OtpCookedConnection conn = home.GetConnection(node); if (conn == null) { return; } conn.Send(Self, name, msg); }
protected string HeaderType(IOtpErlangObject h) { int tag = -1; if (h is OtpErlangTuple tuple) { tag = (int)((OtpErlangLong)tuple.ElementAt(0)).LongValue(); } switch (tag) { case linkTag: return("LINK"); case sendTag: return("SEND"); case exitTag: return("EXIT"); case unlinkTag: return("UNLINK"); case regSendTag: return("REG_SEND"); case groupLeaderTag: return("GROUP_LEADER"); case exit2Tag: return("EXIT2"); case sendTTTag: return("SEND_TT"); case exitTTTag: return("EXIT_TT"); case regSendTTTag: return("REG_SEND_TT"); case exit2TTTag: return("EXIT2_TT"); } return("(unknown type)"); }
/* * this one called explicitely by user code => use exit2 */ public void Exit2(OtpErlangPid from, OtpErlangPid to, IOtpErlangObject reason) { try { SendExit2(from, to, reason); } catch (Exception) { } }
/** * Create a stream containing the encoded version of the given Erlang term. */ public OtpOutputStream(IOtpErlangObject o) : base() => WriteAny(o);
/** * Send an exit2 signal to a remote {@link OtpErlangPid pid}. This method * does not cause any links to be broken, except indirectly if the remote * {@link OtpErlangPid pid} exits as a result of this exit signal. */ public void Exit(OtpErlangPid to, IOtpErlangObject reason) => Exit(2, to, reason);
/** * Write an arbitrary Erlang term to the stream in compressed format. */ public void WriteCompressed(IOtpErlangObject o) => WriteCompressed(o, CompressionLevel.Optimal);
public override void Run() { if (!Connected) { Deliver(new IOException("Not connected")); return; } byte[] lbuf = new byte[4]; int len; try { while (!Stopping) { // don't return until we get a real message // or a failure of some kind (e.g. EXIT) // read length and read buffer must be atomic! // read 4 bytes - get length of incoming packet ReadSock(lbuf); OtpInputStream ibuf = new OtpInputStream(lbuf) { Flags = Local.Flags }; len = ibuf.Read4BE(); // received tick? send tock! if (len == 0) { lock (objWrite) { socket.OutputStream.Write(TOCK, 0, TOCK.Length); socket.OutputStream.Flush(); } continue; } // got a real message (maybe) - read len bytes byte[] tmpbuf = new byte[len]; ReadSock(tmpbuf); ibuf = new OtpInputStream(tmpbuf) { Flags = Local.Flags }; if (ibuf.Read1() != passThrough) { continue; } // got a real message (really) IOtpErlangObject reason = null; OtpErlangAtom cookie = null; IOtpErlangObject tmp = null; OtpErlangTuple head = null; OtpErlangAtom toName; OtpErlangPid to; OtpErlangPid from; int tag; // decode the header tmp = ibuf.ReadAny(); if (!(tmp is OtpErlangTuple)) { continue; } head = (OtpErlangTuple)tmp; if (!(head.ElementAt(0) is OtpErlangLong)) { continue; } // lets see what kind of message this is tag = (int)((OtpErlangLong)head.ElementAt(0)).LongValue(); switch (tag) { case sendTag: // { SEND, Cookie, ToPid } case sendTTTag: // { SEND, Cookie, ToPid, TraceToken } if (!cookieOk) { // we only check this once, he can send us bad cookies later if he likes if (!(head.ElementAt(1) is OtpErlangAtom)) { continue; } cookie = (OtpErlangAtom)head.ElementAt(1); if (sendCookie) { if (cookie.Value != Local.Cookie) { CookieError(Local, cookie); } } else { if (cookie.Value != "") { CookieError(Local, cookie); } } cookieOk = true; } if (TraceLevel >= TraceSend) { Logger.Debug($"<- {HeaderType(head)} {head}"); /* show received payload too */ ibuf.Mark(); Logger.Debug($" {ibuf.ReadAny()}"); ibuf.Reset(); } to = (OtpErlangPid)head.ElementAt(2); Deliver(new OtpMsg(to, ibuf)); break; case regSendTag: // { REG_SEND, FromPid, Cookie, ToName } case regSendTTTag: // { REG_SEND, FromPid, Cookie, ToName, TraceToken } if (!cookieOk) { // we only check this once, he can send us bad cookies later if he likes if (!(head.ElementAt(2) is OtpErlangAtom)) { continue; } cookie = (OtpErlangAtom)head.ElementAt(2); if (sendCookie) { if (cookie.Value != Local.Cookie) { CookieError(Local, cookie); } } else { if (cookie.Value != "") { CookieError(Local, cookie); } } cookieOk = true; } if (TraceLevel >= TraceSend) { Logger.Debug($"<- {HeaderType(head)} {head}"); /* show received payload too */ ibuf.Mark(); Logger.Debug($" {ibuf.ReadAny()}"); ibuf.Reset(); } from = (OtpErlangPid)head.ElementAt(1); toName = (OtpErlangAtom)head.ElementAt(3); Deliver(new OtpMsg(from, toName.Value, ibuf)); break; case exitTag: // { EXIT, FromPid, ToPid, Reason } case exit2Tag: // { EXIT2, FromPid, ToPid, Reason } if (head.ElementAt(3) == null) { continue; } if (TraceLevel >= TraceCTRL) { Logger.Debug($"<- {HeaderType(head)} {head}"); } from = (OtpErlangPid)head.ElementAt(1); to = (OtpErlangPid)head.ElementAt(2); reason = head.ElementAt(3); Deliver(new OtpMsg(tag, from, to, reason)); break; case exitTTTag: // { EXIT, FromPid, ToPid, TraceToken, Reason } case exit2TTTag: // { EXIT2, FromPid, ToPid, TraceToken, Reason } // as above, but bifferent element number if (head.ElementAt(4) == null) { continue; } if (TraceLevel >= TraceCTRL) { Logger.Debug($"<- {HeaderType(head)} {head}"); } from = (OtpErlangPid)head.ElementAt(1); to = (OtpErlangPid)head.ElementAt(2); reason = head.ElementAt(4); Deliver(new OtpMsg(tag, from, to, reason)); break; case linkTag: // { LINK, FromPid, ToPid} case unlinkTag: // { UNLINK, FromPid, ToPid} if (TraceLevel >= TraceCTRL) { Logger.Debug($"<- {HeaderType(head)} {head}"); } from = (OtpErlangPid)head.ElementAt(1); to = (OtpErlangPid)head.ElementAt(2); Deliver(new OtpMsg(tag, from, to)); break; // absolutely no idea what to do with these, so we ignore them... case groupLeaderTag: // { GROUPLEADER, FromPid, ToPid} // (just show trace) if (TraceLevel >= TraceCTRL) { Logger.Debug("<- " + HeaderType(head) + " " + head); } break; default: // garbage? break; } } // this section reachable only with break // we have received garbage from peer Deliver(new OtpExit("Remote is sending garbage")); } catch (OtpAuthException e) { Deliver(e); } catch (OtpDecodeException) { Deliver(new OtpExit("Remote is sending garbage")); } catch (IOException) { Deliver(new OtpExit("Remote has closed connection")); } finally { Close(); } }
/** * Send a message to a named mailbox created from the same node as this * mailbox. */ public void Send(string name, IOtpErlangObject msg) => home.Deliver(new OtpMsg(Self, name, (IOtpErlangObject)msg.Clone()));
/** * Send an exit signal to a remote process. */ protected void SendExit2(OtpErlangPid from, OtpErlangPid dest, IOtpErlangObject reason) { SendExit(exit2Tag, from, dest, reason); }
/** * Send a message to a named process on a remote node. */ public void Send(string dest, IOtpErlangObject msg) => SendBuf(Self.Pid, dest, new OtpOutputStream(msg));
/** * Send an exit signal to a remote process. */ public void Exit(OtpErlangPid dest, IOtpErlangObject reason) => SendExit2(Self.Pid, dest, reason);
/** * Close this mailbox with the given reason. * * After this operation, the mailbox will no longer be able to receive * messages. Any delivered but as yet unretrieved messages can still be * retrieved however. * * If there are links from this mailbox to other {@link OtpErlangPid pids}, * they will be broken when this method is called and exit signals will be * sent. */ public void Exit(IOtpErlangObject reason) => home.CloseMbox(this, reason);
/* * send to remote name dest is recipient's registered name, the nodename is * implied by the choice of connection. */ public void Send(OtpErlangPid from, string dest, IOtpErlangObject msg) => SendBuf(from, dest, new OtpOutputStream(msg));
/** * Create an OtpErlangExit exception with the given reason. */ public OtpExit(IOtpErlangObject reason) : base(reason.ToString()) => Reason = reason;
/* Perform a gen server info to a node and module */ public void Info(string node, string module, IOtpErlangObject msg) { // { self, <msg> } Send(node, module, new OtpErlangTuple(Self, msg)); }
/* Perform a gen_cast to a node and module */ public void Cast(string node, string module, IOtpErlangObject msg) { // { $gen_cast, { self, <msg> } } Send(node, module, new OtpErlangTuple(new OtpErlangAtom("$gen_cast"), msg)); }