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