Build system: Install `rrd_client.h' during `make install'.
[rrdtool.git] / src / rrd_client.c
1 /**
2  * RRDTool - src/rrd_client.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 "rrd.h"
23 #include "rrd_client.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <assert.h>
29 #include <pthread.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33 #include <netdb.h>
34
35 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
36 static int sd = -1;
37
38 static ssize_t sread (void *buffer_void, size_t buffer_size) /* {{{ */
39 {
40   char    *buffer;
41   size_t   buffer_used;
42   size_t   buffer_free;
43   ssize_t  status;
44
45   buffer       = (char *) buffer_void;
46   buffer_used  = 0;
47   buffer_free  = buffer_size;
48
49   while (buffer_free > 0)
50   {
51     status = read (sd, buffer + buffer_used, buffer_free);
52     if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
53       continue;
54
55     if (status < 0)
56       return (-1);
57
58     if (status == 0)
59     {
60       close (sd);
61       sd = -1;
62       errno = EPROTO;
63       return (-1);
64     }
65
66     assert ((0 > status) || (buffer_free >= (size_t) status));
67
68     buffer_free = buffer_free - status;
69     buffer_used = buffer_used + status;
70
71     if (buffer[buffer_used - 1] == '\n')
72       break;
73   }
74
75   if (buffer[buffer_used - 1] != '\n')
76   {
77     errno = ENOBUFS;
78     return (-1);
79   }
80
81   buffer[buffer_used - 1] = 0;
82   return (buffer_used);
83 } /* }}} ssize_t sread */
84
85 static ssize_t swrite (const void *buf, size_t count) /* {{{ */
86 {
87   const char *ptr;
88   size_t      nleft;
89   ssize_t     status;
90
91   ptr   = (const char *) buf;
92   nleft = count;
93
94   while (nleft > 0)
95   {
96     status = write (sd, (const void *) ptr, nleft);
97
98     if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
99       continue;
100
101     if (status < 0)
102       return (status);
103
104     nleft = nleft - status;
105     ptr   = ptr   + status;
106   }
107
108   return (0);
109 } /* }}} ssize_t swrite */
110
111 static int buffer_add_string (const char *str, /* {{{ */
112     char **buffer_ret, size_t *buffer_size_ret)
113 {
114   char *buffer;
115   size_t buffer_size;
116   size_t buffer_pos;
117   size_t i;
118   int status;
119
120   buffer = *buffer_ret;
121   buffer_size = *buffer_size_ret;
122   buffer_pos = 0;
123
124   i = 0;
125   status = -1;
126   while (buffer_pos < buffer_size)
127   {
128     if (str[i] == 0)
129     {
130       buffer[buffer_pos] = ' ';
131       buffer_pos++;
132       status = 0;
133       break;
134     }
135     else if ((str[i] == ' ') || (str[i] == '\\'))
136     {
137       if (buffer_pos >= (buffer_size - 1))
138         break;
139       buffer[buffer_pos] = '\\';
140       buffer_pos++;
141       buffer[buffer_pos] = str[i];
142       buffer_pos++;
143     }
144     else
145     {
146       buffer[buffer_pos] = str[i];
147       buffer_pos++;
148     }
149     i++;
150   } /* while (buffer_pos < buffer_size) */
151
152   if (status != 0)
153     return (-1);
154
155   *buffer_ret = buffer + buffer_pos;
156   *buffer_size_ret = buffer_size - buffer_pos;
157
158   return (0);
159 } /* }}} int buffer_add_string */
160
161 static int buffer_add_value (const char *value, /* {{{ */
162     char **buffer_ret, size_t *buffer_size_ret)
163 {
164   char temp[4096];
165
166   if (strncmp (value, "N:", 2) == 0)
167     snprintf (temp, sizeof (temp), "%lu:%s",
168         (unsigned long) time (NULL), value + 2);
169   else
170     strncpy (temp, value, sizeof (temp));
171   temp[sizeof (temp) - 1] = 0;
172
173   return (buffer_add_string (temp, buffer_ret, buffer_size_ret));
174 } /* }}} int buffer_add_value */
175
176 static int rrdc_connect_unix (const char *path) /* {{{ */
177 {
178   struct sockaddr_un sa;
179   int status;
180
181   assert (path != NULL);
182
183   pthread_mutex_lock (&lock);
184
185   if (sd >= 0)
186   {
187     pthread_mutex_unlock (&lock);
188     return (0);
189   }
190
191   sd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0);
192   if (sd < 0)
193   {
194     status = errno;
195     pthread_mutex_unlock (&lock);
196     return (status);
197   }
198
199   memset (&sa, 0, sizeof (sa));
200   sa.sun_family = AF_UNIX;
201   strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
202
203   status = connect (sd, (struct sockaddr *) &sa, sizeof (sa));
204   if (status != 0)
205   {
206     status = errno;
207     pthread_mutex_unlock (&lock);
208     return (status);
209   }
210
211   pthread_mutex_unlock (&lock);
212
213   return (0);
214 } /* }}} int rrdc_connect_unix */
215
216 int rrdc_connect (const char *addr) /* {{{ */
217 {
218   struct addrinfo ai_hints;
219   struct addrinfo *ai_res;
220   struct addrinfo *ai_ptr;
221   int status;
222
223   if (addr == NULL)
224     addr = RRDCACHED_DEFAULT_ADDRESS;
225
226   if (strncmp ("unix:", addr, strlen ("unix:")) == 0)
227     return (rrdc_connect_unix (addr + strlen ("unix:")));
228   else if (addr[0] == '/')
229     return (rrdc_connect_unix (addr));
230
231   pthread_mutex_lock (&lock);
232
233   if (sd >= 0)
234   {
235     pthread_mutex_unlock (&lock);
236     return (0);
237   }
238
239   memset (&ai_hints, 0, sizeof (ai_hints));
240   ai_hints.ai_flags = 0;
241 #ifdef AI_ADDRCONFIG
242   ai_hints.ai_flags |= AI_ADDRCONFIG;
243 #endif
244   ai_hints.ai_family = AF_UNSPEC;
245   ai_hints.ai_socktype = SOCK_STREAM;
246
247   ai_res = NULL;
248   status = getaddrinfo (addr, RRDCACHED_DEFAULT_PORT, &ai_hints, &ai_res);
249   if (status != 0)
250   {
251     pthread_mutex_unlock (&lock);
252     return (status);
253   }
254
255   for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
256   {
257     sd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
258     if (sd < 0)
259     {
260       status = errno;
261       sd = -1;
262       continue;
263     }
264
265     status = connect (sd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
266     if (status != 0)
267     {
268       status = errno;
269       close (sd);
270       sd = -1;
271       continue;
272     }
273
274     assert (status == 0);
275     break;
276   } /* for (ai_ptr) */
277   pthread_mutex_unlock (&lock);
278
279   return (status);
280 } /* }}} int rrdc_connect */
281
282 int rrdc_disconnect (void) /* {{{ */
283 {
284   pthread_mutex_lock (&lock);
285
286   if (sd < 0)
287   {
288     pthread_mutex_unlock (&lock);
289     return (0);
290   }
291
292   close (sd);
293   sd = -1;
294
295   pthread_mutex_unlock (&lock);
296
297   return (0);
298 } /* }}} int rrdc_disconnect */
299
300 int rrdc_update (const char *filename, int values_num, /* {{{ */
301                 const char * const *values)
302 {
303   char buffer[4096];
304   char *buffer_ptr;
305   size_t buffer_free;
306   size_t buffer_size;
307   int status;
308   int i;
309
310   memset (buffer, 0, sizeof (buffer));
311   buffer_ptr = &buffer[0];
312   buffer_free = sizeof (buffer);
313
314   status = buffer_add_string ("update", &buffer_ptr, &buffer_free);
315   if (status != 0)
316     return (ENOBUFS);
317
318   status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
319   if (status != 0)
320     return (ENOBUFS);
321
322   for (i = 0; i < values_num; i++)
323   {
324     status = buffer_add_value (values[i], &buffer_ptr, &buffer_free);
325     if (status != 0)
326       return (ENOBUFS);
327   }
328
329   assert (buffer_free < sizeof (buffer));
330   buffer_size = sizeof (buffer) - buffer_free;
331   assert (buffer[buffer_size - 1] == ' ');
332   buffer[buffer_size - 1] = '\n';
333
334   pthread_mutex_lock (&lock);
335
336   if (sd < 0)
337   {
338     pthread_mutex_unlock (&lock);
339     return (ENOTCONN);
340   }
341
342   status = swrite (buffer, buffer_size);
343   if (status != 0)
344   {
345     pthread_mutex_unlock (&lock);
346     return (status);
347   }
348
349   status = sread (buffer, sizeof (buffer));
350   if (status < 0)
351   {
352     status = errno;
353     pthread_mutex_unlock (&lock);
354     return (status);
355   }
356   else if (status == 0)
357   {
358     pthread_mutex_unlock (&lock);
359     return (ENODATA);
360   }
361
362   pthread_mutex_unlock (&lock);
363
364   status = atoi (buffer);
365   return (status);
366 } /* }}} int rrdc_update */
367
368 int rrdc_flush (const char *filename) /* {{{ */
369 {
370   char buffer[4096];
371   char *buffer_ptr;
372   size_t buffer_free;
373   size_t buffer_size;
374   int status;
375
376   if (filename == NULL)
377     return (-1);
378
379   memset (buffer, 0, sizeof (buffer));
380   buffer_ptr = &buffer[0];
381   buffer_free = sizeof (buffer);
382
383   status = buffer_add_string ("flush", &buffer_ptr, &buffer_free);
384   if (status != 0)
385     return (ENOBUFS);
386
387   status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
388   if (status != 0)
389     return (ENOBUFS);
390
391   assert (buffer_free < sizeof (buffer));
392   buffer_size = sizeof (buffer) - buffer_free;
393   assert (buffer[buffer_size - 1] == ' ');
394   buffer[buffer_size - 1] = '\n';
395
396   pthread_mutex_lock (&lock);
397
398   if (sd < 0)
399   {
400     pthread_mutex_unlock (&lock);
401     return (ENOTCONN);
402   }
403
404   status = swrite (buffer, buffer_size);
405   if (status != 0)
406   {
407     pthread_mutex_unlock (&lock);
408     return (status);
409   }
410
411   status = sread (buffer, sizeof (buffer));
412   if (status < 0)
413   {
414     status = errno;
415     pthread_mutex_unlock (&lock);
416     return (status);
417   }
418   else if (status == 0)
419   {
420     pthread_mutex_unlock (&lock);
421     return (ENODATA);
422   }
423
424   pthread_mutex_unlock (&lock);
425
426   status = atoi (buffer);
427   return (status);
428 } /* }}} int rrdc_flush */
429
430 /*
431  * vim: set sw=2 sts=2 ts=8 et fdm=marker :
432  */