src/librrdc.c: rrdd_connect: Allow connecting to arbitrary addresses.
[rrdd.git] / src / librrdc.c
1 /**
2  * collectd - src/rrdc.c
3  * Copyright (C) 2008 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; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "rrdd.h"
23 #include "rrdc.h"
24
25 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
26 static int sd;
27 static FILE *sh;
28
29 static int buffer_add_string (const char *str,
30     char **buffer_ret, size_t *buffer_size_ret)
31 {
32   size_t str_size;
33
34   str_size = strlen (str) + 1;
35
36   if (*buffer_size_ret < str_size)
37     return (-1);
38
39   memcpy (*buffer_ret, str, str_size);
40   *buffer_ret += str_size;
41   *buffer_size_ret -= str_size;
42
43   return (0);
44 } /* int buffer_add_string */
45
46 static int buffer_add_value (const char *value,
47     char **buffer_ret, size_t *buffer_size_ret)
48 {
49   char temp[4096];
50
51   if (strncmp (value, "N:", 2) == 0)
52     snprintf (temp, sizeof (temp), "%lu:%s",
53         (unsigned long) time (NULL), value + 2);
54   else
55     strncpy (temp, value, sizeof (temp));
56   temp[sizeof (temp) - 1] = 0;
57
58   return (buffer_add_string (temp, buffer_ret, buffer_size_ret));
59 } /* int buffer_add_value */
60
61 static int rrdd_connect_unix (const char *path)
62 {
63   struct sockaddr_un sa;
64   int status;
65
66   if (path == NULL)
67     path = RRDD_SOCK_PATH;
68
69   pthread_mutex_lock (&lock);
70
71   if (sh != NULL)
72   {
73     pthread_mutex_unlock (&lock);
74     return (0);
75   }
76
77   sd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0);
78   if (sd < 0)
79   {
80     status = errno;
81     pthread_mutex_unlock (&lock);
82     return (status);
83   }
84
85   memset (&sa, 0, sizeof (sa));
86   sa.sun_family = AF_UNIX;
87   strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
88
89   status = connect (sd, (struct sockaddr *) &sa, sizeof (sa));
90   if (status != 0)
91   {
92     status = errno;
93     pthread_mutex_unlock (&lock);
94     return (status);
95   }
96
97   sh = fdopen (sd, "w+");
98   if (sh == NULL)
99   {
100     status = errno;
101     close (sd);
102     sd = -1;
103     pthread_mutex_unlock (&lock);
104     return (status);
105   }
106
107   pthread_mutex_unlock (&lock);
108
109   return (0);
110 } /* int rrdd_connect_unix */
111
112 int rrdd_connect (const char *addr)
113 {
114   struct addrinfo ai_hints;
115   struct addrinfo *ai_res;
116   struct addrinfo *ai_ptr;
117   int status;
118
119   if (addr == NULL)
120     addr = RRDD_SOCK_PATH;
121
122   if (strncmp ("unix:", addr, strlen ("unix:")) == 0)
123     return (rrdd_connect_unix (addr + strlen ("unix:")));
124   else if (addr[0] == '/')
125     return (rrdd_connect_unix (addr));
126
127   pthread_mutex_lock (&lock);
128
129   if (sh != NULL)
130   {
131     pthread_mutex_unlock (&lock);
132     return (0);
133   }
134
135   memset (&ai_hints, 0, sizeof (ai_hints));
136   ai_hints.ai_flags = 0;
137 #ifdef AI_ADDRCONFIG
138   ai_hints.ai_flags |= AI_ADDRCONFIG;
139 #endif
140   ai_hints.ai_family = AF_UNSPEC;
141   ai_hints.ai_socktype = SOCK_STREAM;
142
143   ai_res = NULL;
144   status = getaddrinfo (addr, DEFAULT_PORT, &ai_hints, &ai_res);
145   if (status != 0)
146   {
147     pthread_mutex_unlock (&lock);
148     return (status);
149   }
150
151   for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
152   {
153     sd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
154     if (sd < 0)
155     {
156       status = errno;
157       sd = -1;
158       continue;
159     }
160
161     status = connect (sd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
162     if (status != 0)
163     {
164       status = errno;
165       close (sd);
166       sd = -1;
167       continue;
168     }
169
170     sh = fdopen (sd, "w+");
171     if (sh == NULL)
172     {
173       status = errno;
174       close (sd);
175       sd = -1;
176       continue;
177     } 
178
179     assert (status == 0);
180     break;
181   } /* for (ai_ptr) */
182   pthread_mutex_unlock (&lock);
183
184   return (status);
185 } /* int rrdd_connect */
186
187 int rrdd_disconnect (void)
188 {
189   int status;
190
191   pthread_mutex_lock (&lock);
192
193   if (sh == NULL)
194   {
195     pthread_mutex_unlock (&lock);
196     return (-1);
197   }
198
199   status = fclose (sh);
200   if (status != 0)
201     status = errno;
202
203   sh = NULL;
204   sd = -1;
205
206   pthread_mutex_unlock (&lock);
207
208   return (status);
209 } /* int rrdd_disconnect */
210
211 int rrdd_update (const char *filename, int values_num,
212                 const char * const *values)
213 {
214   char buffer[4096];
215   char *buffer_ptr;
216   size_t buffer_size;
217   int status;
218   int i;
219
220   memset (buffer, 0, sizeof (buffer));
221   buffer_ptr = &buffer[0];
222   buffer_size = sizeof (buffer) - 1;
223
224   buffer_add_string ("update", &buffer_ptr, &buffer_size);
225   buffer_add_string (filename, &buffer_ptr, &buffer_size);
226   for (i = 0; i < values_num; i++)
227     buffer_add_value (values[i], &buffer_ptr, &buffer_size);
228
229   pthread_mutex_lock (&lock);
230
231   if (sh == NULL)
232   {
233     pthread_mutex_unlock (&lock);
234     return (ENOTCONN);
235   }
236
237   status = write (sd, buffer, sizeof (buffer) - buffer_size);
238
239   status = read (sd, buffer, sizeof (buffer));
240   if (status < 0)
241   {
242     status = errno;
243     pthread_mutex_unlock (&lock);
244     return (status);
245   }
246   else if (status == 0)
247   {
248     pthread_mutex_unlock (&lock);
249     return (ENODATA);
250   }
251
252   status = atoi (buffer);
253
254   pthread_mutex_unlock (&lock);
255
256   return (status);
257 } /* int rrd_update_daemon */
258
259 /*
260  * vim: set sw=2 sts=2 ts=8 et fdm=marker :
261  */