Bacula 1.29 User's Guide Chapter
Back
Bacula Memory Management
Index
Index
Next
MD5 Algorithm

TCP/IP Network Protocol

General

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/bnet.c and src/lib/netserver.c.

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.

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.

bnet and Threads

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.

If you envision having two threads using the same BSOCK, think twice, then you must implement some locking mechanism. It would not be appropriate to put locks inside the bnet subroutines.

bnet_open

To establish a connection to a server, use the subroutine:

BSOCK *bnet_open(char *host, char *service, int port)

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.

bnet_send

To send a packet, one uses the subroutine:

int bnet_send(BSOCK *sock)

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->msg and be sock->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:
 * Returns number of bytes sent
 * Returns -1 on error

bnet_fsend

This form uses:

int bnet_fsend(BSOCK *sock, char *format, ...)

and it allows you to send a formatted messages somewhat like fprintf().

bnet_recv

To read a packet, one uses the subroutine:

int bnet_recv(BSOCK *sock)

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->msg and is sock->msglen bytes. If the sock->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:
 * 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

It should be noted that bnet_recv() is a blocking read.

bnet_sig

To send a "signal" from one daemon to another, one uses the subroutine:

int bnet_sig(BSOCK *sock, SIGNAL)

where SIGNAL is one of the following:
  1. BNET_EOF - deprecated use BNET_EOD
  2. BNET_EOD - End of data stream, new data may follow
  3. BNET_EOD_POLL - End of data and poll all in one
  4. BNET_STATUS - Request full status
  5. BNET_TERMINATE - Conversation terminated, doing close()
  6. BNET_POLL - Poll request, I'm hanging on a read
  7. BNET_HEARTBEAT - Heartbeat Response requested
  8. BNET_HB_RESPONSE - Only response permitted to HB
  9. BNET_PROMPT - Prompt for UA

bnet_strerror

Returns a formated string corresponding to the last error that occurred.

bnet_close

The connection with the server remains open until closed by the subroutine:

void net_close(BSOCK *sock)

Becoming a Server

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:

void net_server(int port, void handle_client_request())

where port is the port on which the client will listen, and handle_client_request() is the subroutine to be called when a client makes a valid connection. This routine fork()s for each client request and when the handle_client_request() returns, the child process terminates. Thus, a number of client requests can be processed simultaneously (currently set at 5). If multiple clients connect and there is a need to coordinate their data, the user must supply some form of communication and/or synchronization such as shared memory and semaphores.

Higher Level Conventions

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:
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
Thus a single command is sent, acted upon by the server, and then acknowledged.

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:

while ((n=bnet_recv(bsock)) > 0) {
   act on request
}
if (n < 0)
   error
The client will perform the following:
bnet_send(bsock);
bnet_send(bsock);
...
bnet_sig(bsock, BNET_EOD);
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.


Back
Bacula Memory Management
Index
Index
Next
MD5 Algorithm
Bacula 1.29 User's Guide
The Network Backup Solution
Copyright © 2000-2003
Kern Sibbald and John Walker