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