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