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