CONCEPT:

        erq - External Request Demon

DESCRIPTION:

        Up to version 3.2.1@61, LPMud utilized two external programs
        in an ad-hoc manner to solve problems: the 'hname' program to
        resolve IP addresses into meaningful hostnames, and the
        'indent' program to properly indent LPC files. In version
        3.2.1@61 both functions were united in a generalized 'erq'
        process, to which additional functions may be attached.
        Unfortunately it was never documented by Amylaar, so the
        information presented here had to be reverse engineered
        from the sources - better take it with a grain of salt.

        The erq feature is available if the driver is compiled with
        ERQ_DEMON defined (in config.h).

        When the driver starts up, it tries to fork off the program
        'BINDIR/erq --forked <other args>' (with BINDIR defined in
        the Makefile). If this succeeds, the erq may talk with
        the driver through stdin and stdout (piped through AF_UNIX
        sockets). The erq has to signal its successfull start by
        writing the character '1' back to the driver.

        The erq has to understand these commandline arguments:

          --forked: explained above
          --execdir <dir>: The directory where the callable executables
                    can be found. If not specified, ERQ_DIR is used.
                    <dir> must not end in a '/' and should be absolute.

        At runtime, the erq may be changed/removed from within the
        mudlib using the efun attach_erq_demon(). This efun is given
        an interactive object as argument, and takes the connection
        away(!) from this object and stores it as the erq connection
        to use (an old erq connection is closed first). The object
        (which is now no longer is interactive) is then no longer
        needed, but may continue to exist. The erq attached this way
        of course has to use the sockets it opened to communicate
        with the driver.

        Most of the communication between erq and driver is going to
        be initiated by the driver (the erq has to look up the
        hostnames for given IP addresses), but using the efun
        send_erq() the mudlib may talk with the erq as well.

        The communication between driver and erq is done using
        messages of specified structures and constants (defined in
        util/erq.h resp. sys/erq.h). The 'int32's are signed integers
        of four byte length, and are sent with the MSByte first.
        Every message must be sent atomically!

        The head of the messages is always the same:

          struct erq_msghead {
            int32  msglen;  /* Total size of message in bytes */
            int32  handle;  /* Identification number */
          }

        The 'handle' number is set by the driver (do not make
        assumptions about its value) and is used to associated the erq
        responses with the pending requests. This way the erq is free
        to respond in an order different to those of the incoming
        requests.

        The messages send to the erq follow this symbolic format:

          struct to_erq_msg {
            int32  msglen;
            int32  handle;
            char   request;
            char   data[0];
          }

        The 'request' denotes which service is requested from the erq,
        the size and content of 'data' depends on the requested
        service.

        The answer message from the erq to the driver (if there is one
        at all) may have two forms:

          struct from_erq_msg {
            int32  msglen;
            int32  handle;
            char   data[0];
          }

          struct from_erq_keep_msg {
            int32        msglen;
            const int32  keep = ERQ_KEEP_HANDLE;
            int32        handle;
            char         data[0];
          }

        The replied data from the erq is stored in 'data', which size
        and content depends on the request answered. The answer is
        identified by 'header.handle'. Normally, one request results
        in just one response sent by the erq using struct from_erq_msg,
        so the handle is recycled after this response.

        Shall the erq send several responses (or break one response
        into several parts), the struct from_erq_keep_msg has to be
        used for all but the last response - this message with its
        included special handle keeps the real handle alive.


        Mudlib generated erq-calls specify the 'request' and the
        'data' to be sent, and receive the 'data' replied. When
        dealing with spawned programs, the first byte of the returned
        'data' determines the content type of the received message.
        The actual 'data' which the lpc programs get to see is sent
        and retrieved as arrays of byte integers (integers in the
        range of 0..255).


        The actual interface between erq demon and driver is limited
        to the general message formats and the hostname lookup
        mechanism. The driver is meant to withstand erq demon failures
        at least in a garbage-in garbage-out fashion. You could add
        new requests to the erq demon, or write your own from scratch,
        without changing the driver.


        Currently five services are predefined in the supplied
        erq-demon (util/erq.c in the driver source archive): looking
        up a hostname, execution, forking or spawning an external
        program, authentification of a connection, and handling of
        external UDP/TCP connections. As mentioned above, only the
        hostname-lookup is a true must.

        For a program to be executable for erq, it must be placed in
        or below ERQ_DIR (defined in config.h). On most unix systems,
        it is possible to use a symlink instead of the whole program
        if you want a standard binary. You could even symlink entire
        directories like /usr/sbin, but chances are you make a big
        security hole this way :-)


        Hostname lookup:

          request  : ERQ_RLOOKUP
          data sent: struct in_addr.s_addr addr // the address to resolve
          data recv: struct in_addr.s_addr addr // the resolved address
                     char[]                name // the hostname (if any)

          If the sent address can't be resolved, just the address is
          to be returned. The string need not be 0-terminated.


        Hostname lookup:

          request  : ERQ_LOOKUP
          data sent: char[]                name // the name to resolve
          data recv: struct in_addr.s_addr addr // the resolved address

          If the sent address can't be resolved, no data is returned (the
          driver will get a message with just the header).


        Hostname lookup - IPv6:

          request  : ERQ_RLOOKUPV6
          data sent: char[] addr  // the address to resolve
          data recv: char[] data  // the resolved name

          If the address could be resolved, the returned data is a string,
          with exactly one space, in the form "<addr> <name>". <addr> is
          the address passed to the erq, <name> is the hostname of the
          address or, if there is no reverse-IPv6 entry for <addr>, the
          IPv6 address which may or may not be different from <addr>.

          If the address can not be resolved, the returned data is
          an error message without a space (currently, just "invalid-format"
          and "out-of-memory" are returned).


        Execute/Fork program:

          request  : ERQ_EXECUTE/ERQ_FORK
          data sent: char[] command  // the command to execute
          data recv: char   status = CHILD_FREE
                     char   rc       // the success/error code
                     char   info     // additional information

          The erq executes the sent command using the execv().
          The erq does the processing of the command line arguments
          (which must not contain '\') and checks the validity of the
          command (it must not start with '/' nor contain '..'), which
          is interpreted relative to ERQ_DIR.
          The external program is executed from a fork()ed instance of
          the erq, however, with ERQ_EXECUTE the erq waits until the
          external program finished before replying its response, with
          ERQ_FORK the response is immediately sent back.

          Possible return codes are:
            ERQ_OK         : Operation succeeded.
            ERQ_E_ARGLENGTH: Too long command.
            ERQ_E_ARGFORMAT: Illegal argument given (contains '\');
            ERQ_E_ARGNUMBER: Too much arguments (>= 96).
            ERQ_E_ILLEGAL  : Command from outside ERQ_DIR requested.
            ERQ_E_PATHLEN  : Commandpath too long.
            ERQ_E_FORKFAIL : Command could not be forked;
                             info holds the errno value.

          ERQ_EXECUTE features some more return codes:
            ERQ_OK         : Operation succeeded, <info> holds the exit status.
            ERQ_SIGNALED   : Command terminated the signal <info>.
            ERQ_E_NOTFOUND : No process found to wait() for.
            ERQ_E_UNKNOWN  : Unknown exit condition from wait().


        Spawn program:

          request  : ERQ_SPAWN
          data sent: char[] command  // the command to execute
          data recv: Spawn failed:
                     char   rc       // the error code (see ERQ_FORK)
                     char   info     // additional information
          data recv: Spawn succeeded:
                     char   rc = ERQ_OK
                     char[] ticket        // the spawn ticket.

          The erq executes the sent command as if given an ERQ_FORK
          command, but returns additional information about the
          started process to allow further communication.
          In contrast to ERQ_FORK, ERQ_SPAWNED processes may be
          controlled via ERQ_KILL, receive data from the mud via
          ERQ_SEND on their stdin, and output from their stdout/stderr
          is sent back to the mud.
          The spawned process is identified by its <ticket> (don't
          make any assumptions about its length or content), the transaction
          itself by <handle>.


        Send data to spawned program:

          request  : ERQ_SEND
          data sent: char[]  ticket // the addressed process ticket.
                     char[]  text   // the text to send.
          data recv: char    rc     // the success/error code.
                     int32   info   // opt: additional info.

          The <text> is sent to the stdin of the spawned process
          identified by <ticket>.

          Possible return codes are:
            ERQ_OK          : Operation succeeded, no <info> is replied.
            ERQ_E_TICKET    : The given ticket is invalid, no <info> replied.
            ERQ_E_INCOMPLETE: Only <info> chars of the text have been
                              sent.
                              If a callback is specified, the erq will send
                              a ERQ_OK message once all data has been sent
                              (this may never happen).
            ERQ_E_WOULDBLOCK: Error E_WOULDBLOCK (also stored in <info>)
                              happened while sending the text.
            ERQ_E_PIPE      : Error E_PIPE (also stored in <info>)
                              happened while sending the text.
            ERQ_E_UNKNOWN   : The error with code <info> happened
                              while sending the data.

         Amylaar-erq doesn't try to re-send the remaining data after
         a ERQ_E_INCOMPLETE, so there will never be an ERQ_OK.


        Send a signal to a spawned program:

          request  : ERQ_KILL
          data sent: char[]  ticket // the addressed process ticket
                     int32   signal // the signal to send
          data recv: char    rc     // the success/error code

          The <signal> is sent to the spawned process identified by <ticket>.

          Possible return codes are:
            ERQ_OK          : Operation succeeded, no <info> is replied.
            ERQ_E_TICKET    : The given ticket is invalid, no <info> replied.
            ERQ_E_ILLEGAL   : The given signal is illegal.


        Data replies from spawned programs:

          data recv: char   out_or_err  // type of text output
                     char[] text        // text output by child process

          The child process controlled by the erq did output <text>
          on stdout (<out_or_err> == ERQ_STDOUT) resp. on stderr
          (<out_or_err> == ERQ_STDERR).


        Exit notifications from spawned programs:

          data recv: char   rc          // the exit code
                     char   info        // additional information.

          The child process controlled by the erq did terminate.
          Possible exit codes are:
            ERQ_EXITED    : Process exited with status <info>.
            ERQ_SIGNALED  : Process terminated by signal <info>.
            ERQ_E_UNKNOWN : Process terminated for unknown reason.


        Authenticate connection (see rfc 931):

          request  : ERQ_AUTH
          data sent: struct sockaddr_in remote // the address to check
                     int32              port   // the mud port
             or
          data sent: int32  remote_ip    // remote ip to check
                     int16  remote_port  // remote port to check
                     int16  local_port   // the mud port

          data recv: char[]             reply  // the data received by authd

          The erq attempts to connect the authd on the remote system
          and to verify the connection between the remote port and the
          mud port. The latter will normally be the port number of the
          socket on besides of the gamedriver, retrievable by
          query_ip_number().

          The answer from the authd (one line of text) if there is any
          is returned as result.

          The second form of the ERQ_AUTH command is recognized by
          the xerq as alternative.


        Open an UDP port:

          request  : ERQ_OPEN_UDP
          data sent: char[2] port   // the port number to open (network order)
          data recv: Open failed:
                     char    rc     // the success/error code.
                     char    info   // opt: additional info.
          data recv: Open succeeded:
                     char   rc = ERQ_OK
                     char[] ticket  // the connection ticket.

          The erq opens an UDP-port on the host machine with the given
          port number.
          Possible exit codes are:
            ERQ_OK          : Operation succeeded.
            ERQ_E_ARGLENGTH : The port number given does not consist
                              of two bytes.
            ERQ_E_NSLOTS    : The max number of child processes (given
                              in <info>) is exhausted.
            ERQ_E_UNKNOWN   : Error <info> occured in one of the system
                              calls done to open the port.

          Once the port is open, it is treated as if is just another
          spawned program.


        Send data over an UDP port:

          request  : ERQ_SEND
          data sent: char[]                ticket // the addressed port's ticket.
                     struct in_addr.s_addr addr   // address of receiver.
                     struct addr.sin_port  port   // port of receiver.
                     char[]                text   // the text to send.
          data recv: char   rc     // the success/error code.
                     int32  info   // opt: additional info.

          The <text> is sent from our port <ticket> to the network
          address <addr>, port <port>.

          Possible return codes are:
            ERQ_OK          : Operation succeeded, no <info> is replied.
            ERQ_E_TICKET    : The given ticket is invalid, no <info> replied.
            ERQ_E_INCOMPLETE: Only <info> chars of the text have been
                              sent. The erq will send a ERQ_OK message
                              once all data has been sent.
            ERQ_E_WOULDBLOCK: Error E_WOULDBLOCK (also stored in <info>)
                              happened while sending the text.
            ERQ_E_PIPE      : Error E_PIPE (also stored in <info>)
                              happened while sending the text.
            ERQ_E_UNKNOWN   : The error with code <info> happened
                              while sending the data.


        Close an UDP port:

          request  : ERQ_KILL
          data sent: char[]  ticket // the addressed port's ticket
                     int32   signal // the signal to send (ignored)
          data recv: char    rc = ERQ_OK

          The port <ticket> is closed. The <signal> must be sent, but
          its value is ignored.


        Data received over an UDP connection:

          data recv: char                  out_or_err = ERQ_STDOUT
                     struct in_addr.s_addr addr    // ip-address of sender
                     struct addr.sin_port  port    // port of sender
                     char[]                text    // data received

          The UPD port controlled by the erq did receive <text> over
          the network from the sender at <addr>, reply port number <port>.


        Open a TCP port to listen for connections:

          request  : ERQ_LISTEN
          data sent: struct addr.sin_port port   // the port number to open
          data recv: Open failed:
                     char    rc     // the success/error code.
                     char    info   // opt: additional info.
          data recv: Open succeeded:
                     char   rc = ERQ_OK
                     char[] ticket  // the connection ticket.

          The erq opens a TCP-port on the host machine with the given
          port number to listen for connections.
          Possible exit codes are:
            ERQ_OK          : Operation succeeded.
            ERQ_E_ARGLENGTH : The port number given does not consist
                              of two bytes.
            ERQ_E_NSLOTS    : The max number of child processes (given
                              in <info>) is exhausted.
            ERQ_E_UNKNOWN   : Error <info> occured in one of the system
                              calls done to open the port.

          Once the port is open, it is treated as if is just another
          spawned program.


        Open a TCP port:

          request  : ERQ_OPEN_TCP
          data sent: struct in_addr.s_addr ip   // the ip to address
                     struct addr.sin_port  port // the port to address
          data recv: Open failed:
                     char    rc     // the success/error code.
                     char    info   // opt: additional info.
          data recv: Open succeeded:
                     char   rc = ERQ_OK
                     char[] ticket  // the connection ticket.

          The erq opens a TCP-port on the host machine and tries to connect
          it to the address <ip>:<port>.
          Possible exit codes are:
            ERQ_OK          : Operation succeeded.
            ERQ_E_ARGLENGTH : The port number given does not consist
                              of two bytes.
            ERQ_E_NSLOTS    : The max number of child processes (given
                              in <info>) is exhausted.
            ERQ_E_UNKNOWN   : Error <info> occured in one of the system
                              calls done to open the port.

          Once the port is open, it is treated as if is just another
          spawned program.


        Send data over a TCP connection:

          request  : ERQ_SEND
          data sent: char[]  ticket // the addressed process ticket.
                     char[]  text   // the text to send.
          data recv: char    rc     // the success/error code.
                     int32   info   // opt: additional info.

          The <text> is sent to the stdin of the spawned process
          identified by <ticket>.

          Possible return codes are:
            ERQ_OK          : Operation succeeded, no <info> is replied.
            ERQ_E_TICKET    : The given ticket is invalid, no <info> replied.
            ERQ_E_INCOMPLETE: Only <info> chars of the text have been
                              sent. The erq will send a ERQ_OK message
                              once all data has been sent.
            ERQ_E_WOULDBLOCK: Error E_WOULDBLOCK (also stored in <info>)
                              happened while sending the text.
            ERQ_E_PIPE      : Error E_PIPE (also stored in <info>)
                              happened while sending the text.
            ERQ_E_UNKNOWN   : The error with code <info> happened
                              while sending the data.


        Data ready to read on TCP connection:

          data recv: char    out_or_err = ERQ_OK
                     char[]  ticket  // ticket of this connection

          There is data available to read on the specified TCP connection.


        Data received over a TCP connection:

          data recv: char    out_or_err = ERQ_STDOUT
                     char[]  text    // data received

          The TCP port controlled by the erq did receive <text>.


        TCP connection closes on error:

          data recv: char    out_or_err = ERQ_E_UNKNOWN
                     char    errno   // errno from socket operation

          The TCP connection caused an error <errno> and has been closed.


        TCP connection closed:

          data recv: char    out_or_err = ERQ_EXITED

          The TCP connection closed regularily (End Of File).


        Connection pending on TCP socket:

          data recv: char    out_or_err = ERQ_STDOUT

          The TCP 'listen' port controlled by the erq received
          a connection request.


        Accept a pending connections:

          request  : ERQ_ACCEPT
          data sent: char[]  ticket // the ticket of this socket
          data recv: Accept failed:
                     char    rc     // the success/error code.
                     char    info   // opt: additional info.
          data recv: Accept succeeded:
                     char    rc = ERQ_OK
                     struct in_addr.s_addr ip    // remote side's ip
                     struct addr.sin_port  port  // remote side's port
                     char[]                ticket  // the new ticket.

          The erq accepts a new connection on an accept-TCP-port, creates
          a child and ticket for it, and returns its ticket together with
          the remote's side <ip>:<port> number (in network byte order).
          Possible exit codes are:
            ERQ_OK          : Operation succeeded.
            ERQ_E_ARGLENGTH : The port number given does not consist
                              of two bytes.
            ERQ_E_NSLOTS    : The max number of child processes (given
                              in <info>) is exhausted.
            ERQ_E_TICKET    : the ticket didn't match
            ERQ_E_UNKNOWN   : Error <info> occured in one of the system
                              calls done to open the port.

          Once the port is open, it is treated as if it is just another
          spawned program.

EXAMPLE:

        Assume you have a script 'welcome-mail' to send a welcome mail
        to a new player. Put this script into the directory for the callable
        executables, then you can use it like this:

        void erq_response(mixed * data)
        {
            write_file( "WELCOMELOG"
                      , sprintf("rc %d, info %d\n", data[0], data[1]));
        }

        void send_mail(string player_name, string player_email)
        {
            send_erq( ERQ_EXECUTE
                    , "welcome-mail '"+player_name+"' '"+player_email+"'"
                    , #'erq_response);
        }

HISTORY:

        The erq was introduced with 3.2.1@61.
        ERQ_AUTH was introduced with 3.2.1@81.
        ERQ_SEND, ERQ_SPAWN, ERQ_KILL were introduced with 3.2.1@82.
        ERQ_OPEN_UDP, ERQ_OPEN_TCP, ERQ_LIST were introduced with 3.2.1@98.
        ERQ_RLOOKUPV6 was introduced in 3.2.8.
        LDMud 3.2.9 added the '--execdir' argument to erq, and the ERQ_OK
          after ERQ_E_INCOMPLETE protocol.

SEE ALSO:

	
	attach_erq_demon(E), send_erq(E), stale_erq(M), rfc, 931,
	query_ip_number(E)

UNItopia (mudadm@UNItopia.de)