commit a9044730ec921acc1e7086674c37cf97b5338863
Author: Remy Noulin <loader2x@gmail.com>
Date: Fri, 7 Jul 2023 13:25:31 +0200
heartbeat initial version
.gitignore | 63 ++++
README.md | 11 +
heartbeat.c | 730 ++++++++++++++++++++++++++++++++++++++++++
package.yml | 18 ++
shpPackages/short/main.c | 29 ++
shpPackages/short/package.yml | 15 +
shpPackages/short/short.h | 15 +
7 files changed, 881 insertions(+)
Diffstat:
7 files changed, 881 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,63 @@
+# Vim
+*.sw*
+
+# Debug
+.gdb_history
+
+# Coverage
+*.gcov
+*.gcda
+*.gcno
+
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
diff --git a/README.md b/README.md
@@ -0,0 +1,11 @@
+# Heartbeat
+
+`heartbeat` is a simple monitoring system which uses udp to send logs and sends event by mail.
+
+# Install
+
+# Configuration
+
+# Setup
+
+agents
diff --git a/heartbeat.c b/heartbeat.c
@@ -0,0 +1,730 @@
+#! /usr/bin/env sheepy
+/* or direct path to sheepy: #! /usr/local/bin/sheepy */
+
+/* Libsheepy documentation: https://spartatek.se/libsheepy/ */
+#include "libsheepyObject.h"
+#include "shpPackages/short/short.h"
+
+/*
+Commands:
+no command: load config.yml
+ probe and send messages to bridge, forward messages to bridge
+ if logger, store messages, forward messages to monitor
+config: generate configuration yml files for each machine
+ copy configuration to all machines
+ copy heartbeat executable
+daemon: run as daemon
+monitor: display state
+*/
+
+
+// cfgFile is the main configuration file written by the user
+// it used in the config command to generate agentCfgFile for each machine
+#define cfgFile "heartbeatConfig.yml"
+
+#define home ".heartbeat"
+
+// agentCfgFile is the default configuration file
+#define agentCfgPath ".heartbeat/config.yml"
+#define agentCfgFile "~/" agentCfgPath
+#define binPath "bin/"
+#define bin "~/" binPath
+
+#define defaultPeriod 1
+// TODO #define defaultPeriod 120
+
+// 10mn timeout
+#define agentTimeOut 5
+//TODO #define agentTimeOut 600
+
+int argc; char **argv;
+
+/* enable/disable logging */
+/* #undef pLog */
+/* #define pLog(...) */
+
+void printHelp(void);
+void config(smallJsont *cfg);
+void probe(char *cfgfile);
+
+/* process arguments on command line
+ */
+int main(int ARGC, char** ARGV) {
+
+ argc = ARGC; argv = ARGV;
+
+ initLibsheepy(ARGV[0]);
+ //setLogMode(LOG_VERBOSE);
+ setLogMode(LOG_DATE);
+ //openProgLogFile();
+ setLogSymbols(LOG_UTF8);
+ disableLibsheepyErrorLogs;
+
+ if (argc == 1) {
+ // no arguments
+ cleanCharP(p) = expandHome(agentCfgFile);
+ if (!isPath(p)) {
+ logE("Missing configuration: %s", p);
+ ret 1;
+ }
+ probe(p);
+ ret 0;
+ }
+ elif (argc != 2) {
+ logE("Too many arguments");
+ printHelp();
+ ret 1;
+ }
+ elif (eqG(argv[1], "daemon")) {
+ TODO("daemon");
+ ret 1;
+ }
+ elif (eqG(argv[1], "monitor")) {
+ TODO("monitor");
+ ret 1;
+ }
+ elif (eqG(argv[1], "config")) {
+ cleanAllocateSmallJson(cfg);
+ smallJsont *r = readFileG(cfg, cfgFile);
+
+ if (!r) {
+ logE("Missing "cfgFile);
+ ret 1;
+ }
+ config(cfg);
+ }
+ elif ( eqG(argv[1], "help")
+ or eqG(argv[1], "-h")
+ or eqG(argv[1], "--help")
+ or eqG(argv[1], "-?")
+ or eqG(argv[1], "?")) {
+ printHelp();
+ }
+ else {
+ logE("Invalid command: %s", argv[1]);
+ printHelp();
+ ret 1;
+ }
+
+ ret 0;
+}
+
+void printHelp(void) {
+ puts(BLD GRN"Help"RST);
+ TODO("write help");
+}
+
+/* generate setup for all agents
+ */
+void config(smallJsont *cfg) {
+
+ //lv(cfg);
+
+ // generate agent configs: agentNameConfig.yml
+ // add shell commands to copyScript
+
+ // generate agent configs: agentNameConfig.yml
+ u16 port = u$(cfg, "port");
+ if (!port) {
+ logE("Missing port.");
+ XFailure;
+ }
+ delElemG(cfg, "port");
+
+ // add shell commands to copyScript
+ cleanAllocateSmallArray(copyScript);
+
+ // id is stored in the agent config and sent in the messages
+ // it is the index of the machine in the list
+ u32 id = 0;
+ iter(cfg, D) {
+ cast(smallDictt*, d, D);
+ //logD("key %s", iK(cfg));
+ cleanCharP(thisCfgFile) = catS(iK(cfg), "Config.yml");
+ //lv(thisCfgFile);
+
+ cleanAllocateSmallJson(thisCfg);
+ cleanFinishSmallJsonP(agentCfg) = allocG(rtSmallJsont);
+ setG(thisCfg, "port", (u32)port);
+
+ // populate agent config
+ setG(agentCfg, "id", id);
+
+ if (hasG(d, "logger")) {
+ setG(agentCfg, "logger", TRUE);
+ // TODO("add monitor ip and port in logger agent");
+ }
+
+ // set bridge address
+ if (hasG(d, "bridge")) {
+ cleanCharP(key) = formatS("\"%s\".\"address\"", $(d, "bridge"));
+ char *address = $(cfg, key);
+ //lv(key);
+ //lv(address);
+ setG(agentCfg, "bridge", address);
+ }
+
+ if (hasG(d, "probes")) {
+ setNFreeG(agentCfg, "probes", getNDupG(d, rtSmallArrayt, "probes"));
+ }
+
+ setG(thisCfg, iK(cfg), agentCfg);
+ //lv(thisCfg);
+ writeFileG(thisCfg, thisCfgFile);
+
+ pushNFreeG(copyScript,
+ formatS("# %s", iK(cfg)));
+ if (!$(d, "transfers")) {
+ // ssh/scp
+ pushNFreeG(copyScript,
+ formatS("ssh %s mkdir "home, iK(cfg)));
+ if (hasG(d, "logger")) {
+ pushNFreeG(copyScript,
+ formatS("scp "cfgFile" %s:" home "/" cfgFile, iK(cfg)));
+ }
+ pushNFreeG(copyScript,
+ formatS("scp %s %s:" agentCfgPath, thisCfgFile, iK(cfg)));
+ pushNFreeG(copyScript,
+ formatS("scp heartbeat %s:" binPath, iK(cfg)));
+ }
+ elif (eqG($(d, "transfers"), "copy")) {
+ pushG(copyScript,
+ "mkdir ~/"home);
+ if (hasG(d, "logger")) {
+ pushG(copyScript,
+ "cp "cfgFile" ~/" home "/" cfgFile);
+ }
+ pushNFreeG(copyScript,
+ formatS("cp %s " agentCfgFile, thisCfgFile));
+ pushNFreeG(copyScript,
+ formatS("cp heartbeat " bin));
+ }
+
+ inc id;
+ }
+
+ logG(copyScript);
+}
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+void sendMail(char *cmd);
+
+void setTimeout(int bridgesock, u32 period, u32 usec) {
+ struct timeval timeout = {.tv_sec = period, .tv_usec = usec};
+ if (setsockopt(bridgesock, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)) < 0) {
+ logE("Could not set receive socket time out: %s", strerror(errno));
+ close(bridgesock);
+ XFailure;
+ }
+ if (setsockopt(bridgesock, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof(timeout)) < 0) {
+ logE("Could not set send socket time out: %s", strerror(errno));
+ close(bridgesock);
+ XFailure;
+ }
+}
+
+/* agent, bridge and logger
+ */
+void probe(char *cfgfile) {
+
+ // load agent config
+ // get port and agent name
+ // setup agent
+ // setup logger
+ // create socket for brigde (only if not logger)
+ // start server or bridge for the agents
+ // message structures
+ // inifinite loop
+ // check probes
+ // send message
+ // this is the logger
+ // write messages directly
+ // update logger state and send mail if changed
+ // set remote agents down if there hasn't been a message in a while
+ // send mail
+ // bridge messages from others
+ // when agent is logger, store message
+ // update state for this agent
+ // send mail when agent rebooted
+ // send mail when service is down
+ // send mail
+ // when agent is not logger, forward message
+
+ // load agent config
+ cleanAllocateSmallJson(cfg);
+ readFileG(cfg, cfgfile);
+ //lv(cfg);
+
+ // get port and agent name
+ u16 port = u$(cfg, "port");
+ if (!port) {
+ logE("Missing port.");
+ XFailure;
+ }
+ delElemG(cfg, "port");
+ cleanListP(names) = keysG(cfg);
+ //lv(names);
+ if (lenG(names) != 1) {
+ logE("There should only and at least one agent configured: %d", lenG(names));
+ XFailure;
+ }
+
+ char *name = names[0];
+
+ // setup agent
+ cleanFinishSmallDictP(agent) = getG(cfg, rtSmallDictt, name);
+ //lv(agent);
+ if (!agent) {
+ logE("Agent object has wrong type, should be smallDict.");
+ XFailure;
+ }
+
+ u32 id = u$(agent, "id");
+ bool logger = hasG(agent, "logger");
+ // when dst is null, it is the logger
+ char *dst = $(agent, "bridge");
+ if (!dst and not logger) {
+ logE("Missing bridge address.");
+ XFailure;
+ }
+ cleanFinishSmallArrayP(probes) = getG(agent, rtSmallArrayt, "probes");
+ if (probes and !lenG(probes)) {
+ finishG(probes);
+ probes = null;
+ }
+ u32 period = hasG(agent, "period") ? u$(agent, "period") : defaultPeriod;
+ /* lv(id); */
+ /* lv(logger); */
+ /* lv(dst); */
+ //lv(probes);
+ //lv(period);
+
+ // setup logger
+ cleanSmallJsonP(completeCfg) = null;
+ // agent names
+ cleanListP(agents) = null;
+ // agent db filenames
+ cleanListP(agentdbs) = null;
+ // when logger was started
+ u64 startTime = 0;
+ cleanCharP(mailSubject) = null;
+
+ if (logger) {
+ startTime = getCurrentUnixTime();
+
+ completeCfg = allocG(rtSmallJsont);
+ cleanCharP(p) = expandHome("~/" home "/" cfgFile);
+ smallJsont *r = readFileG(completeCfg, p);
+ if (!r) {
+ logE("Missing %s", p);
+ XFailure;
+ }
+ delElemG(completeCfg, "port");
+
+ // create filename with path in ~/.heartbeat/
+ agents = keysG(completeCfg);
+ agentdbs = keysG(completeCfg);
+ cleanCharP(h) = expandHome("~/" home "/");
+ forEachCharP(agentdbs, ag) {
+ pErrorNULL(iPrependS(ag, h));
+ pErrorNULL(iAppendS(ag, ".bin"));
+ }
+ //lv(agentdbs);
+
+ // mail setup
+ pError0(chDir(h));
+ mailSubject = catS("Heatbeat on ", name);
+ }
+ // open log files
+ int agentf[lenG(agents)];
+ if (logger) {
+ arange(i, agentf) {
+ agentf[i] = open(agentdbs[i], O_APPEND|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (agentf[i] < 0) {
+ logE("Failed to open %s: %s", agentdbs[i], strerror(errno));
+ XFailure;
+ }
+ }
+ // setup objects for monitor
+ setG(completeCfg, "startTime", startTime);
+ iter(completeCfg, D) {
+ if (!isOSmallDict(D)) continue;
+ cast(smallDictt*,d,D);
+ setG(d, "state", "down");
+ setG(d, "last", startTime);
+ setG(d, "mId", 0);
+ setG(d, "rebooted", FALSE);
+ setG(d, "lastBoot", startTime);
+ cleanFinishSmallArrayP(probes) = getG(d, rtSmallArrayt, "probes");
+ iter(probes, P) {
+ cast(smallDictt*,p,P);
+ setG(p, "state", FALSE);
+ setG(p, "last", startTime);
+ setPG(probes, iI(probes), p);
+ }
+ setPG(completeCfg, iK(completeCfg), d);
+ }
+ }
+
+
+ // create socket for brigde (only if not logger)
+ int sock = -1;
+ struct sockaddr_in server;
+ struct hostent *hp;
+
+ if (not logger) {
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ logE("Failed to create socket: %s", strerror(errno));
+ XFailure;
+ }
+
+ server.sin_family = AF_INET;
+
+ hp = gethostbyname(dst);
+ if (hp==0) {
+ logE("gethostbyname failed: %s", strerror(errno));
+ close(sock);
+ XFailure;
+ }
+
+ memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
+ server.sin_port = htons(port);
+ }
+
+ // start server or bridge for the agents
+ int bridgesock;
+ struct sockaddr_in bridge;
+ struct sockaddr client;
+ char buf[1024];
+
+ bridgesock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (bridgesock < 0){
+ logE("Failed to create bridge socket: %s", strerror(errno));
+ XFailure;
+ }
+
+ bridge.sin_family = AF_INET;
+ bridge.sin_addr.s_addr = INADDR_ANY;
+ bridge.sin_port = htons(port);
+
+ if (bind(bridgesock, (struct sockaddr *) &bridge, sizeof(bridge))){
+ logE("bind failed: %s", strerror(errno));
+ XFailure;
+ }
+
+ // message structures
+ typ struct PACKED {
+ u32 id;
+ u64 messageId;
+ u64 status;
+ u32 info;
+ } msgt;
+ typ struct PACKED {
+ u64 time;
+ msgt m;
+ u32 levels[32];
+ } dbt;
+ dbt record = init0Var;
+
+ msgt msg = {.id = id};
+ //lv(sizeof(msg));
+
+ msg.id = id;
+
+ // inifinite loop
+ rangeInf(messageId) {
+ //lv(messageId);
+
+ u64 time = getMonotonicTime();
+ u64 nextTime = time + (u64)period * 1000000000UL;
+
+ msg.messageId = messageId;
+ msg.status = 0;
+ msg.info = 0;
+
+ // check probes
+ if (probes) {
+ iter(probes, P) {
+ cast(smallDictt*,p,P);
+ if (not isOSmallDict(p)) {
+ logE("Invalid probe at index %d", iI(probes));
+ XFailure;
+ }
+ if (hasG(p, "port")) {
+ // try to bind
+ int sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0){
+ msg.status |= 1UL << iI(probes);
+ }
+ else {
+ struct sockaddr_in server;
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+ server.sin_port = htons((u16)u$(p, "port"));
+ if (bind(sock, (struct sockaddr *) &server, sizeof(server)) == 0) {
+ // bind successful
+ msg.status |= 1UL << iI(probes);
+ }
+ close(sock);
+ }
+ }
+ elif (hasG(p, "if")) {
+ // TODO("check if network interface is down");
+ }
+ }
+ } // if probes
+
+ //logD("status %x", msg.status);
+
+ if (not logger) {
+ // send message
+ if (sendto(sock, &msg, sizeof(msg), 0, (const struct sockaddr *)&server, sizeof(server)) < 0) {
+ logE("send failed: %s", strerror(errno));
+ close(sock);
+ XFailure;
+ }
+ }
+ else {
+ // this is the logger
+ // write messages directly
+ record.m = msg;
+ record.time = getCurrentUnixTime();
+ write(agentf[id], &record, sizeof(record));
+ cleanFinishSmallDictP(logAgent) = getG(completeCfg, rtSmallDictt, name);
+ setG(logAgent, "time", getCurrentUnixTime());
+ char *newstate = messageId ? "alive" : "init";
+ if (!eqG($(logAgent, "state"), newstate)) {
+ /* logD("%s is %s, previous state was %s", */
+ /* name#<{(|agent name|)}>#, */
+ /* newstate, */
+ /* $(logAgent,"state")); */
+ setG(logAgent, "state", newstate);
+ setG(logAgent, "last", getCurrentUnixTime());
+ }
+
+ cleanAllocateSmallArray(mailMsg);
+
+ // update logger state and send mail if changed
+ if (probes) {
+ iter(probes, P) {
+ cast(smallDictt*,p,P);
+ bool newstate = msg.status & (1UL << iI(probes));
+ if (getG(p, rtBool, "state") != newstate) {
+ setG(p, "state", newstate);
+ setG(p, "last", getCurrentUnixTime());
+ // send mail when service is down
+ char *state = newstate ? "down" : "up";
+ char *result = newstate ? "failed" : "ok";
+ if (hasG(p, "port")) {
+ cleanCharP(s) = formatS("Service on port %d running in %s is %s", u$(p, "port"), name, state);
+ pushG(mailMsg, s);
+ logW("%s", s);
+ }
+ elif (hasG(p, "if")) {
+ cleanCharP(s) = formatS("Network interface %s in %s is down", u$(p, "if"), name);
+ pushG(mailMsg, s);
+ logW("%s", s);
+ }
+ elif (hasG(p, "cmd")) {
+ cleanCharP(s) = formatS("'%s' running in %s is %s", u$(p, "cmd"), name, result);
+ pushG(mailMsg, s);
+ logW("%s", s);
+ }
+ }
+ }
+ } // if probes
+
+ // set remote agents down if there hasn't been a message in a while
+ iter(completeCfg, D) {
+ if (!isOSmallDict(D)) continue;
+ cast(smallDictt*,d,D);
+ if (eqG($(d, "state"), "down")) continue;
+ if (getCurrentUnixTime() - u$(d, "time") < agentTimeOut) continue;
+ // send mail when agent is down
+ cleanCharP(s) = formatS("%s is down, previous state was %s", iK(completeCfg)/*agent name*/, $(d,"state"));
+ pushG(mailMsg, s);
+ logW("%s", s);
+ setG(d, "state", "down");
+ setG(d, "last", getCurrentUnixTime());
+ }
+
+ // send mail
+ if (lenG(mailMsg)) {
+ pError0(writeFileG(mailMsg, "mail.txt"));
+ cleanFinishSmallArrayP(mails) = getG(logAgent, rtSmallArrayt, "mails");
+ char *email = $(mails, 0);
+ cleanCharP(mutt) = formatS("mutt -s \"%s\" %s < mail.txt", mailSubject, email);
+ sendMail(mutt);
+ }
+
+ // TODO send to monitor
+ //lv(completeCfg);
+ }
+
+ // bridge messages from others
+ // set timeout to period time, to sleep enough time
+ setTimeout(bridgesock, period, 0);
+ listenToOthers:;
+ socklen_t addr_size = sizeof(client);
+ ssize_t r = recvfrom(bridgesock, buf, sizeof(buf), 0, (struct sockaddr *) &client, &addr_size);
+ if (r == -1) {
+ // timeout
+ goto cont;
+ }
+ else {
+ // got a message
+ //lv(r);
+ cast(msgt *, m, buf);
+ /* logD("got message from id %d", m->id); */
+ /* logD("with message id %d", m->messageId); */
+ /* logD("and status %x", m->status); */
+ if (logger) {
+ // when agent is logger, store message
+ //logD("store message");
+ if (m->id >= ARRAY_SIZE(agentf)) {
+ logE("Invalid id");
+ }
+ elif (m->id == id) {
+ logE("Invalid id, the logger doesn't send packets");
+ }
+ else {
+ record.m = *m;
+ record.time = getCurrentUnixTime();
+ write(agentf[m->id], &record, sizeof(record));
+
+ cleanAllocateSmallArray(mailMsg);
+
+ // update state for this agent
+ cleanFinishSmallDictP(agent) = getG(completeCfg, rtSmallDictt, agents[m->id]);
+ setG(agent, "time", getCurrentUnixTime());
+ char *newstate = m->messageId ? "alive" : "init";
+ if (!eqG($(agent, "state"), newstate)) {
+ if (eqG($(agent, "state"), "alive")) {
+ setG(agent, "rebooted", TRUE);
+ setG(agent, "lastBoot", getCurrentUnixTime());
+ // send mail when agent rebooted
+ cleanCharP(s) = formatS("%s rebooted", agents[m->id]);
+ pushG(mailMsg, s);
+ logW("%s", s);
+ }
+ setG(agent, "state", newstate);
+ setG(agent, "last", getCurrentUnixTime());
+ }
+ if (m->messageId < u$(agent,"mId")) {
+ setG(agent, "rebooted", TRUE);
+ setG(agent, "lastBoot", getCurrentUnixTime());
+ // send mail when agent rebooted
+ cleanCharP(s) = formatS("%s rebooted", agents[m->id]);
+ pushG(mailMsg, s);
+ logW("%s", s);
+ }
+ setG(agent, "mId", m->messageId);
+ cleanFinishSmallArrayP(probes) = getG(agent, rtSmallArrayt, "probes");
+ if (probes) {
+ iter(probes, P) {
+ cast(smallDictt*,p,P);
+ bool newstate = m->status & (1UL << iI(probes));
+ if (getG(p, rtBool, "state") != newstate) {
+ setG(p, "state", newstate);
+ setG(p, "last", getCurrentUnixTime());
+ // send mail when service is down
+ char *state = newstate ? "down" : "up";
+ char *result = newstate ? "failed" : "ok";
+ if (hasG(p, "port")) {
+ cleanCharP(s) = formatS("Service on port %d running in %s is %s", u$(p, "port"), agents[m->id], state);
+ pushG(mailMsg, s);
+ logW("%s", s);
+ }
+ elif (hasG(p, "if")) {
+ cleanCharP(s) = formatS("Network interface %s in %s is down", u$(p, "if"), agents[m->id]);
+ pushG(mailMsg, s);
+ logW("%s", s);
+ }
+ elif (hasG(p, "cmd")) {
+ cleanCharP(s) = formatS("'%s' running in %s is %s", u$(p, "cmd"), agents[m->id], result);
+ pushG(mailMsg, s);
+ logW("%s", s);
+ }
+ }
+ }
+ } // if probes
+
+ // send mail
+ if (lenG(mailMsg)) {
+ pError0(writeFileG(mailMsg, "mail.txt"));
+ cleanFinishSmallArrayP(mails) = getG(agent, rtSmallArrayt, "mails");
+ char *email = $(mails, 0);
+ cleanCharP(mutt) = formatS("mutt -s \"%s\" %s < mail.txt", mailSubject, email);
+ sendMail(mutt);
+ }
+
+ // TODO send to monitor
+ //lv(completeCfg);
+ }
+ }
+ else {
+ // when agent is not logger, forward message
+ //logD("forward message");
+ if (sendto(sock, &buf, r, 0, (const struct sockaddr *)&server, sizeof(server)) < 0) {
+ logE("send failed: %s", strerror(errno));
+ close(sock);
+ XFailure;
+ }
+ }
+ time = getMonotonicTime();
+ if (time < nextTime) {
+ // adjust timeout to have period delay between the sent messages from this agent
+ u64 delta = nextTime - time;
+ u64 sec = delta / 1000000000UL;
+ u64 usec = (delta - sec * 1000000000UL) / 1000;
+ /* lv(sec); */
+ /* lv(usec); */
+ setTimeout(bridgesock, sec, usec);
+ goto listenToOthers;
+ }
+ }
+
+ cont:;
+ }
+
+ // close files
+ if (logger) {
+ arange(i, agentf) {
+ close(agentf[i]);
+ }
+ }
+ close(sock);
+}
+
+/* send mail in another process
+ */
+void sendMail(char *cmd) {
+ pid_t pid;
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ logE("spawnProc error: %s", strerror(errno));
+ XFAILURE
+ case 0:;
+ loop(5) {
+ var r = logSystem(cmd);
+ if (!r) break;
+ sleep(5);
+ }
+ XSUCCESS;
+ default:
+ break;
+ }
+
+ return;
+}
+// vim: set expandtab ts=2 sw=2:
diff --git a/package.yml b/package.yml
@@ -0,0 +1,18 @@
+---
+ name: heartbeat
+ version: 0.0.1
+ description: explanation
+ bin: ./heartbeat.c
+ repository:
+ type: git
+ url: "git+https://github.com/USER/heartbeat.git"
+ keywords:
+ - command
+ author: Anonymous
+ license: MIT
+ bugs:
+ url: "https://github.com/USER/heartbeat/issues"
+ homepage: "https://github.com/USER/heartbeat#readme"
+ private: false
+ dependencies:
+ short: ""
diff --git a/shpPackages/short/main.c b/shpPackages/short/main.c
@@ -0,0 +1,29 @@
+#! /usr/bin/env sheepy
+/* or direct path to sheepy: #! /usr/local/bin/sheepy */
+
+/* Libsheepy documentation: http://spartatek.se/libsheepy/ */
+#include "libsheepyObject.h"
+#include "short.h"
+
+int argc; char **argv;
+
+/* enable/disable logging */
+/* #undef pLog */
+/* #define pLog(...) */
+
+int main(int ARGC, char** ARGV) {
+
+ argc = ARGC; argv = ARGV;
+
+ initLibsheepy(ARGV[0]);
+
+ createSmallDict(d);
+
+ $(&d, "wer");
+
+ i_("de");
+
+ lv(&d);
+
+}
+// vim: set expandtab ts=2 sw=2:
diff --git a/shpPackages/short/package.yml b/shpPackages/short/package.yml
@@ -0,0 +1,15 @@
+---
+ name: short
+ version: 0.0.3
+ description: "convenient defines for often used libsheepy function calls making the lines shorter and more readable"
+ bin: ./short.h
+ repository:
+ type: git
+ url: git+https://github.com/USER/short.git
+ keywords:
+ - utility
+ author: Remy
+ license: MIT
+ bugs:
+ url: https://github.com/USER/short/issues
+ homepage: https://github.com/USER/short#readme
diff --git a/shpPackages/short/short.h b/shpPackages/short/short.h
@@ -0,0 +1,15 @@
+#include "libsheepyObject.h"
+
+#define lv logTVarG
+#define i_ logI
+
+#define iI iterIndexG
+#define iK iterKeyG
+
+// get string from array or dict
+#define $(object, key) getG(object, rtChar, key)
+
+// get unsigned int
+#define u$(object, key) getG(object, rtU64, key)
+
+// vim: set expandtab ts=2 sw=2: