forked from extern/alertik
Initial env events impl + some rework
This commit is contained in:
parent
60e22c37d6
commit
0966091f94
5
Makefile
5
Makefile
@ -8,6 +8,7 @@ CFLAGS += -Wall -Wextra
|
||||
LDLIBS += -pthread -lcurl
|
||||
STRIP = strip
|
||||
VERSION = v0.1
|
||||
OBJS = alertik.o events.o env_events.o notifiers.o
|
||||
|
||||
ifeq ($(LOG_FILE),yes)
|
||||
CFLAGS += -DUSE_FILE_AS_LOG
|
||||
@ -32,7 +33,7 @@ CFLAGS += -DGIT_HASH=\"$(GIT_HASH)\"
|
||||
all: alertik Makefile
|
||||
$(STRIP) --strip-all alertik
|
||||
|
||||
alertik: alertik.o events.o
|
||||
alertik: $(OBJS)
|
||||
|
||||
clean:
|
||||
rm -f alertik.o events.o alertik
|
||||
rm -f $(OBJS) alertik
|
||||
|
99
alertik.c
99
alertik.c
@ -19,29 +19,18 @@
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "alertik.h"
|
||||
#include "events.h"
|
||||
#include "env_events.h"
|
||||
#include "notifiers.h"
|
||||
|
||||
/* Uncomment/comment to enable/disable the following settings. */
|
||||
// #define USE_FILE_AS_LOG /* stdout if commented. */
|
||||
// #define CURL_VERBOSE
|
||||
// #define VALIDATE_CERTS
|
||||
// #define DISABLE_NOTIFICATIONS
|
||||
|
||||
#define FIFO_MAX 64
|
||||
#define SYSLOG_PORT 5140
|
||||
#define LOG_FILE "log/log.txt"
|
||||
|
||||
/* Telegram & request settings. */
|
||||
static char *TELEGRAM_BOT_TOKEN;
|
||||
static char *TELEGRAM_CHAT_ID;
|
||||
char *TELEGRAM_NICKNAME;
|
||||
|
||||
#define CURL_USER_AGENT "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " \
|
||||
"(KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
|
||||
|
||||
/* Circular message buffer. */
|
||||
static struct circ_buffer {
|
||||
int head;
|
||||
@ -56,7 +45,7 @@ static pthread_cond_t fifo_new_log_entry = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
/* Misc. */
|
||||
#define LAST_SENT_THRESHOLD_SECS 10 /* Minimum time (in secs) between two */
|
||||
static time_t time_last_sent_notify; /* notifications. */
|
||||
time_t time_last_sent_notify; /* notifications. */
|
||||
static int curr_file;
|
||||
|
||||
//////////////////////////////// LOGGING //////////////////////////////////////
|
||||
@ -228,75 +217,6 @@ static int pop_msg_from_fifo(struct log_event *ev)
|
||||
}
|
||||
|
||||
///////////////////////////// MESSAGE HANDLING ////////////////////////////////
|
||||
|
||||
/* Just to omit the print to stdout. */
|
||||
size_t libcurl_noop_cb(void *ptr, size_t size, size_t nmemb, void *data) {
|
||||
((void)ptr);
|
||||
((void)data);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
int send_telegram_notification(const char *msg)
|
||||
{
|
||||
char full_request_url[4096] = {0};
|
||||
char *escaped_msg = NULL;
|
||||
CURLcode ret_curl;
|
||||
CURL *hnd;
|
||||
int ret;
|
||||
|
||||
ret = -1;
|
||||
|
||||
hnd = curl_easy_init();
|
||||
if (!hnd) {
|
||||
log_msg("> Unable to initialize libcurl!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
log_msg("> Sending notification!\n");
|
||||
|
||||
escaped_msg = curl_easy_escape(hnd, msg, 0);
|
||||
if (!escaped_msg) {
|
||||
log_msg("> Unable to escape notification message...\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
snprintf(
|
||||
full_request_url,
|
||||
sizeof full_request_url - 1,
|
||||
"https://api.telegram.org/bot%s/sendMessage?chat_id=%s&text=%s",
|
||||
TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, escaped_msg);
|
||||
|
||||
curl_easy_setopt(hnd, CURLOPT_URL, full_request_url);
|
||||
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
|
||||
curl_easy_setopt(hnd, CURLOPT_USERAGENT, CURL_USER_AGENT);
|
||||
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 3L);
|
||||
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
|
||||
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, libcurl_noop_cb);
|
||||
#ifdef CURL_VERBOSE
|
||||
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
|
||||
#endif
|
||||
#ifndef VALIDATE_CERTS
|
||||
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_NOTIFICATIONS
|
||||
ret_curl = curl_easy_perform(hnd);
|
||||
if (ret_curl != CURLE_OK) {
|
||||
log_msg("> Unable to send request!\n");
|
||||
goto error;
|
||||
} else {
|
||||
time_last_sent_notify = time(NULL); /* Update the time of our last sent */
|
||||
log_msg("> Done!\n"); /* notification. */
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = 0;
|
||||
error:
|
||||
curl_free(escaped_msg);
|
||||
curl_easy_cleanup(hnd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *handle_messages(void *p)
|
||||
{
|
||||
((void)p);
|
||||
@ -332,21 +252,12 @@ int main(void)
|
||||
|
||||
atexit(close_log_file);
|
||||
|
||||
TELEGRAM_BOT_TOKEN = getenv("TELEGRAM_BOT_TOKEN");
|
||||
TELEGRAM_CHAT_ID = getenv("TELEGRAM_CHAT_ID");
|
||||
TELEGRAM_NICKNAME = getenv("TELEGRAM_NICKNAME");
|
||||
|
||||
#ifndef USE_FILE_AS_LOG
|
||||
curr_file = STDOUT_FILENO;
|
||||
#endif
|
||||
|
||||
if (!TELEGRAM_BOT_TOKEN || !TELEGRAM_CHAT_ID || !TELEGRAM_NICKNAME) {
|
||||
panic("Unable to find env vars, please check if you have all of the "
|
||||
"following set:\n"
|
||||
"- TELEGRAM_BOT_TOKEN\n"
|
||||
"- TELEGRAM_CHAT_ID\n"
|
||||
"- TELEGRAM_NICKNAME\n");
|
||||
}
|
||||
setup_notifiers();
|
||||
init_environment_events();
|
||||
|
||||
log_msg(
|
||||
"Alertik (" GIT_HASH ") (built at " __DATE__ " " __TIME__ ")\n");
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#define panic_errno(s) \
|
||||
do {\
|
||||
@ -23,7 +24,7 @@
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
|
||||
extern char *TELEGRAM_NICKNAME;
|
||||
extern time_t time_last_sent_notify;
|
||||
extern char *get_formatted_time(time_t time, char *time_str);
|
||||
extern void log_msg(const char *fmt, ...);
|
||||
extern int send_telegram_notification(const char *msg);
|
||||
|
131
env_events.c
Normal file
131
env_events.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Alertik: a tiny 'syslog' server & notification tool for Mikrotik routers.
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "env_events.h"
|
||||
#include "alertik.h"
|
||||
#include "notifiers.h"
|
||||
|
||||
/* Event match types. */
|
||||
#define MATCH_TYPES_LEN 2
|
||||
static const char *const match_types[] = {"substr", "regex"};
|
||||
|
||||
/* Environment events list. */
|
||||
struct env_event env_events[MAX_ENV_EVENTS] = {0};
|
||||
|
||||
/**
|
||||
* Safe string-to-int routine that takes into account:
|
||||
* - Overflow and Underflow
|
||||
* - No undefined behavior
|
||||
*
|
||||
* Taken from https://stackoverflow.com/a/12923949/3594716
|
||||
* and slightly adapted: no error classification, because
|
||||
* I don't need to know, error is error.
|
||||
*
|
||||
* @param out Pointer to integer.
|
||||
* @param s String to be converted.
|
||||
*
|
||||
* @return Returns 0 if success and a negative number otherwise.
|
||||
*/
|
||||
static int str2int(int *out, const char *s)
|
||||
{
|
||||
char *end;
|
||||
if (s[0] == '\0' || isspace(s[0]))
|
||||
return -1;
|
||||
errno = 0;
|
||||
|
||||
long l = strtol(s, &end, 10);
|
||||
|
||||
/* Both checks are needed because INT_MAX == LONG_MAX is possible. */
|
||||
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX))
|
||||
return -1;
|
||||
if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN))
|
||||
return -1;
|
||||
if (*end != '\0')
|
||||
return -1;
|
||||
|
||||
*out = l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**/
|
||||
static char *get_event_str(int ev_num, char *str)
|
||||
{
|
||||
char *env;
|
||||
char ev[64] = {0};
|
||||
snprintf(ev, sizeof ev - 1, "EVENT%d_%s", ev_num, str);
|
||||
if (!(env = getenv(ev)))
|
||||
panic("Unable to find event for %s\n", ev);
|
||||
return env;
|
||||
}
|
||||
|
||||
/**/
|
||||
static int
|
||||
get_event_idx(int ev_num, char *str, const char *const *str_list, int size)
|
||||
{
|
||||
char *env = get_event_str(ev_num, str);
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (!strcmp(env, str_list[i]))
|
||||
return i;
|
||||
}
|
||||
panic("String parameter (%s) invalid for %s\n", env, str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**/
|
||||
int init_environment_events(void)
|
||||
{
|
||||
char *tmp;
|
||||
int events;
|
||||
|
||||
tmp = getenv("ENV_EVENTS");
|
||||
if (!tmp || (str2int(&events, tmp) < 0) || events <= 0) {
|
||||
log_msg("Environment events not detected, disabling...\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (events >= MAX_ENV_EVENTS)
|
||||
panic("Environment events exceeds the maximum supported (%d/%d)\n",
|
||||
events, MAX_ENV_EVENTS);
|
||||
|
||||
log_msg("%d environment events found, registering...\n");
|
||||
for (int i = 0; i < events; i++) {
|
||||
/* EVENTn_MATCH_TYPE. */
|
||||
env_events[i].ev_match_type = get_event_idx(i, "MATCH_TYPE",
|
||||
match_types, MATCH_TYPES_LEN);
|
||||
/* EVENTn_NOTIFIER. */
|
||||
env_events[i].ev_notifier_idx = get_event_idx(i, "NOTIFIER",
|
||||
notifiers_str, NUM_NOTIFIERS);
|
||||
/* EVENTn_MATCH_STR. */
|
||||
env_events[i].ev_match_str = get_event_str(i, "MATCH_STR");
|
||||
/* EVENTn_MASK_MSG. */
|
||||
env_events[i].ev_mask_msg = get_event_str(i, "MASK_MSG");
|
||||
}
|
||||
|
||||
log_msg("Environment events summary:\n");
|
||||
for (int i = 0; i < events; i++) {
|
||||
printf(
|
||||
"EVENT%d_MATCH_TYPE: %s\n"
|
||||
"EVENT%d_MATCH_STR: %s\n"
|
||||
"EVENT%d_NOTIFIER: %s\n"
|
||||
"EVENT%d_MASK_MSG: %s\n\n",
|
||||
i, match_types[env_events[i].ev_match_type],
|
||||
i, env_events[i].ev_match_str,
|
||||
i, notifiers_str[env_events[i].ev_notifier_idx],
|
||||
i, env_events[i].ev_mask_msg
|
||||
);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
23
env_events.h
Normal file
23
env_events.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Alertik: a tiny 'syslog' server & notification tool for Mikrotik routers.
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*/
|
||||
|
||||
#ifndef ENV_EVENTS_H
|
||||
#define ENV_EVENTS_H
|
||||
|
||||
#include "events.h"
|
||||
|
||||
#define MAX_ENV_EVENTS 16
|
||||
|
||||
struct env_event {
|
||||
int ev_match_type; /* whether regex or str. */
|
||||
int ev_notifier_idx; /* Telegram, Discord... */
|
||||
const char *ev_match_str; /* regex str or substr here. */
|
||||
const char *ev_mask_msg; /* Mask message to be sent. */
|
||||
};
|
||||
|
||||
extern struct env_event env_events[MAX_ENV_EVENTS];
|
||||
extern int init_environment_events(void);
|
||||
|
||||
#endif /* ENV_EVENTS_H */
|
3
events.c
3
events.c
@ -72,9 +72,8 @@ void handle_wifi_login_attempts(struct log_event *ev)
|
||||
snprintf(
|
||||
notification_message,
|
||||
sizeof notification_message - 1,
|
||||
"%s! %s!, there is someone trying to connect "
|
||||
"There is someone trying to connect "
|
||||
"to your WiFi: %s, with the mac-address: %s, at:%s",
|
||||
TELEGRAM_NICKNAME, TELEGRAM_NICKNAME,
|
||||
wifi_iface,
|
||||
mac_addr,
|
||||
get_formatted_time(ev->timestamp, time_str)
|
||||
|
122
notifiers.c
Normal file
122
notifiers.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Alertik: a tiny 'syslog' server & notification tool for Mikrotik routers.
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "notifiers.h"
|
||||
#include "alertik.h"
|
||||
|
||||
/* Just to omit the print to stdout. */
|
||||
size_t libcurl_noop_cb(void *ptr, size_t size, size_t nmemb, void *data) {
|
||||
((void)ptr);
|
||||
((void)data);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
//////////////////////////////// TELEGRAM //////////////////////////////////////
|
||||
/* Telegram & request settings. */
|
||||
static char *telegram_bot_token;
|
||||
static char *telegram_chat_id;
|
||||
|
||||
void setup_telegram(void)
|
||||
{
|
||||
telegram_bot_token = getenv("TELEGRAM_BOT_TOKEN");
|
||||
telegram_chat_id = getenv("TELEGRAM_CHAT_ID");
|
||||
if (!telegram_bot_token || !telegram_chat_id) {
|
||||
panic(
|
||||
"Unable to find env vars, please check if you have all of the "
|
||||
"following set:\n"
|
||||
"- TELEGRAM_BOT_TOKEN\n"
|
||||
"- TELEGRAM_CHAT_ID\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
int send_telegram_notification(const char *msg)
|
||||
{
|
||||
char full_request_url[4096] = {0};
|
||||
char *escaped_msg = NULL;
|
||||
CURLcode ret_curl;
|
||||
CURL *hnd;
|
||||
int ret;
|
||||
|
||||
ret = -1;
|
||||
|
||||
hnd = curl_easy_init();
|
||||
if (!hnd) {
|
||||
log_msg("> Unable to initialize libcurl!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
log_msg("> Sending notification!\n");
|
||||
|
||||
escaped_msg = curl_easy_escape(hnd, msg, 0);
|
||||
if (!escaped_msg) {
|
||||
log_msg("> Unable to escape notification message...\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
snprintf(
|
||||
full_request_url,
|
||||
sizeof full_request_url - 1,
|
||||
"https://api.telegram.org/bot%s/sendMessage?chat_id=%s&text=%s",
|
||||
telegram_bot_token, telegram_chat_id, escaped_msg);
|
||||
|
||||
curl_easy_setopt(hnd, CURLOPT_URL, full_request_url);
|
||||
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
|
||||
curl_easy_setopt(hnd, CURLOPT_USERAGENT, CURL_USER_AGENT);
|
||||
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 3L);
|
||||
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
|
||||
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, libcurl_noop_cb);
|
||||
#ifdef CURL_VERBOSE
|
||||
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
|
||||
#endif
|
||||
#ifndef VALIDATE_CERTS
|
||||
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_NOTIFICATIONS
|
||||
ret_curl = curl_easy_perform(hnd);
|
||||
if (ret_curl != CURLE_OK) {
|
||||
log_msg("> Unable to send request!\n");
|
||||
goto error;
|
||||
} else {
|
||||
time_last_sent_notify = time(NULL); /* Update the time of our last sent */
|
||||
log_msg("> Done!\n"); /* notification. */
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = 0;
|
||||
error:
|
||||
curl_free(escaped_msg);
|
||||
curl_easy_cleanup(hnd);
|
||||
return ret;
|
||||
}
|
||||
////////////////////////////////// END ////////////////////////////////////////
|
||||
|
||||
const char *const notifiers_str[] = {
|
||||
"Telegram"
|
||||
};
|
||||
|
||||
struct notifier notifiers[] = {
|
||||
/* Telegram. */
|
||||
{
|
||||
.setup = setup_telegram,
|
||||
.send_notification = send_telegram_notification
|
||||
}
|
||||
};
|
||||
|
||||
/* Global setup. */
|
||||
void setup_notifiers(void)
|
||||
{
|
||||
for (int i = 0; i < NUM_NOTIFIERS; i++)
|
||||
notifiers[i].setup();
|
||||
}
|
36
notifiers.h
Normal file
36
notifiers.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Alertik: a tiny 'syslog' server & notification tool for Mikrotik routers.
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*/
|
||||
|
||||
#ifndef NOTIFIERS_H
|
||||
#define NOTIFIERS_H
|
||||
|
||||
/* Uncomment/comment to enable/disable the following settings. */
|
||||
// #define CURL_VERBOSE
|
||||
// #define VALIDATE_CERTS
|
||||
// #define DISABLE_NOTIFICATIONS
|
||||
|
||||
#define CURL_USER_AGENT "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " \
|
||||
"(KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
|
||||
|
||||
#define NUM_NOTIFIERS 1
|
||||
|
||||
/* Notifiers list, like:
|
||||
* - Telegram
|
||||
* - Slack
|
||||
* - Discord
|
||||
* - Teams
|
||||
*/
|
||||
extern const char *const notifiers_str[NUM_NOTIFIERS];
|
||||
|
||||
/* Notifier struct. */
|
||||
struct notifier {
|
||||
void(*setup)(void);
|
||||
int(*send_notification)(const char *msg);
|
||||
};
|
||||
|
||||
extern struct notifier notifiers[NUM_NOTIFIERS];
|
||||
extern void setup_notifiers(void);
|
||||
|
||||
#endif /* NOTIFIERS_H */
|
Loading…
Reference in New Issue
Block a user