]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bnet_server.c
Tweak layout.
[bacula/bacula] / bacula / src / lib / bnet_server.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28  /*
29   * Originally written by Kern Sibbald for inclusion in apcupsd,
30   *  but heavily modified for Bacula
31   *
32   */
33
34 #include "bacula.h"
35 #include <netinet/in.h>
36 #include <sys/socket.h>
37 #include <stdlib.h>
38 #include <arpa/inet.h>
39 #include <netdb.h>
40 #ifdef HAVE_ARPA_NAMESER_H
41 #include <arpa/nameser.h>
42 #endif
43 #ifdef HAVE_RESOLV_H
44 //#include <resolv.h>
45 #endif
46
47
48 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
49
50 #ifdef HAVE_LIBWRAP
51 #include "tcpd.h"
52 int allow_severity = LOG_NOTICE;
53 int deny_severity = LOG_WARNING;
54 #endif
55
56 static bool quit = false;
57
58 void bnet_stop_thread_server(pthread_t tid)
59 {
60    quit = true;
61    if (!pthread_equal(tid, pthread_self())) {
62       pthread_kill(tid, TIMEOUT_SIGNAL);
63    }
64 }
65
66 /*
67  * Become Threaded Network Server
68  *
69  * This function is able to handle multiple server ips in
70  * ipv4 and ipv6 style. The Addresse are give in a comma
71  * seperated string in bind_addr
72  *
73  * At the moment it is inpossible to bind different ports.
74  */
75 void bnet_thread_server(dlist *addr_list, int max_clients, workq_t *client_wq,
76                         void *handle_client_request(void *bsock))
77 {
78    int newsockfd, stat;
79    socklen_t clilen;
80    struct sockaddr cli_addr;       /* client's address */
81    int tlog;
82    int turnon = 1;
83 #ifdef HAVE_LIBWRAP
84    struct request_info request;
85 #endif
86    IPADDR *ipaddr, *next;
87    struct s_sockfd {
88       dlink link;                     /* this MUST be the first item */
89       int fd;
90       int port;
91    } *fd_ptr = NULL;
92    char buf[128];
93    dlist sockfds;
94
95    char allbuf[256 * 10];
96
97    /*
98     * Remove any duplicate addresses.
99     */
100    for (ipaddr = (IPADDR *)addr_list->first(); ipaddr;
101         ipaddr = (IPADDR *)addr_list->next(ipaddr)) {
102       for (next = (IPADDR *)addr_list->next(ipaddr); next;
103            next = (IPADDR *)addr_list->next(next)) {
104          if (ipaddr->get_sockaddr_len() == next->get_sockaddr_len() &&
105              memcmp(ipaddr->get_sockaddr(), next->get_sockaddr(),
106                     ipaddr->get_sockaddr_len()) == 0) {
107             addr_list->remove(next);
108          }
109       }
110    }
111
112    Dmsg1(100, "Addresses %s\n", build_addresses_str(addr_list, allbuf, sizeof(allbuf)));
113
114    foreach_dlist(ipaddr, addr_list) {
115       /* Allocate on stack from -- no need to free */
116       fd_ptr = (s_sockfd *)alloca(sizeof(s_sockfd));
117       fd_ptr->port = ipaddr->get_port_net_order();
118       /*
119        * Open a TCP socket
120        */
121       for (tlog= 60; (fd_ptr->fd=socket(ipaddr->get_family(), SOCK_STREAM, 0)) < 0; tlog -= 10) {
122          if (tlog <= 0) {
123             berrno be;
124             char curbuf[256];
125             Emsg3(M_ABORT, 0, _("Cannot open stream socket. ERR=%s. Current %s All %s\n"),
126                        be.bstrerror(),
127                        ipaddr->build_address_str(curbuf, sizeof(curbuf)),
128                        build_addresses_str(addr_list, allbuf, sizeof(allbuf)));
129          }
130          bmicrosleep(10, 0);
131       }
132       /*
133        * Reuse old sockets
134        */
135       if (setsockopt(fd_ptr->fd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&turnon,
136            sizeof(turnon)) < 0) {
137          berrno be;
138          Emsg1(M_WARNING, 0, _("Cannot set SO_REUSEADDR on socket: %s\n"),
139                be.bstrerror());
140       }
141
142       int tmax = 30 * (60 / 5);    /* wait 30 minutes max */
143       for (tlog = 0; bind(fd_ptr->fd, ipaddr->get_sockaddr(), ipaddr->get_sockaddr_len()) < 0; tlog -= 5) {
144          berrno be;
145          if (tlog <= 0) {
146             tlog = 2 * 60;         /* Complain every 2 minutes */
147             Emsg2(M_WARNING, 0, _("Cannot bind port %d: ERR=%s: Retrying ...\n"),
148                   ntohs(fd_ptr->port), be.bstrerror());
149          }
150          bmicrosleep(5, 0);
151          if (--tmax <= 0) {
152             Emsg2(M_ABORT, 0, _("Cannot bind port %d: ERR=%s.\n"), ntohs(fd_ptr->port),
153                   be.bstrerror());
154          }
155       }
156       listen(fd_ptr->fd, 50);      /* tell system we are ready */
157       sockfds.append(fd_ptr);
158    }
159    /* Start work queue thread */
160    if ((stat = workq_init(client_wq, max_clients, handle_client_request)) != 0) {
161       berrno be;
162       be.set_errno(stat);
163       Emsg1(M_ABORT, 0, _("Could not init client queue: ERR=%s\n"), be.bstrerror());
164    }
165    /*
166     * Wait for a connection from the client process.
167     */
168    for (; !quit;) {
169       unsigned int maxfd = 0;
170       fd_set sockset;
171       FD_ZERO(&sockset);
172       foreach_dlist(fd_ptr, &sockfds) {
173          FD_SET((unsigned)fd_ptr->fd, &sockset);
174          maxfd = maxfd > (unsigned)fd_ptr->fd ? maxfd : fd_ptr->fd;
175       }
176       errno = 0;
177       if ((stat = select(maxfd + 1, &sockset, NULL, NULL, NULL)) < 0) {
178          berrno be;                   /* capture errno */
179          if (errno == EINTR) {
180             continue;
181          }
182          Emsg1(M_FATAL, 0, _("Error in select: %s\n"), be.bstrerror());
183          break;
184       }
185
186       foreach_dlist(fd_ptr, &sockfds) {
187          if (FD_ISSET(fd_ptr->fd, &sockset)) {
188             /* Got a connection, now accept it. */
189             do {
190                clilen = sizeof(cli_addr);
191                newsockfd = accept(fd_ptr->fd, &cli_addr, &clilen);
192             } while (newsockfd < 0 && errno == EINTR);
193             if (newsockfd < 0) {
194                continue;
195             }
196 #ifdef HAVE_LIBWRAP
197             P(mutex);              /* hosts_access is not thread safe */
198             request_init(&request, RQ_DAEMON, my_name, RQ_FILE, newsockfd, 0);
199             fromhost(&request);
200             if (!hosts_access(&request)) {
201                V(mutex);
202                Jmsg2(NULL, M_SECURITY, 0,
203                      _("Connection from %s:%d refused by hosts.access\n"),
204                      sockaddr_to_ascii(&cli_addr, buf, sizeof(buf)),
205                      sockaddr_get_port(&cli_addr));
206                close(newsockfd);
207                continue;
208             }
209             V(mutex);
210 #endif
211
212             /*
213              * Receive notification when connection dies.
214              */
215             if (setsockopt(newsockfd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&turnon,
216                  sizeof(turnon)) < 0) {
217                berrno be;
218                Emsg1(M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"),
219                      be.bstrerror());
220             }
221
222             /* see who client is. i.e. who connected to us. */
223             P(mutex);
224             sockaddr_to_ascii(&cli_addr, buf, sizeof(buf));
225             V(mutex);
226             BSOCK *bs;
227             bs = init_bsock(NULL, newsockfd, "client", buf, ntohs(fd_ptr->port), &cli_addr);
228             if (bs == NULL) {
229                Jmsg0(NULL, M_ABORT, 0, _("Could not create client BSOCK.\n"));
230             }
231
232             /* Queue client to be served */
233             if ((stat = workq_add(client_wq, (void *)bs, NULL, 0)) != 0) {
234                berrno be;
235                be.set_errno(stat);
236                Jmsg1(NULL, M_ABORT, 0, _("Could not add job to client queue: ERR=%s\n"),
237                      be.bstrerror());
238             }
239          }
240       }
241    }
242
243    /* Cleanup open files and pointers to them */
244    while ((fd_ptr = (s_sockfd *)sockfds.first())) {
245       close(fd_ptr->fd);
246       sockfds.remove(fd_ptr);     /* don't free() item it is on stack */
247    }
248
249    /* Stop work queue thread */
250    if ((stat = workq_destroy(client_wq)) != 0) {
251       berrno be;
252       be.set_errno(stat);
253       Emsg1(M_FATAL, 0, _("Could not destroy client queue: ERR=%s\n"),
254             be.bstrerror());
255    }
256 }