root/src/modules/external.c

Revision b9a4230bc7f65c1268a1569d08ffc92d1074bc68, 20.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

first whack at using fcntl everywhere. Heads up, a lot could break. resf #167.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <netdb.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <assert.h>
41 #include <sys/uio.h>
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
45 #ifdef HAVE_SYS_FILIO_H
46 #include <sys/filio.h>
47 #endif
48 #ifdef HAVE_NETINET_IN_SYSTM_H
49 #include <netinet/in_systm.h>
50 #endif
51 #include <pcre.h>
52
53 #include "noit_module.h"
54 #include "noit_check.h"
55 #include "noit_check_tools.h"
56 #include "utils/noit_log.h"
57 #include "utils/noit_security.h"
58 #include "external_proc.h"
59
60 struct check_info {
61   int64_t check_no;
62   u_int16_t argcnt;
63   u_int16_t *arglens;
64   char **args;
65   u_int16_t envcnt;
66   u_int16_t *envlens;
67   char **envs;
68   noit_check_t *check;
69   int exit_code;
70
71   int timedout;
72   char *output;
73   char *error;
74   pcre *matcher;
75   eventer_t timeout_event;
76 };
77
78 typedef struct external_closure {
79   noit_module_t *self;
80   noit_check_t *check;
81 } external_closure_t;
82
83 /* Protocol:
84  *   noit 2 ext:
85  *     int64(check_no)
86  *     uint16(0) -> cancel .end
87
88  *     int64(check_no)
89  *     uint16(argcnt) [argcnt > 0]
90  *     uint16(arglen) x argcnt  (arglen includes \0)
91  *     string of sum(arglen)
92  *     uint16(envcnt)
93  *     uint16(envlen) x envcnt  (envlen includes \0)
94  *     string of sum(envlen) -> execve .end
95  *
96  *   ext 2 noit:
97  *     int64(check_no)
98  *     int32(exitcode) [0 -> good, {1,2} -> bad, 3 -> unknown]
99  *     uint16(outlen) (includes \0)
100  *     string of outlen
101  *     uint16(errlen) (includes \0)
102  *     string of errlen -> complete .end
103  */
104
105 static int external_config(noit_module_t *self, noit_hash_table *options) {
106   external_data_t *data;
107   data = noit_module_get_userdata(self);
108   if(data) {
109     if(data->options) {
110       noit_hash_destroy(data->options, free, free);
111       free(data->options);
112     }
113   }
114   else
115     data = calloc(1, sizeof(*data));
116   data->options = options;
117   if(!data->options) data->options = calloc(1, sizeof(*data->options));
118   noit_module_set_userdata(self, data);
119   return 1;
120 }
121
122 static void external_log_results(noit_module_t *self, noit_check_t *check) {
123   external_data_t *data;
124   struct check_info *ci;
125   stats_t current;
126   struct timeval duration;
127
128   noit_check_stats_clear(&current);
129
130   data = noit_module_get_userdata(self);
131   ci = (struct check_info *)check->closure;
132
133   noitL(data->nldeb, "external(%s) (timeout: %d, exit: %x)\n",
134         check->target, ci->timedout, ci->exit_code);
135
136   gettimeofday(&current.whence, NULL);
137   sub_timeval(current.whence, check->last_fire_time, &duration);
138   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
139   if(ci->timedout) {
140     current.available = NP_UNAVAILABLE;
141     current.state = NP_BAD;
142   }
143   else if(WEXITSTATUS(ci->exit_code) == 3) {
144     current.available = NP_UNKNOWN;
145     current.state = NP_UNKNOWN;
146   }
147   else {
148     current.available = NP_AVAILABLE;
149     current.state = (WEXITSTATUS(ci->exit_code) == 0) ? NP_GOOD : NP_BAD;
150   }
151
152   /* Hack the output into metrics */
153   if(ci->output && ci->matcher) {
154     int rc, len, startoffset = 0;
155     int ovector[30];
156     len = strlen(ci->output);
157     noitL(data->nldeb, "going to match output at %d/%d\n", startoffset, len);
158     while((rc = pcre_exec(ci->matcher, NULL, ci->output, len, startoffset, 0,
159                           ovector, sizeof(ovector)/sizeof(*ovector))) > 0) {
160       char metric[128];
161       char value[128];
162       startoffset = ovector[1];
163       noitL(data->nldeb, "matched at offset %d\n", rc);
164       if(pcre_copy_named_substring(ci->matcher, ci->output, ovector, rc,
165                                    "key", metric, sizeof(metric)) > 0 &&
166          pcre_copy_named_substring(ci->matcher, ci->output, ovector, rc,
167                                    "value", value, sizeof(value)) > 0) {
168         /* We're able to extract something... */
169         noit_stats_set_metric(&current, metric, METRIC_GUESS, value);
170       }
171       noitL(data->nldeb, "going to match output at %d/%d\n", startoffset, len);
172     }
173     noitL(data->nldeb, "match failed.... %d\n", rc);
174   }
175
176   current.status = ci->output;
177   noit_check_set_stats(self, check, &current);
178
179   /* If we didn't exit normally, or we core, or we have stderr to report...
180    * provide a full report.
181    */
182   if((WTERMSIG(ci->exit_code) != SIGQUIT && WTERMSIG(ci->exit_code) != 0) ||
183      WCOREDUMP(ci->exit_code) ||
184      (ci->error && *ci->error)) {
185     char uuid_str[37];
186     uuid_unparse_lower(check->checkid, uuid_str);
187     noitL(data->nlerr, "external/%s: (sig:%d%s) [%s]\n", uuid_str,
188           WTERMSIG(ci->exit_code), WCOREDUMP(ci->exit_code)?", cored":"",
189           ci->error ? ci->error : "");
190   }
191 }
192 static int external_timeout(eventer_t e, int mask,
193                             void *closure, struct timeval *now) {
194   external_closure_t *ecl = (external_closure_t *)closure;
195   struct check_info *data;
196   if(!NOIT_CHECK_KILLED(ecl->check) && !NOIT_CHECK_DISABLED(ecl->check)) {
197     data = (struct check_info *)ecl->check->closure;
198     data->timedout = 1;
199     data->exit_code = 3;
200     external_log_results(ecl->self, ecl->check);
201     data->timeout_event = NULL;
202   }
203   ecl->check->flags &= ~NP_RUNNING;
204   free(ecl);
205   return 0;
206 }
207 static void check_info_clean(struct check_info *ci) {
208   int i;
209   for(i=0; i<ci->argcnt; i++)
210     if(ci->args[i]) free(ci->args[i]);
211   if(ci->arglens) free(ci->arglens);
212   if(ci->args) free(ci->args);
213   for(i=0; i<ci->envcnt; i++)
214     if(ci->envs[i]) free(ci->envs[i]);
215   if(ci->envlens) free(ci->envlens);
216   if(ci->envs) free(ci->envs);
217   if(ci->matcher) pcre_free(ci->matcher);
218   memset(ci, 0, sizeof(*ci));
219 }
220 static int external_handler(eventer_t e, int mask,
221                             void *closure, struct timeval *now) {
222   noit_module_t *self = (noit_module_t *)closure;
223   external_data_t *data;
224
225   data = noit_module_get_userdata(self);
226   while(1) {
227     int inlen, expectlen;
228     noit_check_t *check;
229     struct check_info *ci;
230     void *vci;
231
232     if(!data->cr) {
233       struct external_response r;
234       struct msghdr msg;
235       struct iovec v[3];
236       memset(&r, 0, sizeof(r));
237       v[0].iov_base = &r.check_no;
238       v[0].iov_len = sizeof(r.check_no);
239       v[1].iov_base = &r.exit_code;
240       v[1].iov_len = sizeof(r.exit_code);
241       v[2].iov_base = &r.stdoutlen;
242       v[2].iov_len = sizeof(r.stdoutlen);
243       expectlen = v[0].iov_len + v[1].iov_len + v[2].iov_len;
244
245       /* Make this into a recv'ble message so we can PEEK */
246       memset(&msg, 0, sizeof(msg));
247       msg.msg_iov = v;
248       msg.msg_iovlen = 3;
249       inlen = recvmsg(e->fd, &msg, MSG_PEEK);
250       if(inlen == 0) goto widowed;
251       if((inlen == -1 && errno == EAGAIN) ||
252          (inlen > 0 && inlen < expectlen))
253         return EVENTER_READ | EVENTER_EXCEPTION;
254       if(inlen == -1)
255         noitL(noit_error, "recvmsg() failed: %s\n", strerror(errno));
256       assert(inlen == expectlen);
257       while(-1 == (inlen = recvmsg(e->fd, &msg, 0)) && errno == EINTR);
258       assert(inlen == expectlen);
259       data->cr = calloc(sizeof(*data->cr), 1);
260       memcpy(data->cr, &r, sizeof(r));
261       data->cr->stdoutbuff = malloc(data->cr->stdoutlen);
262     }
263     if(data->cr) {
264       while(data->cr->stdoutlen_sofar < data->cr->stdoutlen) {
265         while((inlen =
266                  read(e->fd,
267                       data->cr->stdoutbuff + data->cr->stdoutlen_sofar,
268                       data->cr->stdoutlen - data->cr->stdoutlen_sofar)) == -1 &&
269                errno == EINTR);
270         if(inlen == -1 && errno == EAGAIN)
271           return EVENTER_READ | EVENTER_EXCEPTION;
272         if(inlen == 0) goto widowed;
273         data->cr->stdoutlen_sofar += inlen;
274       }
275       assert(data->cr->stdoutbuff[data->cr->stdoutlen-1] == '\0');
276       if(!data->cr->stderrbuff) {
277         while((inlen = read(e->fd, &data->cr->stderrlen,
278                             sizeof(data->cr->stderrlen))) == -1 &&
279               errno == EINTR);
280         if(inlen == -1 && errno == EAGAIN)
281           return EVENTER_READ | EVENTER_EXCEPTION;
282         if(inlen == 0) goto widowed;
283         assert(inlen == sizeof(data->cr->stderrlen));
284         data->cr->stderrbuff = malloc(data->cr->stderrlen);
285       }
286       while(data->cr->stderrlen_sofar < data->cr->stderrlen) {
287         while((inlen =
288                  read(e->fd,
289                       data->cr->stderrbuff + data->cr->stderrlen_sofar,
290                       data->cr->stderrlen - data->cr->stderrlen_sofar)) == -1 &&
291                errno == EINTR);
292         if(inlen == -1 && errno == EAGAIN)
293           return EVENTER_READ | EVENTER_EXCEPTION;
294         if(inlen == 0) goto widowed;
295         data->cr->stderrlen_sofar += inlen;
296       }
297       assert(data->cr->stderrbuff[data->cr->stderrlen-1] == '\0');
298     }
299     assert(data->cr && data->cr->stdoutbuff && data->cr->stderrbuff);
300
301     gettimeofday(now, NULL); /* set it, as we care about accuracy */
302
303     /* Lookup data in check_no hash */
304     if(noit_hash_retrieve(&data->external_checks,
305                           (const char *)&data->cr->check_no,
306                           sizeof(data->cr->check_no),
307                           &vci) == 0)
308       vci = NULL;
309     ci = (struct check_info *)vci;
310
311     /* We've seen it, it ain't coming again...
312      * remove it, we'll free it ourselves */
313     noit_hash_delete(&data->external_checks,
314                      (const char *)&data->cr->check_no,
315                      sizeof(data->cr->check_no), NULL, NULL);
316
317     /* If there is no timeout_event, the check must have completed.
318      * We have nothing to do. */
319     if(!ci || !ci->timeout_event) {
320       free(data->cr->stdoutbuff);
321       free(data->cr->stderrbuff);
322       free(data->cr);
323       data->cr = NULL;
324       continue;
325     }
326     ci->exit_code = data->cr->exit_code;
327     ci->output = data->cr->stdoutbuff;
328     ci->error = data->cr->stderrbuff;
329     free(data->cr);
330     data->cr = NULL;
331     check = ci->check;
332     external_log_results(self, check);
333     eventer_remove(ci->timeout_event);
334     free(ci->timeout_event->closure);
335     eventer_free(ci->timeout_event);
336     ci->timeout_event = NULL;
337     check->flags &= ~NP_RUNNING;
338   }
339   return EVENTER_READ;
340
341  widowed:
342   noitL(noit_error, "external module terminated, must restart.\n");
343   exit(1);
344 }
345
346 static int external_init(noit_module_t *self) {
347   external_data_t *data;
348
349   data = noit_module_get_userdata(self);
350   if(!data) data = malloc(sizeof(*data));
351   data->nlerr = noit_log_stream_find("error/external");
352   data->nldeb = noit_log_stream_find("debug/external");
353
354   data->jobq = calloc(1, sizeof(*data->jobq));
355   eventer_jobq_init(data->jobq, "external");
356   data->jobq->backq = eventer_default_backq();
357   eventer_jobq_increase_concurrency(data->jobq);
358
359   if(socketpair(AF_UNIX, SOCK_STREAM, 0, data->pipe_n2e) != 0 ||
360      socketpair(AF_UNIX, SOCK_STREAM, 0, data->pipe_e2n) != 0) {
361     noitL(noit_error, "external: pipe() failed: %s\n", strerror(errno));
362     return -1;
363   }
364
365   data->child = fork();
366   if(data->child == -1) {
367     /* No child, bail. */
368     noitL(noit_error, "external: fork() failed: %s\n", strerror(errno));
369     return -1;
370   }
371
372   /* parent must close the read side of n2e and the write side of e2n */
373   /* The child must do the opposite */
374   close(data->pipe_n2e[(data->child == 0) ? 1 : 0]);
375   close(data->pipe_e2n[(data->child == 0) ? 0 : 1]);
376
377   /* Now the parent must set its bits non-blocking, the child need not */
378   if(data->child != 0) {
379     /* in the parent */
380     if(eventer_set_fd_nonblocking(data->pipe_e2n[0]) == -1) {
381       close(data->pipe_n2e[1]);
382       close(data->pipe_e2n[0]);
383       noitL(noit_error,
384             "external: could not set pipe non-blocking: %s\n",
385             strerror(errno));
386       return -1;
387     }
388     eventer_t newe;
389     newe = eventer_alloc();
390     newe->fd = data->pipe_e2n[0];
391     newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
392     newe->callback = external_handler;
393     newe->closure = self;
394     eventer_add(newe);
395   }
396   else {
397     const char *user = NULL, *group = NULL;
398     if(data->options) {
399       noit_hash_retr_str(data->options, "user", 4, &user);
400       noit_hash_retr_str(data->options, "group", 4, &group);
401     }
402     noit_security_usergroup(user, group);
403     exit(external_child(data));
404   }
405   noit_module_set_userdata(self, data);
406   return 0;
407 }
408
409 static void external_cleanup(noit_module_t *self, noit_check_t *check) {
410   struct check_info *ci = (struct check_info *)check->closure;
411   if(ci) {
412     if(ci->timeout_event) {
413       eventer_remove(ci->timeout_event);
414       free(ci->timeout_event->closure);
415       eventer_free(ci->timeout_event);
416       ci->timeout_event = NULL;
417     }
418   }
419 }
420 #define assert_write(fd, w, s) assert(write(fd, w, s) == s)
421 static int external_enqueue(eventer_t e, int mask, void *closure,
422                             struct timeval *now) {
423   external_closure_t *ecl = (external_closure_t *)closure;
424   struct check_info *ci = (struct check_info *)ecl->check->closure;
425   external_data_t *data;
426   int fd, i;
427
428   if(mask == EVENTER_ASYNCH_CLEANUP) {
429     e->mask = 0;
430     return 0;
431   }
432   if(!(mask & EVENTER_ASYNCH_WORK)) return 0;
433   data = noit_module_get_userdata(ecl->self);
434   fd = data->pipe_n2e[1];
435   assert_write(fd, &ci->check_no, sizeof(ci->check_no));
436   assert_write(fd, &ci->argcnt, sizeof(ci->argcnt));
437   assert_write(fd, ci->arglens, sizeof(*ci->arglens)*ci->argcnt);
438   for(i=0; i<ci->argcnt; i++)
439     assert_write(fd, ci->args[i], ci->arglens[i]);
440   assert_write(fd, &ci->envcnt, sizeof(ci->envcnt));
441   assert_write(fd, ci->envlens, sizeof(*ci->envlens)*ci->envcnt);
442   for(i=0; i<ci->envcnt; i++)
443     assert_write(fd, ci->envs[i], ci->envlens[i]);
444   return 0;
445 }
446 static int external_invoke(noit_module_t *self, noit_check_t *check) {
447   struct timeval when, p_int;
448   external_closure_t *ecl;
449   struct check_info *ci = (struct check_info *)check->closure;
450   eventer_t newe;
451   external_data_t *data;
452   noit_hash_table check_attrs_hash = NOIT_HASH_EMPTY;
453   int i, klen;
454   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
455   const char *name, *value;
456   char interp_fmt[4096], interp_buff[4096];
457
458   data = noit_module_get_userdata(self);
459
460   check->flags |= NP_RUNNING;
461   noitL(data->nldeb, "external_invoke(%p,%s)\n",
462         self, check->target);
463
464   /* remove a timeout if we still have one -- we should unless someone
465    * has set a lower timeout than the period.
466    */
467   if(ci->timeout_event) {
468     eventer_remove(ci->timeout_event);
469     free(ci->timeout_event->closure);
470     eventer_free(ci->timeout_event);
471     ci->timeout_event = NULL;
472   }
473
474   check_info_clean(ci);
475
476   gettimeofday(&when, NULL);
477   memcpy(&check->last_fire_time, &when, sizeof(when));
478
479   /* Setup all our check bits */
480   ci->check_no = noit_atomic_inc64(&data->check_no_seq);
481   ci->check = check;
482   /* We might want to extract metrics */
483   if(noit_hash_retr_str(check->config,
484                         "output_extract", strlen("output_extract"),
485                         &value) != 0) {
486     const char *error;
487     int erroffset;
488     ci->matcher = pcre_compile(value, 0, &error, &erroffset, NULL);
489     if(!ci->matcher) {
490       noitL(data->nlerr, "external pcre /%s/ failed @ %d: %s\n",
491             value, erroffset, error);
492     }
493   }
494
495   noit_check_make_attrs(check, &check_attrs_hash);
496
497   /* Count the args */
498   i = 1;
499   while(1) {
500     char argname[10];
501     snprintf(argname, sizeof(argname), "arg%d", i);
502     if(noit_hash_retr_str(check->config, argname, strlen(argname),
503                           &value) == 0) break;
504     i++;
505   }
506   ci->argcnt = i + 1; /* path, arg0, (i-1 more args) */
507   ci->arglens = calloc(ci->argcnt, sizeof(*ci->arglens));
508   ci->args = calloc(ci->argcnt, sizeof(*ci->args));
509
510   /* Make the command */
511   if(noit_hash_retr_str(check->config, "command", strlen("command"),
512                         &value) == 0) {
513     value = "/bin/true";
514   }
515   ci->args[0] = strdup(value);
516   ci->arglens[0] = strlen(ci->args[0]) + 1;
517
518   i = 0;
519   while(1) {
520     char argname[10];
521     snprintf(argname, sizeof(argname), "arg%d", i);
522     if(noit_hash_retr_str(check->config, argname, strlen(argname),
523                           &value) == 0) {
524       if(i == 0) {
525         /* if we don't have arg0, make it last element of path */
526         char *cp = ci->args[0] + strlen(ci->args[0]);
527         while(cp > ci->args[0] && *(cp-1) != '/') cp--;
528         value = cp;
529       }
530       else break; /* if we don't have argn, we're done */
531     }
532     noit_check_interpolate(interp_buff, sizeof(interp_buff), value,
533                            &check_attrs_hash, check->config);
534     ci->args[i+1] = strdup(interp_buff);
535     ci->arglens[i+1] = strlen(ci->args[i+1]) + 1;
536     i++;
537   }
538
539   /* Make the environment */
540   memset(&iter, 0, sizeof(iter));
541   ci->envcnt = 0;
542   while(noit_hash_next_str(check->config, &iter, &name, &klen, &value))
543     if(!strncasecmp(name, "env_", 4))
544       ci->envcnt++;
545   memset(&iter, 0, sizeof(iter));
546   ci->envlens = calloc(ci->envcnt, sizeof(*ci->envlens));
547   ci->envs = calloc(ci->envcnt, sizeof(*ci->envs));
548   ci->envcnt = 0;
549   while(noit_hash_next_str(check->config, &iter, &name, &klen, &value))
550     if(!strncasecmp(name, "env_", 4)) {
551       snprintf(interp_fmt, sizeof(interp_fmt), "%s=%s", name+4, value);
552       noit_check_interpolate(interp_buff, sizeof(interp_buff), interp_fmt,
553                              &check_attrs_hash, check->config);
554       ci->envs[ci->envcnt] = strdup(interp_buff);
555       ci->envlens[ci->envcnt] = strlen(ci->envs[ci->envcnt]) + 1;
556       ci->envcnt++;
557     }
558
559   noit_hash_destroy(&check_attrs_hash, NULL, NULL);
560
561   noit_hash_store(&data->external_checks,
562                   (const char *)&ci->check_no, sizeof(ci->check_no),
563                   ci);
564
565   /* Setup a timeout */
566   newe = eventer_alloc();
567   newe->mask = EVENTER_TIMER;
568   gettimeofday(&when, NULL);
569   p_int.tv_sec = check->timeout / 1000;
570   p_int.tv_usec = (check->timeout % 1000) * 1000;
571   add_timeval(when, p_int, &newe->whence);
572   ecl = calloc(1, sizeof(*ecl));
573   ecl->self = self;
574   ecl->check = check;
575   newe->closure = ecl;
576   newe->callback = external_timeout;
577   eventer_add(newe);
578   ci->timeout_event = newe;
579
580   /* Setup push */
581   newe = eventer_alloc();
582   newe->mask = EVENTER_ASYNCH;
583   add_timeval(when, p_int, &newe->whence);
584   ecl = calloc(1, sizeof(*ecl));
585   ecl->self = self;
586   ecl->check = check;
587   newe->closure = ecl;
588   newe->callback = external_enqueue;
589   eventer_add(newe);
590
591   return 0;
592 }
593 static int external_initiate_check(noit_module_t *self, noit_check_t *check,
594                                     int once, noit_check_t *cause) {
595   if(!check->closure) check->closure = calloc(1, sizeof(struct check_info));
596   INITIATE_CHECK(external_invoke, self, check);
597   return 0;
598 }
599
600 static int external_onload(noit_image_t *self) {
601   eventer_name_callback("external/timeout", external_timeout);
602   eventer_name_callback("external/handler", external_handler);
603   return 0;
604 }
605
606 #include "external.xmlh"
607 noit_module_t external = {
608   {
609     NOIT_MODULE_MAGIC,
610     NOIT_MODULE_ABI_VERSION,
611     "external",
612     "checks via external programs",
613     external_xml_description,
614     external_onload
615   },
616   external_config,
617   external_init,
618   external_initiate_check,
619   external_cleanup
620 };
621
Note: See TracBrowser for help on using the browser.