1 /*----------------------------------------------------------------------------*/
2 /* Xymon RRD handler module. */
3 /* */
4 /* This module handles any message with data in the form */
5 /* NAME: VALUE */
6 /* */
7 /* Copyright (C) 2004-2011 Henrik Storner <henrik@hswn.dk> */
8 /* split-ncv added by Charles Goyard November 2006 */
9 /* */
10 /* This program is released under the GNU General Public License (GPL), */
11 /* version 2. See the file "COPYING" for details. */
12 /* */
13 /*----------------------------------------------------------------------------*/
14
15 static char ncv_rcsid[] = "$Id: do_ncv.c 7711 2015-11-02 06:15:28Z jccleaver $";
16
do_ncv_rrd(char * hostname,char * testname,char * classname,char * pagepaths,char * msg,time_t tstamp)17 int do_ncv_rrd(char *hostname, char *testname, char *classname, char *pagepaths, char *msg, time_t tstamp)
18 {
19 char **params = NULL;
20 int paridx;
21 char dsdef[1024]; /* destination DS syntax for rrd engine */
22 char *l, *name, *val;
23 char *envnam;
24 char *dstypes = NULL; /* contain NCV_testname value */
25 int split_ncv = 0;
26 int skipblock = 0;
27 int dslen;
28
29 snprintf(rrdvalues, sizeof(rrdvalues), "%d", (int)tstamp);
30 params = (char **)calloc(1, sizeof(char *));
31 paridx = 0;
32
33 /* Get the NCV_* or SPLITNCV_* environment setting */
34 envnam = (char *)malloc(9 + strlen(testname) + 1);
35 sprintf(envnam, "SPLITNCV_%s", testname);
36 l = getenv(envnam);
37 if (l) {
38 split_ncv = 1;
39 dslen = 200;
40 }
41 else {
42 split_ncv = 0;
43 dslen = 19;
44 setupfn("%s.rrd", testname);
45 sprintf(envnam, "NCV_%s", testname);
46 l = getenv(envnam);
47 }
48
49 if (l) {
50 dstypes = (char *)malloc(strlen(l)+3);
51 sprintf(dstypes, ",%s,", l);
52 }
53 xfree(envnam);
54
55 l = strchr(msg, '\n'); if (l) l++;
56 while (l && *l && strncmp(l, "@@\n", 3)) {
57 name = val = NULL;
58
59 l += strspn(l, " \t\n");
60 if (*l) {
61 /* Look for a sign to alter processing */
62 if (strncmp(l, "<!-- ncv_", 9) == 0) {
63 /* expandable for future use */
64 l += 9;
65
66 if (strncmp(l, "skip -->", 8) == 0) {
67 /* skip the entire line */
68 l += strcspn(l, "\n"); l++; continue;
69 }
70 else if (strncmp(l, "skipstart -->", 13) == 0) {
71 /* begin ignoring lines until told to stop */
72 skipblock = 1;
73 }
74 else if (strncmp(l, "skipend -->", 11) == 0) {
75 /* we're done skipping, */
76 skipblock = 0;
77 l += 11; continue;
78 }
79 else if (strncmp(l, "ignore -->", 10) == 0) {
80 /* allowed syntax: <!-- ncv_ignore --> assorted text without html </--> label : value */
81 l += 10; l += strcspn(l, ">\n"); /* search for closing '>' or the eol */
82
83 /* See if it's the expected end marker '</-->', and repeat until we find it (or eol) */
84 while ((*l != '\n') && (strncmp((l-4), "</-->", 5) != 0) ) { l++; l += strcspn(l, ">\n"); }
85
86 /* Did we hit the end? Move on. If not, skip any (now) leading whitespace and continue on */
87 if (*l == '\n') { l++; continue; }
88 else { l++; l += strspn(l, " \t\n"); }
89 }
90 else if (strncmp(l, "end -->", 7) == 0) break; /* abort */
91 else {
92 dbgprintf("Unexpected NCV directive found\n");
93 /* skip past directive */
94 l += strcspn(l, ">"); l++; l += strspn(l, " \t\n");
95 }
96 }
97
98 if (skipblock) { l += strcspn(l, "\n"); l++; continue; } /* We're still in a comment block */
99
100 /* See if this line contains a '=' or ':' sign */
101 name = l;
102 l += strcspn(l, ":=\n");
103 if (*l) {
104 if (( *l == '=') || (*l == ':')) {
105 *l = '\0'; l++;
106 }
107 else {
108 /* No marker, so skip this line */
109 name = NULL;
110 }
111 }
112 else break; /* We've hit the end of the message */
113 }
114
115 /* Skip any color marker "&COLOR " in front of the ds name */
116 if (name && (*name == '&')) {
117 name++;
118 name += strspn(name, "abcdefghijklmnopqrstuvwxyz");
119 name += strspn(name, " \t");
120 if (*name == '\0') name = NULL;
121 }
122
123 if (name) {
124 val = l + strspn(l, " \t");
125 /* Find the end of the value string */
126 l = val; if ((*l == '-') || (*l == '+')) l++; /* Pass leading sign */
127 l += strspn(l, "0123456789.+-eE"); /* and the numbers. */
128 if( *val ) {
129 int iseol = (*l == '\n');
130
131 *l = '\0';
132 if (!iseol) {
133 /* If extra data after the value, skip to end of line */
134 l = strchr(l+1, '\n');
135 if (l) l++;
136 }
137 else {
138 l++;
139 }
140 }
141 else break; /* No value data */
142 }
143
144 if (name && val && *val) {
145 char *endptr;
146 double dummy;
147
148 dummy = strtod(val, &endptr); /* Dont care - we're only interested in endptr */
149 if (isspace((int)*endptr) || (*endptr == '\0')) {
150 char dsname[250]; /* name of ncv in status message (with space and all) */
151 char dskey[252]; /* name of final DS key (stripped) */
152 char *dstype = NULL; /* type of final DS */
153 char *inp;
154 int outidx = 0;
155 /* val contains a valid number */
156
157 /* rrdcreate(1) says: ds must be in the set [a-zA-Z0-9_] ... */
158 for (inp=name,outidx=0; (*inp && (outidx < dslen)); inp++) {
159 if ( ((*inp >= 'A') && (*inp <= 'Z')) ||
160 ((*inp >= 'a') && (*inp <= 'z')) ||
161 ((*inp >= '0') && (*inp <= '9')) ) {
162 dsname[outidx++] = *inp;
163 }
164 /* ... however, for split ncv, we replace anything else */
165 /* with an underscore, compacting successive invalid */
166 /* characters into a single one */
167 else if (split_ncv && ((outidx == 0) || (dsname[outidx - 1] != '_'))) {
168 dsname[outidx++] = '_';
169 }
170 }
171
172 if ((outidx > 0) && (dsname[outidx-1] == '_')) {
173 dsname[outidx-1] = '\0';
174 }
175 else {
176 dsname[outidx] = '\0';
177 }
178
179 snprintf(dskey, sizeof(dskey), ",%s:", dsname);
180 if (split_ncv) setupfn2("%s,%s.rrd", testname, dsname);
181
182 if (dstypes) {
183 dstype = strstr(dstypes, dskey);
184 if (!dstype) {
185 strcpy(dskey, ",*:");
186 dstype = strstr(dstypes, dskey);
187 }
188 }
189
190 if (dstype) { /* if ds type is forced */
191 char *p;
192
193 dstype += strlen(dskey);
194 p = strchr(dstype, ','); if (p) *p = '\0';
195 if(split_ncv) {
196 snprintf(dsdef, sizeof(dsdef), "DS:lambda:%s:600:U:U", dstype);
197 }
198 else {
199 snprintf(dsdef, sizeof(dsdef), "DS:%s:%s:600:U:U", dsname, dstype);
200 }
201 if (p) *p = ',';
202 }
203 else { /* nothing specified in the environnement, and no '*:' default */
204 if(split_ncv) {
205 strcpy(dsdef, "DS:lambda:DERIVE:600:U:U");
206 }
207 else {
208 snprintf(dsdef, sizeof(dsdef), "DS:%s:DERIVE:600:U:U", dsname);
209 }
210 }
211
212 if (!dstype || (strncasecmp(dstype, "NONE", 4) != 0)) { /* if we have something */
213 params[paridx] = strdup(dsdef);
214 paridx++;
215 params = (char **)realloc(params, (1 + paridx)*sizeof(char *));
216 params[paridx] = NULL;
217 snprintf(rrdvalues+strlen(rrdvalues), sizeof(rrdvalues)-strlen(rrdvalues), ":%s", val);
218 }
219 }
220
221 if (split_ncv && (paridx > 0)) {
222 create_and_update_rrd(hostname, testname, classname, pagepaths, params, NULL);
223
224 /* We've created one RRD, so reset the params for the next one */
225 for (paridx=0; (params[paridx] != NULL); paridx++) xfree(params[paridx]);
226 paridx = 0;
227 params[0] = NULL;
228 snprintf(rrdvalues, sizeof(rrdvalues), "%d", (int)tstamp);
229 }
230 }
231 } /* end of while */
232
233 if (!split_ncv && params[0]) create_and_update_rrd(hostname, testname, classname, pagepaths, params, NULL);
234
235 for (paridx=0; (params[paridx] != NULL); paridx++) xfree(params[paridx]);
236 xfree(params);
237 if (dstypes) xfree(dstypes);
238
239 return 0;
240 }
241
242