]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bnet_server.c
update version
[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     This function is able to handle multiple server ips in
69     ipv4 and ipv6 style. The Addresse are give in a comma
70     seperated string in bind_addr
71     In the moment it is inpossible to bind different ports.
72 */
73 void
74 bnet_thread_server(dlist *addrs, int max_clients, workq_t *client_wq,
75                    void *handle_client_request(void *bsock))
76 {
77    int newsockfd, stat;
78    socklen_t clilen;
79    struct sockaddr cli_addr;       /* client's address */
80    int tlog;
81    int turnon = 1;
82 #ifdef HAVE_LIBWRAP
83    struct request_info request;
84 #endif
85    IPADDR *p;
86    struct s_sockfd {
87       dlink link;                     /* this MUST be the first item */
88       int fd;
89       int port;
90    } *fd_ptr = NULL;
91    char buf[128];
92    dlist sockfds;
93
94    char allbuf[256 * 10];
95    Dmsg1(100, "Addresses %s\n", build_addresses_str(addrs, allbuf, sizeof(allbuf)));
96
97    foreach_dlist(p, addrs) {
98       /* Allocate on stack from -- no need to free */
99       fd_ptr = (s_sockfd *)alloca(sizeof(s_sockfd));
100       fd_ptr->port = p->get_port_net_order();
101       /*
102        * Open a TCP socket
103        */
104       for (tlog= 60; (fd_ptr->fd=socket(p->get_family(), SOCK_STREAM, 0)) < 0; tlog -= 10) {
105          if (tlog <= 0) {
106             berrno be;
107             char curbuf[256];
108             Emsg3(M_ABORT, 0, _("Cannot open stream socket. ERR=%s. Current %s All %s\n"),
109                        be.bstrerror(),
110                        p->build_address_str(curbuf, sizeof(curbuf)),
111                        build_addresses_str(addrs, allbuf, sizeof(allbuf)));
112          }
113          bmicrosleep(10, 0);
114       }
115       /*
116        * Reuse old sockets
117        */
118       if (setsockopt(fd_ptr->fd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&turnon,
119            sizeof(turnon)) < 0) {
120          berrno be;
121          Emsg1(M_WARNING, 0, _("Cannot set SO_REUSEADDR on socket: %s\n"),
122                be.bstrerror());
123       }
124
125       int tmax = 30 * (60 / 5);    /* wait 30 minutes max */
126       for (tlog = 0; bind(fd_ptr->fd, p->get_sockaddr(), p->get_sockaddr_len()) < 0; tlog -= 5) {
127          berrno be;
128          if (tlog <= 0) {
129             tlog = 2 * 60;         /* Complain every 2 minutes */
130             Emsg2(M_WARNING, 0, _("Cannot bind port %d: ERR=%s: Retrying ...\n"),
131                   ntohs(fd_ptr->port), be.bstrerror());
132          }
133          bmicrosleep(5, 0);
134          if (--tmax <= 0) {
135             Emsg2(M_ABORT, 0, _("Cannot bind port %d: ERR=%s.\n"), ntohs(fd_ptr->port),
136                   be.bstrerror());
137          }
138       }
139       listen(fd_ptr->fd, 50);      /* tell system we are ready */
140       sockfds.append(fd_ptr);
141    }
142    /* Start work queue thread */
143    if ((stat = workq_init(client_wq, max_clients, handle_client_request)) != 0) {
144       berrno be;
145       be.set_errno(stat);
146       Emsg1(M_ABORT, 0, _("Could not init client queue: ERR=%s\n"), be.bstrerror());
147    }
148    /*
149     * Wait for a connection from the client process.
150     */
151    for (; !quit;) {
152       unsigned int maxfd = 0;
153       fd_set sockset;
154       FD_ZERO(&sockset);
155       foreach_dlist(fd_ptr, &sockfds) {
156          FD_SET((unsigned)fd_ptr->fd, &sockset);
157          maxfd = maxfd > (unsigned)fd_ptr->fd ? maxfd : fd_ptr->fd;
158       }
159       errno = 0;
160       if ((stat = select(maxfd + 1, &sockset, NULL, NULL, NULL)) < 0) {
161          berrno be;                   /* capture errno */
162          if (errno == EINTR) {
163             continue;
164          }
165          Emsg1(M_FATAL, 0, _("Error in select: %s\n"), be.bstrerror());
166          break;
167       }
168
169       foreach_dlist(fd_ptr, &sockfds) {
170          if (FD_ISSET(fd_ptr->fd, &sockset)) {
171             /* Got a connection, now accept it. */
172             do {
173                clilen = sizeof(cli_addr);
174                newsockfd = accept(fd_ptr->fd, &cli_addr, &clilen);
175             } while (newsockfd < 0 && errno == EINTR);
176             if (newsockfd < 0) {
177                continue;
178             }
179 #ifdef HAVE_LIBWRAP
180             P(mutex);              /* hosts_access is not thread safe */
181             request_init(&request, RQ_DAEMON, my_name, RQ_FILE, newsockfd, 0);
182             fromhost(&request);
183             if (!hosts_access(&request)) {
184                V(mutex);
185                Jmsg2(NULL, M_SECURITY, 0,
186                      _("Connection from %s:%d refused by hosts.access\n"),
187                      sockaddr_to_ascii(&cli_addr, buf, sizeof(buf)),
188                      sockaddr_get_port(&cli_addr));
189                close(newsockfd);
190                continue;
191             }
192             V(mutex);
193 #endif
194
195             /*
196              * Receive notification when connection dies.
197              */
198             if (setsockopt(newsockfd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&turnon,
199                  sizeof(turnon)) < 0) {
200                berrno be;
201                Emsg1(M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"),
202                      be.bstrerror());
203             }
204
205             /* see who client is. i.e. who connected to us. */
206             P(mutex);
207             sockaddr_to_ascii(&cli_addr, buf, sizeof(buf));
208             V(mutex);
209             BSOCK *bs;
210             bs = init_bsock(NULL, newsockfd, "client", buf, fd_ptr->port, &cli_addr);
211             if (bs == NULL) {
212                Jmsg0(NULL, M_ABORT, 0, _("Could not create client BSOCK.\n"));
213             }
214
215             /* Queue client to be served */
216             if ((stat = workq_add(client_wq, (void *)bs, NULL, 0)) != 0) {
217                berrno be;
218                be.set_errno(stat);
219                Jmsg1(NULL, M_ABORT, 0, _("Could not add job to client queue: ERR=%s\n"),
220                      be.bstrerror());
221             }
222          }
223       }
224    }
225
226    /* Cleanup open files and pointers to them */
227    while ((fd_ptr = (s_sockfd *)sockfds.first())) {
228       close(fd_ptr->fd);
229       sockfds.remove(fd_ptr);     /* don't free() item it is on stack */
230    }
231
232    /* Stop work queue thread */
233    if ((stat = workq_destroy(client_wq)) != 0) {
234       berrno be;
235       be.set_errno(stat);
236       Emsg1(M_FATAL, 0, _("Could not destroy client queue: ERR=%s\n"),
237             be.bstrerror());
238    }
239 }