libcollectdclient: don't close socket twice
[collectd.git] / src / libcollectdclient / network.c
1 /**
2  * collectd - src/libcollectdclient/network.c
3  * Copyright (C) 2005-2012  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <assert.h>
35
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netdb.h>
39
40 #if HAVE_NETINET_IN_H
41 # include <netinet/in.h>
42 #endif
43
44 #include "collectd/network.h"
45 #include "collectd/network_buffer.h"
46
47 /*
48  * Private data types
49  */
50 struct lcc_network_s
51 {
52   lcc_server_t *servers;
53 };
54
55 struct lcc_server_s
56 {
57   char *node;
58   char *service;
59
60   int ttl;
61   lcc_security_level_t security_level;
62   char *username;
63   char *password;
64
65   int fd;
66   struct sockaddr *sa;
67   socklen_t sa_len;
68
69   lcc_network_buffer_t *buffer;
70
71   lcc_server_t *next;
72 };
73
74 /*
75  * Private functions
76  */
77 static int server_close_socket (lcc_server_t *srv) /* {{{ */
78 {
79   if (srv == NULL)
80     return (EINVAL);
81
82   if (srv->fd < 0)
83     return (0);
84
85   close (srv->fd);
86   srv->fd = -1;
87   free (srv->sa);
88   srv->sa = NULL;
89   srv->sa_len = 0;
90
91   return (0);
92 } /* }}} int server_close_socket */
93
94 static void int_server_destroy (lcc_server_t *srv) /* {{{ */
95 {
96   lcc_server_t *next;
97
98   if (srv == NULL)
99     return;
100
101   server_close_socket (srv);
102
103   next = srv->next;
104
105   free (srv->node);
106   free (srv->service);
107   free (srv->username);
108   free (srv->password);
109   free (srv);
110
111   int_server_destroy (next);
112 } /* }}} void int_server_destroy */
113
114 static int server_open_socket (lcc_server_t *srv) /* {{{ */
115 {
116   struct addrinfo ai_hints = { 0 };
117   struct addrinfo *ai_list = NULL;
118   struct addrinfo *ai_ptr;
119   int status;
120
121   if (srv == NULL)
122     return (EINVAL);
123
124   if (srv->fd >= 0)
125     server_close_socket (srv);
126
127 #ifdef AI_ADDRCONFIG
128   ai_hints.ai_flags |= AI_ADDRCONFIG;
129 #endif
130   ai_hints.ai_family   = AF_UNSPEC;
131   ai_hints.ai_socktype = SOCK_DGRAM;
132
133   status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
134   if (status != 0)
135     return (status);
136   assert (ai_list != NULL);
137
138   for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
139   {
140     srv->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
141     if (srv->fd < 0)
142       continue;
143
144     if (ai_ptr->ai_family == AF_INET)
145     {
146       struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
147       int optname;
148
149       if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
150         optname = IP_MULTICAST_TTL;
151       else
152         optname = IP_TTL;
153
154       status = setsockopt (srv->fd, IPPROTO_IP, optname,
155           &srv->ttl, sizeof (srv->ttl));
156     }
157     else if (ai_ptr->ai_family == AF_INET6)
158     {
159       /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
160       struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
161       int optname;
162
163       if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
164         optname = IPV6_MULTICAST_HOPS;
165       else
166         optname = IPV6_UNICAST_HOPS;
167
168       status = setsockopt (srv->fd, IPPROTO_IPV6, optname,
169           &srv->ttl, sizeof (srv->ttl));
170     }
171     if (status != 0)
172     {
173       /* setsockopt failed. */
174       close (srv->fd);
175       srv->fd = -1;
176       continue;
177     }
178
179     srv->sa = malloc (ai_ptr->ai_addrlen);
180     if (srv->sa == NULL)
181     {
182       close (srv->fd);
183       srv->fd = -1;
184       continue;
185     }
186
187     memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
188     srv->sa_len = ai_ptr->ai_addrlen;
189     break;
190   }
191
192   freeaddrinfo (ai_list);
193
194   if (srv->fd < 0)
195     return (-1);
196   return (0);
197 } /* }}} int server_open_socket */
198
199 static int server_send_buffer (lcc_server_t *srv) /* {{{ */
200 {
201   char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
202   size_t buffer_size;
203   int status;
204
205   if (srv->fd < 0)
206   {
207     status = server_open_socket (srv);
208     if (status != 0)
209       return (status);
210   }
211
212   memset (buffer, 0, sizeof (buffer));
213   buffer_size = sizeof (buffer);
214
215   lcc_network_buffer_finalize (srv->buffer);
216   status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size);
217   lcc_network_buffer_initialize (srv->buffer);
218
219   if (status != 0)
220     return (status);
221
222   if (buffer_size > sizeof (buffer))
223     buffer_size = sizeof (buffer);
224
225   while (42)
226   {
227     assert (srv->fd >= 0);
228     assert (srv->sa != NULL);
229     status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0,
230         srv->sa, srv->sa_len);
231     if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
232       continue;
233
234     break;
235   }
236
237   if (status < 0)
238     return (status);
239   return (0);
240 } /* }}} int server_send_buffer */
241
242 static int server_value_add (lcc_server_t *srv, /* {{{ */
243     const lcc_value_list_t *vl)
244 {
245   int status;
246
247   status = lcc_network_buffer_add_value (srv->buffer, vl);
248   if (status == 0)
249     return (0);
250
251   server_send_buffer (srv);
252   return (lcc_network_buffer_add_value (srv->buffer, vl));
253 } /* }}} int server_value_add */
254
255 /*
256  * Public functions
257  */
258 lcc_network_t *lcc_network_create (void) /* {{{ */
259 {
260   lcc_network_t *net;
261
262   net = malloc (sizeof (*net));
263   if (net == NULL)
264     return (NULL);
265   memset (net, 0, sizeof (*net));
266
267   net->servers = NULL;
268
269   return (net);
270 } /* }}} lcc_network_t *lcc_network_create */
271
272 void lcc_network_destroy (lcc_network_t *net) /* {{{ */
273 {
274   if (net == NULL)
275     return;
276   int_server_destroy (net->servers);
277   free (net);
278 } /* }}} void lcc_network_destroy */
279
280 lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
281     const char *node, const char *service)
282 {
283   lcc_server_t *srv;
284
285   if ((net == NULL) || (node == NULL))
286     return (NULL);
287   if (service == NULL)
288     service = NET_DEFAULT_PORT;
289
290   srv = malloc (sizeof (*srv));
291   if (srv == NULL)
292     return (NULL);
293   memset (srv, 0, sizeof (*srv));
294
295   srv->fd = -1;
296   srv->security_level = NONE;
297   srv->username = NULL;
298   srv->password = NULL;
299   srv->next = NULL;
300
301   srv->node = strdup (node);
302   if (srv->node == NULL)
303   {
304     free (srv);
305     return (NULL);
306   }
307
308   srv->service = strdup (service);
309   if (srv->service == NULL)
310   {
311     free (srv->node);
312     free (srv);
313     return (NULL);
314   }
315
316   srv->buffer = lcc_network_buffer_create (/* size = */ 0);
317   if (srv->buffer == NULL)
318   {
319     free (srv->service);
320     free (srv->node);
321     free (srv);
322     return (NULL);
323   }
324
325   if (net->servers == NULL)
326   {
327     net->servers = srv;
328   }
329   else
330   {
331     lcc_server_t *last = net->servers;
332
333     while (last->next != NULL)
334       last = last->next;
335
336     last->next = srv;
337   }
338
339   return (srv);
340 } /* }}} lcc_server_t *lcc_server_create */
341
342 int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
343 {
344   if ((net == NULL) || (srv == NULL))
345     return (EINVAL);
346
347   if (net->servers == srv)
348   {
349     net->servers = srv->next;
350     srv->next = NULL;
351   }
352   else
353   {
354     lcc_server_t *prev = net->servers;
355
356     while ((prev != NULL) && (prev->next != srv))
357       prev = prev->next;
358
359     if (prev == NULL)
360       return (ENOENT);
361
362     prev->next = srv->next;
363     srv->next = NULL;
364   }
365
366   int_server_destroy (srv);
367
368   return (0);
369 } /* }}} int lcc_server_destroy */
370
371 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
372 {
373   if (srv == NULL)
374     return (EINVAL);
375
376   srv->ttl = (int) ttl;
377
378   return (0);
379 } /* }}} int lcc_server_set_ttl */
380
381 int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
382     lcc_security_level_t level,
383     const char *username, const char *password)
384 {
385   return (lcc_network_buffer_set_security_level (srv->buffer,
386         level, username, password));
387 } /* }}} int lcc_server_set_security_level */
388
389 int lcc_network_values_send (lcc_network_t *net, /* {{{ */
390     const lcc_value_list_t *vl)
391 {
392   lcc_server_t *srv;
393
394   if ((net == NULL) || (vl == NULL))
395     return (EINVAL);
396
397   for (srv = net->servers; srv != NULL; srv = srv->next)
398     server_value_add (srv, vl);
399
400   return (0);
401 } /* }}} int lcc_network_values_send */
402
403 /* vim: set sw=2 sts=2 et fdm=marker : */