diff --git a/Makefile b/Makefile index d43127d..992bc98 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ WITH_RRD=yes PLUGINS := ctstat diskstat hwmon load memory mount netdev random rtstat stat uptime vmstat -PLUGINS += apache mysql conntrack alixusv +PLUGINS += apache mysql conntrack alixusv ts2 DESTDIR = BINARY_DIR = /usr/local/bin diff --git a/plugins/ts2.c b/plugins/ts2.c new file mode 100644 index 0000000..a8f95da --- /dev/null +++ b/plugins/ts2.c @@ -0,0 +1,215 @@ +/*************************************************************************** + * Copyright (C) 02/2010 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include +#include + +#include +#include +#include +#include + +#include "configfile.h" +#include "helper.h" +#include "list.h" +#include "logging.h" +#include "plugins.h" +#include "probe.h" +#include "sockaddr.h" + +#define TS2_RESP_SIZE 1024 + +struct sammler_plugin plugin; + +struct server_entry { + struct list_head list; + + char *name; + int errors; + struct sockaddr_in sa; +}; + +static LIST_HEAD(server_list); +static char *ts2_resp_buf; + +static const char *ds_def = { + "DS:users:GAUGE:90:0:U " + "DS:channels:GAUGE:90:0:U " +}; + +static const char * get_ds(int ds_id) +{ + return ds_def; +} + +static int probe(void) +{ + struct server_entry *entry; + list_for_each_entry(entry, &server_list, list) { + + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) { + log_print(LOG_WARN, "plugin ts2: socket()"); + continue; + } + + int ret = connect(sock, (struct sockaddr *)&entry->sa, sizeof(entry->sa)); + if (ret != 0) { + if (entry->errors++ == 0) + log_print(LOG_ERROR, "plugin ts2: connect(%s)", get_sockaddr_buf(&entry->sa)); + + close(sock); + continue; + } + + /* read prompt "[TS]" */ + int len = read(sock, ts2_resp_buf, TS2_RESP_SIZE); + if (len <= 0) { + if (entry->errors++ == 0) + log_print(LOG_ERROR, "ts2: read(1)"); + + close(sock); + continue; + } + + if (strncmp(ts2_resp_buf, "[TS]", 4) != 0) { + if (entry->errors++ == 0) + log_print(LOG_ERROR, "ts2: invalid prompt from server"); + + close(sock); + continue; + } + + /* query "general informations" */ + char cmd1[] = "gi\n"; + write(sock, cmd1, sizeof(cmd1)); + + len = read(sock, ts2_resp_buf, TS2_RESP_SIZE); + if (len <= 0) { + if (entry->errors++ == 0) + log_print(LOG_ERROR, "ts2: read(2)"); + + close(sock); + continue; + } + + /* send quit (but we close the socket anyway..) */ + char cmd2[] = "quit"; + write(sock, cmd2, sizeof(cmd2)); + + close(sock); + + /* terminate response */ + ts2_resp_buf[len] = '\0'; + + char *tok, *tmp, *resp = ts2_resp_buf; + int users = 0, channels = 0; + while ((tok = strtok_r(resp, "\r\n", &tmp)) != NULL) { + /* total_users_online=14 */ + if (strncmp(tok, "total_users_online", 18) == 0) { + users = atoi(tok +19); + + /* total_channels=3 */ + } else if (strncmp(tok, "total_channels", 14) == 0) { + channels = atoi(tok +15); + } + resp = NULL; + } + + char filename[32]; + len = snprintf(filename, sizeof(filename), "ts2-%s.rrd", entry->name); + if (len < 0 || len >= sizeof(filename)) + continue; + + probe_submit(&plugin, filename, 0, "%d:%d", users, channels); + + if (entry->errors > 0) { + log_print(LOG_ERROR, "plugin ts2: success (%s) after %d errors", + entry->name, entry->errors); + + entry->errors = 0; + } + } + + return 0; +} + +static int init_cb(const char *parameter, void *privdata) +{ + char *part[2]; + int ret = strsplit((char *)parameter, ",", part, 2); + if (ret < 2) { + log_print(LOG_ERROR, "p_ts2: parse error"); + return -1; + } + + struct server_entry *entry = malloc(sizeof(struct server_entry)); + if (entry == NULL) { + log_print(LOG_ERROR, "p_ts2: out of memory"); + return -1; + } + + entry->name = strdup(part[0]); + entry->errors = 0; + + if (parse_sockaddr((char *)part[1], &entry->sa) < 0) { + log_print(LOG_ERROR, "p_ts2: invalid address: <%s>", part[1]); + free(entry->name); + free(entry); + return -1; + } + + log_print(LOG_INFO, "p_ts2: added server '%s'", entry->name); + list_add_tail(&entry->list, &server_list); + return 0; +} + +static int init(void) +{ + ts2_resp_buf = malloc(TS2_RESP_SIZE); + if (ts2_resp_buf == NULL) { + log_print(LOG_ERROR, "p_ts2: out of memory"); + return -1; + } + + config_get_strings("p_ts2", "server", init_cb, NULL); + return 0; +} + +static int fini(void) +{ + struct server_entry *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &server_list, list) { + free(entry->name); + free(entry); + } + + free(ts2_resp_buf); + return 0; +} + +struct sammler_plugin plugin = { + .name = "ts2", + .interval = 60, + .init = &init, + .fini = &fini, + .probe = &probe, + .get_ds = &get_ds, +}; diff --git a/sammler.conf b/sammler.conf index 2ad0028..c3e5410 100644 --- a/sammler.conf +++ b/sammler.conf @@ -27,6 +27,7 @@ plugin random.so #plugin conntrack.so #plugin hwmon.so #plugin alixusv.so +#plugin ts2.so # 1h(10s), 12h(1min), 48h(2min), 14d(15min), 4w(60min), 2y(12h) rra RRA:MIN:0.5:1:360 RRA:AVERAGE:0.5:1:360 RRA:MAX:0.5:1:360 @@ -47,4 +48,7 @@ rra RRA:MIN:0.5:4320:1440 RRA:AVERAGE:0.5:4320:1440 RRA:MAX:0.5:4320:1440 #temp cpu,/sys/class/hwmon/hwmon0/device/temp2_input [p_alixusv] -socket /var/run/alix-usvd.sock +#socket /var/run/alix-usvd.sock + +[p_ts2] +#server localhost,127.0.0.1:51234 diff --git a/sammler.php b/sammler.php index e3ed8f4..52846bc 100644 --- a/sammler.php +++ b/sammler.php @@ -182,6 +182,7 @@ function get_rrd_type($filename) { "mount", "apache", "mysql", + "ts2" )); $tmp1 = explode('-', $filename); @@ -794,6 +795,16 @@ function show_rrd($conf) { 'STACK:b#00CF00:"Free Swap " GPRINT:b:LAST:"Current\:%8.2lf %s" GPRINT:b:AVERAGE:"Average\:%8.2lf %s" GPRINT:b:MAX:"Maximum\:%8.2lf %s\n" '; break; + case 'ts2': + $cmd .= "--base=1000 --height={$height} --width={$width} --alt-autoscale-max --lower-limit 0 --vertical-label=\"users/chans\" ". + "DEF:users={$rrdfile}:users:AVERAGE ". + "DEF:chans={$rrdfile}:channels:AVERAGE ". + 'CDEF:err=users,UN,INF,UNKN,IF '. + 'AREA:err#FFD0D0 '. + 'AREA:chans#00CF00:"Channels \n" '. + 'LINE1:users#002A97:"Users "'; + break; + case 'uptime': $cmd .= "--base=1000 --height={$height} --width={$width} --alt-autoscale-max --lower-limit 0 --vertical-label=\"days\" ". "DEF:ups={$rrdfile}:uptime:AVERAGE ". @@ -802,7 +813,7 @@ function show_rrd($conf) { 'CDEF:idle=idles,86400,/ '. 'CDEF:err=ups,UN,INF,UNKN,IF '. 'AREA:err#FFD0D0 '. - 'AREA:up#00CF00:"Uptime " '. + 'AREA:up#00CF00:"Uptime \n" '. 'LINE1:idle#002A97:"Idletime "'; break;