Moved the *_HAVE_READ defines: *_submit may only be included when *_read is included...
[collectd.git] / src / multicast.c
1 /**
2  * collectd - src/multicast.c
3  * Copyright (C) 2005  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <netdb.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <syslog.h>
32 #include <errno.h>
33
34 #include "multicast.h"
35 #include "common.h"
36
37 /*
38  * From RFC2365:
39  *
40  * The IPv4 Organization Local Scope -- 239.192.0.0/14
41  *
42  * 239.192.0.0/14 is defined to be the IPv4 Organization Local Scope, and is
43  * the space from which an organization should allocate sub-ranges when
44  * defining scopes for private use.
45  *
46  * Port 25826 is not assigned as of 2005-09-12
47  */
48
49 #define MCAST_GROUP "239.192.74.66"
50 #define UDP_PORT 25826
51
52 /* 1500 - 40 - 8  =  Ethernet packet - IPv6 header - UDP header */
53 #define BUFF_SIZE 1452
54
55 int get_read_socket (void)
56 {
57         static int sd = -1; /* socket descriptor */
58         int optval;
59
60         struct sockaddr_in addr;
61         struct ip_mreq mreq;
62
63         if (sd != -1)
64                 return (sd);
65
66         /* Create UDP sicket */
67         if ((sd = socket (PF_INET, SOCK_DGRAM, 0)) == -1)
68         {
69                 syslog (LOG_ERR, "socket: %s", strerror (errno));
70                 return (-1);
71         }
72
73         optval = 1;
74         if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1)
75         {
76                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
77                 shutdown (sd, SHUT_RD);
78                 sd = -1;
79                 return (-1);
80         }
81
82         memset (&addr, '\0', sizeof(addr));
83         addr.sin_family = AF_INET;
84         addr.sin_addr.s_addr = htonl (INADDR_ANY);
85         addr.sin_port = htons (UDP_PORT);
86         if (bind (sd, (struct sockaddr *) &addr, sizeof (addr)) == -1)
87         {
88                 syslog (LOG_ERR, "bind: %s", strerror (errno));
89                 shutdown (sd, SHUT_RD);
90                 sd = -1;
91                 return (-1);
92         }
93
94         mreq.imr_multiaddr.s_addr = inet_addr (MCAST_GROUP);
95         mreq.imr_interface.s_addr = htonl (INADDR_ANY);
96         if (setsockopt (sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == -1)
97         {
98                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
99                 shutdown (sd, SHUT_RD);
100                 sd = -1;
101                 return (-1);
102         }
103
104         return (sd);
105 }
106
107 int get_write_socket (void)
108 {
109         static int sd = -1;
110
111         if (sd != -1)
112                 return (sd);
113
114         if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) == -1)
115         {
116                 syslog (LOG_ERR, "socket: %s", strerror (errno));
117                 return (-1);
118         }
119
120         return (sd);
121 }
122
123 char *addr_to_host (struct sockaddr_in *addr)
124 {
125         char *host;
126         struct hostent *he;
127
128         if ((he = gethostbyaddr ((char *) &addr->sin_addr, sizeof (addr->sin_addr), AF_INET)) != NULL)
129         {
130                 host = strdup (he->h_name);
131         }
132         else
133         {
134                 char *tmp = inet_ntoa (addr->sin_addr);
135                 host = strdup (tmp);
136         }
137
138         return (host);
139 }
140
141 int multicast_receive (char **host, char **type, char **instance, char **value)
142 {
143         int sd = get_read_socket ();
144
145         char buffer[BUFF_SIZE];
146
147         struct sockaddr_in addr;
148         socklen_t addr_size;
149
150         char *fields[4];
151
152         *host     = NULL;
153         *type     = NULL;
154         *instance = NULL;
155         *value    = NULL;
156
157         if (sd == -1)
158                 return (-1);
159
160         addr_size = sizeof (addr);
161
162         if (recvfrom (sd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addr_size) == -1)
163         {
164                 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
165                 return (-1);
166         }
167
168         if (strsplit (buffer, fields, 4) != 3)
169                 return (-1);
170
171         *host     = addr_to_host (&addr);
172         *type     = strdup (fields[0]);
173         *instance = strdup (fields[1]);
174         *value    = strdup (fields[2]);
175
176         if (*host == NULL || *type == NULL || *instance == NULL || *value == NULL)
177                 return (-1);
178
179         return (0);
180 }
181
182 int multicast_send (char *type, char *instance, char *value)
183 {
184         int sd = get_write_socket ();
185         struct sockaddr_in addr;
186
187         char buf[BUFF_SIZE];
188         int buflen;
189
190         if (sd == -1)
191                 return (-1);
192
193         if ((buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, instance, value)) >= BUFF_SIZE)
194         {
195                 syslog (LOG_WARNING, "multicast_send: Output truncated..");
196                 return (-1);
197         }
198         buf[buflen++] = '\0';
199
200         memset(&addr, '\0', sizeof (addr));
201         addr.sin_family = AF_INET;
202         addr.sin_addr.s_addr = inet_addr (MCAST_GROUP);
203         addr.sin_port = htons (UDP_PORT);
204
205         return (sendto (sd, buf, buflen, 0, (struct sockaddr *) &addr, sizeof (addr)));
206 }