1 /*----------------------------------------------------------------------------*/
2 /* Xymon RRD handler module.                                                  */
3 /*                                                                            */
4 /* Copyright (C) 2004-2009 Henrik Storner <henrik@hswn.dk>                    */
5 /*                                                                            */
6 /* This program is released under the GNU General Public License (GPL),       */
7 /* version 2. See the file "COPYING" for details.                             */
8 /*                                                                            */
9 /*----------------------------------------------------------------------------*/
10 
11 static char external_rcsid[] = "$Id: do_external.c 7026 2012-07-13 14:05:20Z storner $";
12 
do_external_rrd(char * hostname,char * testname,char * classname,char * pagepaths,char * msg,time_t tstamp)13 int do_external_rrd(char *hostname, char *testname, char *classname, char *pagepaths, char *msg, time_t tstamp)
14 {
15 	pid_t childpid;
16 
17 	dbgprintf("-> do_external(%s, %s)\n", hostname, testname);
18 
19 	childpid = fork();
20 	if (childpid == 0) {
21 		FILE *fd;
22 		char fn[PATH_MAX];
23 		enum { R_DEFS, R_FN, R_DATA, R_NEXT } pstate;
24 		FILE *extfd;
25 		char extcmd[2*PATH_MAX];
26 		strbuffer_t *inbuf;
27 		char *p;
28 		char **params = NULL;
29 		int paridx = 0;
30 		pid_t mypid = getpid();
31 
32 		MEMDEFINE(fn); MEMDEFINE(extcmd);
33 
34 		snprintf(fn, sizeof(fn), "%s/rrd_msg_%d", xgetenv("XYMONTMP"), (int) getpid());
35 		dbgprintf("%09d : Saving msg to file %s\n", (int)mypid, fn);
36 
37 		fd = fopen(fn, "w");
38 		if (fd == NULL) {
39 			errprintf("Cannot create temp file %s\n", fn);
40 			exit(1);
41 		}
42 		if (fwrite(msg, strlen(msg), 1, fd) != 1) {
43 			errprintf("Error writing to file %s: %s\n", fn, strerror(errno));
44 			exit(1) ;
45 		}
46 		if (fclose(fd)) errprintf("Error closing file %s: %s\n", fn, strerror(errno));
47 
48 		/*
49 		 * Disable the RRD update cache.
50 		 * We cannot use the cache, because this child
51 		 * process terminates without flushing the cache,
52 		 * and it cannot feed the update-data back to the
53 		 * parent process which owns the cache. So using
54 		 * an external handler means the updates will be
55 		 * sync'ed to disk immediately.
56 		 *
57 		 * NB: It is OK to do this now and not re-enable it,
58 		 * since we're running in the external helper
59 		 * child process - so this only affects the current
60 		 * update.
61 		 *
62 		 * Thanks to Graham Nayler for the analysis.
63 		 */
64 		use_rrd_cache = 0;
65 
66 		inbuf = newstrbuffer(0);
67 
68 		/* Now call the external helper */
69 		snprintf(extcmd, sizeof(extcmd), "%s %s %s %s", exthandler, hostname, testname, fn);
70 		dbgprintf("%09d : Calling helper script %s\n", (int)mypid, extcmd);
71 		extfd = popen(extcmd, "r");
72 		if (extfd) {
73 			pstate = R_DEFS;
74 			initfgets(extfd);
75 
76 			while (unlimfgets(inbuf, extfd)) {
77 				p = strchr(STRBUF(inbuf), '\n'); if (p) *p = '\0';
78 				dbgprintf("%09d : Helper input '%s'\n", (int)mypid, STRBUF(inbuf));
79 				if (STRBUFLEN(inbuf) == 0) continue;
80 
81 				if (pstate == R_NEXT) {
82 					/* After doing one set of data, allow script to re-use the same DS defs */
83 					if (strncasecmp(STRBUF(inbuf), "DS:", 3) == 0) {
84 						/* New DS definitions, scratch the old ones */
85 						if (params) {
86 							for (paridx=0; (params[paridx] != NULL); paridx++)
87 								xfree(params[paridx]);
88 							xfree(params);
89 							params = NULL;
90 						}
91 						pstate = R_DEFS;
92 					}
93 					else pstate = R_FN;
94 				}
95 
96 				switch (pstate) {
97 				  case R_DEFS:
98 					if (params == NULL) {
99 						params = (char **)calloc(1, sizeof(char *));
100 						paridx = 0;
101 					}
102 
103 					if (strncasecmp(STRBUF(inbuf), "DS:", 3) == 0) {
104 						/* Dataset definition */
105 						params[paridx] = strdup(STRBUF(inbuf));
106 						paridx++;
107 						params = (char **)realloc(params, (1 + paridx)*sizeof(char *));
108 						params[paridx] = NULL;
109 						break;
110 					}
111 					else {
112 						/* No more DS defs */
113 						pstate = R_FN;
114 					}
115 					/* Fall through */
116 				  case R_FN:
117 					setupfn("%s", STRBUF(inbuf));
118 					pstate = R_DATA;
119 					break;
120 
121 				  case R_DATA:
122 					snprintf(rrdvalues, sizeof(rrdvalues)-1, "%d:%s", (int)tstamp, STRBUF(inbuf));
123 					rrdvalues[sizeof(rrdvalues)-1] = '\0';
124 					create_and_update_rrd(hostname, testname, classname, pagepaths, params, NULL);
125 					pstate = R_NEXT;
126 					break;
127 
128 				  case R_NEXT:
129 					/* Should not happen */
130 					break;
131 				}
132 			}
133 			pclose(extfd);
134 		}
135 		else {
136 			errprintf("Pipe open of RRD handler failed: %s\n", strerror(errno));
137 		}
138 
139 		if (params) {
140 			for (paridx=0; (params[paridx] != NULL); paridx++) xfree(params[paridx]);
141 			xfree(params);
142 		}
143 
144 		dbgprintf("%09d : Unlinking temp file\n", (int)mypid);
145 		unlink(fn);
146 		freestrbuffer(inbuf);
147 
148 		exit(0);
149 	}
150 	else if (childpid > 0) {
151 		/* Parent continues */
152 	}
153 	else {
154 		errprintf("Fork failed in RRD handler: %s\n", strerror(errno));
155 	}
156 
157 	dbgprintf("<- do_external(%s, %s)\n", hostname, testname);
158 	return 0;
159 }
160 
161