From vances@REDACTED Thu Jun 1 01:20:53 2000 From: vances@REDACTED (Vance Shipley) Date: Wed, 31 May 2000 19:20:53 -0400 Subject: Registered Processes in Application Specifications Message-ID: Is there any way to specify a form of registered process name in an application specification without providing a complete list of the registered process names? We have applications which may have hundreds of registered processes (i.e. chan001, chan002, ... chan192). If I have to add each one to the application specification (.app file) it is quite cumbersome: {registered, [chan001, chan002, chan003, chan004, ... chan192]} As far as I can tell the only reason to have this in the specification is to ensure that included applications don't define the same registered processes. Is this true? Can I leave these names out? -Vance From matthias@REDACTED Thu Jun 1 14:25:03 2000 From: matthias@REDACTED (matthias@REDACTED) Date: Thu, 1 Jun 2000 07:25:03 -0500 (CDT) Subject: Scheme and Functional Programming 2000 Message-ID: <200006011225.HAA16784@africa.cs.rice.edu> [-- Apologies for multiple copies --] ============================================================================ CALL FOR PAPERS Scheme and Functional Programming Montreal, Canada, 17 September 2000 The workshop forms part of PLI 2000, which consists of the ICFP and PPDP conferences and other workshops. http://www.cs.rice.edu/~matthias/Scheme2000/ ============================================================================ Background and Theme: --------------------- Over the past few years, Scheme and its implementations have made tremendous progress in support of generative programming, shell scripting, COMponent support and distributed programming, graphics and GUI support, OO programming, and various other aspects of engineering real-world software. We are calling on implementors and users of Scheme systems to share their ideas at a workshop during PLI 2000 in Montreal. The organizers goal is to attract presentations on a broad spectrum of topics: * applications * environments * extensions * implementations * logics (predicate types) * programs The committee will consider submissions concerning Scheme and other functional languages (Erlang, Haskell, LISP, ML, XML, etc.). Submission details ------------------ Deadline for submission: 15 June 2000 The organizers seek two kinds of presentations: * regular: These presentations are like conventional workshop and conference presentation. * short: These presentations last up to 15 mins and are on unconventional topics. Examples include unusual applications, nifty Scheme programming tricks, ideas on implementations, designs and requests for implementations, etc. Program committee ------------------ Kent Dybvig, Indiana University Marc Feeley, Universit? de Montr?al Matthias Felleisen, Rice University Daniel Friedman, Indiana University Manuel Serrano, Universit? de Nice (Sophia-Antipolis) Olin Shivers, Massachusetts Institute of Technology Details ------- http://www.cs.rice.edu/~matthias/Scheme2000/ ============================================================================ From v_chak@REDACTED Thu Jun 1 17:51:00 2000 From: v_chak@REDACTED (Vijay Chakravarthy) Date: Thu, 1 Jun 2000 08:51:00 -0700 (PDT) Subject: Potential erlang jobs Message-ID: <20000601155100.2589.qmail@web1402.mail.yahoo.com> Hi, I apologize if this is the wrong forum for this. We are an e-commerce company in the bay area, and are looking for people skilled in Erlang. If you are interested please contact me at v_chak@REDACTED Vijay __________________________________________________ Do You Yahoo!? Send instant messages & get email alerts with Yahoo! Messenger. http://im.yahoo.com/ From mbj@REDACTED Fri Jun 2 11:48:18 2000 From: mbj@REDACTED (Martin Bjorklund) Date: Fri, 02 Jun 2000 11:48:18 +0200 Subject: Registered Processes in Application Specifications In-Reply-To: Your message of "Wed, 31 May 2000 19:20:53 -0400" References: Message-ID: <20000602114818Z.mbj@bluetail.com> "Vance Shipley" wrote: > As far as I can tell the only reason to have this in the specification > is to ensure that included applications don't define the same registered > processes. Is this true? Yes. > Can I leave these names out? Yes! There are some parts of the app spec which are not very useful; registered, maxP and maxT. These should probably be removed from the docs. and examples. /martin From elutz@REDACTED Fri Jun 2 12:35:28 2000 From: elutz@REDACTED (Eberhard Lutz) Date: Fri, 2 Jun 2000 12:35:28 +0200 Subject: Manipulating Data Structures Message-ID: <001401bfcc7e$4ae524e0$8913a8c0@ELUTZ> Hello together, I hope you forgive this newbie question from a C++, Perl, etc. geek who is willing to convert to Erlang in order to set up a fault-tolerant and distributed system without having to write base classes for the next 5 years .... The question is about data structures and the copy overhead of their modification. Let's say you have in Perl something like $record = { "language" => 'Java', "status" => 'overhyped', "usage" => 'with care', "samples" => [ $prog1, $prog2 ] %% each $progX is a 200kByte data chunk } Now, how do I most effeciently do something like $record->{status} = "dead". So to put it simple: how do I update large data structures without running into useless copy operations of the whole structure. I assume that Erlang is handling these matters as elegantly as it handles the rest of the world, but being suspicious by nature it would make me sleep better if I get tons of replies like 'Hey silly newbie, this is simple ....'. Thanks in advance ... With best regards Eberhard Lutz -------------- next part -------------- An HTML attachment was scrubbed... URL: From etxuwig@REDACTED Fri Jun 2 16:23:17 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Fri, 2 Jun 2000 16:23:17 +0200 (MET DST) Subject: Manipulating Data Structures In-Reply-To: <001401bfcc7e$4ae524e0$8913a8c0@ELUTZ> Message-ID: On Fri, 2 Jun 2000, Eberhard Lutz wrote: >Hello together, > >I hope you forgive this newbie question from a C++, Perl, etc. >geek who is willing to convert to Erlang in order to set up a >fault-tolerant and distributed system without having to write >base classes for the next 5 years .... A noble ambition... >The question is about data structures and the copy overhead >of their modification. >Let's say you have in Perl something like > $record = { > "language" => 'Java', > "status" => 'overhyped', > "usage" => 'with care', > "samples" => [ $prog1, $prog2 ] %% each $progX is a 200kByte data chunk > } >Now, how do I most effeciently do something like > $record->{status} = "dead". > >So to put it simple: how do I update large data structures without >running into useless copy operations of the whole structure. Well, first of all, you *will* copy the whole record if you update one attribute. In Erlang syntax, you'd want to come up with a logical name for $record (other than "record"): -record(language, {name, status, usage, samples}). And then create your instance like this: Java = #language{name = java, status = overhyped, usage = carefully, samples = [Sample1, Sample2, ...]}. Now, changing status from overhyped to dead would copy the record, BUT the actual record structure is only a set of pointers. Physically, the record is stored as a tuple (for now): {language, java, overhyped, carefully, [Sample1, Sample2, ...]}. The tags that start with lowercase are atoms (essentially labels). They are a 4-byte pointer into an atom table. The list is a 4-byte pointer to a cons cell (a pointer to a data object + a pointer to the next cons cell -- or is the cons cell stored directly in the record? I don't remember.) Basically, I believe that the record is an array of pointers to the data objects (in your case 5, including the record tag.) In other words, 20 bytes will be copied when you change the status attribute. Thus, the cost of updating a record is roughly proportional to the number of other elements in the record -- not their size. However, if you'd store the updated record in ETS or mnesia afterwards, the whole record will be copied. As the brunt of the data in your record resided in the 'samples' element, you could break that out, and store them in a separate ETS table. That way, you wouldn't have to copy them every time you read/write the record to ETS. >I assume that Erlang is handling these matters as elegantly as it >handles the rest of the world, but being suspicious by nature it >would make me sleep better if I get tons of replies like 'Hey silly >newbie, this is simple ....'. Well, Erlang probably doesn't do everything it could to reduce the copying overhead. I'm sure it would be possible for the compiler to perform destructive update behind the scenes in many cases. How badly you'll be hurt by the copying overhead depends largely on what you're trying to implement. I once wrote a few variations on Dijkstra's "shortest path first" algorithm, and then we implemented the same algorithm in C -- C was invariably 50-100 times faster. In that particular case, the performance difference turned out not to be that critical after all, since the frequency of updates was pretty small. You will have to make up your own mind about what's the best balance between productivity (=Erlang), fault-tolerance (=Erlang), readability (=Erlang) and raw speed (=C, if the problem is simple enough, otherwise, possibly a toss-up, or maybe even Erlang). But make sure you measure real performance, and not just try to predict how it will perform. It's very difficult to try to calculate what the VM will do and how much it will cost. We've seen this a number of times. If you want to build a fault-tolerant distributed system, write a prototype in Erlang. I can almost promise you that you'll be able to meet your performance criteria in, if not all aspects, at least 90% of them. Then figure out where your bottlenecks are, try to understand what makes them bottlenecks, and then decide what to do with them (switch algorithms? switch to C?) You'll find some experienced Erlang hackers on this list to discuss your options with. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From compudata@REDACTED Fri Jun 2 17:05:37 2000 From: compudata@REDACTED (Christopher Clark) Date: Fri, 02 Jun 2000 09:05:37 -0600 Subject: Manipulating Data Structures References: <001401bfcc7e$4ae524e0$8913a8c0@ELUTZ> Message-ID: <3937CD41.EF153529@cadvision.com> > The question is about data structures and the copy overhead > of their modification. > Let's say you have in Perl something like > $record = { > "language" => 'Java', > "status" => 'overhyped', > "usage" => 'with care', > "samples" => [ $prog1, $prog2 ] %% each $progX is a > 200kByte data chunk > } > Now, how do I most effeciently do something like > $record->{status} = "dead". > > So to put it simple: how do I update large data structures without > running into useless copy operations of the whole structure. Well, I am also a newbie, but the ``proper'' way to do this seems to be... ============ -record(programming_language, {language, status, usage, samples = []}). Java = #programming_language{language = java, status = overhyped, usage = with_care, samples = [Prog1, Prog2]}. New_Java = Java#programming_language{status = dead}. ============ (Note to self: in real life, use shorter record names.) I would assume, having not gone over the interpreter source code, that this will be as efficient as it seems (i.e. only changing the one field). Am I right, o seasoned Erlang practitioners? -- Christopher Clark We hadn't changed the principles, we hadn't changed who we are, we hadn't changed anything except how we presented it. We said `Code Morphing software' and, snap, we got funding. -- Doug Laird From ranga.mahee@REDACTED Tue Jun 6 11:58:07 2000 From: ranga.mahee@REDACTED (Maheedar) Date: Tue, 6 Jun 2000 15:28:07 +0530 Subject: How to communicate between two machines each having a single node Message-ID: <00ac01bfcf9d$b7aac360$97250cc4@wipsys.stph.net> Hi, I am from Wipro Technologies working for Ericsson project on Erlang. I want to know how to communicate between two machines each having a node. I want to communicate between these two nodes. I tried with setting the cookies in the two machines using "erlang:set_cookie(nodename, cookie)". But, it doesn't work. If I can have the answer at the earliest, I will be grateful. ******************************************************************* Ranga Maheedhar Goud Senior Systems Engineer-Telecom Solutions Wipro Technologies Lakshmi Buildings Hyderabad - 500003, India Tel : 7896008 ext 4615 mailto:ranga.mahee@REDACTED www.wipro.com The World's First SEI CMM Level 5 Software Services Company ******************************************************************** -------------- next part -------------- An HTML attachment was scrubbed... URL: From gunilla@REDACTED Tue Jun 6 12:26:03 2000 From: gunilla@REDACTED (Gunilla Hugosson) Date: Tue, 06 Jun 2000 12:26:03 +0200 Subject: How to communicate between two machines each having a single node References: <00ac01bfcf9d$b7aac360$97250cc4@wipsys.stph.net> Message-ID: <393CD1BB.6077737B@erix.ericsson.se> Do you give the Erlang nodes a name when you start them? Example: erl -name ernie There is a chapter about Distributed Erlang in 'Getting Started' in the documentation. (The documentation can be viewed using a web browser. The top index page is situated at ROOT/doc/index.html where ROOT is the root directory of the Open Source Erlang installation. Or check out http://www.erlang.org/documentation/doc/index.html). / Gunilla > Maheedar wrote: > > Hi, > I am from Wipro Technologies working for Ericsson project on Erlang. I > want to know how to communicate between two machines each having a > node. I want to communicate between these two nodes. I tried with > setting the cookies in the two machines using > "erlang:set_cookie(nodename, cookie)". But, it doesn't work. If I can > have the answer at the earliest, I will be grateful. > > ******************************************************************* > > Ranga Maheedhar Goud > Senior Systems Engineer-Telecom Solutions > Wipro Technologies > Lakshmi Buildings > Hyderabad - 500003, India > Tel : 7896008 ext 4615 > mailto:ranga.mahee@REDACTED > www.wipro.com > The World's First SEI CMM Level 5 Software Services Company > ******************************************************************** > > > > -- _____Gunilla Hugosson____________________________________________ Ericsson Utvecklings AB / Open Systems Gunilla.Hugosson@REDACTED http://www.erlang.se office +46-8-7275730 cellular +46-70-5247429 From Matthias.Lang@REDACTED Tue Jun 6 12:26:43 2000 From: Matthias.Lang@REDACTED (Matthias.Lang@REDACTED) Date: Tue, 6 Jun 2000 12:26:43 +0200 (MET DST) Subject: How to communicate between two machines each having a single node In-Reply-To: <00ac01bfcf9d$b7aac360$97250cc4@wipsys.stph.net> References: <00ac01bfcf9d$b7aac360$97250cc4@wipsys.stph.net> Message-ID: <14652.53731.785436.703727@gargle.gargle.HOWL> Hi, There are many ways to prevent communication between erlang nodes from working. Did you start your node as a distributed node? Are you using the right node naming scheme for your network? Here's a suggestion from the FAQ on how to get things going. | 9.6. Why won't my distributed Erlang nodes communicate? | | For Erlang nodes to be able to communicate, you need | | * A working tcp network between the nodes. On unix systems you | can check this by using telnet. | | * The nodes to use the same node naming scheme (you cannot have | a system where some nodes use fully qualified names and | others use short names). | | * The nodes must agree to use the same "magic security cookie". | | Here's an example of how to create two nodes on different machines | called martell and grolsch and verify that they're connected. On | one machine: | | ~ >rlogin martell | Last login: Sat Feb 5 20:40:52 from super | ~ >erl -sname first_node | Eshell V4.9.1.1 (abort with ^G) | (first_node@REDACTED)1> erlang:set_cookie(first_node, nocookie). | true | | | And on the other | | ~ >rlogin grolsch | Last login: Thu Feb 3 10:54:20 from :0 | ~ >erl -sname second_node | Eshell V4.9.1.1 (abort with ^G) | (second_node@REDACTED)1> erlang:set_cookie(second_node, nocookie). | true | (second_node@REDACTED)2> net:ping(first_node@REDACTED). | pong | (second_node@REDACTED)3> rpc:call(first_node@REDACTED, os, type, []). | {unix,sunos} | | | The pong tells us that the connection works, the result of | net:ping() is pang when the connection isn't working. The rpc:call() | command illustrates executing a command on the other node. | Question 9.6.1 (http://www.erlang.org/faq/x699.html#AEN760) has some troubleshooting suggestions. If it still doesn't work, some information about which steps above work and which ones don't will help solving the problem. Matthias --- original message follows ---- Maheedar> I am from Wipro Technologies working for Ericsson Maheedar> project on Erlang. I want to know how to communicate Maheedar> between two machines each having a node. I want to Maheedar> communicate between these two nodes. I tried with Maheedar> setting the cookies in the two machines using Maheedar> "erlang:set_cookie(nodename, cookie)". But, it doesn't Maheedar> work. If I can have the answer at the earliest, I will Maheedar> be grateful. From tonyp@REDACTED Wed Jun 7 13:28:08 2000 From: tonyp@REDACTED (Tony Pedley) Date: Wed, 07 Jun 2000 12:28:08 +0100 Subject: coerce and exact operators problem Message-ID: <393E31C8.458D560C@terminus.ericsson.se> Excuse me for my stupidity but could anyone explain why the coerce and exact operators do not work as advertised.... % Surely one must match...... match(X) -> io:format("when X is ~p\nX==0 results in ~p\nX=:=0 results in ~p\n",[X,X==0,X=:=0]), if X == 0 -> coerce_match; X =:= 0 -> exact_match; true -> no_match end. ------------------------------> 41> tmp:match(0.0). when X is 0.00000e+0, X==0 results in true X=:=0 results in false no_match -- ______________________________________________________________________ Tony Pedley mailto:tonyp@REDACTED Ericsson Intracom Ltd. Intranet : http://intracom.ericsson.se 1 Bede Island Internet : http://www.ericsson.co.uk/datacom/index.htm Leicester memoID : ECOM.CBERAM England Tel : +44 (0)116 2 542 400 LE2 7EU Fax : +44 (0)116 2 046 111 ______________________________________________________________________ -------------- next part -------------- An HTML attachment was scrubbed... URL: From Matthias.Lang@REDACTED Wed Jun 7 13:56:48 2000 From: Matthias.Lang@REDACTED (Matthias.Lang@REDACTED) Date: Wed, 7 Jun 2000 13:56:48 +0200 (MET DST) Subject: coerce and exact operators problem In-Reply-To: <393E31C8.458D560C@terminus.ericsson.se> References: <393E31C8.458D560C@terminus.ericsson.se> Message-ID: <14654.14464.142780.817952@gargle.gargle.HOWL> Hi, The bug you demonstrate doesn't appear to be present in R6B, so I'd say it's a fixed bug. (I can reproduce it in R5b, but not in R6B). I couldn't find that bug listed in the release notes, though the runtime system release notes mention a fix for something similar. Matt Tony Pedley writes: > Excuse me for my stupidity but could anyone explain why the coerce > and exact operators do not work as advertised.... > > % Surely one must match...... > > match(X) -> > io:format("when X is ~p\nX==0 results in ~p\nX=:=0 results in ~p\n",[X,X==0,X=:=0]), > if > X == 0 -> > coerce_match; > X =:= 0 -> > exact_match; > true -> > no_match > end. > > ------------------------------> > > 41> tmp:match(0.0). > > when X is 0.00000e+0, > X==0 results in true > X=:=0 results in false > no_match > > From tonyp@REDACTED Wed Jun 7 14:05:30 2000 From: tonyp@REDACTED (Tony Pedley) Date: Wed, 07 Jun 2000 13:05:30 +0100 Subject: coerce and exact operators problem References: <393E31C8.458D560C@terminus.ericsson.se> <14654.14464.142780.817952@gargle.gargle.HOWL> Message-ID: <393E3A89.EE15FD79@terminus.ericsson.se> Thanks, thats a relief, at least the stupidity factor has been removed (for the time being anyway) Matthias.Lang@REDACTED wrote: > Hi, > > The bug you demonstrate doesn't appear to be present in R6B, so I'd > say it's a fixed bug. (I can reproduce it in R5b, but not in R6B). I > couldn't find that bug listed in the release notes, though the runtime > system release notes mention a fix for something similar. > > Matt > > Tony Pedley writes: > > > Excuse me for my stupidity but could anyone explain why the coerce > > and exact operators do not work as advertised.... > > > > > % Surely one must match...... > > > > match(X) -> > > io:format("when X is ~p\nX==0 results in ~p\nX=:=0 results in ~p\n",[X,X==0,X=:=0]), > > if > > X == 0 -> > > coerce_match; > > X =:= 0 -> > > exact_match; > > true -> > > no_match > > end. > > > > ------------------------------> > > > > 41> tmp:match(0.0). > > > > when X is 0.00000e+0, > > X==0 results in true > > X=:=0 results in false > > no_match > > > > -- ______________________________________________________________________ Tony Pedley mailto:tonyp@REDACTED Ericsson Intracom Ltd. Intranet : http://intracom.ericsson.se 1 Bede Island Internet : http://www.ericsson.co.uk/datacom/index.htm Leicester memoID : ECOM.CBERAM England Tel : +44 (0)116 2 542 400 LE2 7EU Fax : +44 (0)116 2 046 111 ______________________________________________________________________ -------------- next part -------------- An HTML attachment was scrubbed... URL: From thomas@REDACTED Fri Jun 9 13:56:36 2000 From: thomas@REDACTED (Thomas Arts) Date: Fri, 09 Jun 2000 13:56:36 +0200 Subject: IFL 2000 Message-ID: <3940DB74.DA56A727@cslab.ericsson.se> Hi all Don't forget the international workshop on the implementation of functional languages. http://www-i2.informatik.rwth-aachen.de/ifl2000/ This workshop focusses on implementation, but also on the USE of functional languages. Since Erlang is probably the most used functional language, there is a special track within this workshop devoted to Erlang. Please contribute to this workshop by submitting a short abstract - about a program you wrote - about how Erlang solved your problems - about the problems you got when using Erlang - about the tool you developed for Erlang - about ... We still have room for some interesting presentations at this workshop (Aachen, Germany, September 4-7). If you feel uncertain about whether your idea suits the workshop, please send me an email mailto:thomas@REDACTED Regards Thomas Note that it could be a good opportunity to meet fellow Erlang hackers and get ideas from functional languages different from Erlang. From snwight@REDACTED Fri Jun 9 20:00:07 2000 From: snwight@REDACTED (Stephen Wight) Date: Fri, 9 Jun 2000 11:00:07 -0700 (PDT) Subject: IFL 2000 In-Reply-To: <3940DB74.DA56A727@cslab.ericsson.se> References: <3940DB74.DA56A727@cslab.ericsson.se> Message-ID: <14657.12455.927099.78492@adventure.oak.informix.com> From tri@REDACTED Tue Jun 13 09:16:21 2000 From: tri@REDACTED (Tri-Chi Giang) Date: Tue, 13 Jun 2000 17:16:21 +1000 (EST) Subject: Mnesia bag table problem Message-ID: Hi, I have a table that is a bag. When I update this table I remove all the entries in the table and then add in the new entries (all in the same transaction). The problem is that every second time I do this, the table ends up being empty. ie. When this bag table has entries and I do an update (as described above) it results in nothing being in the table. When the table is empty and I do an update, it works. Has anybody had this problem before? Tri From dgud@REDACTED Tue Jun 13 13:35:49 2000 From: dgud@REDACTED (Dan Gudmundsson) Date: Tue, 13 Jun 2000 13:35:49 +0200 (MET DST) Subject: Mnesia bag table problem In-Reply-To: References: Message-ID: <14662.7317.320936.951712@gargle.gargle.HOWL> That looks like a bug I'll fix it in the next rel... /Dan Tri-Chi Giang writes: > > Hi, > > I have a table that is a bag. When I update this table I > remove all the entries in the table and then add in the new entries (all > in the same transaction). > > The problem is that every second time I do this, the table ends up > being empty. > ie. When this bag table has entries and I do an update (as described > above) it results in nothing being in the table. When the table is empty > and I do an update, it works. > > Has anybody had this problem before? > > Tri > > From Sean.Hinde@REDACTED Tue Jun 13 17:53:14 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Tue, 13 Jun 2000 16:53:14 +0100 Subject: Distribution by another means Message-ID: <402DD461F109D411977E0008C791C312564F2D@imp02mbx.one2one.co.uk> I need to connect up two separate Erlang/OTP based systems which are separated amongst other things by a firewall. I don't want to run the standard distribution mechanism between the two systems for various reasons but need a simple rpc type call in both directions. Out of all the mechanisms available in OTP what are peoples views on which would be the quickest and easiest mechanism requiring least overhead and minimum complexity. Full on CORBA would work but is probably overkill?? Views? Sean From cesarini@REDACTED Tue Jun 13 17:59:35 2000 From: cesarini@REDACTED (Francesco Cesarini) Date: Tue, 13 Jun 2000 16:59:35 +0100 Subject: Distribution by another means References: <402DD461F109D411977E0008C791C312564F2D@imp02mbx.one2one.co.uk> Message-ID: <39465A67.284F39BF@terminus.ericsson.se> Might seem far fetched, but when discussing this problem a few years ago, one of the simplest ideas and solutions which came to up was to run the calls through the WWW proxy, masking the http requests as arguments in the URL. //fc Sean Hinde wrote: > > I need to connect up two separate Erlang/OTP based systems which are > separated amongst other things by a firewall. > > I don't want to run the standard distribution mechanism between the two > systems for various reasons but need a simple rpc type call in both > directions. > > Out of all the mechanisms available in OTP what are peoples views on which > would be the quickest and easiest mechanism requiring least overhead and > minimum complexity. Full on CORBA would work but is probably overkill?? > > Views? > > Sean -- Francesco Cesarini Erlang/OTP consultant Cellular: +44-(0)7776 250381 ECN: 832-707192 From mbj@REDACTED Tue Jun 13 18:03:45 2000 From: mbj@REDACTED (Martin Bjorklund) Date: Tue, 13 Jun 2000 18:03:45 +0200 Subject: Distribution by another means In-Reply-To: Your message of "Tue, 13 Jun 2000 16:53:14 +0100" <402DD461F109D411977E0008C791C312564F2D@imp02mbx.one2one.co.uk> References: <402DD461F109D411977E0008C791C312564F2D@imp02mbx.one2one.co.uk> Message-ID: <20000613180345T.mbj@bluetail.com> Sean Hinde wrote: > Out of all the mechanisms available in OTP what are peoples views on which > would be the quickest and easiest mechanism requiring least overhead and > minimum complexity. Set up a socket, possibly perform a handshake alg, maybe md5 digest based, and send erlang binaries: gen_tcp:send(Sock, term_to_binary({apply, M, F, A})) [You might have to take care of which node is responsible for connecting (simple alg: smallest node name), or handle simultanoeus connects.] If each system consists of more than one node, you might run this server in a distributed application, to make it fault tolerant. > Full on CORBA would work but is probably overkill?? I would definitely think so ;-) /martin From etxuwig@REDACTED Tue Jun 13 18:09:35 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 13 Jun 2000 18:09:35 +0200 (MET DST) Subject: Distribution by another means In-Reply-To: <39465A67.284F39BF@terminus.ericsson.se> Message-ID: I'm just now building a network of cooperating web servers using "HTTP-based RPC". That is, the web servers run Erlang, and when they need to communicate, they use a set of predefined "internal requests" via HTTP: query_internal(Host, Port, What, Req) -> {ok, Sock} = gen_tcp:connect(Host, Port, [binary, {packet, 0}]), Auth = ccv_auth:internal_pack_user(Req#ccv_req.user), Query = ["/ccviewer/internal/", What], Cmd = ["GET ", Query, " HTTP/1.0\r\n" "Cookie: ", Auth, ";\r\n" "\r\n"], ok=io:format("connected~n", []), gen_tcp:send(Sock, list_to_binary(Cmd)), do_recv(Sock). do_recv(Sock) -> receive {tcp, Sock, Data} -> ok=io:format("received ~p~n", [Data]), {B1,B2} = split_binary(Data, 4), LengthBytes = binary_to_list(B1), Length = i32(LengthBytes), ok=io:format("Length = ~p~n", [Length]), case size(B2) of L when L == Length -> ok=io:format("Got whole packet~n", []), gen_tcp:close(Sock), binary_to_term(B2); _ -> ok=io:format("wait for more~n", []), do_recv(Sock, Length, B2) end after 10000 -> exit(timeout) end. do_recv(Sock, Length, Bin) -> receive {tcp, Sock, Data} -> Bin2 = list_to_binary([Bin, Data]), case size(Bin2) of Length -> ok=io:format("Got whole packet~n", []), gen_tcp:close(Sock), binary_to_term(Bin2); _ -> ok=io:format("wait for more~n", []), do_recv(Sock, Length, Bin2) end after 10000 -> exit(timeout) end. i32([B1,B2,B3,B4]) -> ((B1 bsl 24) bor (B2 bsl 16) bor (B3 bsl 8) bor B4); i32(I) when integer(I) -> B1 = (I band 16#ff000000) bsr 24, B2 = (I band 16#00ff0000) bsr 16, B3 = (I band 16#0000ff00) bsr 8, B4 = I band 16#000000ff, [B1, B2, B3, B4]. And the server side looks kinda like this: ccv_handler({get, "/internal/" ++ Query, Args}, Socket, Env) -> X = {get, Query, Args}, put(socket, Socket), Result = case ccv_auth:internal_request(X, Socket, Env) of {'EXIT', Reason} -> {error, Reason}; {ok, NewX, Req} -> put(request, Req), handle_internal_request(NewX, Req); Other -> Other end, Bin = term_to_binary(Result), gen_tcp:send(Socket, [ccv_lib:i32(size(Bin)), Bin]); Of course, one also needs an HTTP server. Personally, I use a home-grown, based on Joe's/Luke's pico. A more natural choice perhaps would be to use INETS, but what fun would that be? At AXD301, we had a similar problem, and solved it there with CORBA. It's also reasonably straightforward. I can dig up the code if you'd like. /Uffe On Tue, 13 Jun 2000, Francesco Cesarini wrote: >Might seem far fetched, but when discussing this problem a few years >ago, one of the simplest ideas and solutions which came to up was to run >the calls through the WWW proxy, masking the http requests as arguments >in the URL. > >//fc > >Sean Hinde wrote: >> >> I need to connect up two separate Erlang/OTP based systems which are >> separated amongst other things by a firewall. >> >> I don't want to run the standard distribution mechanism between the two >> systems for various reasons but need a simple rpc type call in both >> directions. >> >> Out of all the mechanisms available in OTP what are peoples views on which >> would be the quickest and easiest mechanism requiring least overhead and >> minimum complexity. Full on CORBA would work but is probably overkill?? >> >> Views? >> >> Sean > > -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From Sean.Hinde@REDACTED Tue Jun 13 19:33:46 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Tue, 13 Jun 2000 18:33:46 +0100 Subject: Distribution by another means Message-ID: <402DD461F109D411977E0008C791C312564F2E@imp02mbx.one2one.co.uk> Martin, > gen_tcp:send(Sock, term_to_binary({apply, M, F, A})) > > [You might have to take care of which node is responsible for > connecting (simple alg: smallest node name), or handle simultanoeus > connects.] > I like this, I wish I'd thought of it ;-) I guess using this with the inets {packet, 2} socket option (which as far as I can tell guarantees that a single receive returns the whole packet) could well be the basis of the simplest rpc ever. On the other hand this still means that I have to write the tcp client and server implementations myself which though not that hard is significant. The advantage I see of CORBA is that once I have figured out how to use the beast it takes care of maintaining the connections, mapping requests and replies etc... Much more fun to do your own though. I'll post if I get something together (possibly based on pico - thanks for the tip Ulf. BTW I'd definitely be interested in seeing a good real example of CORBA usage if you find the time :-) BR, Sean From Sean.Hinde@REDACTED Wed Jun 14 14:13:32 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 14 Jun 2000 13:13:32 +0100 Subject: Distribution by another means Message-ID: <402DD461F109D411977E0008C791C312564F32@imp02mbx.one2one.co.uk> OK, here it is.. A non blocking persistent client with heartbeats for socket maintenance. Implemented using a gen_fsm (rpc_client) A multithreaded tcp/ip server which allows for multiple connections. Implemented using two gen_servers (rpc_listen.erl, rpc_socket.erl) E.g Usage: on the server node (note non distributed so no cheating!) 10>rpc_listen:start_link(3456). {ok, <0.335.0>} On the client node: 3>rpc_client:start_link("sean", "localhost", 3456). {ok, <0.123.0>} 4>rpc_client:call("sean", io, format, ["How cool is Erlang?~n"], 3000). ok Please tear my code apart at will. I'm never very sure of the best way to deal with socket errors on send, receive, and particularly in accept so this needs some work (suggestions?) Also it needs some security and more testing. Sean <> <> <> ============================================================ %%%---------------------------------------------------------------------- %%% File : rpc_client.erl %%% Author : shinde@REDACTED local account %%% Purpose : %%% Created : 11 Apr 2000 by shinde local account %%%---------------------------------------------------------------------- -module(rpc_client). -author('shinde@REDACTED'). -vsn(1). -behaviour(gen_fsm). %% External exports -export([start_link/3]). %% gen_fsm callbacks -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -export([connecting/2]). -export([connected/3, connecting/3, wait_first_heart/3]). -export([call/5]). -define(HEART_TIMEOUT, 8000). % Heartbeat response timeout -define(HEART_PERIOD, 15000). % Inter heartbeat timeout -define(MAX_CMD_TIMEOUT, 5000). % Command response timeout -define(HEART_FAIL_THRESHOLD, 3). % Max number of heartbeat failures before closing socket -define(RETRY_PERIOD, 5000). % Period to wait before retrying the connection -record(state, {heart_fails = 0, % counter of heartbeat failures heart_ref = none, % Timer Ref of repeating heartbeat timer heart_timeout_ref = none, % Timer Ref for heartbeat send timeout cmds = 0, % ets table containing currently outstanding commands name = "", % Given name for this connection socket = 0, % connected socket hostname = "", % hostname to maintain client connection to port = 0}). % listening port on above host %% cmds contains {{cmd, Timer_ref}, Reply_to} %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link(Name, Hostname, Port) -> Reg_name = list_to_atom("rpc_client_" ++ Name), gen_fsm:start_link({local, Reg_name}, ?MODULE, {Name, Hostname, Port}, []). call(Name, M, F, A, Timeout) -> Reg_name = list_to_atom("rpc_client_" ++ Name), gen_fsm:sync_send_event({global, Reg_name}, {apply, M, F, A, Timeout}, ?MAX_CMD_TIMEOUT). %%%---------------------------------------------------------------------- %%% Callback functions from gen_fsm %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, StateName, StateData} | %% {ok, StateName, StateData, Timeout} | %% ignore | %% {stop, StopReason} %%---------------------------------------------------------------------- init({Name, Hostname, Port}) -> Reg_name = list_to_atom("rpc_client_" ++ Name), global:re_register_name(Reg_name, self()), {ok, connecting, #state{cmds = ets:new(cmds, []), name = Name, hostname = Hostname, port = Port}, 0}. % send timeout immediately %%---------------------------------------------------------------------- %% Func: StateName/2 %% Called when gen_fsm:send_event/2,3 is invoked (async) %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %%---------------------------------------------------------------------- %% This is called immediately on startup as a timeout from init/1 connecting(timeout, StateData) -> case gen_tcp:connect(StateData#state.hostname, StateData#state.port, [binary, {active, true}, {packet, 2}], 5000) of {ok, Socket} -> Ref = erlang:start_timer(?HEART_TIMEOUT, self(), heart_timeout), case gen_tcp:send(Socket, term_to_binary({heartbeat, Ref})) of ok -> cancel_timer(StateData#state.heart_timeout_ref), {next_state, wait_first_heart, StateData#state{socket = Socket, heart_timeout_ref = Ref, heart_fails = 0}}; {error, _} -> cancel_timer(Ref), gen_tcp:close(Socket), timer:sleep(5000), % Wait for a while before retrying {next_state, connecting, StateData, 0} end; {error, _} -> timer:sleep(5000), {next_state, connecting, StateData, 0} end; connecting(_, StateData) -> {next_state, connecting, StateData}. %%---------------------------------------------------------------------- %% Func: StateName/3 %% Called when gen_fsm:sync_send_event/2,3 is invoked. %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {reply, Reply, NextStateName, NextStateData} | %% {reply, Reply, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} | %% {stop, Reason, Reply, NewStateData} %%---------------------------------------------------------------------- wait_first_heart({apply, _, _, _, _}, From, StateData) -> {reply, {error, not_yet_heartbeating}, connecting, StateData}. connecting({apply, _, _, _, _}, From, StateData) -> {reply, {error, not_connected}, connecting, StateData}. connected({apply, M, F, A, Timeout}, From, StateData) -> Ref = erlang:start_timer(Timeout, self(), cmd_timeout), case gen_tcp:send(StateData#state.socket, term_to_binary({apply, M, F, A, Ref})) of ok -> ets:insert(StateData#state.cmds, {{cmd, Ref}, From}), {next_state, connected, StateData}; {error, Reason} -> cancel_timer(Ref), {reply, {error, Reason}, connected, StateData} end. %%---------------------------------------------------------------------- %% Func: handle_event/3 %% Called when gen_fsm:send_all_state_event/2 is invoked. %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %%---------------------------------------------------------------------- handle_event(Event, StateName, StateData) -> {next_state, StateName, StateData}. %%---------------------------------------------------------------------- %% Func: handle_sync_event/4 %% Called when gen_fsm:sync_send_all_state_event/2,3 is invoked %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {reply, Reply, NextStateName, NextStateData} | %% {reply, Reply, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} | %% {stop, Reason, Reply, NewStateData} %%---------------------------------------------------------------------- handle_sync_event(Event, From, StateName, StateData) -> Reply = ok, {reply, Reply, StateName, StateData}. %%---------------------------------------------------------------------- %% Func: handle_info/3 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% State: wait_first_heart %%---------------------------------------------------------------------- %% Waiting for first heartbeat. Received timeout which is the current attempt handle_info({timeout, Ref, heart_timeout}, wait_first_heart, #state{heart_timeout_ref = Ref} = StateData) -> gen_tcp:close(StateData#state.socket), {next_state, connecting, StateData#state{socket = 0, heart_timeout_ref = none}, 0}; %% Waiting for first heartbeat. Received timeout which could just %% be a none cancelled timer from a previous attempt. %% Continue to wait for the real one. handle_info({timeout, Ref, heart_timeout}, wait_first_heart, StateData) -> {next_state, connecting, StateData, 0}; %% Wow, received some data handle_info({tcp, Socket, Data}, wait_first_heart, #state{socket = Socket} = StateData) -> case catch binary_to_term(Data) of {heart_reply, Ref} when Ref == StateData#state.heart_timeout_ref -> Next_heart = erlang:start_timer(?HEART_PERIOD, self(), send_heart), % io:format("First Heart_timer_set: ~p~n", [Next_heart]), {next_state, connected, StateData#state{heart_ref = Next_heart}}; _ -> gen_tcp:close(Socket), cancel_timer(StateData#state.heart_timeout_ref), {next_state, connecting, StateData, 0} end; %%---------------------------------------------------------------------- %% State: connected %%---------------------------------------------------------------------- %% Received Nth heartbeat timeout in connected phase which means we %% should close and start again. handle_info({timeout, Ref, heart_timeout}, connected, StateData) when StateData#state.heart_fails >= ?HEART_FAIL_THRESHOLD-> gen_tcp:close(StateData#state.socket), cancel_timer(StateData#state.heart_ref), {next_state, connecting, StateData#state{socket = 0, heart_timeout_ref = none}, 0}; %% Received non final heartbeat timeout in connected phase, increment counter. handle_info({timeout, Ref, heart_timeout}, connected, StateData) -> Heart_fails = StateData#state.heart_fails, {next_state, connected, StateData#state{heart_fails = Heart_fails + 1}}; %% Time to send a new heartbeat. handle_info({timeout, Ref, send_heart}, connected, StateData) -> cancel_timer(StateData#state.heart_timeout_ref), % Just in case New_ref = erlang:start_timer(?HEART_TIMEOUT, self(), heart_timeout), Next_heart = erlang:start_timer(?HEART_PERIOD, self(), send_heart), % io:format("Heart_timer_set: ~p~n", [Next_heart]), case gen_tcp:send(StateData#state.socket, term_to_binary({heartbeat, New_ref})) of ok -> {next_state, connected, StateData#state{heart_timeout_ref = New_ref, heart_ref = Next_heart}}; {error, _} -> cancel_timer(New_ref), Heart_fails = StateData#state.heart_fails, {next_state, connected, StateData#state{heart_fails = Heart_fails + 1, heart_timeout_ref = null, heart_ref = Next_heart}} end; %% Received timeout for a sent command. handle_info({timeout, Ref, cmd_timeout}, connected, StateData) -> Cmds = StateData#state.cmds, case ets:lookup(Cmds, {cmd, Ref}) of [{{cmd, Ref}, Reply_to}] -> gen_fsm:reply(Reply_to, {error, timed_out}), ets:delete(Cmds, {cmd, Ref}), {next_state, connected, StateData}; [] -> {next_state, connected, StateData} end; %% The real stuff - received a reply or heartbeat while connected handle_info({tcp, Socket, Data}, connected, #state{socket = Socket} = StateData) -> Cmds = StateData#state.cmds, case catch binary_to_term(Data) of {reply, Ref, Reply} -> case ets:lookup(Cmds, {cmd, Ref}) of [{{cmd, Ref}, Reply_to}] -> gen_fsm:reply(Reply_to, Reply), ets:delete(Cmds, {cmd, Ref}), cancel_timer(Ref), {next_state, connected, StateData}; [] -> {next_state, connected, StateData} end; {heart_reply, Ref} when Ref == StateData#state.heart_timeout_ref -> cancel_timer(Ref), {next_state, connected, StateData#state{heart_fails = 0}}; _ -> {next_state, connected, StateData} end; %%---------------------------------------------------------------------- %% State: all states %%---------------------------------------------------------------------- handle_info({tcp, _, _}, StateName, StateData) -> % Ignore Packets received in other states {next_state, StateName, StateData}; handle_info({tcp_closed, Socket}, StateName, #state{socket = Socket} = StateData) -> cancel_timer(StateData#state.heart_ref), cancel_timer(StateData#state.heart_timeout_ref), reply_to_all(StateData#state.cmds), {next_state, connecting, StateData#state{socket = 0},0}; handle_info({tcp_error, Socket, Reason}, StateName, #state{socket = Socket} = StateData) -> io:format("Tcp_Error: ~p~n", [Reason]), {next_state, StateName, StateData}; handle_info(Info, StateName, StateData) -> io:format("Unexpected Info: ~p~n", [Info]), {next_state, StateName, StateData}. %%---------------------------------------------------------------------- %% Func: terminate/3 %% Purpose: Shutdown the fsm %% Returns: any %%---------------------------------------------------------------------- terminate(Reason, StateName, StatData) -> ok. %%---------------------------------------------------------------------- %% Func: code_change/4 %% Purpose: Convert process state when code is changed %% Returns: {ok, NewState, NewStateData} %%---------------------------------------------------------------------- code_change(OldVsn, StateName, StateData, Extra) -> {ok, StateName, StateData}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- cancel_timer(none) -> ok; cancel_timer(Ref) -> erlang:cancel_timer(Ref), receive {timeout, Ref, _} -> ok after 0 -> ok end. reply_to_all(Ets) -> ok. ===================================================================== %%%---------------------------------------------------------------------- %%% File : rpc_listen.erl %%% Author : shinde local account %%% Purpose : Act as a tcp/ip server and spawn processes for socket connections %%% Created : 26 May 1999 by ottuser local account %%%---------------------------------------------------------------------- -module(rpc_listen). -vsn(1). -author('shinde@REDACTED'). -export([start_link/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([create/2]). -behaviour(gen_server). -record(state,{listen_socket = []}). % Listener socket reference %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link(Port) -> Name = list_to_atom(lists:flatten(io_lib:format("rpc_server_~w",[Port]))), gen_server:start_link({local, Name}, ?MODULE, Port, []). %% Access to this server from socket servers create(ServerPid, Pid) -> gen_server:cast(ServerPid, {create, Pid}). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init(Port) -> process_flag(trap_exit, true), case gen_tcp:listen(Port,[binary,{packet,2},{active, true}, {reuseaddr,true}]) of {ok, ListenSocket} -> {ok, Pid} = rpc_socket:start(self(), ListenSocket), % Start acceptor process rpc_socket:get_connection(Pid), % Tell it to start accepting {ok, #state{listen_socket = ListenSocket}}; {error, Reason} -> {stop, Reason} end. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call(Request,From,State) -> {reply,ok,State}. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast({create,Pid}, State) -> {ok, NewPid} = rpc_socket:start(self(), State#state.listen_socket), rpc_socket:get_connection(NewPid), {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- % handle_info({'EXIT', Pid, {error, accept_failed}}, State) -> create(self(), self()), % Start off new acceptor as listen socket is still open {noreply,State}; % normal shutdown of socket process handle_info({'EXIT', Pid, normal}, State) -> {noreply,State}; handle_info(Info, State) -> io:format("Unexpected info: ~p~n",[Info]), {noreply,State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(Reason,State) -> gen_tcp:close(State#state.listen_socket), ok. %%---------------------------------------------------------------------- %% Func: code_change/3 %% Purpose: Convert process state when code is changed %% Returns: {ok, NewState} %%---------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- ============================================================== %%%---------------------------------------------------------------------- %%% File : rpc_socket.erl %%% Author : ottuser local account %%% Purpose : Accept a tcp/ip connection and then handle in and ivr protocols %%% Created : 26 May 1999 by ottuser local account %%%---------------------------------------------------------------------- %%% This gen_server exists for the life of a socket connection %%% It is spawned by tcp_listen, which send it it's own PID %%% so we can ask it to set up a new listener if/when this one accepts %%% a socket connection. %%%---------------------------------------------------------------------- -module(rpc_socket). -vsn(1). -author('shinde@REDACTED'). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([start/2, get_connection/1, worker/5]). -behaviour(gen_server). % Internal state for this socket process -record(state,{listen_pid, % Pid of Listener lis_socket, % Listener Socket socket = undefined}). % Socket ref %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start(ListenPid, ListenSocket) -> gen_server:start_link(rpc_socket, {ListenPid, ListenSocket},[]). get_connection(Pid) -> gen_server:cast(Pid, get_conn). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init({ListenPid, ListenSocket}) -> {ok, #state{listen_pid = ListenPid, lis_socket = ListenSocket}}. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call(Request,From,State) -> {reply,ok,State}. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast(get_conn, State) -> case catch gen_tcp:accept(State#state.lis_socket) of {error, closed} -> {stop, {error, accept_failed}, State}; {error, Reason} -> {stop, {error, accept_failed}, State}; {'EXIT', Reason} -> {stop, {error, accept_failed}, State}; {ok, Socket} -> rpc_listen:create(State#state.listen_pid, self()), {noreply, State#state{socket = Socket}} end; handle_cast(_Reply ,State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_info({tcp, Socket, Packet}, State) -> case catch binary_to_term(Packet) of {heartbeat, Ref} -> % io:format("Heartbeat Received~n"), gen_tcp:send(Socket, term_to_binary({heart_reply, Ref})), {noreply, State}; {apply, M, F, A, Ref} -> Pid = spawn_link(?MODULE, worker, [M, F, A, Ref, Socket]), {noreply, State}; Else -> io:format("Socket Received Else: ~p~n",[Else]), {noreply, State} end; handle_info({tcp_closed, Socket}, State) -> {stop, rpc_skt_closed, State}; handle_info({tcp_error, Socket, Reason}, State) -> gen_tcp:close(State#state.socket), {stop, rpc_skt_error, State}; handle_info(Anymessage, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(Reason, #state{socket = undefined}) -> ok; terminate(Reason, #state{socket = Socket}) -> gen_tcp:close(Socket), ok. %%---------------------------------------------------------------------- %% Func: code_change/3 %% Purpose: Convert process state when code is changed %% Returns: {ok, NewState} %%---------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- worker(M, F, A, Ref, Socket) -> Reply = (catch apply(M, F, A)), % io:format("Reply: ~p~n", [Reply]), gen_tcp:send(Socket, term_to_binary({reply, Ref, Reply})). ==================================================END -------------- next part -------------- A non-text attachment was scrubbed... Name: rpc_client.erl Type: application/octet-stream Size: 13185 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: rpc_listen.erl Type: application/octet-stream Size: 4526 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: rpc_socket.erl Type: application/octet-stream Size: 5362 bytes Desc: not available URL: From Sean.Hinde@REDACTED Wed Jun 14 16:08:34 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 14 Jun 2000 15:08:34 +0100 Subject: Distribution by another means Message-ID: <402DD461F109D411977E0008C791C312564F36@imp02mbx.one2one.co.uk> All, The security didn't prove too hard so here is a new version. Based on the RADIUS mechanism, an md5 hash of a shared secret concatenated with the Timer reference is sent with each command. It is still open to spoofing of replies but I guess that is not too dangerous. It now also requires the crypto app to be started Feel free to make use as you wish, but please share any fixes and improvements Sean %%%---------------------------------------------------------------------- %%% File : rpc_socket.erl %%% Author : ottuser local account %%% Purpose : Accept a tcp/ip connection and then handle in and ivr protocols %%% Created : 26 May 1999 by ottuser local account %%%---------------------------------------------------------------------- %%% This gen_server exists for the life of a socket connection %%% It is spawned by tcp_listen, which send it it's own PID %%% so we can ask it to set up a new listener if/when this one accepts %%% a socket connection. %%%---------------------------------------------------------------------- -module(rpc_socket). -vsn('$Revision: 1.2 $ '). -author('shinde@REDACTED'). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([start/3, get_connection/1, worker/5]). -behaviour(gen_server). % Internal state for this socket process -record(state,{listen_pid, % Pid of Listener lis_socket, % Listener Socket socket = undefined, % Socket ref secret = null}). % Shared Secret %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start(ListenPid, ListenSocket, Secret) -> gen_server:start_link(rpc_socket, {ListenPid, ListenSocket, Secret},[]). get_connection(Pid) -> gen_server:cast(Pid, get_conn). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init({ListenPid, ListenSocket, Secret}) -> {ok, #state{listen_pid = ListenPid, lis_socket = ListenSocket, secret = Secret}}. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call(Request,From,State) -> {reply,ok,State}. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast(get_conn, State) -> case catch gen_tcp:accept(State#state.lis_socket) of {error, closed} -> {stop, {error, accept_failed}, State}; {error, Reason} -> {stop, {error, accept_failed}, State}; {'EXIT', Reason} -> {stop, {error, accept_failed}, State}; {ok, Socket} -> rpc_listen:create(State#state.listen_pid, self()), {noreply, State#state{socket = Socket}} end; handle_cast(_Reply ,State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_info({tcp, Socket, Packet}, State) -> case catch binary_to_term(Packet) of {heartbeat, Ref, Checksum} -> io:format("Heartbeat Received~n"), case check(Ref, State#state.secret, Checksum) of true -> io:format("check ok~n"), gen_tcp:send(Socket, term_to_binary({heart_reply, Ref})), {noreply, State}; false -> {noreply, State} end; {apply, M, F, A, Ref, Checksum} -> case check(Ref, State#state.secret, Checksum) of true -> Pid = spawn_link(?MODULE, worker, [M, F, A, Ref, Socket]), {noreply, State}; false -> {noreply, State} end; Else -> io:format("Socket Received Else: ~p~n",[Else]), {noreply, State} end; handle_info({tcp_closed, Socket}, State) -> {stop, rpc_skt_closed, State}; handle_info({tcp_error, Socket, Reason}, State) -> gen_tcp:close(State#state.socket), {stop, rpc_skt_error, State}; handle_info(Anymessage, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(Reason, #state{socket = undefined}) -> ok; terminate(Reason, #state{socket = Socket}) -> gen_tcp:close(Socket), ok. %%---------------------------------------------------------------------- %% Func: code_change/3 %% Purpose: Convert process state when code is changed %% Returns: {ok, NewState} %%---------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- worker(M, F, A, Ref, Socket) -> Reply = (catch apply(M, F, A)), % io:format("Reply: ~p~n", [Reply]), gen_tcp:send(Socket, term_to_binary({reply, Ref, Reply})). check(Ref, Secret, Checksum) -> Checksum == crypto:md5(concat_binary([term_to_binary(Ref), Secret])). -----------------------------cut-------------------------------------------- ------------------ %%%---------------------------------------------------------------------- %%% File : rpc_listen.erl %%% Author : shinde local account %%% Purpose : Act as a tcp/ip server and spawn processes for socket connections %%% Created : 26 May 1999 by ottuser local account %%%---------------------------------------------------------------------- -module(rpc_listen). -vsn('$Revision: 1.2 $ '). -author('shinde@REDACTED'). -export([start_link/2]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([create/2]). -behaviour(gen_server). -record(state,{listen_socket = [], % Listener socket reference secret = null}). % Shared Secret %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link(Port, Secret) -> Name = list_to_atom(lists:flatten(io_lib:format("rpc_server_~w",[Port]))), gen_server:start_link({local, Name}, ?MODULE, {Port, Secret}, []). %% Access to this server from socket servers create(ServerPid, Pid) -> gen_server:cast(ServerPid, {create, Pid}). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init({Port, Secret}) -> process_flag(trap_exit, true), Bin_secret = list_to_binary(Secret), case gen_tcp:listen(Port,[binary,{packet,2},{active, true}, {reuseaddr,true}]) of {ok, ListenSocket} -> {ok, Pid} = rpc_socket:start(self(), ListenSocket, Bin_secret), % Start acceptor process rpc_socket:get_connection(Pid), % Tell it to start accepting {ok, #state{listen_socket = ListenSocket, secret = Bin_secret}}; {error, Reason} -> {stop, Reason} end. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call(Request,From,State) -> {reply,ok,State}. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast({create,Pid}, State) -> {ok, NewPid} = rpc_socket:start(self(), State#state.listen_socket, State#state.secret), rpc_socket:get_connection(NewPid), {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- % handle_info({'EXIT', Pid, {error, accept_failed}}, State) -> create(self(), self()), % Start off new acceptor as listen socket is still open {noreply,State}; % normal shutdown of socket process handle_info({'EXIT', Pid, normal}, State) -> {noreply,State}; handle_info(Info, State) -> io:format("Unexpected info: ~p~n",[Info]), {noreply,State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(Reason,State) -> gen_tcp:close(State#state.listen_socket), ok. %%---------------------------------------------------------------------- %% Func: code_change/3 %% Purpose: Convert process state when code is changed %% Returns: {ok, NewState} %%---------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- --------------------------------cut-------------------------- %%%---------------------------------------------------------------------- %%% File : rpc_client.erl %%% Author : shinde@REDACTED local account %%% Purpose : %%% Created : 11 Apr 2000 by shinde local account %%%---------------------------------------------------------------------- -module(rpc_client). -author('shinde@REDACTED'). -vsn('$Revision: 1.2 $ '). -behaviour(gen_fsm). %% External exports -export([start_link/4]). %% gen_fsm callbacks -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -export([connecting/2]). -export([connected/3, connecting/3, wait_first_heart/3]). -export([call/5]). -define(HEART_TIMEOUT, 8000). % Heartbeat response timeout -define(HEART_PERIOD, 15000). % Inter heartbeat timeout -define(MAX_CMD_TIMEOUT, 5000). % Command response timeout -define(HEART_FAIL_THRESHOLD, 3). % Max number of heartbeat failures before closing socket -define(RETRY_PERIOD, 5000). % Period to wait before retrying the connection -record(state, {heart_fails = 0, % counter of heartbeat failures heart_ref = none, % Timer Ref of repeating heartbeat timer heart_timeout_ref = none, % Timer Ref for heartbeat send timeout cmds = 0, % ets table containing currently outstanding commands name = "", % Given name for this connection socket = 0, % connected socket hostname = "", % hostname to maintain client connection to port = 0, % listening port on above host secret = null}). % shared secret for security %% cmds contains {{cmd, Timer_ref}, Reply_to} %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link(Name, Hostname, Port, Secret) -> Reg_name = list_to_atom("rpc_client_" ++ Name), gen_fsm:start_link({local, Reg_name}, ?MODULE, {Name, Hostname, Port, Secret}, []). call(Name, M, F, A, Timeout) -> Reg_name = list_to_atom("rpc_client_" ++ Name), gen_fsm:sync_send_event({global, Reg_name}, {apply, M, F, A, Timeout}, ?MAX_CMD_TIMEOUT). %%%---------------------------------------------------------------------- %%% Callback functions from gen_fsm %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, StateName, StateData} | %% {ok, StateName, StateData, Timeout} | %% ignore | %% {stop, StopReason} %%---------------------------------------------------------------------- init({Name, Hostname, Port, Secret}) -> Reg_name = list_to_atom("rpc_client_" ++ Name), global:re_register_name(Reg_name, self()), {ok, connecting, #state{cmds = ets:new(cmds, []), name = Name, hostname = Hostname, secret = list_to_binary(Secret), port = Port}, 0}. % send timeout immediately %%---------------------------------------------------------------------- %% Func: StateName/2 %% Called when gen_fsm:send_event/2,3 is invoked (async) %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %%---------------------------------------------------------------------- %% This is called immediately on startup as a timeout from init/1 connecting(timeout, StateData) -> case gen_tcp:connect(StateData#state.hostname, StateData#state.port, [binary, {active, true}, {packet, 2}], 5000) of {ok, Socket} -> Ref = erlang:start_timer(?HEART_TIMEOUT, self(), heart_timeout), case gen_tcp:send(Socket, term_to_binary({heartbeat, Ref, md5(Ref, StateData#state.secret)})) of ok -> cancel_timer(StateData#state.heart_timeout_ref), {next_state, wait_first_heart, StateData#state{socket = Socket, heart_timeout_ref = Ref, heart_fails = 0}}; {error, _} -> cancel_timer(Ref), gen_tcp:close(Socket), timer:sleep(5000), % Wait for a while before retrying {next_state, connecting, StateData, 0} end; {error, _} -> timer:sleep(5000), {next_state, connecting, StateData, 0} end; connecting(_, StateData) -> {next_state, connecting, StateData}. %%---------------------------------------------------------------------- %% Func: StateName/3 %% Called when gen_fsm:sync_send_event/2,3 is invoked. %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {reply, Reply, NextStateName, NextStateData} | %% {reply, Reply, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} | %% {stop, Reason, Reply, NewStateData} %%---------------------------------------------------------------------- wait_first_heart({apply, _, _, _, _}, From, StateData) -> {reply, {error, not_yet_heartbeating}, connecting, StateData}. connecting({apply, _, _, _, _}, From, StateData) -> {reply, {error, not_connected}, connecting, StateData}. connected({apply, M, F, A, Timeout}, From, StateData) -> Ref = erlang:start_timer(Timeout, self(), cmd_timeout), case gen_tcp:send(StateData#state.socket, term_to_binary({apply, M, F, A, Ref, md5(Ref, StateData#state.secret)})) of ok -> ets:insert(StateData#state.cmds, {{cmd, Ref}, From}), {next_state, connected, StateData}; {error, Reason} -> cancel_timer(Ref), {reply, {error, Reason}, connected, StateData} end. %%---------------------------------------------------------------------- %% Func: handle_event/3 %% Called when gen_fsm:send_all_state_event/2 is invoked. %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %%---------------------------------------------------------------------- handle_event(Event, StateName, StateData) -> {next_state, StateName, StateData}. %%---------------------------------------------------------------------- %% Func: handle_sync_event/4 %% Called when gen_fsm:sync_send_all_state_event/2,3 is invoked %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {reply, Reply, NextStateName, NextStateData} | %% {reply, Reply, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} | %% {stop, Reason, Reply, NewStateData} %%---------------------------------------------------------------------- handle_sync_event(Event, From, StateName, StateData) -> Reply = ok, {reply, Reply, StateName, StateData}. %%---------------------------------------------------------------------- %% Func: handle_info/3 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% State: wait_first_heart %%---------------------------------------------------------------------- %% Waiting for first heartbeat. Received timeout which is the current attempt handle_info({timeout, Ref, heart_timeout}, wait_first_heart, #state{heart_timeout_ref = Ref} = StateData) -> gen_tcp:close(StateData#state.socket), {next_state, connecting, StateData#state{socket = 0, heart_timeout_ref = none}, 0}; %% Waiting for first heartbeat. Received timeout which could just %% be a none cancelled timer from a previous attempt. %% Continue to wait for the real one. handle_info({timeout, Ref, heart_timeout}, wait_first_heart, StateData) -> {next_state, connecting, StateData, 0}; %% Wow, received some data handle_info({tcp, Socket, Data}, wait_first_heart, #state{socket = Socket} = StateData) -> case catch binary_to_term(Data) of {heart_reply, Ref} when Ref == StateData#state.heart_timeout_ref -> Next_heart = erlang:start_timer(?HEART_PERIOD, self(), send_heart), io:format("First Heart_timer_set: ~p~n", [Next_heart]), {next_state, connected, StateData#state{heart_ref = Next_heart}}; _ -> gen_tcp:close(Socket), cancel_timer(StateData#state.heart_timeout_ref), {next_state, connecting, StateData, 0} end; %%---------------------------------------------------------------------- %% State: connected %%---------------------------------------------------------------------- %% Received Nth heartbeat timeout in connected phase which means we %% should close and start again. handle_info({timeout, Ref, heart_timeout}, connected, StateData) when StateData#state.heart_fails >= ?HEART_FAIL_THRESHOLD-> gen_tcp:close(StateData#state.socket), cancel_timer(StateData#state.heart_ref), {next_state, connecting, StateData#state{socket = 0, heart_timeout_ref = none}, 0}; %% Received non final heartbeat timeout in connected phase, increment counter. handle_info({timeout, Ref, heart_timeout}, connected, StateData) -> Heart_fails = StateData#state.heart_fails, {next_state, connected, StateData#state{heart_fails = Heart_fails + 1}}; %% Time to send a new heartbeat. handle_info({timeout, Ref, send_heart}, connected, StateData) -> cancel_timer(StateData#state.heart_timeout_ref), % Just in case New_ref = erlang:start_timer(?HEART_TIMEOUT, self(), heart_timeout), Next_heart = erlang:start_timer(?HEART_PERIOD, self(), send_heart), % io:format("Heart_timer_set: ~p~n", [Next_heart]), case gen_tcp:send(StateData#state.socket, term_to_binary({heartbeat, New_ref, md5(New_ref, StateData#state.secret)})) of ok -> {next_state, connected, StateData#state{heart_timeout_ref = New_ref, heart_ref = Next_heart}}; {error, _} -> cancel_timer(New_ref), Heart_fails = StateData#state.heart_fails, {next_state, connected, StateData#state{heart_fails = Heart_fails + 1, heart_timeout_ref = null, heart_ref = Next_heart}} end; %% Received timeout for a sent command. handle_info({timeout, Ref, cmd_timeout}, connected, StateData) -> Cmds = StateData#state.cmds, case ets:lookup(Cmds, {cmd, Ref}) of [{{cmd, Ref}, Reply_to}] -> gen_fsm:reply(Reply_to, {error, timed_out}), ets:delete(Cmds, {cmd, Ref}), {next_state, connected, StateData}; [] -> {next_state, connected, StateData} end; %% The real stuff - received a reply or heartbeat while connected handle_info({tcp, Socket, Data}, connected, #state{socket = Socket} = StateData) -> Cmds = StateData#state.cmds, case catch binary_to_term(Data) of {reply, Ref, Reply} -> case ets:lookup(Cmds, {cmd, Ref}) of [{{cmd, Ref}, Reply_to}] -> gen_fsm:reply(Reply_to, Reply), ets:delete(Cmds, {cmd, Ref}), cancel_timer(Ref), {next_state, connected, StateData}; [] -> {next_state, connected, StateData} end; {heart_reply, Ref} when Ref == StateData#state.heart_timeout_ref -> cancel_timer(Ref), {next_state, connected, StateData#state{heart_fails = 0}}; Else -> io:format("unknown Packet received~p~n", [Else]), {next_state, connected, StateData} end; %%---------------------------------------------------------------------- %% State: all states %%---------------------------------------------------------------------- handle_info({tcp, _, _}, StateName, StateData) -> % Ignore Packets received in other states {next_state, StateName, StateData}; handle_info({tcp_closed, Socket}, StateName, #state{socket = Socket} = StateData) -> cancel_timer(StateData#state.heart_ref), cancel_timer(StateData#state.heart_timeout_ref), reply_to_all(StateData#state.cmds), {next_state, connecting, StateData#state{socket = 0},0}; handle_info({tcp_error, Socket, Reason}, StateName, #state{socket = Socket} = StateData) -> io:format("Tcp_Error: ~p~n", [Reason]), {next_state, StateName, StateData}; handle_info(Info, StateName, StateData) -> io:format("Unexpected Info: ~p~n", [Info]), {next_state, StateName, StateData}. %%---------------------------------------------------------------------- %% Func: terminate/3 %% Purpose: Shutdown the fsm %% Returns: any %%---------------------------------------------------------------------- terminate(Reason, StateName, StatData) -> ok. %%---------------------------------------------------------------------- %% Func: code_change/4 %% Purpose: Convert process state when code is changed %% Returns: {ok, NewState, NewStateData} %%---------------------------------------------------------------------- code_change(OldVsn, StateName, StateData, Extra) -> {ok, StateName, StateData}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- cancel_timer(none) -> ok; cancel_timer(Ref) -> erlang:cancel_timer(Ref), receive {timeout, Ref, _} -> ok after 0 -> ok end. reply_to_all(Ets) -> ok. md5(Ref, Secret) -> crypto:md5(concat_binary([term_to_binary(Ref), Secret])). From hal@REDACTED Sat Jun 17 05:09:49 2000 From: hal@REDACTED (Hal Snyder) Date: 16 Jun 2000 22:09:49 -0500 Subject: inet_dns Message-ID: <87itv9t342.fsf@ghidra.vail> We are studying eddieware source and learning quite a bit about Erlang software systems in the process. In the DNS server code, there is reference to a kernel module "inet_dns". In fact, kernel sources contain several inet_* modules. 1. Are kernel inet_* modules documented someplace - i.e. did I miss something obvious? 2. If not, are they stable - i.e., what is the risk they will change in a future release? BTW - I see that there was a new release of eddieware yesterday. Hats off to the eddie team. From hal@REDACTED Sat Jun 17 05:26:22 2000 From: hal@REDACTED (Hal Snyder) Date: 16 Jun 2000 22:26:22 -0500 Subject: robustness Message-ID: <87em5xt2ch.fsf@ghidra.vail> Erlang literature mentions linked processes to alert one process if another signals an exit, and heartbeat processes to react if the local node stops. Is there a common Erlang approach to dealing with loss of a remote node? How does a set of networked nodes know, for example, if one of them suddenly ceases to function (loses power, etc.) without sending any exit signals? From hal@REDACTED Sat Jun 17 05:35:57 2000 From: hal@REDACTED (Hal Snyder) Date: 16 Jun 2000 22:35:57 -0500 Subject: erl closing sockets & tables Message-ID: <87aeglt1wi.fsf@ghidra.vail> (Last question for tonight.) While testing programs with erl, we've noticed that errors such as badmatch and invalid arguments will cause open sockets and (d)ets tables to be closed. Is this a bug or a feature? :-) Is there an accepted way to change this behavior? From geoff@REDACTED Sat Jun 17 08:36:04 2000 From: geoff@REDACTED (Geoff Wong) Date: Sat, 17 Jun 2000 16:36:04 +1000 (EST) Subject: robustness In-Reply-To: <87em5xt2ch.fsf@ghidra.vail> from "Hal Snyder" at Jun 16, 2000 10:26:22 PM Message-ID: <200006170636.GAA19643@gecko.serc.rmit.edu.au> > Is there a common Erlang approach to dealing with loss of a remote > node? Using net_kernerl:monitor_nodes(true) seems to be the accepted way to do this. The monitoring process should get a { nodedown, Node } or { nodeup, Node } as appropriate. > How does a set of networked nodes know, for example, if one of them > suddenly ceases to function (loses power, etc.) without sending any > exit signals? Hmm - a question for an Erlang kernel person :-). Geoff From per@REDACTED Sat Jun 17 14:01:21 2000 From: per@REDACTED (Per Hedeland) Date: Sat, 17 Jun 2000 14:01:21 +0200 (MET DST) Subject: robustness In-Reply-To: <87em5xt2ch.fsf@ghidra.vail> References: <87em5xt2ch.fsf@ghidra.vail> Message-ID: <200006171201.e5HC1Lo01210@super.du.uab.ericsson.se> Hal Snyder wrote: >Erlang literature mentions linked processes to alert one process if >another signals an exit, and heartbeat processes to react if the local >node stops. Yes, though of course in the latter case it has to be an external process, such as implemented by the 'heart' module. >Is there a common Erlang approach to dealing with loss of a remote >node? Well, how to deal with it is basically application-specific - to detect it, you can besides the net_kernel:monitor_nodes/1 that Geoff mentioned use the monitor_node/2 BIF - plus of course if your process is linked to a process on a remote node, it will receive an EXIT signal also if the node "dies" (i.e. it is synthesized locally). >How does a set of networked nodes know, for example, if one of them >suddenly ceases to function (loses power, etc.) without sending any >exit signals? It uses a mechanism similar to TCP "keepalive" - i.e. "tick" messages are sent periodically between connected nodes if there is no other traffic, and lack of reception of any messages from a node for a longer period is taken as an indication that it is dead. See e.g. the description of net_ticktime in the kernel application documentation. (There's a typo there, s/anything/nothing/.:-) --Per Hedeland per@REDACTED From per@REDACTED Sat Jun 17 14:38:59 2000 From: per@REDACTED (Per Hedeland) Date: Sat, 17 Jun 2000 14:38:59 +0200 (MET DST) Subject: erl closing sockets & tables In-Reply-To: <87aeglt1wi.fsf@ghidra.vail> References: <87aeglt1wi.fsf@ghidra.vail> Message-ID: <200006171238.e5HCcx601429@super.du.uab.ericsson.se> Hal Snyder wrote: >While testing programs with erl, we've noticed that errors such as >badmatch and invalid arguments will cause open sockets and (d)ets >tables to be closed. Well, sort of - - sockets etc are closed when the process owning them exits - such errors (by default) cause the calling process to exit - the erlang shell is just another erlang process, i.e. it will exit too, and sockets etc opened interactively in the shell will be closed - but: - a new erlang shell is started "automatically" when it exits. >Is this a bug or a feature? :-) The underlying mechanisms (decribed above) that cause the behaviour you're seeing are fundamental features.:-) >Is there an accepted way to change this behavior? There is no way to change it as far as I know - you can of course use 'catch' to prevent the errors from causing an exit, or you can do your testing differently: E.g. spawn a process that is the one opening sockets etc, and then talk to that process from the shell (perhaps via "interface functions") - the shell exiting and restarting will not affect the spawned process. --Per Hedeland per@REDACTED From raimo@REDACTED Mon Jun 19 12:55:55 2000 From: raimo@REDACTED (Raimo Niskanen) Date: Mon, 19 Jun 2000 12:55:55 +0200 Subject: inet_dns References: <87itv9t342.fsf@ghidra.vail> Message-ID: <394DFC3B.5A67F87@erix.ericsson.se> Hal Snyder wrote: > > We are studying eddieware source and learning quite a bit about Erlang > software systems in the process. In the DNS server code, there is > reference to a kernel module "inet_dns". In fact, kernel sources > contain several inet_* modules. > > 1. Are kernel inet_* modules documented someplace - i.e. did I miss > something obvious? > > 2. If not, are they stable - i.e., what is the risk they will change > in a future release? > > BTW - I see that there was a new release of eddieware yesterday. Hats > off to the eddie team. They are internal modules and not documented. Your applications should use modules inet, gen_tcp, gen_udp and such documented modules. They are not stable either, in fact they are currently being rewritten for Erlang OTP-R7 (release in august), to improve socket performance. Many internal interfaces in inet_* will remain essentially the same while others will change drastically. / Raimo Niskanen raimo.niskanen@REDACTED From per@REDACTED Mon Jun 19 23:50:01 2000 From: per@REDACTED (Per Bergqvist) Date: Mon, 19 Jun 2000 23:50:01 +0200 Subject: Nice alternative for distributed Erlang Message-ID: <394E9589.F32253BF@cellpt.com> Hi all, Attended to a signaling conference last week. Apart from all the U.S. vendors of VoIP solution trying to explain how simple telephony really is, yawn, there were a few nice parts as well. The IETF Signaling Transport group, SIGTRAN, are proposing a new protocol called Stream Control Transmission Protocol, SCTP. It is primarily intended for carrying various SS7 protocol over IP but the problems SCTP address are common to most IP based applications with very high reliability requirements. Excerpt from the draft abstract: > SCTP is a reliable datagram transfer protocol operating on top of an > unreliable routed packet network such as IP. It offers the following > services to its users: > > -- acknowledged error-free non-duplicated transfer of user data, > -- data segmentation to conform to discovered path MTU size, > -- sequenced delivery of user messages within multiple streams, > with an option for order-of-arrival delivery of individual > user messages, > -- optional multiplexing of user messages into SCTP datagrams, and > -- network-level fault tolerance through supporting of multi-homing > at either or both ends of an association. > > The design of SCTP includes appropriate congestion avoidance behavior > and resistance to flooding and masquerade attacks. There was a SCTP bake-off hosted by Siemens in Munich two weeks ago. Twelve companies interop tested their implementations, Ericsson was one of theme. The results were reported to exceed expectation and according to the several speakers on the subject no show-stoppers have been identified so far. While still only a draft I think SCTP looks very promising and it might be just what distributed Erlang needs to really provide high availability services. While at it, some attention should also be put on node naming to get rid of all the weird shit happening when trying to use Erlang in a multihomed environment. Comments anyone ? /Per Per Bergqvist Director WAP Innovation & Development CellPoint Systems AB From bjarne@REDACTED Tue Jun 20 23:20:23 2000 From: bjarne@REDACTED (Bjarne =?iso-8859-1?Q?D=E4cker?=) Date: Tue, 20 Jun 2000 23:20:23 +0200 Subject: Welcome to this year's Erlang User Conference Message-ID: <394FE017.58AD41C7@erix.ericsson.se> Dear Erlang Friends, Please check out http://www.erlang.se/euc/00/ and the exciting program for the first Erlang User Conference of this century. Sessions on applications, implementations, developments as well as posters and demos. Plus the chance to mingle with other Erlangers while cruising out into the Stockholm archipelago. Welcome ! Bjarne From bparsia@REDACTED Wed Jun 21 18:48:46 2000 From: bparsia@REDACTED (Bijan Parsia) Date: Wed, 21 Jun 2000 12:48:46 -0400 (EDT) Subject: Bit of publicity for: (Re: Welcome to this year's Erlang User Conference) In-Reply-To: <394FE017.58AD41C7@erix.ericsson.se> Message-ID: Hello folks, Upon getting all excited about the program for the Conference (which I, alas, will just have to wait until the papers are posted :(), I did a little (er...little long) write up about the conference and Erlang in general for http://monkeyfist.com/ Technical, grammatical, and tastful corrections, comments, and emendations are welcome. (It's the top article of the moment, but the direct url is: http://monkeyfist.com/articles/580) We don't (yet) have a very large audience, but it's been steadily growing since we launched in Jan. I'll do a follow up as well when the articles come on line. Cheers, Bijan Parsia. From bjarne@REDACTED Wed Jun 21 19:16:48 2000 From: bjarne@REDACTED (Bjarne =?iso-8859-1?Q?D=E4cker?=) Date: Wed, 21 Jun 2000 19:16:48 +0200 Subject: Erlang User Conference' References: Message-ID: <3950F880.A6204450@erix.ericsson.se> Erlang users and other interested parties are invited to the Sixth EUC (Erlang/OTP User Conference) which will take place in ?lvsj?, Stockholm, on the 3rd of October. The conference in particular is aimed for - Applications developers using Erlang and OTP (Open Telecom Platform). - Researchers and technology developers working with Erlang and OTP. - People interested in the development and use of functional programming. - People interested in the development of large scale industrial control systems. The conference program, how to register etc etc is available at http://www.erlang.se/euc/00/ Welcome ! Bjarne D?cker Computer Science Lab Ericsson Utvecklings AB PO Box 1505 S-125 25 ?lvsj? Sweden From vances@REDACTED Thu Jun 22 00:35:47 2000 From: vances@REDACTED (Vance Shipley) Date: Wed, 21 Jun 2000 18:35:47 -0400 Subject: Abstract Form Message-ID: I've been looking at the stdlib modules which parse Erlang. There are a few which return {ok, AbsForm}. I can't find a definition for this anywhere. The manual page for erl_parse says: parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} ... AbsForm = term() ... See section Abstract Form below for a description of AbsForm. Unfortunately the section "Abstract Form" at the bottom of this page says: To be supplied What I am interested in doing is scanning my gen_fsm behaviour source files and creating a graph of their state machines. I intend to feed this to dot to automatically create state diagrams from the source. I had started to do this using awk but thought that an Erlang solution would be most logical. Any comments on the best approach to this problem are welcome. -Vance [dot is an open source filter tool for drawing directed graphs, it can be found at http://www.research.att.com/sw/tools/graphviz/ ] From jhague@REDACTED Mon Jun 26 05:06:16 2000 From: jhague@REDACTED (James Hague) Date: Sun, 25 Jun 00 22:06:16 -0500 Subject: Constant lists Message-ID: <200006260306.WAA20991@node-02.advancenet.net> In Erlang, if you want to have a constant, medium-sized list, what's the best way of dealing with it? In Lisp, for example, you use defparameter to define a list and then reference it when you need it. In Erlang, you could do this: constant_list() -> [item1, item2, item3, item4, ...]. but then that list is actually created each time you call it, which is messy. It's not really a constant in this case. For association lists, you could code it in a Prolog style: name(john) -> 7; name(bjorn) -> 3; name(james) -> 9. but this isn't general. Or, you could read the term from disk, but then only the current process has access to it. Putting it in a global ets table results in the list being copied each time it is fetched. Sure would be nice to have honest-to-goodness constant lists and tuples. Any thoughts? James From tobbe@REDACTED Mon Jun 26 07:57:59 2000 From: tobbe@REDACTED (Torbjorn Tornkvist) Date: 26 Jun 2000 07:57:59 +0200 Subject: Constant lists In-Reply-To: James Hague's message of "Sun, 25 Jun 00 22:06:16 -0500" References: <200006260306.WAA20991@node-02.advancenet.net> Message-ID: > In Erlang, you could do this: > > constant_list() -> [item1, item2, item3, item4, ...]. Or this: -define(CONSTANT_LIST, [item1, item2, item3, item4, ...] ). ... X = ?CONSTANT_LIST, ... Cheers /Tobbe From etxuwig@REDACTED Mon Jun 26 09:05:37 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 26 Jun 2000 09:05:37 +0200 (MET DST) Subject: Bit of publicity for: (Re: Welcome to this year's Erlang User Conference) In-Reply-To: Message-ID: Nice article! I found only a couple of typos: - Towards the end, in the "(Ok, ok, ...)" paragraph, 'dereferencing' is spelled wrong. - and two paragraphs down, you've misspelled 'regexes' the first time around. Regards, Uffe On Wed, 21 Jun 2000, Bijan Parsia wrote: >Hello folks, > >Upon getting all excited about the program for the Conference (which I, >alas, will just have to wait until the papers are posted :(), I did a >little (er...little long) write up about the conference and Erlang in >general for http://monkeyfist.com/ > >Technical, grammatical, and tastful corrections, comments, and emendations >are welcome. > >(It's the top article of the moment, but the direct url >is: http://monkeyfist.com/articles/580) > >We don't (yet) have a very large audience, but it's been steadily growing >since we launched in Jan. I'll do a follow up as well when the articles >come on line. > >Cheers, >Bijan Parsia. > > -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From etxuwig@REDACTED Mon Jun 26 09:39:59 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 26 Jun 2000 09:39:59 +0200 (MET DST) Subject: Bit of publicity for: (Re: Welcome to this year's Erlang User Conference) In-Reply-To: Message-ID: Oops! That wasn't supposed to go to the list -- sorry. /Uffe On Mon, 26 Jun 2000, Ulf Wiger wrote: > >Nice article! > >I found only a couple of typos: > >- Towards the end, in the "(Ok, ok, ...)" paragraph, 'dereferencing' > is spelled wrong. > >- and two paragraphs down, you've misspelled 'regexes' the first time > around. > >Regards, >Uffe > >On Wed, 21 Jun 2000, Bijan Parsia wrote: > >>Hello folks, >> >>Upon getting all excited about the program for the Conference (which I, >>alas, will just have to wait until the papers are posted :(), I did a >>little (er...little long) write up about the conference and Erlang in >>general for http://monkeyfist.com/ >> >>Technical, grammatical, and tastful corrections, comments, and emendations >>are welcome. >> >>(It's the top article of the moment, but the direct url >>is: http://monkeyfist.com/articles/580) >> >>We don't (yet) have a very large audience, but it's been steadily growing >>since we launched in Jan. I'll do a follow up as well when the articles >>come on line. >> >>Cheers, >>Bijan Parsia. >> >> > > -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From klacke@REDACTED Mon Jun 26 09:52:19 2000 From: klacke@REDACTED (Klacke) Date: Mon, 26 Jun 2000 09:52:19 +0200 Subject: Constant lists In-Reply-To: References: <200006260306.WAA20991@node-02.advancenet.net> Message-ID: <20000626095219.B10318@bluetail.com> On Mon, Jun 26, 2000 at 07:57:59AM +0200, Torbjorn Tornkvist wrote: > > > In Erlang, you could do this: > > > > constant_list() -> [item1, item2, item3, item4, ...]. > > Or this: > > -define(CONSTANT_LIST, [item1, item2, item3, item4, ...] ). > > ... > X = ?CONSTANT_LIST, > ... Which ain't that good either since it will also recreate the list every time the macro is used. No I suggest using the dictionary, new_const_list() -> [1,2,3,4,5,6,7]. const_list() -> case get(const_list) of undefined -> N = new_const_list(), put(const_list, N), N; List -> List end. /klacke -- Claes Wikstrom Bluetail AB http://www.bluetail.com From bparsia@REDACTED Mon Jun 26 13:25:40 2000 From: bparsia@REDACTED (Bijan Parsia) Date: Mon, 26 Jun 2000 07:25:40 -0400 (EDT) Subject: Bit of publicity for: (Re: Welcome to this year's Erlang User Conference) In-Reply-To: Message-ID: On Mon, 26 Jun 2000, Ulf Wiger wrote: > > Oops! That wasn't supposed to go to the list -- sorry. > > /Uffe Quite alright! Thanks for the feedback, and to everyone else who's responded. Cheers, Bijan Parsia. From martin@REDACTED Wed Jun 28 22:17:18 2000 From: martin@REDACTED (Martin Logan) Date: Wed, 28 Jun 2000 15:17:18 -0500 (CDT) Subject: C to Erlang Message-ID: I have been hacking erlang for a few weeks now and have found, so far, that it meets and surpasses all my expectations, and more importantly, needs. I know that erlang has the ability to interface with C. I am now working on a project that is going to require the reverse. Can anyone tell me if it is possable to call erlang modules from C or C++. Thank you, Martin From per@REDACTED Fri Jun 30 21:10:34 2000 From: per@REDACTED (Per Hedeland) Date: Fri, 30 Jun 2000 21:10:34 +0200 (MET DST) Subject: C to Erlang In-Reply-To: References: Message-ID: <200006301910.e5UJAYU00446@super.du.uab.ericsson.se> Martin Logan wrote: >I have been hacking erlang for a few weeks now and have found, so far, >that it meets and surpasses all my expectations, and more importantly, >needs. I know that erlang has the ability to interface with C. I am now >working on a project that is going to require the reverse. Can anyone >tell me if it is possable to call erlang modules from C or C++. Well, depending on exactly what semantics you associate with "call", the erl_interface library could be what you're looking for. It lets a "C node" communicate with distributed Erlang nodes, and part of the functionality this provides is of course the possibility to make remote procedure calls - i.e. you can call an Erlang function in an Erlang node from your C node. --Per Hedeland per@REDACTED