619a32a2ce91b1fd4b121bbb42baac4d51ddbef7
[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;
37 static FILE *sh;
38
39 static int buffer_add_string (const char *str, /* {{{ */
40     char **buffer_ret, size_t *buffer_size_ret)
41 {
42   size_t str_size;
43
44   str_size = strlen (str) + 1;
45
46   if (*buffer_size_ret < str_size)
47     return (-1);
48
49   memcpy (*buffer_ret, str, str_size);
50   *buffer_ret += str_size;
51   *buffer_size_ret -= str_size;
52
53   return (0);
54 } /* }}} int buffer_add_string */
55
56 static int buffer_add_value (const char *value, /* {{{ */
57     char **buffer_ret, size_t *buffer_size_ret)
58 {
59   char temp[4096];
60
61   if (strncmp (value, "N:", 2) == 0)
62     snprintf (temp, sizeof (temp), "%lu:%s",
63         (unsigned long) time (NULL), value + 2);
64   else
65     strncpy (temp, value, sizeof (temp));
66   temp[sizeof (temp) - 1] = 0;
67
68   return (buffer_add_string (temp, buffer_ret, buffer_size_ret));
69 } /* }}} int buffer_add_value */
70
71 static int rrdd_connect_unix (const char *path) /* {{{ */
72 {
73   struct sockaddr_un sa;
74   int status;
75
76   if (path == NULL)
77     path = RRDD_SOCK_PATH;
78
79   pthread_mutex_lock (&lock);
80
81   if (sh != NULL)
82   {
83     pthread_mutex_unlock (&lock);
84     return (0);
85   }
86
87   sd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0);
88   if (sd < 0)
89   {
90     status = errno;
91     pthread_mutex_unlock (&lock);
92     return (status);
93   }
94
95   memset (&sa, 0, sizeof (sa));
96   sa.sun_family = AF_UNIX;
97   strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
98
99   status = connect (sd, (struct sockaddr *) &sa, sizeof (sa));
100   if (status != 0)
101   {
102     status = errno;
103     pthread_mutex_unlock (&lock);
104     return (status);
105   }
106
107   sh = fdopen (sd, "w+");
108   if (sh == NULL)
109   {
110     status = errno;
111     close (sd);
112     sd = -1;
113     pthread_mutex_unlock (&lock);
114     return (status);
115   }
116
117   pthread_mutex_unlock (&lock);
118
119   return (0);
120 } /* }}} int rrdd_connect_unix */
121
122 int rrdd_connect (const char *addr) /* {{{ */
123 {
124   struct addrinfo ai_hints;
125   struct addrinfo *ai_res;
126   struct addrinfo *ai_ptr;
127   int status;
128
129   if (addr == NULL)
130     addr = RRDD_SOCK_PATH;
131
132   if (strncmp ("unix:", addr, strlen ("unix:")) == 0)
133     return (rrdd_connect_unix (addr + strlen ("unix:")));
134   else if (addr[0] == '/')
135     return (rrdd_connect_unix (addr));
136
137   pthread_mutex_lock (&lock);
138
139   if (sh != NULL)
140   {
141     pthread_mutex_unlock (&lock);
142     return (0);
143   }
144
145   memset (&ai_hints, 0, sizeof (ai_hints));
146   ai_hints.ai_flags = 0;
147 #ifdef AI_ADDRCONFIG
148   ai_hints.ai_flags |= AI_ADDRCONFIG;
149 #endif
150   ai_hints.ai_family = AF_UNSPEC;
151   ai_hints.ai_socktype = SOCK_STREAM;
152
153   ai_res = NULL;
154   status = getaddrinfo (addr, DEFAULT_PORT, &ai_hints, &ai_res);
155   if (status != 0)
156   {
157     pthread_mutex_unlock (&lock);
158     return (status);
159   }
160
161   for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
162   {
163     sd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
164     if (sd < 0)
165     {
166       status = errno;
167       sd = -1;
168       continue;
169     }
170
171     status = connect (sd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
172     if (status != 0)
173     {
174       status = errno;
175       close (sd);
176       sd = -1;
177       continue;
178     }
179
180     sh = fdopen (sd, "w+");
181     if (sh == NULL)
182     {
183       status = errno;
184       close (sd);
185       sd = -1;
186       continue;
187     } 
188
189     assert (status == 0);
190     break;
191   } /* for (ai_ptr) */
192   pthread_mutex_unlock (&lock);
193
194   return (status);
195 } /* }}} int rrdd_connect */
196
197 int rrdd_disconnect (void) /* {{{ */
198 {
199   int status;
200
201   pthread_mutex_lock (&lock);
202
203   if (sh == NULL)
204   {
205     pthread_mutex_unlock (&lock);
206     return (-1);
207   }
208
209   status = fclose (sh);
210   if (status != 0)
211     status = errno;
212
213   sh = NULL;
214   sd = -1;
215
216   pthread_mutex_unlock (&lock);
217
218   return (status);
219 } /* }}} int rrdd_disconnect */
220
221 int rrdd_update (const char *filename, int values_num, /* {{{ */
222                 const char * const *values)
223 {
224   char buffer[4096];
225   char *buffer_ptr;
226   size_t buffer_size;
227   int status;
228   int i;
229
230   memset (buffer, 0, sizeof (buffer));
231   buffer_ptr = &buffer[0];
232   buffer_size = sizeof (buffer) - 1;
233
234   buffer_add_string ("update", &buffer_ptr, &buffer_size);
235   buffer_add_string (filename, &buffer_ptr, &buffer_size);
236   for (i = 0; i < values_num; i++)
237     buffer_add_value (values[i], &buffer_ptr, &buffer_size);
238
239   pthread_mutex_lock (&lock);
240
241   if (sh == NULL)
242   {
243     pthread_mutex_unlock (&lock);
244     return (ENOTCONN);
245   }
246
247   status = write (sd, buffer, sizeof (buffer) - buffer_size);
248
249   status = read (sd, buffer, sizeof (buffer));
250   if (status < 0)
251   {
252     status = errno;
253     pthread_mutex_unlock (&lock);
254     return (status);
255   }
256   else if (status == 0)
257   {
258     pthread_mutex_unlock (&lock);
259     return (ENODATA);
260   }
261
262   status = atoi (buffer);
263
264   pthread_mutex_unlock (&lock);
265
266   return (status);
267 } /* }}} int rrd_update_daemon */
268
269 /*
270  * vim: set sw=2 sts=2 ts=8 et fdm=marker :
271  */