X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fteamspeak2.c;h=502145d36dc89bd459be4f32636da13747510665;hb=61a1fa91ba73e4fe3a34949f77c5f017056f2b7a;hp=d0d85fff5b3e987561e652f8d438e4c3edd9049e;hpb=416e3311ea598cfc5f6506ac4e0c99aaae8b63fa;p=collectd.git diff --git a/src/teamspeak2.c b/src/teamspeak2.c index d0d85fff..502145d3 100644 --- a/src/teamspeak2.c +++ b/src/teamspeak2.c @@ -70,8 +70,12 @@ static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); */ static int tss2_add_vserver (int vserver_port) { + /* + * Adds a new vserver to the linked list + */ vserver_list_t *entry; + /* Check port range */ if ((vserver_port <= 0) || (vserver_port > 65535)) { ERROR ("teamspeak2 plugin: VServer port is invalid: %i", @@ -79,6 +83,7 @@ static int tss2_add_vserver (int vserver_port) return (-1); } + /* Allocate memory */ entry = (vserver_list_t *) malloc (sizeof (vserver_list_t)); if (entry == NULL) { @@ -87,8 +92,10 @@ static int tss2_add_vserver (int vserver_port) } memset (entry, 0, sizeof (vserver_list_t)); + /* Save data */ entry->port = vserver_port; + /* Insert to list */ if(server_list == NULL) { /* Add the server as the first element */ server_list = entry; @@ -108,9 +115,13 @@ static int tss2_add_vserver (int vserver_port) return (0); } /* int tss2_add_vserver */ -static void tss2_submit_gauge (const char *plugin_instance, const char *type, +static void tss2_submit_gauge (const char *plugin_instance, + const char *type, const char *type_instance, gauge_t value) { + /* + * Submits a gauge value to the collectd daemon + */ value_t values[1]; value_list_t vl = VALUE_LIST_INIT; @@ -118,20 +129,28 @@ static void tss2_submit_gauge (const char *plugin_instance, const char *type, vl.values = values; vl.values_len = 1; - vl.time = time (NULL); sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "teamspeak2", sizeof (vl.plugin)); if (plugin_instance != NULL) sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); + + sstrncpy (vl.type, type, sizeof (vl.type)); + + if (type_instance != NULL) + sstrncpy (vl.type_instance, type_instance, + sizeof (vl.type_instance)); - plugin_dispatch_values (type, &vl); + plugin_dispatch_values (&vl); } /* void tss2_submit_gauge */ static void tss2_submit_io (const char *plugin_instance, const char *type, counter_t rx, counter_t tx) { + /* + * Submits the io rx/tx tuple to the collectd daemon + */ value_t values[2]; value_list_t vl = VALUE_LIST_INIT; @@ -140,19 +159,23 @@ static void tss2_submit_io (const char *plugin_instance, const char *type, vl.values = values; vl.values_len = 2; - vl.time = time (NULL); sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "teamspeak2", sizeof (vl.plugin)); if (plugin_instance != NULL) sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); - - plugin_dispatch_values (type, &vl); + + sstrncpy (vl.type, type, sizeof (vl.type)); + + plugin_dispatch_values (&vl); } /* void tss2_submit_gauge */ static void tss2_close_socket (void) { + /* + * Closes all sockets + */ if (global_write_fh != NULL) { fputs ("quit\r\n", global_write_fh); @@ -173,14 +196,20 @@ static void tss2_close_socket (void) static int tss2_get_socket (FILE **ret_read_fh, FILE **ret_write_fh) { + /* + * Returns connected file objects or establishes the connection + * if it's not already present + */ struct addrinfo ai_hints; struct addrinfo *ai_head; struct addrinfo *ai_ptr; int sd = -1; int status; + /* Check if we already got opened connections */ if ((global_read_fh != NULL) && (global_write_fh != NULL)) { + /* If so, use them */ if (ret_read_fh != NULL) *ret_read_fh = global_read_fh; if (ret_write_fh != NULL) @@ -188,6 +217,7 @@ static int tss2_get_socket (FILE **ret_read_fh, FILE **ret_write_fh) return (0); } + /* Get all addrs for this hostname */ memset (&ai_hints, 0, sizeof (ai_hints)); #ifdef AI_ADDRCONFIG ai_hints.ai_flags |= AI_ADDRCONFIG; @@ -206,8 +236,10 @@ static int tss2_get_socket (FILE **ret_read_fh, FILE **ret_write_fh) return (-1); } + /* Try all given hosts until we can connect to one */ for (ai_ptr = ai_head; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { + /* Create socket */ sd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); if (sd < 0) @@ -218,6 +250,7 @@ static int tss2_get_socket (FILE **ret_read_fh, FILE **ret_write_fh) continue; } + /* Try to connect */ status = connect (sd, ai_ptr->ai_addr, ai_ptr->ai_addrlen); if (status != 0) { @@ -228,14 +261,19 @@ static int tss2_get_socket (FILE **ret_read_fh, FILE **ret_write_fh) continue; } + /* + * Success, we can break. Don't need more than one connection + */ break; } /* for (ai_ptr) */ freeaddrinfo (ai_head); + /* Check if we really got connected */ if (sd < 0) return (-1); + /* Create file objects from sockets */ global_read_fh = fdopen (sd, "r"); if (global_read_fh == NULL) { @@ -261,6 +299,13 @@ static int tss2_get_socket (FILE **ret_read_fh, FILE **ret_write_fh) char *buffer_ptr; buffer_ptr = fgets (buffer, sizeof (buffer), global_read_fh); + if (buffer_ptr == NULL) + { + WARNING ("teamspeak2 plugin: Unexpected EOF received " + "from remote host %s:%s.", + config_host ? config_host : DEFAULT_HOST, + config_port ? config_port : DEFAULT_PORT); + } buffer[sizeof (buffer) - 1] = 0; if (memcmp ("[TS]\r\n", buffer, 6) != 0) @@ -271,8 +316,10 @@ static int tss2_get_socket (FILE **ret_read_fh, FILE **ret_write_fh) tss2_close_socket (); return (-1); } + DEBUG ("teamspeak2 plugin: Server send correct banner, connected!"); } + /* Copy the new filehandles to the given pointers */ if (ret_read_fh != NULL) *ret_read_fh = global_read_fh; if (ret_write_fh != NULL) @@ -282,6 +329,9 @@ static int tss2_get_socket (FILE **ret_read_fh, FILE **ret_write_fh) static int tss2_send_request (FILE *fh, const char *request) { + /* + * This function puts a request to the server socket + */ int status; status = fputs (request, fh); @@ -291,12 +341,16 @@ static int tss2_send_request (FILE *fh, const char *request) tss2_close_socket (); return (-1); } + fflush (fh); return (0); } /* int tss2_send_request */ static int tss2_receive_line (FILE *fh, char *buffer, int buffer_size) { + /* + * Receive a single line from the given file object + */ char *temp; /* @@ -319,12 +373,15 @@ static int tss2_receive_line (FILE *fh, char *buffer, int buffer_size) static int tss2_select_vserver (FILE *read_fh, FILE *write_fh, vserver_list_t *vserver) { + /* + * Tell the server to select the given vserver + */ char command[128]; char response[128]; int status; - snprintf (command, sizeof (command), "sel %i\r\n", vserver->port); - command[sizeof (command) - 1] = 0; + /* Send request */ + ssnprintf (command, sizeof (command), "sel %i\r\n", vserver->port); status = tss2_send_request (write_fh, command); if (status != 0) @@ -333,15 +390,17 @@ static int tss2_select_vserver (FILE *read_fh, FILE *write_fh, vserver_list_t *v return (-1); } + /* Get answer */ status = tss2_receive_line (read_fh, response, sizeof (response)); if (status != 0) { ERROR ("teamspeak2 plugin: tss2_receive_line failed."); return (-1); } - response[sizeof (response)] = 0; + response[sizeof (response) - 1] = 0; - if ((strncmp ("OK", response, 2) == 0) + /* Check answer */ + if ((strncasecmp ("OK", response, 2) == 0) && ((response[2] == 0) || (response[2] == '\n') || (response[2] == '\r'))) @@ -353,15 +412,104 @@ static int tss2_select_vserver (FILE *read_fh, FILE *write_fh, vserver_list_t *v return (-1); } /* int tss2_select_vserver */ +static int tss2_vserver_gapl (FILE *read_fh, FILE *write_fh, + gauge_t *ret_value) +{ + /* + * Reads the vserver's average packet loss and submits it to collectd. + * Be sure to run the tss2_read_vserver function before calling this so + * the vserver is selected correctly. + */ + gauge_t packet_loss = NAN; + int status; + + status = tss2_send_request (write_fh, "gapl\r\n"); + if (status != 0) + { + ERROR("teamspeak2 plugin: tss2_send_request (gapl) failed."); + return (-1); + } + + while (42) + { + char buffer[4096]; + char *value; + char *endptr = NULL; + + status = tss2_receive_line (read_fh, buffer, sizeof (buffer)); + if (status != 0) + { + /* Set to NULL just to make sure noone uses these FHs anymore. */ + read_fh = NULL; + write_fh = NULL; + ERROR ("teamspeak2 plugin: tss2_receive_line failed."); + return (-1); + } + buffer[sizeof (buffer) - 1] = 0; + + if (strncmp ("average_packet_loss=", buffer, + strlen ("average_packet_loss=")) == 0) + { + /* Got average packet loss, now interpret it */ + value = &buffer[20]; + /* Replace , with . */ + while (*value != 0) + { + if (*value == ',') + { + *value = '.'; + break; + } + value++; + } + + value = &buffer[20]; + + packet_loss = strtod (value, &endptr); + if (value == endptr) + { + /* Failed */ + WARNING ("teamspeak2 plugin: Could not read average package " + "loss from string: %s", buffer); + continue; + } + } + else if (strncasecmp ("OK", buffer, 2) == 0) + { + break; + } + else if (strncasecmp ("ERROR", buffer, 5) == 0) + { + ERROR ("teamspeak2 plugin: Server returned an error: %s", buffer); + return (-1); + } + else + { + WARNING ("teamspeak2 plugin: Server returned unexpected string: %s", + buffer); + } + } + + *ret_value = packet_loss; + return (0); +} /* int tss2_vserver_gapl */ + static int tss2_read_vserver (vserver_list_t *vserver) { + /* + * Poll information for the given vserver and submit it to collect. + * If vserver is NULL the global server information will be queried. + */ int status; gauge_t users = NAN; + gauge_t channels = NAN; + gauge_t servers = NAN; counter_t rx_octets = 0; counter_t tx_octets = 0; counter_t rx_packets = 0; counter_t tx_packets = 0; + gauge_t packet_loss = NAN; int valid = 0; char plugin_instance[DATA_MAX_NAME_LEN]; @@ -369,6 +517,7 @@ static int tss2_read_vserver (vserver_list_t *vserver) FILE *read_fh; FILE *write_fh; + /* Get the send/receive sockets */ status = tss2_get_socket (&read_fh, &write_fh); if (status != 0) { @@ -378,19 +527,22 @@ static int tss2_read_vserver (vserver_list_t *vserver) if (vserver == NULL) { + /* Request global information */ memset (plugin_instance, 0, sizeof (plugin_instance)); status = tss2_send_request (write_fh, "gi\r\n"); } else { - snprintf (plugin_instance, sizeof (plugin_instance), "vserver%i", + /* Request server information */ + ssnprintf (plugin_instance, sizeof (plugin_instance), "vserver%i", vserver->port); - plugin_instance[sizeof (plugin_instance) - 1] = 0; + /* Select the server */ status = tss2_select_vserver (read_fh, write_fh, vserver); if (status != 0) return (status); + status = tss2_send_request (write_fh, "si\r\n"); } @@ -400,13 +552,15 @@ static int tss2_read_vserver (vserver_list_t *vserver) return (-1); } + /* Loop until break */ while (42) { char buffer[4096]; char *key; char *value; char *endptr = NULL; - + + /* Read one line of the server's answer */ status = tss2_receive_line (read_fh, buffer, sizeof (buffer)); if (status != 0) { @@ -417,13 +571,13 @@ static int tss2_read_vserver (vserver_list_t *vserver) break; } - if (strncmp ("ERROR", buffer, 5) == 0) + if (strncasecmp ("ERROR", buffer, 5) == 0) { ERROR ("teamspeak2 plugin: Server returned an error: %s", buffer); break; } - else if (strncmp ("OK", buffer, 2) == 0) + else if (strncasecmp ("OK", buffer, 2) == 0) { break; } @@ -437,6 +591,7 @@ static int tss2_read_vserver (vserver_list_t *vserver) } key++; + /* Evaluate assignment */ value = strchr (key, '='); if (value == NULL) { @@ -446,6 +601,9 @@ static int tss2_read_vserver (vserver_list_t *vserver) *value = 0; value++; + /* Check for known key and save the given value */ + /* global info: users_online, + * server info: currentusers. */ if ((strcmp ("currentusers", key) == 0) || (strcmp ("users_online", key) == 0)) { @@ -453,6 +611,22 @@ static int tss2_read_vserver (vserver_list_t *vserver) if (value != endptr) valid |= 0x01; } + /* global info: channels, + * server info: currentchannels. */ + else if ((strcmp ("currentchannels", key) == 0) + || (strcmp ("channels", key) == 0)) + { + channels = strtod (value, &endptr); + if (value != endptr) + valid |= 0x40; + } + /* global only */ + else if (strcmp ("servers", key) == 0) + { + servers = strtod (value, &endptr); + if (value != endptr) + valid |= 0x80; + } else if (strcmp ("bytesreceived", key) == 0) { rx_octets = strtoll (value, &endptr, 0); @@ -477,21 +651,68 @@ static int tss2_read_vserver (vserver_list_t *vserver) if (value != endptr) valid |= 0x10; } + else if ((strncmp ("allow_codec_", key, strlen ("allow_codec_")) == 0) + || (strncmp ("bwinlast", key, strlen ("bwinlast")) == 0) + || (strncmp ("bwoutlast", key, strlen ("bwoutlast")) == 0) + || (strncmp ("webpost_", key, strlen ("webpost_")) == 0) + || (strcmp ("adminemail", key) == 0) + || (strcmp ("clan_server", key) == 0) + || (strcmp ("countrynumber", key) == 0) + || (strcmp ("id", key) == 0) + || (strcmp ("ispname", key) == 0) + || (strcmp ("linkurl", key) == 0) + || (strcmp ("maxusers", key) == 0) + || (strcmp ("name", key) == 0) + || (strcmp ("password", key) == 0) + || (strcmp ("platform", key) == 0) + || (strcmp ("server_platform", key) == 0) + || (strcmp ("server_uptime", key) == 0) + || (strcmp ("server_version", key) == 0) + || (strcmp ("udpport", key) == 0) + || (strcmp ("uptime", key) == 0) + || (strcmp ("users_maximal", key) == 0) + || (strcmp ("welcomemessage", key) == 0)) + /* ignore */; else { - DEBUG ("teamspeak2 plugin: Unknown key-value-pair: key = %s; value = %s;", - key, value); + INFO ("teamspeak2 plugin: Unknown key-value-pair: " + "key = %s; value = %s;", key, value); } } /* while (42) */ + /* Collect vserver packet loss rates only if the loop above did not exit + * with an error. */ + if ((status == 0) && (vserver != NULL)) + { + status = tss2_vserver_gapl (read_fh, write_fh, &packet_loss); + if (status == 0) + { + valid |= 0x20; + } + else + { + WARNING ("teamspeak2 plugin: Reading package loss " + "for vserver %i failed.", vserver->port); + } + } + if ((valid & 0x01) == 0x01) - tss2_submit_gauge (plugin_instance, "users", users); + tss2_submit_gauge (plugin_instance, "users", NULL, users); if ((valid & 0x06) == 0x06) - tss2_submit_io (plugin_instance, "if_octets", rx_octets, tx_octets); + tss2_submit_io (plugin_instance, "io_octets", rx_octets, tx_octets); if ((valid & 0x18) == 0x18) - tss2_submit_io (plugin_instance, "if_packets", rx_packets, tx_packets); + tss2_submit_io (plugin_instance, "io_packets", rx_packets, tx_packets); + + if ((valid & 0x20) == 0x20) + tss2_submit_gauge (plugin_instance, "percent", "packet_loss", packet_loss); + + if ((valid & 0x40) == 0x40) + tss2_submit_gauge (plugin_instance, "gauge", "channels", channels); + + if ((valid & 0x80) == 0x80) + tss2_submit_gauge (plugin_instance, "gauge", "servers", servers); if (valid == 0) return (-1); @@ -500,6 +721,9 @@ static int tss2_read_vserver (vserver_list_t *vserver) static int tss2_config (const char *key, const char *value) { + /* + * Interpret configuration values + */ if (strcasecmp ("Host", key) == 0) { char *temp; @@ -537,7 +761,7 @@ static int tss2_config (const char *key, const char *value) } else { - /* Unknow variable found */ + /* Unknown variable found */ return (-1); } @@ -546,6 +770,10 @@ static int tss2_config (const char *key, const char *value) static int tss2_read (void) { + /* + * Poll function which collects global and vserver information + * and submits it to collectd + */ vserver_list_t *vserver; int success = 0; int status; @@ -561,6 +789,7 @@ static int tss2_read (void) WARNING ("teamspeak2 plugin: Reading global server variables failed."); } + /* Handle vservers */ for (vserver = server_list; vserver != NULL; vserver = vserver->next) { status = tss2_read_vserver (vserver); @@ -583,6 +812,9 @@ static int tss2_read (void) static int tss2_shutdown(void) { + /* + * Shutdown handler + */ vserver_list_t *entry; tss2_close_socket (); @@ -607,6 +839,9 @@ static int tss2_shutdown(void) void module_register(void) { + /* + * Mandatory module_register function + */ plugin_register_config ("teamspeak2", tss2_config, config_keys, config_keys_num); plugin_register_read ("teamspeak2", tss2_read);