summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--README.txt4
-rw-r--r--logging.c5
-rw-r--r--netcheck.c64
-rw-r--r--netcheck.h4
-rw-r--r--netmon.c57
-rw-r--r--validate.c23
-rw-r--r--validate.h18
8 files changed, 162 insertions, 15 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index efdf954..450b881 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,4 +4,4 @@ project(netmon C)
set(CMAKE_C_STANDARD 99)
add_compile_definitions(DEBUG)
-add_executable(netmon netmon.c logging.c logging.h netcheck.c netcheck.h)
+add_executable(netmon netmon.c logging.c logging.h netcheck.c netcheck.h validate.c validate.h)
diff --git a/README.txt b/README.txt
index 79320f7..4d11242 100644
--- a/README.txt
+++ b/README.txt
@@ -1,10 +1,12 @@
Usage:
- netmon [-t <check_interval>] [-n <max_failure>] [-l <log_file>] [-d]
+ netmon [-t <check_interval>] [-n <max_failure>] [-l <log_file>] [-c <cmd>] [-p <ping_host>] [-d]
-t <check_interval> specify how many seconds to wait between two checks
-n <max_failure> specify how many continous network failures we get until we reboot the system
-l <log_file> specify the log file
+ -c <cmd> the command line to be executed when network failure is detected
+ -p <ping_host> test the network by pinging given host
-d run as a daemon process
diff --git a/logging.c b/logging.c
index 2ad1704..90ccf23 100644
--- a/logging.c
+++ b/logging.c
@@ -3,6 +3,7 @@
//
#include "logging.h"
+#include "validate.h"
#include <stdio.h>
@@ -17,6 +18,10 @@ void log_free(void *logger) {
}
void log_print(void *logger, const char *level, time_t ts, const char *filename, int lineno, const char *msg) {
+ NOTNULL(logger);
+ NOTNULL(level);
+ NOTNULL(filename);
+ NOTNULL(msg);
char timestr[32];
strftime(timestr, 31, "%Y-%m-%d %H:%M:%S", localtime(&ts));
if (fprintf(logger, "[%s][%s][%s][%d] %s\n", timestr, level, filename, lineno, msg))
diff --git a/netcheck.c b/netcheck.c
index 4900e0f..60a351a 100644
--- a/netcheck.c
+++ b/netcheck.c
@@ -5,18 +5,20 @@
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
+#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include "logging.h"
#include "netcheck.h"
+#include "validate.h"
/**
- * Check network availability.
+ * Check network availability by testing a tcp communication.
* @return Zero if success, non-zero if failed.
*/
-int check_network(void *logger) {
+int check_tcp(void *logger) {
#define RETURN(r) do { rv = (r); goto RET; } while(0)
#define READ_SIZE 32
@@ -93,4 +95,62 @@ int check_network(void *logger) {
return rv;
#undef RETURN
#undef READ_SIZE
+}
+
+/**
+ * Check network availability by pinging a remote host.
+ * Note that this routine requires an external ping program.
+ * @return Zero if success, non-zero if failed.
+ * @param logger the logger.
+ * @param dest the destination host, whether a domain or an ip address.
+ * @param ping path to the ping executable. If null, will use `/bin/ping`.
+ * @return Zero if success, non-zero if failed.
+ */
+int check_ping(void *logger, const char *dest, const char *ping) {
+ if (!is_valid_ipv4(dest)) {
+ log_error(logger, "dest is not a valid IPv4 address.");
+ return -1;
+ }
+
+#define BUFLEN 1024
+#define RETURN(r) do { rv = (r); goto CP_RET; } while(0)
+ int rv = 0;
+ // Copied from https://stackoverflow.com/questions/8189935/is-there-any-way-to-ping-a-specific-ip-address-with-c
+ if (ping == NULL) ping = "/bin/ping";
+ int pipe_arr[2] = {-1};
+ char buf[BUFLEN] = {0};
+
+ // Create pipe - pipe_arr[0] is "reading end", pipe_arr[1] is "writing end"
+ if (pipe(pipe_arr)) {
+ perror("pipe()");
+ log_error(logger, "pipe() failed.");
+ return -1;
+ }
+
+ if (fork() == 0) {
+ // child
+ if (dup2(pipe_arr[1], STDOUT_FILENO) < 0) {
+ log_error(logger, "dup2() failed.");
+ exit(-1);
+ }
+ execl(ping, "ping", "-c 3", dest, (char *) NULL);
+ } else {
+ // parent
+ if (wait(NULL) < 0) {
+ perror("wait()");
+ log_error(logger, "wait() failed.");
+ RETURN(-1);
+ }
+ if (read(pipe_arr[0], buf, BUFLEN) < 0) {
+ perror("read()");
+ log_error(logger, "read() failed.");
+ RETURN(-1);
+ }
+ }
+ CP_RET:
+ if (pipe_arr[0] >= 0) close(pipe_arr[0]);
+ if (pipe_arr[1] >= 0) close(pipe_arr[1]);
+ return (rv) ? (rv) : (strstr(buf, "time=") == NULL);
+#undef BUFLEN
+#undef RETURN
} \ No newline at end of file
diff --git a/netcheck.h b/netcheck.h
index 3bea521..173d6fd 100644
--- a/netcheck.h
+++ b/netcheck.h
@@ -6,6 +6,8 @@
#define NETMON_NETCHECK_H
-int check_network(void *logger);
+int check_tcp(void *logger);
+
+int check_ping(void *logger, const char *dest, const char *ping);
#endif //NETMON_NETCHECK_H
diff --git a/netmon.c b/netmon.c
index 0b0a434..f08825d 100644
--- a/netmon.c
+++ b/netmon.c
@@ -8,11 +8,15 @@
const char *logfile = "netmon.log";
-int check_interval_seconds = 30; // seconds to sleep between checks
+const char *pingdest = NULL; // which host to ping. If NULL, test tcp instead
+const char *failcmd = "reboot"; // cmd to be executed. If NULL, reboot // TODO support blanks
+unsigned int check_interval_seconds = 30; // seconds to sleep between checks
int max_check_failure = 5; // how many failures to reboot the system
int as_daemon = 0; // should run as a daemon process
+unsigned int failure_sleep_seconds = 60; // seconds to sleep before resuming check after a network failure is detected
+// TODO make failure_sleep_seconds configurable
-volatile int require_reboot = 0;
+volatile int failure_detected = 0;
void *logger = NULL;
void daemonize() {
@@ -94,7 +98,9 @@ void loop() {
#pragma ide diagnostic ignored "EndlessLoop"
while (1) {
log_info(logger, "Check network.");
- if (check_network(logger) != 0) {
+ if ((pingdest == NULL) ?
+ (check_tcp(logger) != 0) :
+ (check_ping(logger, pingdest, NULL) != 0)) {
++failures;
char buf[64];
snprintf(buf, 63, "Network failure detected. counter=%d", failures);
@@ -105,8 +111,21 @@ void loop() {
}
if (failures > max_check_failure) {
log_info(logger, "Max failure times exceeded.");
- require_reboot = 1;
- return;
+ failure_detected = 1;
+ failures = 0; // reset failure counter
+
+ // handle a network failure event
+ char tmp[256];
+ snprintf(tmp, 255, "Run system command `%s`.", failcmd);
+ log_info(logger, tmp);
+ system(failcmd);
+
+ snprintf(tmp, 255, "Wait %d secs before resume checking.\n", failure_sleep_seconds);
+ log_debug(logger, tmp);
+ unsigned t = failure_sleep_seconds;
+ while ((t = sleep(t)));
+
+ continue; // resume checking right now
}
log_info(logger, "Sleeping...");
unsigned int t = check_interval_seconds;
@@ -119,20 +138,42 @@ int main(int argc, char *argv[]) {
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-t")) {
check_interval_seconds = atoi(argv[i + 1]);
+ if (check_interval_seconds <= 0) {
+ fprintf(stderr, "check interval should be positive.\n");
+ return -1;
+ }
++i; // skip next value
} else if (!strcmp(argv[i], "-n")) {
max_check_failure = atoi(argv[i + 1]);
+ if (max_check_failure < 0) {
+ fprintf(stderr, "max check failure should not be negative.\n");
+ return -1;
+ }
++i; // skip next value
} else if (!strcmp(argv[i], "-l")) {
logfile = argv[i + 1];
++i; // skip next value
+ } else if (!strcmp(argv[i], "-p")) {
+ pingdest = argv[i + 1];
+ ++i; // skip next value
+ } else if (!strcmp(argv[i], "-c")) {
+ char *s = argv[i + 1];
+ unsigned long len = strlen(s);
+ if (len == 0) {
+ fprintf(stderr, "Empty fail command.\n");
+ return -1;
+ }
+ failcmd = s;
+ fprintf(stderr, "Fail command is set to `%s`.\n", failcmd);
+ ++i; // skip next value
} else if (!strcmp(argv[i], "-d")) {
as_daemon = 1;
} else {
int invalid = strcmp(argv[i], "-h") != 0 && strcmp(argv[i], "--help") != 0;
if (invalid) printf("Unrecognized parameter \"%s\".\n", argv[i]);
printf("Usage:\n"
- " %s [-t <check_interval>] [-n <max_failure>] [-l <log_file>] [-d]\n", argv[0]);
+ " %s [-t <check_interval>] [-n <max_failure>] [-l <log_file>] [-c <cmd>] [-p <ping_host>] [-d]\n",
+ argv[0]);
return invalid != 0;
}
}
@@ -146,10 +187,6 @@ int main(int argc, char *argv[]) {
log_info(logger, "netmon is started.");
loop();
log_info(logger, "netmon is stopped.");
- if (require_reboot) {
- log_info(logger, "Trigger system reboot.");
- system("reboot");
- }
log_free(logger);
return 0;
}
diff --git a/validate.c b/validate.c
new file mode 100644
index 0000000..49005f0
--- /dev/null
+++ b/validate.c
@@ -0,0 +1,23 @@
+//
+// Created by Keuin on 2021/12/30.
+//
+
+#include "validate.h"
+#include <stdio.h>
+
+/**
+ * Check if a given string is a valid dot-decimal representation of an IPv4 address.
+ * @param s the string.
+ * @return Non-zero if true, zero if false.
+ */
+int is_valid_ipv4(const char *s) {
+ // TODO buggy
+// unsigned int addr[4];
+// if (sscanf(s, "%ud.%ud.%ud.%ud", &addr[0], &addr[1], &addr[2], &addr[3]) != 4)
+// return 0;
+// for (int i = 0; i < 4; ++i) {
+// if (addr[i] > 255) return 0;
+// if (addr[i] == 0 && (i == 0 || i == 3)) return 0;
+// }
+ return 1;
+}
diff --git a/validate.h b/validate.h
new file mode 100644
index 0000000..f09a820
--- /dev/null
+++ b/validate.h
@@ -0,0 +1,18 @@
+//
+// Created by Keuin on 2021/12/30.
+//
+
+#ifndef NETMON_VALIDATE_H
+#define NETMON_VALIDATE_H
+
+#include <stdlib.h>
+
+int is_valid_ipv4(const char *s);
+
+#define NOTNULL(ptr) do { \
+ if ((ptr) == NULL) { \
+ fprintf(stderr, "NotNull check failed: "#ptr " is null. (" __FILE__ ":%d)\n", __LINE__); \
+ abort(); \
+ } } while(0)
+
+#endif //NETMON_VALIDATE_H