X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Flibcollectdclient%2Fclient.c;h=2adf6d48821d49590bde1186dd8c047c78eaa17a;hb=28460d4f235bb0272c98c830d14b9d7a73f812b7;hp=768554494ea96bb7c521b9e4c651e2dc5e7494d7;hpb=288fdea7d379ea9c6a9e72e5cf722005dc41ccaf;p=collectd.git diff --git a/src/libcollectdclient/client.c b/src/libcollectdclient/client.c index 76855449..2adf6d48 100644 --- a/src/libcollectdclient/client.c +++ b/src/libcollectdclient/client.c @@ -19,35 +19,16 @@ * Florian octo Forster **/ -/* Set to C99 and POSIX code */ -#ifndef _ISOC99_SOURCE -# define _ISOC99_SOURCE -#endif -#ifndef _POSIX_SOURCE -# define _POSIX_SOURCE -#endif -#ifndef _POSIX_C_SOURCE -# define _POSIX_C_SOURCE 200112L -#endif -#ifndef _REENTRANT -# define _REENTRANT -#endif - -/* Disable non-standard extensions */ -#ifdef _BSD_SOURCE -# undef _BSD_SOURCE -#endif -#ifdef _SVID_SOURCE -# undef _SVID_SOURCE -#endif -#ifdef _GNU_SOURCE -# undef _GNU_SOURCE +#if HAVE_CONFIG_H +# include "config.h" #endif #if !defined(__GNUC__) || !__GNUC__ # define __attribute__(x) /**/ #endif +#include "lcc_features.h" + #include #include #include @@ -57,17 +38,38 @@ #include #include #include +#include #include #include "client.h" +/* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11 + * to no longer define it. We'll use the old, RFC 2553 value here. */ +#ifndef NI_MAXHOST +# define NI_MAXHOST 1025 +#endif + +/* OpenBSD doesn't have EPROTO, FreeBSD doesn't have EILSEQ. Oh what joy! */ +#ifndef EILSEQ +# ifdef EPROTO +# define EILSEQ EPROTO +# else +# define EILSEQ EINVAL +# endif +#endif + +/* Secure/static macros. They work like `strcpy' and `strcat', but assure null + * termination. They work for static buffers only, because they use `sizeof'. + * The `SSTRCATF' combines the functionality of `snprintf' and `strcat' which + * is very useful to add formatted stuff to the end of a buffer. */ #define SSTRCPY(d,s) do { \ strncpy ((d), (s), sizeof (d)); \ (d)[sizeof (d) - 1] = 0; \ } while (0) #define SSTRCAT(d,s) do { \ - strncat ((d), (s), sizeof (d)); \ + size_t _l = strlen (d); \ + strncpy ((d) + _l, (s), sizeof (d) - _l); \ (d)[sizeof (d) - 1] = 0; \ } while (0) @@ -84,7 +86,7 @@ (c)->errbuf[sizeof ((c)->errbuf) - 1] = 0; \ } while (0) -#if 1 +#if COLLECT_DEBUG # define LCC_DEBUG(...) printf (__VA_ARGS__) #else # define LCC_DEBUG(...) /**/ @@ -111,38 +113,65 @@ typedef struct lcc_response_s lcc_response_t; /* * Private functions */ +/* Even though Posix requires "strerror_r" to return an "int", + * some systems (e.g. the GNU libc) return a "char *" _and_ + * ignore the second argument ... -tokkee */ +static char *sstrerror (int errnum, char *buf, size_t buflen) +{ + buf[0] = 0; + +#if !HAVE_STRERROR_R + snprintf (buf, buflen, "Error #%i; strerror_r is not available.", errnum); +/* #endif !HAVE_STRERROR_R */ + +#elif STRERROR_R_CHAR_P + { + char *temp; + temp = strerror_r (errnum, buf, buflen); + if (buf[0] == 0) + { + if ((temp != NULL) && (temp != buf) && (temp[0] != 0)) + strncpy (buf, temp, buflen); + else + strncpy (buf, "strerror_r did not return " + "an error message", buflen); + } + } +/* #endif STRERROR_R_CHAR_P */ + +#else + if (strerror_r (errnum, buf, buflen) != 0) + { + snprintf (buf, buflen, "Error #%i; " + "Additionally, strerror_r failed.", + errnum); + } +#endif /* STRERROR_R_CHAR_P */ + + buf[buflen - 1] = 0; + + return (buf); +} /* char *sstrerror */ + static int lcc_set_errno (lcc_connection_t *c, int err) /* {{{ */ { if (c == NULL) return (-1); - strerror_r (err, c->errbuf, sizeof (c->errbuf)); + sstrerror (err, c->errbuf, sizeof (c->errbuf)); c->errbuf[sizeof (c->errbuf) - 1] = 0; return (0); } /* }}} int lcc_set_errno */ -/* lcc_strdup: Since `strdup' is an XSI extension, we provide our own version - * here. */ -__attribute__((malloc, nonnull (1))) -static char *lcc_strdup (const char *str) /* {{{ */ -{ - size_t strsize; - char *ret; - - strsize = strlen (str) + 1; - ret = (char *) malloc (strsize); - if (ret != NULL) - memcpy (ret, str, strsize); - return (ret); -} /* }}} char *lcc_strdup */ - -__attribute__((nonnull (1, 2))) -static char *lcc_strescape (char *dest, char *src, size_t dest_size) /* {{{ */ +static char *lcc_strescape (char *dest, const char *src, size_t dest_size) /* {{{ */ { size_t dest_pos; size_t src_pos; + if ((dest == NULL) || (src == NULL)) + return (NULL); + dest_pos = 0; src_pos = 0; @@ -199,6 +228,53 @@ static void lcc_chomp (char *str) /* {{{ */ } } /* }}} void lcc_chomp */ +static int lcc_identifier_cmp (const void *a, const void *b) +{ + const lcc_identifier_t *ident_a, *ident_b; + + int status; + + ident_a = a; + ident_b = b; + + status = strcasecmp (ident_a->host, ident_b->host); + if (status != 0) + return (status); + + status = strcmp (ident_a->plugin, ident_b->plugin); + if (status != 0) + return (status); + + if ((*ident_a->plugin_instance != '\0') || (*ident_b->plugin_instance != '\0')) + { + if (*ident_a->plugin_instance == '\0') + return (-1); + else if (*ident_b->plugin_instance == '\0') + return (1); + + status = strcmp (ident_a->plugin_instance, ident_b->plugin_instance); + if (status != 0) + return (status); + } + + status = strcmp (ident_a->type, ident_b->type); + if (status != 0) + return (status); + + if ((*ident_a->type_instance != '\0') || (*ident_b->type_instance != '\0')) + { + if (*ident_a->type_instance == '\0') + return (-1); + else if (*ident_b->type_instance == '\0') + return (1); + + status = strcmp (ident_a->type_instance, ident_b->type_instance); + if (status != 0) + return (status); + } + return (0); +} /* }}} int lcc_identifier_cmp */ + static void lcc_response_free (lcc_response_t *res) /* {{{ */ { size_t i; @@ -296,7 +372,7 @@ static int lcc_receive (lcc_connection_t *c, /* {{{ */ lcc_chomp (buffer); LCC_DEBUG ("receive: <-- %s\n", buffer); - res.lines[i] = lcc_strdup (buffer); + res.lines[i] = strdup (buffer); if (res.lines[i] == NULL) { lcc_set_errno (c, ENOMEM); @@ -326,6 +402,12 @@ static int lcc_sendreceive (lcc_connection_t *c, /* {{{ */ lcc_response_t res; int status; + if (c->fh == NULL) + { + lcc_set_errno (c, EBADF); + return (-1); + } + status = lcc_send (c, command); if (status != 0) return (status); @@ -348,7 +430,9 @@ static int lcc_open_unixsocket (lcc_connection_t *c, const char *path) /* {{{ */ assert (c->fh == NULL); assert (path != NULL); - fd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0); + /* Don't use PF_UNIX here, because it's broken on Mac OS X (10.4, possibly + * others). */ + fd = socket (AF_UNIX, SOCK_STREAM, /* protocol = */ 0); if (fd < 0) { lcc_set_errno (c, errno); @@ -516,9 +600,25 @@ static int lcc_open_socket (lcc_connection_t *c, const char *addr) /* {{{ */ /* * Public functions */ +unsigned int lcc_version (void) /* {{{ */ +{ + return (LCC_VERSION); +} /* }}} unsigned int lcc_version */ + +const char *lcc_version_string (void) /* {{{ */ +{ + return (LCC_VERSION_STRING); +} /* }}} const char *lcc_version_string */ + +const char *lcc_version_extra (void) /* {{{ */ +{ + return (LCC_VERSION_EXTRA); +} /* }}} const char *lcc_version_extra */ + int lcc_connect (const char *address, lcc_connection_t **ret_con) /* {{{ */ { lcc_connection_t *c; + int status; if (address == NULL) return (-1); @@ -531,8 +631,15 @@ int lcc_connect (const char *address, lcc_connection_t **ret_con) /* {{{ */ return (-1); memset (c, 0, sizeof (*c)); + status = lcc_open_socket (c, address); + if (status != 0) + { + lcc_disconnect (c); + return (status); + } + *ret_con = c; - return (lcc_open_socket (c, address)); + return (0); } /* }}} int lcc_connect */ int lcc_disconnect (lcc_connection_t *c) /* {{{ */ @@ -643,7 +750,7 @@ int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */ key = res.lines[i]; value = strchr (key, '='); if (value == NULL) - BAIL_OUT (EPROTO); + BAIL_OUT (EILSEQ); *value = 0; value++; @@ -660,7 +767,7 @@ int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */ if (values_names != NULL) { - values_names[i] = lcc_strdup (key); + values_names[i] = strdup (key); if (values_names[i] == NULL) BAIL_OUT (ENOMEM); } @@ -673,6 +780,8 @@ int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */ if (ret_values_names != NULL) *ret_values_names = values_names; + lcc_response_free (&res); + return (0); } /* }}} int lcc_getval */ @@ -680,7 +789,7 @@ int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */ { char ident_str[6 * LCC_NAME_LEN]; char ident_esc[12 * LCC_NAME_LEN]; - char command[1024]; + char command[1024] = ""; lcc_response_t res; int status; size_t i; @@ -697,24 +806,16 @@ int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */ if (status != 0) return (status); - snprintf (command, sizeof (command), "PUTVAL %s", + SSTRCATF (command, "PUTVAL %s", lcc_strescape (ident_esc, ident_str, sizeof (ident_esc))); - command[sizeof (command) - 1] = 0; if (vl->interval > 0) - { - char option[64]; - - snprintf (option, sizeof (option), " interval=%i", vl->interval); - option[sizeof (option) - 1] = 0; - - SSTRCAT (command, option); - } + SSTRCATF (command, " interval=%i", vl->interval); if (vl->time > 0) - SSTRCATF (command, "%u", (unsigned int) vl->time); + SSTRCATF (command, " %u", (unsigned int) vl->time); else - SSTRCAT (command, "N"); + SSTRCAT (command, " N"); for (i = 0; i < vl->values_len; i++) { @@ -723,10 +824,15 @@ int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */ else if (vl->values_types[i] == LCC_TYPE_GAUGE) { if (isnan (vl->values[i].gauge)) - SSTRCPY (command, ":U"); + SSTRCATF (command, ":U"); else SSTRCATF (command, ":%g", vl->values[i].gauge); } + else if (vl->values_types[i] == LCC_TYPE_DERIVE) + SSTRCATF (command, ":%"PRIu64, vl->values[i].derive); + else if (vl->values_types[i] == LCC_TYPE_ABSOLUTE) + SSTRCATF (command, ":%"PRIu64, vl->values[i].absolute); + } /* for (i = 0; i < vl->values_len; i++) */ status = lcc_sendreceive (c, command, &res); @@ -744,8 +850,58 @@ int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */ return (0); } /* }}} int lcc_putval */ -/* TODO: Implement lcc_flush */ -int lcc_flush (lcc_connection_t *c, lcc_identifier_t *ident, int timeout); +int lcc_flush (lcc_connection_t *c, const char *plugin, /* {{{ */ + lcc_identifier_t *ident, int timeout) +{ + char command[1024] = ""; + lcc_response_t res; + int status; + + if (c == NULL) + { + lcc_set_errno (c, EINVAL); + return (-1); + } + + SSTRCPY (command, "FLUSH"); + + if (timeout > 0) + SSTRCATF (command, " timeout=%i", timeout); + + if (plugin != NULL) + { + char buffer[2 * LCC_NAME_LEN]; + SSTRCATF (command, " plugin=%s", + lcc_strescape (buffer, plugin, sizeof (buffer))); + } + + if (ident != NULL) + { + char ident_str[6 * LCC_NAME_LEN]; + char ident_esc[12 * LCC_NAME_LEN]; + + status = lcc_identifier_to_string (c, ident_str, sizeof (ident_str), ident); + if (status != 0) + return (status); + + SSTRCATF (command, " identifier=%s", + lcc_strescape (ident_esc, ident_str, sizeof (ident_esc))); + } + + status = lcc_sendreceive (c, command, &res); + if (status != 0) + return (status); + + if (res.status != 0) + { + LCC_SET_ERRSTR (c, "Server error: %s", res.message); + lcc_response_free (&res); + return (-1); + } + + lcc_response_free (&res); + return (0); +} /* }}} int lcc_flush */ /* TODO: Implement lcc_putnotif */ @@ -808,7 +964,7 @@ int lcc_listval (lcc_connection_t *c, /* {{{ */ if (*ident_str == 0) { - lcc_set_errno (c, EPROTO); + lcc_set_errno (c, EILSEQ); status = -1; break; } @@ -893,7 +1049,7 @@ int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */ char *type; char *type_instance; - string_copy = lcc_strdup (string); + string_copy = strdup (string); if (string_copy == NULL) { lcc_set_errno (c, ENOMEM); @@ -949,4 +1105,17 @@ int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */ return (0); } /* }}} int lcc_string_to_identifier */ +int lcc_sort_identifiers (lcc_connection_t *c, /* {{{ */ + lcc_identifier_t *idents, size_t idents_num) +{ + if (idents == NULL) + { + lcc_set_errno (c, EINVAL); + return (-1); + } + + qsort (idents, idents_num, sizeof (*idents), lcc_identifier_cmp); + return (0); +} /* }}} int lcc_sort_identifiers */ + /* vim: set sw=2 sts=2 et fdm=marker : */