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