libcollectdclient: Add defines for Windows compatibility.
[collectd.git] / src / libcollectdclient / client.c
1 /**
2  * libcollectdclient - src/libcollectdclient/client.c
3  * Copyright (C) 2008-2014  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 #if HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #if !defined(__GNUC__) || !__GNUC__
32 # define __attribute__(x) /**/
33 #endif
34
35 #include "collectd/lcc_features.h"
36
37 #if WIN32
38
39 #include <winsock2.h>
40 #include <wspiapi.h>
41 #include <windows.h>
42 #include <io.h>
43 #include <stdio.h>
44 #include <math.h>
45 #include <assert.h>
46
47 #else
48
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <unistd.h>
52 #include <sys/types.h>
53 #include <sys/socket.h>
54 #include <sys/un.h>
55 #include <string.h>
56 #include <assert.h>
57 #include <errno.h>
58 #include <math.h>
59 #include <netdb.h>
60
61 #endif
62
63 #include "collectd/client.h"
64
65 /* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11
66  * to no longer define it. We'll use the old, RFC 2553 value here. */
67 #ifndef NI_MAXHOST
68 # define NI_MAXHOST 1025
69 #endif
70
71 /* OpenBSD doesn't have EPROTO, FreeBSD doesn't have EILSEQ. Oh what joy! */
72 #ifndef EILSEQ
73 # ifdef EPROTO
74 #  define EILSEQ EPROTO
75 # else
76 #  define EILSEQ EINVAL
77 # endif
78 #endif
79
80 /* Secure/static macros. They work like `strcpy' and `strcat', but assure null
81  * termination. They work for static buffers only, because they use `sizeof'.
82  * The `SSTRCATF' combines the functionality of `snprintf' and `strcat' which
83  * is very useful to add formatted stuff to the end of a buffer. */
84 #define SSTRCPY(d,s) do { \
85     strncpy ((d), (s), sizeof (d)); \
86     (d)[sizeof (d) - 1] = 0; \
87   } while (0)
88
89 #define SSTRCAT(d,s) do { \
90     size_t _l = strlen (d); \
91     strncpy ((d) + _l, (s), sizeof (d) - _l); \
92     (d)[sizeof (d) - 1] = 0; \
93   } while (0)
94
95 #define SSTRCATF(d, ...) do { \
96     char _b[sizeof (d)]; \
97     snprintf (_b, sizeof (_b), __VA_ARGS__); \
98     _b[sizeof (_b) - 1] = 0; \
99     SSTRCAT ((d), _b); \
100   } while (0)
101     
102
103 #define LCC_SET_ERRSTR(c, ...) do { \
104   snprintf ((c)->errbuf, sizeof ((c)->errbuf), __VA_ARGS__); \
105   (c)->errbuf[sizeof ((c)->errbuf) - 1] = 0; \
106 } while (0)
107
108 #if COLLECT_DEBUG
109 # define LCC_DEBUG(...) printf (__VA_ARGS__)
110 #else
111 # define LCC_DEBUG(...) /**/
112 #endif
113
114 /*
115  * Types
116  */
117 struct lcc_connection_s
118 {
119   FILE *fh;
120   char errbuf[1024];
121 };
122
123 struct lcc_response_s
124 {
125   int status;
126   char message[1024];
127   char **lines;
128   size_t lines_num;
129 };
130 typedef struct lcc_response_s lcc_response_t;
131
132 /*
133  * Private functions
134  */
135 /* Even though Posix requires "strerror_r" to return an "int",
136  * some systems (e.g. the GNU libc) return a "char *" _and_
137  * ignore the second argument ... -tokkee */
138 static char *sstrerror (int errnum, char *buf, size_t buflen)
139 {
140   buf[0] = 0;
141
142 #if !HAVE_STRERROR_R
143   snprintf (buf, buflen, "Error #%i; strerror_r is not available.", errnum);
144 /* #endif !HAVE_STRERROR_R */
145
146 #elif STRERROR_R_CHAR_P
147   {
148     char *temp;
149     temp = strerror_r (errnum, buf, buflen);
150     if (buf[0] == 0)
151     {
152       if ((temp != NULL) && (temp != buf) && (temp[0] != 0))
153         strncpy (buf, temp, buflen);
154       else
155         strncpy (buf, "strerror_r did not return "
156             "an error message", buflen);
157     }
158   }
159 /* #endif STRERROR_R_CHAR_P */
160
161 #else
162   if (strerror_r (errnum, buf, buflen) != 0)
163   {
164     snprintf (buf, buflen, "Error #%i; "
165         "Additionally, strerror_r failed.",
166         errnum);
167   }
168 #endif /* STRERROR_R_CHAR_P */
169
170   buf[buflen - 1] = 0;
171
172   return (buf);
173 } /* char *sstrerror */
174
175 static int lcc_set_errno (lcc_connection_t *c, int err) /* {{{ */
176 {
177   if (c == NULL)
178     return (-1);
179
180   sstrerror (err, c->errbuf, sizeof (c->errbuf));
181   c->errbuf[sizeof (c->errbuf) - 1] = 0;
182
183   return (0);
184 } /* }}} int lcc_set_errno */
185
186 static char *lcc_strescape (char *dest, const char *src, size_t dest_size) /* {{{ */
187 {
188   size_t dest_pos;
189   size_t src_pos;
190
191   if ((dest == NULL) || (src == NULL))
192     return (NULL);
193
194   dest_pos = 0;
195   src_pos = 0;
196
197   assert (dest_size >= 3);
198
199   dest[dest_pos] = '"';
200   dest_pos++;
201
202   while (42)
203   {
204     if ((dest_pos == (dest_size - 2))
205         || (src[src_pos] == 0))
206       break;
207
208     if ((src[src_pos] == '"') || (src[src_pos] == '\\'))
209     {
210       /* Check if there is enough space for both characters.. */
211       if (dest_pos == (dest_size - 3))
212         break;
213
214       dest[dest_pos] = '\\';
215       dest_pos++;
216     }
217
218     dest[dest_pos] = src[src_pos];
219     dest_pos++;
220     src_pos++;
221   }
222
223   assert (dest_pos <= (dest_size - 2));
224
225   dest[dest_pos] = '"';
226   dest_pos++;
227
228   dest[dest_pos] = 0;
229   dest_pos++;
230   src_pos++;
231
232   return (dest);
233 } /* }}} char *lcc_strescape */
234
235 /* lcc_chomp: Removes all control-characters at the end of a string. */
236 static void lcc_chomp (char *str) /* {{{ */
237 {
238   size_t str_len;
239
240   str_len = strlen (str);
241   while (str_len > 0)
242   {
243     if (str[str_len - 1] >= 32)
244       break;
245     str[str_len - 1] = 0;
246     str_len--;
247   }
248 } /* }}} void lcc_chomp */
249
250 static void lcc_response_free (lcc_response_t *res) /* {{{ */
251 {
252   size_t i;
253
254   if (res == NULL)
255     return;
256
257   for (i = 0; i < res->lines_num; i++)
258     free (res->lines[i]);
259   free (res->lines);
260   res->lines = NULL;
261 } /* }}} void lcc_response_free */
262
263 static int lcc_send (lcc_connection_t *c, const char *command) /* {{{ */
264 {
265   int status;
266
267   LCC_DEBUG ("send:    --> %s\n", command);
268
269   status = fprintf (c->fh, "%s\r\n", command);
270   if (status < 0)
271   {
272     lcc_set_errno (c, errno);
273     return (-1);
274   }
275
276   return (0);
277 } /* }}} int lcc_send */
278
279 static int lcc_receive (lcc_connection_t *c, /* {{{ */
280     lcc_response_t *ret_res)
281 {
282   lcc_response_t res;
283   char *ptr;
284   char buffer[4096];
285   size_t i;
286
287   memset (&res, 0, sizeof (res));
288
289   /* Read the first line, containing the status and a message */
290   ptr = fgets (buffer, sizeof (buffer), c->fh);
291   if (ptr == NULL)
292   {
293     lcc_set_errno (c, errno);
294     return (-1);
295   }
296   lcc_chomp (buffer);
297   LCC_DEBUG ("receive: <-- %s\n", buffer);
298
299   /* Convert the leading status to an integer and make `ptr' to point to the
300    * beginning of the message. */
301   ptr = NULL;
302   errno = 0;
303   res.status = strtol (buffer, &ptr, 0);
304   if ((errno != 0) || (ptr == &buffer[0]))
305   {
306     lcc_set_errno (c, errno);
307     return (-1);
308   }
309
310   /* Skip white spaces after the status number */
311   while ((*ptr == ' ') || (*ptr == '\t'))
312     ptr++;
313
314   /* Now copy the message. */
315   strncpy (res.message, ptr, sizeof (res.message));
316   res.message[sizeof (res.message) - 1] = 0;
317
318   /* Error or no lines follow: We're done. */
319   if (res.status <= 0)
320   {
321     memcpy (ret_res, &res, sizeof (res));
322     return (0);
323   }
324
325   /* Allocate space for the char-pointers */
326   res.lines_num = (size_t) res.status;
327   res.status = 0;
328   res.lines = (char **) malloc (res.lines_num * sizeof (char *));
329   if (res.lines == NULL)
330   {
331     lcc_set_errno (c, ENOMEM);
332     return (-1);
333   }
334
335   /* Now receive all the lines */
336   for (i = 0; i < res.lines_num; i++)
337   {
338     ptr = fgets (buffer, sizeof (buffer), c->fh);
339     if (ptr == NULL)
340     {
341       lcc_set_errno (c, errno);
342       break;
343     }
344     lcc_chomp (buffer);
345     LCC_DEBUG ("receive: <-- %s\n", buffer);
346
347     res.lines[i] = strdup (buffer);
348     if (res.lines[i] == NULL)
349     {
350       lcc_set_errno (c, ENOMEM);
351       break;
352     }
353   }
354
355   /* Check if the for-loop exited with an error. */
356   if (i < res.lines_num)
357   {
358     while (i > 0)
359     {
360       i--;
361       free (res.lines[i]);
362     }
363     free (res.lines);
364     return (-1);
365   }
366
367   memcpy (ret_res, &res, sizeof (res));
368   return (0);
369 } /* }}} int lcc_receive */
370
371 static int lcc_sendreceive (lcc_connection_t *c, /* {{{ */
372     const char *command, lcc_response_t *ret_res)
373 {
374   lcc_response_t res;
375   int status;
376
377   if (c->fh == NULL)
378   {
379     lcc_set_errno (c, EBADF);
380     return (-1);
381   }
382
383   status = lcc_send (c, command);
384   if (status != 0)
385     return (status);
386
387   memset (&res, 0, sizeof (res));
388   status = lcc_receive (c, &res);
389   if (status == 0)
390     memcpy (ret_res, &res, sizeof (*ret_res));
391
392   return (status);
393 } /* }}} int lcc_sendreceive */
394
395 #if !WIN32
396 static int lcc_open_unixsocket (lcc_connection_t *c, const char *path) /* {{{ */
397 {
398   struct sockaddr_un sa;
399   int fd;
400   int status;
401
402   assert (c != NULL);
403   assert (c->fh == NULL);
404   assert (path != NULL);
405
406   /* Don't use PF_UNIX here, because it's broken on Mac OS X (10.4, possibly
407    * others). */
408   fd = socket (AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
409   if (fd < 0)
410   {
411     lcc_set_errno (c, errno);
412     return (-1);
413   }
414
415   memset (&sa, 0, sizeof (sa));
416   sa.sun_family = AF_UNIX;
417   strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
418
419   status = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
420   if (status != 0)
421   {
422     lcc_set_errno (c, errno);
423     close (fd);
424     return (-1);
425   }
426
427   c->fh = fdopen (fd, "r+");
428   if (c->fh == NULL)
429   {
430     lcc_set_errno (c, errno);
431     close (fd);
432     return (-1);
433   }
434
435   return (0);
436 } /* }}} int lcc_open_unixsocket */
437 #endif /* !WIN32 */
438
439 static int lcc_open_netsocket (lcc_connection_t *c, /* {{{ */
440     const char *addr_orig)
441 {
442   struct addrinfo ai_hints;
443   struct addrinfo *ai_res;
444   struct addrinfo *ai_ptr;
445   char addr_copy[NI_MAXHOST];
446   char *addr;
447   char *port;
448   int fd;
449   int status;
450
451   assert (c != NULL);
452   assert (c->fh == NULL);
453   assert (addr_orig != NULL);
454
455   strncpy(addr_copy, addr_orig, sizeof(addr_copy));
456   addr_copy[sizeof(addr_copy) - 1] = '\0';
457   addr = addr_copy;
458
459   memset (&ai_hints, 0, sizeof (ai_hints));
460   ai_hints.ai_flags = 0;
461 #ifdef AI_ADDRCONFIG
462   ai_hints.ai_flags |= AI_ADDRCONFIG;
463 #endif
464   ai_hints.ai_family = AF_UNSPEC;
465   ai_hints.ai_socktype = SOCK_STREAM;
466
467   port = NULL;
468   if (*addr == '[') /* IPv6+port format */
469   {
470     /* `addr' is something like "[2001:780:104:2:211:24ff:feab:26f8]:12345" */
471     addr++;
472
473     port = strchr (addr, ']');
474     if (port == NULL)
475     {
476       LCC_SET_ERRSTR (c, "malformed address: %s", addr_orig);
477       return (-1);
478     }
479     *port = 0;
480     port++;
481
482     if (*port == ':')
483       port++;
484     else if (*port == 0)
485       port = NULL;
486     else
487     {
488       LCC_SET_ERRSTR (c, "garbage after address: %s", port);
489       return (-1);
490     }
491   } /* if (*addr = ']') */
492   else if (strchr (addr, '.') != NULL) /* Hostname or IPv4 */
493   {
494     port = strrchr (addr, ':');
495     if (port != NULL)
496     {
497       *port = 0;
498       port++;
499     }
500   }
501
502   ai_res = NULL;
503   status = getaddrinfo (addr,
504                         port == NULL ? LCC_DEFAULT_PORT : port,
505                         &ai_hints, &ai_res);
506   if (status != 0)
507   {
508     LCC_SET_ERRSTR (c, "getaddrinfo: %s", gai_strerror (status));
509     return (-1);
510   }
511
512   for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
513   {
514     fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
515     if (fd < 0)
516     {
517       status = errno;
518       fd = -1;
519       continue;
520     }
521
522     status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
523     if (status != 0)
524     {
525       status = errno;
526       close (fd);
527       fd = -1;
528       continue;
529     }
530
531     c->fh = fdopen (fd, "r+");
532     if (c->fh == NULL)
533     {
534       status = errno;
535       close (fd);
536       fd = -1;
537       continue;
538     }
539
540     assert (status == 0);
541     break;
542   } /* for (ai_ptr) */
543
544   if (status != 0)
545   {
546     lcc_set_errno (c, status);
547     return (-1);
548   }
549
550   return (0);
551 } /* }}} int lcc_open_netsocket */
552
553 static int lcc_open_socket (lcc_connection_t *c, const char *addr) /* {{{ */
554 {
555   int status = 0;
556
557   if (addr == NULL)
558     return (-1);
559
560   assert (c != NULL);
561   assert (c->fh == NULL);
562   assert (addr != NULL);
563
564 #if !WIN32
565   if (strncmp ("unix:", addr, strlen ("unix:")) == 0)
566     status = lcc_open_unixsocket (c, addr + strlen ("unix:"));
567   else if (addr[0] == '/')
568     status = lcc_open_unixsocket (c, addr);
569   else
570 #endif
571     status = lcc_open_netsocket (c, addr);
572
573   return (status);
574 } /* }}} int lcc_open_socket */
575
576 /*
577  * Public functions
578  */
579 unsigned int lcc_version (void) /* {{{ */
580 {
581   return (LCC_VERSION);
582 } /* }}} unsigned int lcc_version */
583
584 const char *lcc_version_string (void) /* {{{ */
585 {
586   return (LCC_VERSION_STRING);
587 } /* }}} const char *lcc_version_string */
588
589 const char *lcc_version_extra (void) /* {{{ */
590 {
591   return (LCC_VERSION_EXTRA);
592 } /* }}} const char *lcc_version_extra */
593
594 int lcc_connect (const char *address, lcc_connection_t **ret_con) /* {{{ */
595 {
596   lcc_connection_t *c;
597   int status;
598
599   if (address == NULL)
600     return (-1);
601
602   if (ret_con == NULL)
603     return (-1);
604
605   c = (lcc_connection_t *) malloc (sizeof (*c));
606   if (c == NULL)
607     return (-1);
608   memset (c, 0, sizeof (*c));
609
610   status = lcc_open_socket (c, address);
611   if (status != 0)
612   {
613     lcc_disconnect (c);
614     return (status);
615   }
616
617   *ret_con = c;
618   return (0);
619 } /* }}} int lcc_connect */
620
621 int lcc_disconnect (lcc_connection_t *c) /* {{{ */
622 {
623   if (c == NULL)
624     return (-1);
625
626   if (c->fh != NULL)
627   {
628     fclose (c->fh);
629     c->fh = NULL;
630   }
631
632   free (c);
633   return (0);
634 } /* }}} int lcc_disconnect */
635
636 int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */
637     size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names)
638 {
639   char ident_str[6 * LCC_NAME_LEN];
640   char ident_esc[12 * LCC_NAME_LEN];
641   char command[14 * LCC_NAME_LEN];
642
643   lcc_response_t res;
644   size_t   values_num;
645   gauge_t *values = NULL;
646   char   **values_names = NULL;
647
648   size_t i;
649   int status;
650
651   if (c == NULL)
652     return (-1);
653
654   if (ident == NULL)
655   {
656     lcc_set_errno (c, EINVAL);
657     return (-1);
658   }
659
660   /* Build a commend with an escaped version of the identifier string. */
661   status = lcc_identifier_to_string (c, ident_str, sizeof (ident_str), ident);
662   if (status != 0)
663     return (status);
664
665   snprintf (command, sizeof (command), "GETVAL %s",
666       lcc_strescape (ident_esc, ident_str, sizeof (ident_esc)));
667   command[sizeof (command) - 1] = 0;
668
669   /* Send talk to the daemon.. */
670   status = lcc_sendreceive (c, command, &res);
671   if (status != 0)
672     return (status);
673
674   if (res.status != 0)
675   {
676     LCC_SET_ERRSTR (c, "Server error: %s", res.message);
677     lcc_response_free (&res);
678     return (-1);
679   }
680
681   values_num = res.lines_num;
682
683 #define BAIL_OUT(e) do { \
684   lcc_set_errno (c, (e)); \
685   free (values); \
686   if (values_names != NULL) { \
687     for (i = 0; i < values_num; i++) { \
688       free (values_names[i]); \
689     } \
690   } \
691   free (values_names); \
692   lcc_response_free (&res); \
693   return (-1); \
694 } while (0)
695
696   /* If neither the values nor the names are requested, return here.. */
697   if ((ret_values == NULL) && (ret_values_names == NULL))
698   {
699     if (ret_values_num != NULL)
700       *ret_values_num = values_num;
701     lcc_response_free (&res);
702     return (0);
703   }
704
705   /* Allocate space for the values */
706   if (ret_values != NULL)
707   {
708     values = (gauge_t *) malloc (values_num * sizeof (*values));
709     if (values == NULL)
710       BAIL_OUT (ENOMEM);
711   }
712
713   if (ret_values_names != NULL)
714   {
715     values_names = (char **) calloc (values_num, sizeof (*values_names));
716     if (values_names == NULL)
717       BAIL_OUT (ENOMEM);
718   }
719
720   for (i = 0; i < res.lines_num; i++)
721   {
722     char *key;
723     char *value;
724     char *endptr;
725
726     key = res.lines[i];
727     value = strchr (key, '=');
728     if (value == NULL)
729       BAIL_OUT (EILSEQ);
730
731     *value = 0;
732     value++;
733
734     if (values != NULL)
735     {
736       endptr = NULL;
737       errno = 0;
738       values[i] = strtod (value, &endptr);
739
740       if ((endptr == value) || (errno != 0))
741         BAIL_OUT (errno);
742     }
743
744     if (values_names != NULL)
745     {
746       values_names[i] = strdup (key);
747       if (values_names[i] == NULL)
748         BAIL_OUT (ENOMEM);
749     }
750   } /* for (i = 0; i < res.lines_num; i++) */
751
752   if (ret_values_num != NULL)
753     *ret_values_num = values_num;
754   if (ret_values != NULL)
755     *ret_values = values;
756   if (ret_values_names != NULL)
757     *ret_values_names = values_names;
758
759   lcc_response_free (&res);
760
761   return (0);
762 } /* }}} int lcc_getval */
763
764 int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */
765 {
766   char ident_str[6 * LCC_NAME_LEN];
767   char ident_esc[12 * LCC_NAME_LEN];
768   char command[1024] = "";
769   lcc_response_t res;
770   int status;
771   size_t i;
772
773   if ((c == NULL) || (vl == NULL) || (vl->values_len < 1)
774       || (vl->values == NULL) || (vl->values_types == NULL))
775   {
776     lcc_set_errno (c, EINVAL);
777     return (-1);
778   }
779
780   status = lcc_identifier_to_string (c, ident_str, sizeof (ident_str),
781       &vl->identifier);
782   if (status != 0)
783     return (status);
784
785   SSTRCATF (command, "PUTVAL %s",
786       lcc_strescape (ident_esc, ident_str, sizeof (ident_esc)));
787
788   if (vl->interval > 0.0)
789     SSTRCATF (command, " interval=%.3f", vl->interval);
790
791   if (vl->time > 0.0)
792     SSTRCATF (command, " %.3f", vl->time);
793   else
794     SSTRCAT (command, " N");
795
796   for (i = 0; i < vl->values_len; i++)
797   {
798     if (vl->values_types[i] == LCC_TYPE_COUNTER)
799       SSTRCATF (command, ":%"PRIu64, vl->values[i].counter);
800     else if (vl->values_types[i] == LCC_TYPE_GAUGE)
801     {
802       if (isnan (vl->values[i].gauge))
803         SSTRCATF (command, ":U");
804       else
805         SSTRCATF (command, ":%g", vl->values[i].gauge);
806     }
807     else if (vl->values_types[i] == LCC_TYPE_DERIVE)
808         SSTRCATF (command, ":%"PRIu64, vl->values[i].derive);
809     else if (vl->values_types[i] == LCC_TYPE_ABSOLUTE)
810         SSTRCATF (command, ":%"PRIu64, vl->values[i].absolute);
811
812   } /* for (i = 0; i < vl->values_len; i++) */
813
814   status = lcc_sendreceive (c, command, &res);
815   if (status != 0)
816     return (status);
817
818   if (res.status != 0)
819   {
820     LCC_SET_ERRSTR (c, "Server error: %s", res.message);
821     lcc_response_free (&res);
822     return (-1);
823   }
824
825   lcc_response_free (&res);
826   return (0);
827 } /* }}} int lcc_putval */
828
829 int lcc_flush (lcc_connection_t *c, const char *plugin, /* {{{ */
830     lcc_identifier_t *ident, int timeout)
831 {
832   char command[1024] = "";
833   lcc_response_t res;
834   int status;
835
836   if (c == NULL)
837   {
838     lcc_set_errno (c, EINVAL);
839     return (-1);
840   }
841
842   SSTRCPY (command, "FLUSH");
843
844   if (timeout > 0)
845     SSTRCATF (command, " timeout=%i", timeout);
846
847   if (plugin != NULL)
848   {
849     char buffer[2 * LCC_NAME_LEN];
850     SSTRCATF (command, " plugin=%s",
851         lcc_strescape (buffer, plugin, sizeof (buffer)));
852   }
853
854   if (ident != NULL)
855   {
856     char ident_str[6 * LCC_NAME_LEN];
857     char ident_esc[12 * LCC_NAME_LEN];
858
859     status = lcc_identifier_to_string (c, ident_str, sizeof (ident_str), ident);
860     if (status != 0)
861       return (status);
862
863     SSTRCATF (command, " identifier=%s",
864         lcc_strescape (ident_esc, ident_str, sizeof (ident_esc)));
865   }
866
867   status = lcc_sendreceive (c, command, &res);
868   if (status != 0)
869     return (status);
870
871   if (res.status != 0)
872   {
873     LCC_SET_ERRSTR (c, "Server error: %s", res.message);
874     lcc_response_free (&res);
875     return (-1);
876   }
877
878   lcc_response_free (&res);
879   return (0);
880 } /* }}} int lcc_flush */
881
882 /* TODO: Implement lcc_putnotif */
883
884 int lcc_listval (lcc_connection_t *c, /* {{{ */
885     lcc_identifier_t **ret_ident, size_t *ret_ident_num)
886 {
887   lcc_response_t res;
888   size_t i;
889   int status;
890
891   lcc_identifier_t *ident;
892   size_t ident_num;
893
894   if (c == NULL)
895     return (-1);
896
897   if ((ret_ident == NULL) || (ret_ident_num == NULL))
898   {
899     lcc_set_errno (c, EINVAL);
900     return (-1);
901   }
902
903   status = lcc_sendreceive (c, "LISTVAL", &res);
904   if (status != 0)
905     return (status);
906
907   if (res.status != 0)
908   {
909     LCC_SET_ERRSTR (c, "Server error: %s", res.message);
910     lcc_response_free (&res);
911     return (-1);
912   }
913
914   ident_num = res.lines_num;
915   ident = (lcc_identifier_t *) malloc (ident_num * sizeof (*ident));
916   if (ident == NULL)
917   {
918     lcc_response_free (&res);
919     lcc_set_errno (c, ENOMEM);
920     return (-1);
921   }
922
923   for (i = 0; i < res.lines_num; i++)
924   {
925     char *time_str;
926     char *ident_str;
927
928     /* First field is the time. */
929     time_str = res.lines[i];
930
931     /* Set `ident_str' to the beginning of the second field. */
932     ident_str = time_str;
933     while ((*ident_str != ' ') && (*ident_str != '\t') && (*ident_str != 0))
934       ident_str++;
935     while ((*ident_str == ' ') || (*ident_str == '\t'))
936     {
937       *ident_str = 0;
938       ident_str++;
939     }
940
941     if (*ident_str == 0)
942     {
943       lcc_set_errno (c, EILSEQ);
944       status = -1;
945       break;
946     }
947
948     status = lcc_string_to_identifier (c, ident + i, ident_str);
949     if (status != 0)
950       break;
951   }
952
953   lcc_response_free (&res);
954
955   if (status != 0)
956   {
957     free (ident);
958     return (-1);
959   }
960
961   *ret_ident = ident;
962   *ret_ident_num = ident_num;
963
964   return (0);
965 } /* }}} int lcc_listval */
966
967 const char *lcc_strerror (lcc_connection_t *c) /* {{{ */
968 {
969   if (c == NULL)
970     return ("Invalid object");
971   return (c->errbuf);
972 } /* }}} const char *lcc_strerror */
973
974 int lcc_identifier_to_string (lcc_connection_t *c, /* {{{ */
975     char *string, size_t string_size, const lcc_identifier_t *ident)
976 {
977   if ((string == NULL) || (string_size < 6) || (ident == NULL))
978   {
979     lcc_set_errno (c, EINVAL);
980     return (-1);
981   }
982
983   if (ident->plugin_instance[0] == 0)
984   {
985     if (ident->type_instance[0] == 0)
986       snprintf (string, string_size, "%s/%s/%s",
987           ident->host,
988           ident->plugin,
989           ident->type);
990     else
991       snprintf (string, string_size, "%s/%s/%s-%s",
992           ident->host,
993           ident->plugin,
994           ident->type,
995           ident->type_instance);
996   }
997   else
998   {
999     if (ident->type_instance[0] == 0)
1000       snprintf (string, string_size, "%s/%s-%s/%s",
1001           ident->host,
1002           ident->plugin,
1003           ident->plugin_instance,
1004           ident->type);
1005     else
1006       snprintf (string, string_size, "%s/%s-%s/%s-%s",
1007           ident->host,
1008           ident->plugin,
1009           ident->plugin_instance,
1010           ident->type,
1011           ident->type_instance);
1012   }
1013
1014   string[string_size - 1] = 0;
1015   return (0);
1016 } /* }}} int lcc_identifier_to_string */
1017
1018 int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */
1019     lcc_identifier_t *ident, const char *string)
1020 {
1021   char *string_copy;
1022   char *host;
1023   char *plugin;
1024   char *plugin_instance;
1025   char *type;
1026   char *type_instance;
1027
1028   string_copy = strdup (string);
1029   if (string_copy == NULL)
1030   {
1031     lcc_set_errno (c, ENOMEM);
1032     return (-1);
1033   }
1034
1035   host = string_copy;
1036   plugin = strchr (host, '/');
1037   if (plugin == NULL)
1038   {
1039     LCC_SET_ERRSTR (c, "Malformed identifier string: %s", string);
1040     free (string_copy);
1041     return (-1);
1042   }
1043   *plugin = 0;
1044   plugin++;
1045
1046   type = strchr (plugin, '/');
1047   if (type == NULL)
1048   {
1049     LCC_SET_ERRSTR (c, "Malformed identifier string: %s", string);
1050     free (string_copy);
1051     return (-1);
1052   }
1053   *type = 0;
1054   type++;
1055
1056   plugin_instance = strchr (plugin, '-');
1057   if (plugin_instance != NULL)
1058   {
1059     *plugin_instance = 0;
1060     plugin_instance++;
1061   }
1062
1063   type_instance = strchr (type, '-');
1064   if (type_instance != NULL)
1065   {
1066     *type_instance = 0;
1067     type_instance++;
1068   }
1069
1070   memset (ident, 0, sizeof (*ident));
1071
1072   SSTRCPY (ident->host, host);
1073   SSTRCPY (ident->plugin, plugin);
1074   if (plugin_instance != NULL)
1075     SSTRCPY (ident->plugin_instance, plugin_instance);
1076   SSTRCPY (ident->type, type);
1077   if (type_instance != NULL)
1078     SSTRCPY (ident->type_instance, type_instance);
1079
1080   free (string_copy);
1081   return (0);
1082 } /* }}} int lcc_string_to_identifier */
1083
1084 int lcc_identifier_compare (const lcc_identifier_t *i0, /* {{{ */
1085     const lcc_identifier_t *i1)
1086 {
1087   int status;
1088
1089   if ((i0 == NULL) && (i1 == NULL))
1090     return (0);
1091   else if (i0 == NULL)
1092     return (-1);
1093   else if (i1 == NULL)
1094     return (1);
1095
1096 #define CMP_FIELD(f) do {         \
1097   status = strcmp (i0->f, i1->f); \
1098   if (status != 0)                \
1099     return (status);              \
1100 } while (0);
1101
1102     CMP_FIELD (host);
1103     CMP_FIELD (plugin);
1104     CMP_FIELD (plugin_instance);
1105     CMP_FIELD (type);
1106     CMP_FIELD (type_instance);
1107
1108 #undef CMP_FIELD
1109
1110     return (0);
1111 } /* }}} int lcc_identifier_compare */
1112
1113 int lcc_sort_identifiers (lcc_connection_t *c, /* {{{ */
1114     lcc_identifier_t *idents, size_t idents_num)
1115 {
1116   if (idents == NULL)
1117   {
1118     lcc_set_errno (c, EINVAL);
1119     return (-1);
1120   }
1121
1122   qsort (idents, idents_num, sizeof (*idents),
1123       (void *) lcc_identifier_compare);
1124   return (0);
1125 } /* }}} int lcc_sort_identifiers */
1126
1127 /* vim: set sw=2 sts=2 et fdm=marker : */