
#include "devel_template.inc" title="Bacula TCP/IP Network Protocol"

<H1>TCP/IP Network Protocol</H1>

<h3>General</h3>
This document describes the TCP/IP protocol used by Bacula
to communicate between the various daemons and services. The
definitive definition of the protocol can be found in 
src/lib/bsock.h, src/lib/bnet.c and src/lib/bnet_server.c.
<p>
Bacula's network protocol is basically a &quot;packet oriented&quot;
protocol built on a standard TCP/IP streams. 
At the lowest level all packet transfers are done with read() and write()
requests on system sockets. Pipes are not used as they
are considered unreliable for large serial
data transfers between various hosts.

<p>Using the routines described below (bnet_open, bnet_write,
bnet_recv, and bnet_close) guarantees that the number of
bytes you write into the socket will be received as
a single record on the other end regardless of how
many low level write() and read() calls are needed.
All data transferred are considered to be binary data.

<h3>bnet and Threads</h3>
These bnet routines work fine in a threaded environment. 
However, they assume that there is only one reader or writer
on the socket at any time. It is highly recommended that
only a single thread access any BSOCK packet. The exception
to this rule is when the socket is first opened and it
is waiting for a job to start. The wait in the Storage
daemon is done in one thread and then passed to another
thread for subsequent handling.
<p>If you envision having two threads using the same BSOCK,
think twice, then you must implement some locking mechanism.
However, it probably
would not be appropriate to put locks inside the bnet
subroutines for efficiency reasons.

<h3>bnet_open</h3>
To establish a connection to a server, use the
subroutine:
<p class="tty">BSOCK *bnet_open(void *jcr, char *host, char *service, int port,
  int *fatal)</p>
bnet_open(), if successful, returns the Bacula sock descriptor pointer
to be used in subsequent bnet_send() and bnet_read() requests. If not
successful, bnet_open() returns a NULL.  If fatal is set on return, it
means that a fatal error occurred and that you should not repeatedly call
bnet_open(). Any error message will generally be sent to the JCR.

<h3>bnet_send</h3>
To send a packet, one uses the subroutine:
<p class="tty">int bnet_send(BSOCK *sock)</p>
This routine is equivalent to a write() except that it handles
the low level details. The data to be sent is expected to
be in sock-&gt;msg and be sock-&gt;msglen bytes.
To send a packet, bnet_send() first
writes four bytes in network byte order than 
indicate the size of the following data packet.
It returns:
<pre>
 Returns 0 on failure
 Returns 1 on success
</pre>
In the case of a failure, an error message will be sent to the JCR
contained within the bsock packet.

<h3>bnet_fsend</h3>
This form uses:
<p class="tty">int bnet_fsend(BSOCK *sock, char *format, ...)</p>
and it allows you to send a formatted messages somewhat like
fprintf(). The return status is the same as bnet_send.

<h3>Additional Error information</h3>
Fro additional error information, you can call <b>is_bnet_error(BSOCK *bsock)</b>
which will return 0 if there is no error or non-zero if there is an error
on the last transmission. The <b>is_bnet_stop(BSOCK *bsock)</b> function
will return 0 if there no errors and you can continue sending. It will
return non-zero if there are errors or the line is closed (no more
transmissions should be sent).

<h3>bnet_recv</h3>
To read a packet, one uses the subroutine:
<p class="tty">int bnet_recv(BSOCK *sock)</p>
This routine is similar to a read() except that it handles
the low level details. bnet_read() first reads packet length
that follows as four bytes in network byte order. The
data is read into sock-&gt;msg and is sock-&gt;msglen bytes. If
the sock-&gt;msg is not large enough, bnet_recv() realloc()
the buffer.
It will return an error (-2) if maxbytes is less than
the record size sent.
It returns:
<pre>
 * Returns number of bytes read
 * Returns 0 on end of file
 * Returns -1 on hard end of file (i.e. network connection close)
 * Returns -2 on error
</pre>
<p>It should be noted that bnet_recv() is a blocking read.

<h3>bnet_sig</h3>
To send a &quot;signal&quot; from one daemon to another,
one uses the subroutine:
<p class="tty">int bnet_sig(BSOCK *sock, SIGNAL)</p>
where SIGNAL is one of the following:
<ol>
  <li>BNET_EOF - deprecated use BNET_EOD</li>
  <li>BNET_EOD - End of data stream, new data may follow</li>
  <li>BNET_EOD_POLL - End of data and poll all in one </li>
  <li>BNET_STATUS - Request full status</li>
  <li>BNET_TERMINATE - Conversation terminated, doing close() </li>
  <li>BNET_POLL - Poll request, I'm hanging on a read </li>
  <li>BNET_HEARTBEAT - Heartbeat Response requested </li>
  <li>BNET_HB_RESPONSE - Only response permitted to HB </li>
  <li>BNET_PROMPT - Prompt for UA </li>
</ol>

<h3>bnet_strerror</h3>
Returns a formated string corresponding to the last
error that occurred.

<h3>bnet_close</h3>
The connection with the server remains open until closed
by the subroutine:
<p class="tty">void bnet_close(BSOCK *sock)</p>

<h3>Becoming a Server</h3>
The bnet_open() and bnet_close() routines described above
are used on the client side to establish a connection
and terminate a connection with the server. To become
a server (i.e. wait for a connection from a client),
use the routine <b>bnet_thread_server</b>. The calling
sequence is a bit complicated, please refer to the code
in bnet_server.c and the code at the beginning of each
daemon as examples of how to call it.

<h3>Higher Level Conventions</h3>
Within Bacula, we have established the convention that any time a
single record is passed, it is sent with bnet_send() and read with
bnet_recv().  Thus the normal exchange between the server (S) and
the client (C) are:
<pre>
S: wait for connection            C: attempt connection
S: accept connection              C: bnet_send() send request
S: bnet_recv() wait for request    
S: act on request
S: bnet_send() send ack            C: bnet_recv() wait for ack          
</pre>
Thus a single command is sent, acted upon by the server, and
then acknowledged.
<p>In certain cases, such as the transfer of the data for
a file, all the information or data cannot be sent in a single
packet. In this case, the convention is that the client will
send a command to the server, who knows that more than one
packet will be returned. In this case, the server will 
enter a loop:
<pre>
while ((n=bnet_recv(bsock)) &gt; 0) {
   act on request
}
if (n &lt; 0)
   error
</pre>
The client will perform the following:
<pre>
bnet_send(bsock);
bnet_send(bsock);
...
bnet_sig(bsock, BNET_EOD);
</pre>
Thus the client will send multiple packets and signal to the
server when all the packets have been sent by sending a
zero length record.


<page_prev name="Bacula Memory Management" url="mempool.html">
<page_next name="MD5 Algorithm" url="md5.html">
