martedì, 24 febbraio 2009
Hello folks!
I've made a new patch to fit new ejabberd 2.0.3.
The file can be found here
Hope it works for you all.
posted() stefko times() 12:30 | Permalink | commenti | commenti (popup)
flash, patch, ejabberd, xml socket
sabato, 07 febbraio 2009
A little bit of Zeak (JsJaC) posted this note about ejabberd memory leak and a possible bug fix in Erlang OTP/R12B:
http://blog.jwchat.org/2009/01/14/ejabberd-memory-leak/

The problem regards how R12 does garbage collecting as we read here:
http://erlang.org/pipermail/erlang-questions/2008-July/036559.html

Well, I'm managing a large cluster of ejabberd servers installation for server-side development and I experienced a similar (very) fastidious memory leak, due to a large amount of HTTP-POLL client connections (JSJaC library), These TCP connections seems to not be closed on the server side modules (ERLANG modules), causing memory crunching (over one gig) and finally the OOM kills the BEAM process.
As we read in the R12B release notes there was this fix too:

"OTP-7615  Fixed memory leak of unclosed TCP-ports. A gen_tcp:send()
          followed by a failing gen_tcp:recv() could in some cases
          cause the port to linger after being closed."

A hard solution (stefko) was to cron the Ejabberd restart every hour, in order to avoid the OOM to kill it and go out of service.
I updated all cluster's nodes to ERLANG OTP/R12B, and I'm monitoring the cluster memory to check the fix gone.
Currently I'm running ejabberd 1.1.3 and the ajax client uses JSJaC 1.3.2.

LP

The Erlang OTP/R12B release notes: http://erlang.org/download/otp_src_R12B-5.readme
The Ejabberd HTTP-POLL: http://www.ejabberd.im/node/94
JSJaC XMPP Client LIB: http://blog.jwchat.org/jsjac/
posted() loretoparisi times() 15:48 | Permalink | commenti | commenti (popup)

venerdì, 30 maggio 2008
A copy of "Programming Erlang, Software for A Concurrent World" by Mr. Joe Armstrong just landed in my lucky hands! I can't wait to read  and learn from Erlangs'creator :)

I'm writing the introduction:


The World is parallel.
If we want to write programs that behave as other objects behave in
the real world, then these programs will have a concurrent structure.

Use a language that was designed for writing concurrent applications,
and development becomes  lot easier.

Erlang programs model how we think and interact

Joe Armstrong

(Programming Erlang, Software for a Concurrent World, Joe Armstrong, Pragmatic Bookshelf, @2007 armstrononsoftware)

Links:

Programming Erlang, Software for a Concurrent World, The Pragmatic Bookshelf.
Armstrononsoftware, Blogger
posted() loretoparisi times() 11:28 | Permalink | commenti (2) | commenti (2) (popup)
programming erlang, joe armstrong
mercoledì, 23 aprile 2008
In Erlang we can use many method to access to lists and tuples, and some of them are very quick and easy.
With ++ and -- operators you can add and subtract lists, like in the following example:

Thelist=[1,2,3,4,5,6,7,8,9,10],
Even = lists:filter(fun(E) ->
       E rem 2 == 0
      end,
      Thelist),
Odd=Thelist -- Even,
Newlist = Odd ++ [ one, five ].

So Odd list will contains [1,3,5,7,9], Even list will contains [2,4,6,8,10] and Newlist will contain [1,3,5,7,9,one,five].
posted() stefko times() 10:20 | Permalink | commenti | commenti (popup)
list, erlang, arithmetic
lunedì, 07 aprile 2008
I've recently been involved in the problem of Flash (7.0 and following) XML socket.
Flash uses a "strange" kind of packet transfer in XML socket so Ejabberd 2.0.0 is not compatible with some Flash version.
I've seen a patch for Ejabberd 1.1.X versions and I've adapted it to fit with ejabberd 2.0.0.
Please note that this patch changes configure.ac file, but you must DO NOT run aclocal command.
At the moment aclocal.m4 file contains some macro that aclocal command will overwrite!
So please follow these steps to apply this patch to Ejabberd 2.0.0:

patch -p0 <flash-xml-ejabberd-2.0.0.diff
rm configure
autoconf
./configure --enable-flash-hack
make


And you'll be able to use XMPP like this:
<?xml version='1.0'?>
<flash:stream to='example.net' xmlns='jabber:client'
xmlns:flash='http://www.jabber.com/streams/flash' version='1.0'>
</flash:stream>
The patch is available here.
posted() stefko times() 13:03 | Permalink | commenti (1) | commenti (1) (popup)
flash, patch, ejabberd, xml socket
lunedì, 07 aprile 2008
As promised I'm releasing the cookie patch for ejabberd 2.0. The patch is quite simple and takes few minutes to check it out.
To apply the patch cumulatively, simply do the following

ejabberd/src/web/ $ patch < $HOME/ejabberd-2.0-cookie.patch
Otherwise you could apply the patch to single files:

ejabberd/src/web/ $ patch ejabberd_http.hrl $HOME/ejabberd_http_hrl.patch
ejabberd/src/web/ $ patch ejabberd_http.erl $HOME/ejabberd_http_erl.patch

Let me know if you got errors applying this patch.

Links:
ejabberd 2.0 Cumulative Cookie Patch
ejabberd_http Source Cookie Patch
ejabberd_http Header Cookie Patch
posted() loretoparisi times() 11:37 | Permalink | commenti | commenti (popup)
cookie, patch, ejabberd
venerdì, 04 aprile 2008
To add cookies to HTTP POST/GET requests in ejabberd (1.1.x and 2.x) we have to add some fields in the request headers, setting up the request and state records in ejabberd_http.erl module and its header file ejabberd_http.hrl.

First of all, we will add the field cookie to the record request in ejabberd_http.hrl header file as follows:

-record(request, {method,
                  path,
                  q = [],
                  us,
                  auth,
                  lang = "",
                  data = "",
                  cookie = "", %% lp: cookie request field
                  ip
                 }).


As you can see we are referring to ejabberd 2.0 (in the request record we have the new field ip for new user info).

Now we will edit the ejabberd_http.erl module, adding the field request_cookie to the record state:

-record(state, {sockmod,
                socket,
                request_method,
                request_version,
                request_path,
                request_auth,
                request_cookie, %% lp: cookie request field
                request_keepalive,
                (...)

 
At this point, we have to pass cookies to handlers, modifying the process_headers and process_request functions:

In the process_header, we will add to the case construct

process_header(State, Data) ->
    SockMod = State#state.sockmod,
    Socket = State#state.socket,
    case Data of
        {ok, {http_request, Method, Uri, Version}} ->
   
    (...)
        {ok, {http_header, _, 'Authorization', _, Auth}} ->
            State#state{request_auth = parse_auth(Auth)};
        {ok, {http_header, _, 'Cookie', _, Cookie}} ->
            %% lp: setting up request header cookie
            State#state{request_cookie = Cookie};
        {ok, {http_header, _, 'Content-Length', _, SLen}} ->
    (...)


In the process_request we have to modify the function header as follows:

process_request(#state{request_method = 'GET',
                       request_path = {abs_path, Path},
                       request_auth = Auth,
                       request_lang = Lang,
                       request_cookie = Cookie, %% lp: cookie request field
                       request_handlers = RequestHandlers,
                       sockmod = SockMod,
                       socket = Socket} = State) ->
                       
                        (...)
                        Request = #request{method = 'GET',
                               path = LPath,
                               q = LQuery,
                               auth = Auth,
                               lang = Lang,
                               cookie = Cookie, %% lp: here again :)
                               ip=IP},
                        (...)


and in the next header too:

process_request(#state{request_method = 'POST',
                       request_path = {abs_path, Path},
                       request_auth = Auth,
                       request_content_length = Len,
                       request_lang = Lang,
                       request_cookie = Cookie, %% lp: cookie
                       sockmod = SockMod,
                       socket = Socket,
                       request_handlers = RequestHandlers} = State)
  when is_integer(Len) ->

              (...)
              Request = #request{method = 'POST',
                               path = LPath,
                               q = LQuery,
                               auth = Auth,
                               cookie = Cookie, %% lp: cookie the last one ;)
                               data = Data,
                               lang = Lang},
              case process(RequestHandlers, Request) of
                 (...)


Now, we are ready to add the cookie request's field to our modules processing http requests:

process(#request{us = _US,
                     path = "login",
                     q = _Query,
                     lang = _Lang,
                     cookie = _Cookie} = Request) ->
          
              %% _Cookie will contain the request cookie now
              %% Manage request by cookies
              %% Send response :)



I will eventually post a addon patch for this :)

Enjoy your Jabber,
LP


PS. We used ejabberd 2.0, but you can do this in ejabberd 1.1.x in the same way


Links:
http://www.process-one.net/en/wiki/ejabberd_module_development/
posted() loretoparisi times() 18:05 | Permalink | commenti | commenti (popup)
cookie, request headers
martedì, 01 aprile 2008
In the previous post stefko told us about the new logging facility in ejabberd 2.0. But how can we apply logging verbosity levels in previous releases of ejabberd, such as ejabberd 1.1.2 ?
Well,
I modififed the ejabberd.hrl in this way:

%%
%%    --- 2007-05-10 - LP: LOGGER ---
%%     Verbosity levels:
%%
%%         Level 0 - No verbosity
%%         Level 1 - Error
%%         Level 2 - Warning
%%         Level 3 - Info
%%         Level 4 - Debug
%%

%% LP: Logger environment verbosity level variable
-define(LOGGER_ENV_LEVEL, "EJABBERD_LOGGER_LEVEL").

%% LP: Default Logger Level to 3 - Info level
-define(LOGGER_DEFAULTLEVEL, 3).%% LP: Get verbosity level

-define(LOGGER_SETLEVEL(Level),
    case string:to_integer(os:getenv(?LOGGER_ENV_LEVEL)) of
        {_,[]} ->
            case ({Level,[]} =< string:to_integer(os:getenv(?LOGGER_ENV_LEVEL))) of
                true -> Level;
                false -> string:to_integer(os:getenv(?LOGGER_ENV_LEVEL))
            end;   
        _ ->
            ?LOGGER_DEFAULTLEVEL
    end).

%% LP: Log messages
-define(LOGGER_LOG(Format, Args, Level),
    %% switch verbosity against level
    case Level of
           0 ->
            %% NO VERBOSITY
               ok;
        1 ->
            %% ERROR LEVEL
            error_logger:error_msg("ERROR(~p:~p:~p): "++Format++"~n",
                        [self(),?MODULE,?LINE]++Args);
           2 ->
            %% WARNING LEVEL
               error_logger:warning_msg("WARN(~p:~p:~p): "++Format++"~n",
                           [self(),?MODULE,?LINE]++Args);
          3 ->
              %% INFO_MSG LEVEL
              error_logger:info_msg("INFO(~p:~p:~p): "++Format++"~n",
                         [self(),?MODULE,?LINE]++Args);
          4 ->
              %% DEBUG LEVEL - no custom file logger
            error_logger:info_msg("DEBUG(~p:~p:~p) : "++Format++"~n",
                           [self(),?MODULE,?LINE]++Args);
          _ ->
             %% UNDEFINED LEVEL
              ok
       end).


Then redifining the ?DEBUG, ?INFO_MSG, ?WARNING_MSG and ?ERROR_MSG with the new ?LOGGER_LOG define and adjusting the logger level with ?LOGGER_SETLEVEL(Value), whereas Value goes from 0 (no messages) to 4 (debug mode) as follows:


%% LP: Debug Message
-define(DEBUG(Format, Args), ?LOGGER_LOG(Format, Args, ?LOGGER_SETLEVEL(4))).

%% LP: Info Message
-define(INFO_MSG(Format, Args), ?LOGGER_LOG(Format, Args, ?LOGGER_SETLEVEL(3))).

%% LP: Warning Message
-define(WARNING_MSG(Format, Args), ?LOGGER_LOG(Format, Args, ?LOGGER_SETLEVEL(2))).

%% LP: Error Message
-define(ERROR_MSG(Format, Args), ?LOGGER_LOG(Format, Args, ?LOGGER_SETLEVEL(1))).


In this way it's possible to log errors, warnings, messages using the same code (no need to change other modules log instructions) by setting up the environment variable "EJABBERD_LOGGER_LEVEL", called by os:getenv in the erl shell command:

erl ... -env ERL_MAX_PORTS 32000 \
        -env EJABBERD_LOGGER_LEVEL 4 \

LP

posted() loretoparisi times() 13:04 | Permalink | commenti | commenti (popup)
debug, loglevel, verbosity, logger
mercoledì, 26 marzo 2008
There's a very useful method to dynamically change verbosity level in ejabberd 2.0.0.
You must connect with a remote shell to the node you're interested to debug and simply write:

ejabberd_loglevel:set(LogLevel).

Where LogLevel is:
  • 0: No ejabberd log at all (not recommended)
  • 1: Critical
  • 2: Error
  • 3: Warning
  • 4: Info
  • 5: Debug
posted() stefko times() 11:46 | Permalink | commenti | commenti (popup)
debug, ejabberd, concurrent programming, loglevel
lunedì, 17 marzo 2008
In a previous post,  STDLIB: List To Record, we looked at a way to convert a list to a record by means of matching record fields. We made use of lists:mapfoldl and setelement lists and tuples's functions. That was just an exercise to practise with lists fun.
Now we'll a see a better way to implement this function that make more sense, using built-in function list_to_tuple and doing exactly the same job.

list_to_record(Query)->
   
    Fields=record_info(fields,qs),
    QS=#qs{},
       Record=lists:map(fun(Key) ->
        BKey=atom_to_list(Key),
          Vl=case catch lists:keysearch(BKey,1,Query) of
              {value,VKey} ->
                {K,V}=VKey,
                V;
             false ->  undefined;
             {'EXIT',Reason} -> undefined
             end
    end,Fields),
    list_to_tuple(lists:merge([qs],Record)).


That's all,
LP
posted() loretoparisi times() 10:13 | Permalink | commenti | commenti (popup)
record, list, erlang, stdlib, mapfoldl, tuple