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