aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile2
-rw-r--r--alert.c166
-rw-r--r--alert.h10
-rw-r--r--config.c2
-rw-r--r--events.log2
-rw-r--r--main.c6
-rw-r--r--monitor.c12
-rw-r--r--monitor.cfg6
-rw-r--r--monitor.cfg.example27
10 files changed, 225 insertions, 9 deletions
diff --git a/.gitignore b/.gitignore
index dd3516c..816d6b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
monitor
+monitor.cfg
diff --git a/Makefile b/Makefile
index 292b32a..c10078c 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ CFLAGS = -g -Wall -pedantic
LDFLAGS = -lmicrohttpd -lcurl -lm
BIN = monitor
-SRC = main.c monitor.c config.c check.c
+SRC = main.c monitor.c config.c check.c alert.c
$(BIN): $(SRC)
$(CC) -o $@ $(CFLAGS) $^ $(LDFLAGS)
diff --git a/alert.c b/alert.c
index e69de29..a9f51f1 100644
--- a/alert.c
+++ b/alert.c
@@ -0,0 +1,166 @@
+#include "alert.h"
+
+#include "config.h"
+
+#include <curl/curl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef enum {
+ TYPE_API,
+ TYPE_EMAIL
+} alert_type_t;
+
+typedef struct {
+ alert_type_t type;
+ char *target; /* in api: endpoint, in email: address */
+ char *extra; /* in api: Content-Type, in email: subject template */
+ char *body_tmpl;
+} alert_t;
+
+alert_t *alerts = NULL;
+size_t alerts_size = 0, alerts_capacity = INIT_VEC_CAPACITY;
+
+static const char *type_str[] = { "api", "email" };
+static const char *status_str[] = { "down", "up" };
+static const char *from = NULL;
+
+
+static size_t
+write_data(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+ return size * nmemb;
+}
+
+static int
+send_api(const target_t *target, const char *endpoint, const char *content_type,
+ const char *body_tmpl)
+{
+ static char buff[4096];
+
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ fprintf(stderr, "Error allocating cURL handle\n");
+ return -1;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);
+
+ curl_easy_setopt(curl, CURLOPT_URL, endpoint);
+
+ struct curl_slist *list = NULL;
+ snprintf(buff, 256, "Content-Type: %s", content_type);
+ list = curl_slist_append(list, buff); /* copies */
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
+
+ snprintf(buff, 4096, body_tmpl, target->name, status_str[target->status]);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, buff);
+
+ CURLcode curl_code = curl_easy_perform(curl);
+ if (curl_code != CURLE_OK) {
+ printf("curl_easy_perform() failed: %s\n",
+ curl_easy_strerror(curl_code));
+ return STATUS_DOWN;
+ }
+
+ long http_code;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+
+ curl_easy_cleanup(curl);
+
+ printf("%ld\n", http_code);
+
+ return http_code == 200 ? STATUS_UP : STATUS_DOWN;
+}
+
+
+static int
+send_email(const target_t *target, const char *address, const char *subject_tmpl,
+ const char *body_tmpl)
+{
+
+}
+
+
+int
+alert_init()
+{
+ alerts = malloc(INIT_VEC_CAPACITY * sizeof(alert_t));
+
+ from = alert_config.from;
+
+ printf("alerts:\n");
+
+ char line[256];
+ char *src_line = alert_config.alert_config;
+ while (src_line != (void*)1) {
+ char *next_line = strchr(src_line, '\n');
+ size_t linelen = next_line ? next_line - src_line : strlen(src_line);
+ strncpy(line, src_line, linelen);
+ line[linelen] = '\0';
+ src_line = next_line + 1;
+
+ if (*line == '\n' || *line == '\0')
+ continue;
+
+ char *type = strtok(line, ",");
+ char *target = strtok(NULL, ",");
+ char *extra = strtok(NULL, ",");
+ char *body_tmpl = strtok(NULL, ",");
+
+ if (!type || !target) {
+ fprintf(stderr, "malformed config line: %s\n", line);
+ continue;
+ }
+
+ if (strcmp(type, "api") == 0)
+ alerts[alerts_size].type = TYPE_API;
+ else if (strcmp(type, "email") == 0)
+ alerts[alerts_size].type = TYPE_EMAIL;
+ else {
+ fprintf(stderr, "unknown alert type: %s\n", line);
+ continue;
+ }
+
+ alerts[alerts_size].target = strdup(target);
+ alerts[alerts_size].extra = strdup(extra);
+ alerts[alerts_size].body_tmpl = strdup(body_tmpl);
+
+ printf("\t%s: %s\n",
+ alerts[alerts_size].target,
+ type_str[alerts[alerts_size].type]
+ );
+
+ alerts_size++;
+ }
+
+ return alerts_size;
+}
+
+void
+alert_trigger(const target_t *target)
+{
+ static const int (*send_funcs[])(const target_t *, const char *,
+ const char *, const char *) =
+ {
+ send_api,
+ send_email
+ };
+
+ static char timestr[256];
+
+ time_t time_now = time(NULL);
+ struct tm *tm_now = gmtime(&time_now);
+ strftime(timestr, 256, "%F %T", tm_now);
+
+ for (int i = 0; i < alerts_size; i++) {
+ printf("[%s] [monitor] alerted %s about %s\n",
+ timestr, alerts[i].target, target->name);
+ send_funcs[target->type](target, alerts[i].target, alerts[i].extra,
+ alerts[i].body_tmpl);
+ }
+}
+
diff --git a/alert.h b/alert.h
index e69de29..b68db73 100644
--- a/alert.h
+++ b/alert.h
@@ -0,0 +1,10 @@
+#ifndef _ALERT_H
+#define _ALERT_H
+
+#include "monitor.h"
+
+int alert_init();
+void alert_trigger(const target_t *target);
+
+#endif /* _ALERT_H */
+
diff --git a/config.c b/config.c
index 35fa90e..39dc1b9 100644
--- a/config.c
+++ b/config.c
@@ -73,7 +73,7 @@ config_load(const char *conf_path)
cfgsize - (target_pos - monitor_config.target_config),
"%s", value);
} else if (strcmp(line, "alert") == 0) {
- target_pos += snprintf(alert_pos,
+ alert_pos += snprintf(alert_pos,
cfgsize - (alert_pos - alert_config.alert_config),
"%s", value);
} else {
diff --git a/events.log b/events.log
index d74a182..8399053 100644
--- a/events.log
+++ b/events.log
@@ -76,3 +76,5 @@ http,2025-10-24T12:38:19Z,up
ipv4,2025-10-27T15:43:17+0000,up
dns,2025-10-27T15:43:17+0000,up
https,2025-10-27T15:43:17+0000,up
+http,2025-11-04T18:39:12Z,down
+http,2025-11-04T18:40:12Z,up
diff --git a/main.c b/main.c
index a587397..784c0e2 100644
--- a/main.c
+++ b/main.c
@@ -15,6 +15,7 @@
#include "monitor.h"
#include "config.h"
#include "check.h"
+#include "alert.h"
#define CFG_FILE "monitor.cfg"
#define TMPL_FILE "index.htm.tmpl"
@@ -95,7 +96,10 @@ int main() {
if (check_init() < 0)
return 1;
- if (monitor_init(CFG_FILE, LOG_FILE) < 0)
+ if (monitor_init() < 0)
+ return 1;
+
+ if (alert_init() < 0)
return 1;
/* start server */
diff --git a/monitor.c b/monitor.c
index 5d34824..109de89 100644
--- a/monitor.c
+++ b/monitor.c
@@ -24,7 +24,8 @@ typedef struct {
} incident_t;
-const char *type_str[] = { "reach", "dns", "web" };
+static const char *status_str[] = { "down", "up" };
+static const char *type_str[] = { "reach", "dns", "web" };
target_t *targets = NULL;
size_t targets_size = 0, targets_capacity = INIT_VEC_CAPACITY;
@@ -35,7 +36,6 @@ static size_t incidents_size = 0, incidents_capacity = 0;
static char timestr[256];
-static char *status_str[] = { "down", "up" };
static void
@@ -215,7 +215,7 @@ monitor_init()
char *name = strtok(NULL, ",");
char *target = strtok(NULL, ",");
- if (!target || !name || !target) {
+ if (!type || !name || !target) {
fprintf(stderr, "malformed config line: %s\n", line);
continue;
}
@@ -226,6 +226,10 @@ monitor_init()
targets[targets_size].type = TYPE_DNS;
else if (strcmp(type, "web") == 0)
targets[targets_size].type = TYPE_WEB;
+ else {
+ fprintf(stderr, "unknown target type: %s\n", line);
+ continue;
+ }
targets[targets_size].name = strdup(name);
targets[targets_size].target = strdup(target);
@@ -246,7 +250,7 @@ monitor_init()
);
targets_size++;
- }
+ }
incidents = malloc(sizeof(incident_t) * INIT_VEC_CAPACITY);
incidents_capacity = INIT_VEC_CAPACITY;
diff --git a/monitor.cfg b/monitor.cfg
index 03ca7b1..226a88e 100644
--- a/monitor.cfg
+++ b/monitor.cfg
@@ -20,6 +20,8 @@ target=web,https,https://arf20.com
from=status@arf20.com
# what to alert
-alert=api,https://arf20.com/%s
-alert=email,arf20@arf20.com
+# alert=api,<url>,<content-type>,<body template>
+alert=api,https://arf20.com/%s,application/json,{"content":"%s is %s"}
+# alert=email,<address>,<subject template>,<body template>
+alert=email,arf20@arf20.com,%s is %s,%s is %s
diff --git a/monitor.cfg.example b/monitor.cfg.example
new file mode 100644
index 0000000..226a88e
--- /dev/null
+++ b/monitor.cfg.example
@@ -0,0 +1,27 @@
+# Monitor config
+# target=type,name,target
+
+# listen port
+port=8888
+
+# monitor interval in seconds (sleep)
+interval=5
+
+# monitor events log path
+log=events.log
+
+# targets to monitor
+target=reach,ipv4,2.59.235.35
+target=dns,dns,arf20.com
+target=web,http,http://arf20.com
+target=web,https,https://arf20.com
+
+# email From
+from=status@arf20.com
+
+# what to alert
+# alert=api,<url>,<content-type>,<body template>
+alert=api,https://arf20.com/%s,application/json,{"content":"%s is %s"}
+# alert=email,<address>,<subject template>,<body template>
+alert=email,arf20@arf20.com,%s is %s,%s is %s
+