]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bnet_server.c
kes Extend new GUI api code to tree commands.
[bacula/bacula] / bacula / src / lib / bnet_server.c
1  /*
2   * Originally written by Kern Sibbald for inclusion in apcupsd,
3   *  but heavily modified for Bacula
4   *
5   *   Version $Id$
6   */
7
8 /*
9    Bacula® - The Network Backup Solution
10
11    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
12
13    The main author of Bacula is Kern Sibbald, with contributions from
14    many others, a complete list can be found in the file AUTHORS.
15    This program is Free Software; you can redistribute it and/or
16    modify it under the terms of version two of the GNU General Public
17    License as published by the Free Software Foundation plus additions
18    that are listed in the file LICENSE.
19
20    This program is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23    General Public License for more details.
24
25    You should have received a copy of the GNU General Public License
26    along with this program; if not, write to the Free Software
27    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28    02110-1301, USA.
29
30    Bacula® is a registered trademark of John Walker.
31    The licensor of Bacula is the Free Software Foundation Europe
32    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
33    Switzerland, email:ftf@fsfeurope.org.
34 */
35
36
37 #include "bacula.h"
38 #include <netinet/in.h>
39 #include <sys/socket.h>
40 #include <stdlib.h>
41 #include <arpa/inet.h>
42 #include <netdb.h>
43 #ifdef HAVE_ARPA_NAMESER_H
44 #include <arpa/nameser.h>
45 #endif
46 #ifdef HAVE_RESOLV_H
47 #include <resolv.h>
48 #endif
49
50
51 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
52
53 #ifdef HAVE_LIBWRAP
54 #include "tcpd.h"
55 int allow_severity = LOG_NOTICE;
56 int deny_severity = LOG_WARNING;
57 #endif
58
59 static bool quit = false;
60
61 void bnet_stop_thread_server(pthread_t tid)
62 {
63    quit = true;
64    if (!pthread_equal(tid, pthread_self())) {
65       pthread_kill(tid, TIMEOUT_SIGNAL);
66    }
67 }
68
69 /*
70         Become Threaded Network Server
71     This function is able to handle multiple server ips in
72     ipv4 and ipv6 style. The Addresse are give in a comma
73     seperated string in bind_addr
74     In the moment it is inpossible to bind different ports.
75 */
76 void
77 bnet_thread_server(dlist *addrs, int max_clients, workq_t *client_wq,
78                    void *handle_client_request(void *bsock))
79 {
80    int newsockfd, stat;
81    socklen_t clilen;
82    struct sockaddr cli_addr;       /* client's address */
83    int tlog;
84    int turnon = 1;
85 #ifdef HAVE_LIBWRAP
86    struct request_info request;
87 #endif
88    IPADDR *p;
89    struct s_sockfd {
90       dlink link;                     /* this MUST be the first item */
91       int fd;
92       int port;
93    } *fd_ptr = NULL;
94    char buf[128];
95    dlist sockfds;
96
97    char allbuf[256 * 10];
98    Dmsg1(100, "Addresses %s\n", build_addresses_str(addrs, allbuf, sizeof(allbuf)));
99
100    foreach_dlist(p, addrs) {
101       /* Allocate on stack frame -- no need to free */
102       fd_ptr = (s_sockfd *)alloca(sizeof(s_sockfd));
103       fd_ptr->port = p->get_port_net_order();
104       /*
105        * Open a TCP socket
106        */
107       for (tlog= 60; (fd_ptr->fd=socket(p->get_family(), SOCK_STREAM, 0)) < 0; tlog -= 10) {
108          if (tlog <= 0) {
109             berrno be;
110             char curbuf[256];
111             Emsg3(M_ABORT, 0, _("Cannot open stream socket. ERR=%s. Current %s All %s\n"),
112                        be.strerror(),
113                        p->build_address_str(curbuf, sizeof(curbuf)),
114                        build_addresses_str(addrs, allbuf, sizeof(allbuf)));
115          }
116          bmicrosleep(10, 0);
117       }
118       /*
119        * Reuse old sockets
120        */
121       if (setsockopt(fd_ptr->fd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&turnon,
122            sizeof(turnon)) < 0) {
123          berrno be;
124          Emsg1(M_WARNING, 0, _("Cannot set SO_REUSEADDR on socket: %s\n"),
125                be.strerror());
126       }
127
128       int tmax = 30 * (60 / 5);    /* wait 30 minutes max */
129       for (tlog = 0; bind(fd_ptr->fd, p->get_sockaddr(), p->get_sockaddr_len()) < 0; tlog -= 5) {
130          berrno be;
131          if (tlog <= 0) {
132             tlog = 2 * 60;         /* Complain every 2 minutes */
133             Emsg2(M_WARNING, 0, _("Cannot bind port %d: ERR=%s: Retrying ...\n"),
134                   ntohs(fd_ptr->port), be.strerror());
135          }
136          bmicrosleep(5, 0);
137          if (--tmax <= 0) {
138             Emsg2(M_ABORT, 0, _("Cannot bind port %d: ERR=%s.\n"), ntohs(fd_ptr->port),
139                   be.strerror());
140          }
141       }
142       listen(fd_ptr->fd, 5);       /* tell system we are ready */
143       sockfds.append(fd_ptr);
144    }
145    /* Start work queue thread */
146    if ((stat = workq_init(client_wq, max_clients, handle_client_request)) != 0) {
147       berrno be;
148       be.set_errno(stat);
149       Emsg1(M_ABORT, 0, _("Could not init client queue: ERR=%s\n"), be.strerror());
150    }
151    /*
152     * Wait for a connection from the client process.
153     */
154    for (; !quit;) {
155       unsigned int maxfd = 0;
156       fd_set sockset;
157       FD_ZERO(&sockset);
158       foreach_dlist(fd_ptr, &sockfds) {
159          FD_SET((unsigned)fd_ptr->fd, &sockset);
160          maxfd = maxfd > (unsigned)fd_ptr->fd ? maxfd : fd_ptr->fd;
161       }
162       errno = 0;
163       if ((stat = select(maxfd + 1, &sockset, NULL, NULL, NULL)) < 0) {
164          berrno be;                   /* capture errno */
165          if (errno == EINTR) {
166             continue;
167          }
168          /* Error, get out */
169          foreach_dlist(fd_ptr, &sockfds) {
170             close(fd_ptr->fd);
171          }
172          Emsg1(M_FATAL, 0, _("Error in select: %s\n"), be.strerror());
173          break;
174       }
175
176       foreach_dlist(fd_ptr, &sockfds) {
177          if (FD_ISSET(fd_ptr->fd, &sockset)) {
178             /* Got a connection, now accept it. */
179             do {
180                clilen = sizeof(cli_addr);
181                newsockfd = accept(fd_ptr->fd, &cli_addr, &clilen);
182             } while (newsockfd < 0 && errno == EINTR);
183             if (newsockfd < 0) {
184                continue;
185             }
186 #ifdef HAVE_LIBWRAP
187             P(mutex);              /* hosts_access is not thread safe */
188             request_init(&request, RQ_DAEMON, my_name, RQ_FILE, newsockfd, 0);
189             fromhost(&request);
190             if (!hosts_access(&request)) {
191                V(mutex);
192                Jmsg2(NULL, M_SECURITY, 0,
193                      _("Connection from %s:%d refused by hosts.access\n"),
194                      sockaddr_to_ascii(&cli_addr, buf, sizeof(buf)),
195                      sockaddr_get_port(&cli_addr));
196                close(newsockfd);
197                continue;
198             }
199             V(mutex);
200 #endif
201
202             /*
203              * Receive notification when connection dies.
204              */
205             if (setsockopt(newsockfd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&turnon,
206                  sizeof(turnon)) < 0) {
207                berrno be;
208                Emsg1(M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"),
209                      be.strerror());
210             }
211
212             /* see who client is. i.e. who connected to us. */
213             P(mutex);
214             sockaddr_to_ascii(&cli_addr, buf, sizeof(buf));
215             V(mutex);
216             BSOCK *bs;
217             bs = init_bsock(NULL, newsockfd, "client", buf, fd_ptr->port, &cli_addr);
218             if (bs == NULL) {
219                Jmsg0(NULL, M_ABORT, 0, _("Could not create client BSOCK.\n"));
220             }
221
222             /* Queue client to be served */
223             if ((stat = workq_add(client_wq, (void *)bs, NULL, 0)) != 0) {
224                berrno be;
225                be.set_errno(stat);
226                Jmsg1(NULL, M_ABORT, 0, _("Could not add job to client queue: ERR=%s\n"),
227                      be.strerror());
228             }
229          }
230       }
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.strerror());
239    }
240 }
241
242
243 #ifdef REALLY_USED
244 /*
245  * Bind an address so that we may accept connections
246  * one at a time.
247  */
248 BSOCK *bnet_bind(int port)
249 {
250    int sockfd;
251    struct sockaddr_in serv_addr;   /* our address */
252    int tlog;
253    int turnon = 1;
254
255    /*
256     * Open a TCP socket
257     */
258    for (tlog = 0; (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0; tlog -= 10) {
259       if (errno == EINTR || errno == EAGAIN) {
260          continue;
261       }
262       if (tlog <= 0) {
263          tlog = 2 * 60;
264          Emsg1(M_ERROR, 0, _("Cannot open stream socket: %s\n"), strerror(errno));
265       }
266       bmicrosleep(60, 0);
267    }
268
269    /*
270     * Reuse old sockets
271     */
272    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&turnon, sizeof(turnon)) < 0) {
273       Emsg1(M_WARNING, 0, _("Cannot set SO_REUSEADDR on socket: %s\n"),
274             strerror(errno));
275    }
276
277    /*
278     * Bind our local address so that the client can send to us.
279     */
280    bzero((char *)&serv_addr, sizeof(serv_addr));
281    serv_addr.sin_family = AF_INET;
282    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
283    serv_addr.sin_port = htons(port);
284
285    for (tlog = 0; bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0;
286         tlog -= 5) {
287       berrno be;
288       if (errno == EINTR || errno == EAGAIN) {
289          continue;
290       }
291       if (tlog <= 0) {
292          tlog = 2 * 60;
293          Emsg2(M_WARNING, 0, _("Cannot bind port %d: ERR=%s: retrying ...\n"), port,
294                be.strerror());
295       }
296       bmicrosleep(5, 0);
297    }
298    listen(sockfd, 1);              /* tell system we are ready */
299    return init_bsock(NULL, sockfd, _("Server socket"), _("client"), port,
300                      &serv_addr);
301 }
302
303 /*
304  * Accept a single connection
305  */
306 BSOCK *bnet_accept(BSOCK * bsock, char *who)
307 {
308    fd_set ready, sockset;
309    int newsockfd, stat, len;
310    socklen_t clilen;
311    struct sockaddr_in cli_addr;    /* client's address */
312    char *caller, *buf;
313    BSOCK *bs;
314    int turnon = 1;
315 #ifdef HAVE_LIBWRAP
316    struct request_info request;
317 #endif
318
319    /*
320     * Wait for a connection from the client process.
321     */
322    FD_ZERO(&sockset);
323    FD_SET((unsigned)bsock->fd, &sockset);
324
325    for (;;) {
326       /*
327        * Wait for a connection from a client process.
328        */
329       ready = sockset;
330       if ((stat = select(bsock->fd + 1, &ready, NULL, NULL, NULL)) < 0) {
331          if (errno == EINTR || errno = EAGAIN) {
332             errno = 0;
333             continue;
334          }
335          Emsg1(M_FATAL, 0, _("Error in select: %s\n"), strerror(errno));
336          newsockfd = -1;
337          break;
338       }
339       do {
340          clilen = sizeof(cli_addr);
341          newsockfd = accept(bsock->fd, (struct sockaddr *)&cli_addr, &clilen);
342       } while (newsockfd < 0 && (errno == EINTR || errno = EAGAIN));
343       if (newsockfd >= 0) {
344          break;
345       }
346    }
347
348 #ifdef HAVE_LIBWRAP
349    P(mutex);
350    request_init(&request, RQ_DAEMON, my_name, RQ_FILE, newsockfd, 0);
351    fromhost(&request);
352    if (!hosts_access(&request)) {
353       V(mutex);
354       Emsg2(M_SECURITY, 0, _("Connection from %s:%d refused by hosts.access\n"),
355             inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
356       close(newsockfd);
357       return NULL;
358    }
359    V(mutex);
360 #endif
361
362    /*
363     * Receive notification when connection dies.
364     */
365    if (setsockopt(newsockfd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&turnon, sizeof(turnon)) < 0) {
366       Emsg1(M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"),
367             strerror(errno));
368    }
369
370    /* see who client is. I.e. who connected to us.
371     * return it in the input message buffer.
372     */
373    if ((caller = inet_ntoa(cli_addr.sin_addr)) != NULL) {
374       pm_strcpy(&bsock->msg, caller);
375    } else {
376       bsock->msg[0] = 0;
377    }
378    bsock->msglen = strlen(bsock->msg);
379
380    if (newsockfd < 0) {
381       Emsg2(M_FATAL, 0, _("Socket accept error for %s. ERR=%s\n"), who,
382             strerror(errno));
383       return NULL;
384    } else {
385       if (caller == NULL) {
386          caller = _("unknown");
387       }
388       len = strlen(caller) + strlen(who) + 3;
389       buf = (char *)malloc(len);
390       bstrncpy(buf, len, who);
391       bstrncat(buf, len, ": ");
392       bstrncat(buf, len, caller);
393       bs = init_bsock(NULL, newsockfd, _("client"), buf, bsock->port, &cli_addr);
394       free(buf);
395       return bs;                   /* return new BSOCK */
396    }
397 }
398
399 #endif