]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bnet_server.c
eb3969d199f0e1bfd8327a81e0da3c2dc9f5c14b
[bacula/bacula] / bacula / src / lib / bnet_server.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 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 two of the GNU 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 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   *   Version $Id$
33   */
34
35 #include "bacula.h"
36 #include <netinet/in.h>
37 #include <sys/socket.h>
38 #include <stdlib.h>
39 #include <arpa/inet.h>
40 #include <netdb.h>
41 #ifdef HAVE_ARPA_NAMESER_H
42 #include <arpa/nameser.h>
43 #endif
44 #ifdef HAVE_RESOLV_H
45 #include <resolv.h>
46 #endif
47
48
49 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
50
51 #ifdef HAVE_LIBWRAP
52 #include "tcpd.h"
53 int allow_severity = LOG_NOTICE;
54 int deny_severity = LOG_WARNING;
55 #endif
56
57 static bool quit = false;
58
59 void bnet_stop_thread_server(pthread_t tid)
60 {
61    quit = true;
62    if (!pthread_equal(tid, pthread_self())) {
63       pthread_kill(tid, TIMEOUT_SIGNAL);
64    }
65 }
66
67 /*
68         Become Threaded Network Server
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     In the moment it is inpossible to bind different ports.
73 */
74 void
75 bnet_thread_server(dlist *addrs, 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 *p;
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    Dmsg1(100, "Addresses %s\n", build_addresses_str(addrs, allbuf, sizeof(allbuf)));
97
98    foreach_dlist(p, addrs) {
99       /* Allocate on stack from -- no need to free */
100       fd_ptr = (s_sockfd *)alloca(sizeof(s_sockfd));
101       fd_ptr->port = p->get_port_net_order();
102       /*
103        * Open a TCP socket
104        */
105       for (tlog= 60; (fd_ptr->fd=socket(p->get_family(), SOCK_STREAM, 0)) < 0; tlog -= 10) {
106          if (tlog <= 0) {
107             berrno be;
108             char curbuf[256];
109             Emsg3(M_ABORT, 0, _("Cannot open stream socket. ERR=%s. Current %s All %s\n"),
110                        be.bstrerror(),
111                        p->build_address_str(curbuf, sizeof(curbuf)),
112                        build_addresses_str(addrs, allbuf, sizeof(allbuf)));
113          }
114          bmicrosleep(10, 0);
115       }
116       /*
117        * Reuse old sockets
118        */
119       if (setsockopt(fd_ptr->fd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&turnon,
120            sizeof(turnon)) < 0) {
121          berrno be;
122          Emsg1(M_WARNING, 0, _("Cannot set SO_REUSEADDR on socket: %s\n"),
123                be.bstrerror());
124       }
125
126       int tmax = 30 * (60 / 5);    /* wait 30 minutes max */
127       for (tlog = 0; bind(fd_ptr->fd, p->get_sockaddr(), p->get_sockaddr_len()) < 0; tlog -= 5) {
128          berrno be;
129          if (tlog <= 0) {
130             tlog = 2 * 60;         /* Complain every 2 minutes */
131             Emsg2(M_WARNING, 0, _("Cannot bind port %d: ERR=%s: Retrying ...\n"),
132                   ntohs(fd_ptr->port), be.bstrerror());
133          }
134          bmicrosleep(5, 0);
135          if (--tmax <= 0) {
136             Emsg2(M_ABORT, 0, _("Cannot bind port %d: ERR=%s.\n"), ntohs(fd_ptr->port),
137                   be.bstrerror());
138          }
139       }
140       listen(fd_ptr->fd, 50);      /* tell system we are ready */
141       sockfds.append(fd_ptr);
142    }
143    /* Start work queue thread */
144    if ((stat = workq_init(client_wq, max_clients, handle_client_request)) != 0) {
145       berrno be;
146       be.set_errno(stat);
147       Emsg1(M_ABORT, 0, _("Could not init client queue: ERR=%s\n"), be.bstrerror());
148    }
149    /*
150     * Wait for a connection from the client process.
151     */
152    for (; !quit;) {
153       unsigned int maxfd = 0;
154       fd_set sockset;
155       FD_ZERO(&sockset);
156       foreach_dlist(fd_ptr, &sockfds) {
157          FD_SET((unsigned)fd_ptr->fd, &sockset);
158          maxfd = maxfd > (unsigned)fd_ptr->fd ? maxfd : fd_ptr->fd;
159       }
160       errno = 0;
161       if ((stat = select(maxfd + 1, &sockset, NULL, NULL, NULL)) < 0) {
162          berrno be;                   /* capture errno */
163          if (errno == EINTR) {
164             continue;
165          }
166          Emsg1(M_FATAL, 0, _("Error in select: %s\n"), be.bstrerror());
167          break;
168       }
169
170       foreach_dlist(fd_ptr, &sockfds) {
171          if (FD_ISSET(fd_ptr->fd, &sockset)) {
172             /* Got a connection, now accept it. */
173             do {
174                clilen = sizeof(cli_addr);
175                newsockfd = accept(fd_ptr->fd, &cli_addr, &clilen);
176             } while (newsockfd < 0 && errno == EINTR);
177             if (newsockfd < 0) {
178                continue;
179             }
180 #ifdef HAVE_LIBWRAP
181             P(mutex);              /* hosts_access is not thread safe */
182             request_init(&request, RQ_DAEMON, my_name, RQ_FILE, newsockfd, 0);
183             fromhost(&request);
184             if (!hosts_access(&request)) {
185                V(mutex);
186                Jmsg2(NULL, M_SECURITY, 0,
187                      _("Connection from %s:%d refused by hosts.access\n"),
188                      sockaddr_to_ascii(&cli_addr, buf, sizeof(buf)),
189                      sockaddr_get_port(&cli_addr));
190                close(newsockfd);
191                continue;
192             }
193             V(mutex);
194 #endif
195
196             /*
197              * Receive notification when connection dies.
198              */
199             if (setsockopt(newsockfd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&turnon,
200                  sizeof(turnon)) < 0) {
201                berrno be;
202                Emsg1(M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"),
203                      be.bstrerror());
204             }
205
206             /* see who client is. i.e. who connected to us. */
207             P(mutex);
208             sockaddr_to_ascii(&cli_addr, buf, sizeof(buf));
209             V(mutex);
210             BSOCK *bs;
211             bs = init_bsock(NULL, newsockfd, "client", buf, fd_ptr->port, &cli_addr);
212             if (bs == NULL) {
213                Jmsg0(NULL, M_ABORT, 0, _("Could not create client BSOCK.\n"));
214             }
215
216             /* Queue client to be served */
217             if ((stat = workq_add(client_wq, (void *)bs, NULL, 0)) != 0) {
218                berrno be;
219                be.set_errno(stat);
220                Jmsg1(NULL, M_ABORT, 0, _("Could not add job to client queue: ERR=%s\n"),
221                      be.bstrerror());
222             }
223          }
224       }
225    }
226
227    /* Cleanup open files and pointers to them */
228    while ((fd_ptr = (s_sockfd *)sockfds.first())) {
229       close(fd_ptr->fd);
230       sockfds.remove(fd_ptr);     /* don't free() item it is on stack */
231    }
232
233    /* Stop work queue thread */
234    if ((stat = workq_destroy(client_wq)) != 0) {
235       berrno be;
236       be.set_errno(stat);
237       Emsg1(M_FATAL, 0, _("Could not destroy client queue: ERR=%s\n"),
238             be.bstrerror());
239    }
240 }
241