[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 16/23] WIP api alert


MIME-Version: 1.0
Content-Transfer-Encoding: 8bit

From: arf20 <aruizfernandez05@xxxxxxxxx>

---
 .gitignore          |   1 +
 Makefile            |   2 +-
 alert.c             | 166 ++++++++++++++++++++++++++++++++++++++++++++
 alert.h             |  10 +++
 config.c            |   2 +-
 events.log          |   2 +
 main.c              |   6 +-
 monitor.c           |  12 ++--
 monitor.cfg         |   6 +-
 monitor.cfg.example |  27 +++++++
 10 files changed, 225 insertions(+), 9 deletions(-)
 create mode 100644 monitor.cfg.example

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@xxxxxxxxx
 
 # what to alert
-alert=api,https://arf20.com/%s
-alert=email,arf20@xxxxxxxxx
+# 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@xxxxxxxxx,%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@xxxxxxxxx
+
+# 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@xxxxxxxxx,%s is %s,%s is %s
+
-- 
2.47.3


References:
[arfnet2-status PATCH 00/23] First releasearf20 <arf20@xxxxxxxxx>