From 25ff5ceca3e6ce5ffcc1131a0e0608f7be01e688 Mon Sep 17 00:00:00 2001 From: Jan-Piet Mens Date: Thu, 25 Feb 2016 17:29:39 +0100 Subject: [PATCH] Support most config options in defaults file --- README.md | 30 ++++++++++++- etc/ot-recorder.default | 65 ++++++++++++++++++++++++++++ misc.c | 66 +++++++++++++++++++++++++++++ recorder.c | 93 ++++++++++++++++++++++------------------- udata.h | 7 ++++ 5 files changed, 217 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index da04671..ba07340 100644 --- a/README.md +++ b/README.md @@ -369,6 +369,33 @@ As mentioned earlier, data is stored in files, and these files are relative to ` You should definitely **not** modify or touch these files: they remain under the control of the _recorder_. You can of course, remove old `.rec` files if they consume too much space. +## Configuration file + +The recorder attempts to read its startup configuration from a configuration file; the path to this is compiled into the Recorder (typically `/etc/defaults/ot-recorder`). The format of this file approximates that of a shell script with variables to be exported (the intention is so that it can be sourced by a shell script). Lines beginning with an octothorp (`#`) are ignored as are blank lines. Configuration settings proper are set as follows (note that some older versions of _libconfig_ require a trailing semicolon (`;`) on assignment: + +``` +OTR_STORAGEDIR="/var/spool/owntracks/recorder/store" +``` + +The following configuration settings may be applied (a `Y` in column `$` means an environment variable of the same name overrides a setting in the config file): + +| Variable | $ | Default | Usage +| --------------------- | :--- | :------------ | --------------- +| `OTR_STORAGEDIR` | Y | compiled in | Pathname to the storage directory +| `OTR_HOST` | Y | `localhost` | MQTT hostname/address to connect to +| `OTR_PORT` | Y | `1883` | MQTT port number to connect to +| `OTR_USER` | Y | | MQTT username +| `OTR_PASS` | Y | | MQTT password +| `OTR_QOS` | | `2` | MQTT QoS +| `OTR_HTTPHOST` | | `localhost` | Address for the HTTP module to bind to +| `OTR_HTTPPORT` | | `8083` | Port number of the HTTP module to bind to +| `OTR_LUASCRIPT` | | | Path to the Lua script +| `OTR_PRECISION` | | `7` | Reverse-geo precision +| `OTR_GEOKEY` | | | API key for reverse-geo lookups +| `OTR_TOPICS` | | | String containing a space-separated list of topics to subscribe to for MQTT (overrides command-line arguments) + +Note that options passed to `ot-recorder` override both configuration file settings and environment variables. + ## Reverse Geo @@ -625,7 +652,8 @@ After running `otr_hook()`, the _recorder_ attempts to invoke a Lua function for You define a hooklet function only if you're interested in expressly triggering on a particular JSON element. -#### Environment + +### Environment The following environment variables control _ocat_'s behaviour: diff --git a/etc/ot-recorder.default b/etc/ot-recorder.default index aff721b..34502d2 100644 --- a/etc/ot-recorder.default +++ b/etc/ot-recorder.default @@ -4,4 +4,69 @@ # and its associated utilities to override compiled-in defaults. # *** In libconfig versions < 1.4 a trailing semicolon is mandatory +# ----------------------------------------------------- +# Storage directory +# + # OTR_STORAGEDIR = "/var/spool/owntracks/recorder/store" + +# ----------------------------------------------------- +# Address or hostname of the MQTT broker +# + +# OTR_HOST="localhost" + +# ----------------------------------------------------- +# Port number of the MQTT broker +# + +# OTR_PORT=1883 + +# ----------------------------------------------------- +# Username for the MQTT connection +# + +# OTR_USER="" + +# ----------------------------------------------------- +# Password for the MQTT connection +# + +# OTR_PASS="" + +# ----------------------------------------------------- +# QoS for MQTT connection +# + +# OTR_QOS=2 + +# ----------------------------------------------------- +# Address for the HTTP module to bind to (default: localhost) +# + +# OTR_HTTPHOST="localhost" + +# ----------------------------------------------------- +# Port number for the HTTP module to bind to (default: 8083) +# + +# OTR_HTTPPORT=8083 + +# ----------------------------------------------------- +# API key for reverse-geo lookups +# + +# OTR_GEOKEY="" + +# ----------------------------------------------------- +# Reverse geo precision +# + +# OTR_PRECISION=7 + +# ----------------------------------------------------- +# List of topics for MQTT to subscribe to, blank separated in a string +# + +# OTR_TOPICS="owntracks/+/+" + diff --git a/misc.c b/misc.c index 626d775..de499a7 100644 --- a/misc.c +++ b/misc.c @@ -101,6 +101,7 @@ void get_defaults(char *filename, struct udata *ud) { config_t cfg, *cf; const char *value; + int ival; if (access(filename, R_OK) == -1) return; @@ -118,6 +119,71 @@ void get_defaults(char *filename, struct udata *ud) if (config_lookup_string(cf, "OTR_STORAGEDIR", &value) != CONFIG_FALSE) strcpy(STORAGEDIR, value); +#if WITH_MQTT + if (config_lookup_string(cf, "OTR_HOST", &value) != CONFIG_FALSE) { + if (ud->hostname) free(ud->hostname); + ud->hostname = strdup(value); + } + if (config_lookup_int(cf, "OTR_PORT", &ival) != CONFIG_FALSE) { + ud->port = ival; + } + if (config_lookup_string(cf, "OTR_USER", &value) != CONFIG_FALSE) { + if (ud->username) free(ud->username); + ud->username = strdup(value); + } + if (config_lookup_string(cf, "OTR_PASS", &value) != CONFIG_FALSE) { + if (ud->password) free(ud->password); + ud->password = strdup(value); + } + if (config_lookup_int(cf, "OTR_QOS", &ival) != CONFIG_FALSE) { + ud->qos = ival; + } + + /* Topics is a blank-separated string of words; split and add to JSON array */ + if (config_lookup_string(cf, "OTR_TOPICS", &value) != CONFIG_FALSE) { + char *parts[40]; + int np, n; + if (ud->topics) json_delete(ud->topics); + + if ((np = splitter((char *)value, " ", parts)) < 1) { + olog(LOG_ERR, "Illegal value in OTR_TOPICS"); + exit(2); + } + ud->topics = json_mkarray(); + + for (n = 0; n < np; n++) { + json_append_element(ud->topics, json_mkstring(parts[n])); + } + splitterfree(parts); + } +#endif /* WITH_MQTT */ + + if (config_lookup_string(cf, "OTR_GEOKEY", &value) != CONFIG_FALSE) { + if (ud->geokey) free(ud->geokey); + ud->geokey = strdup(value); + } + + if (config_lookup_int(cf, "OTR_PRECISION", &ival) != CONFIG_FALSE) { + geohash_setprec(ival); + printf("PREC=%d\n", geohash_prec()); + } + +#if WITH_HTTP + if (config_lookup_string(cf, "OTR_HTTPHOST", &value) != CONFIG_FALSE) { + if (ud->http_host) free(ud->http_host); + ud->http_host = strdup(value); + } + if (config_lookup_int(cf, "OTR_HTTPPORT", &ival) != CONFIG_FALSE) { + ud->http_port = ival; + } +#endif /* WITH_HTTP */ + +#if WITH_LUA + if (config_lookup_string(cf, "OTR_LUASCRIPT", &value) != CONFIG_FALSE) { + if (ud->luascript) free(ud->luascript); + ud->luascript = strdup(value); + } +#endif config_destroy(cf); } diff --git a/recorder.c b/recorder.c index a2f1b26..dc80078 100644 --- a/recorder.c +++ b/recorder.c @@ -1112,33 +1112,31 @@ int main(int argc, char **argv) { #if WITH_MQTT struct mosquitto *mosq = NULL; - char *username, *password, *cafile; - char *hostname = strdup("localhost"); - int port = 1883; + char *cafile; UT_string *clientid; int rc, i; struct utsname uts; #endif /* WITH_MQTT */ char err[1024], *p; char *logfacility = "local0"; -#ifdef WITH_LUA - char *luascript = NULL; -#endif #if WITH_MQTT int loop_timeout = 1000; #endif int ch, initialize = FALSE; static struct udata udata, *ud = &udata; #ifdef WITH_HTTP - int http_port = 8083; char *doc_root = DOCROOT; - char *http_host = strdup("localhost"); #endif char *progname = *argv; #if WITH_MQTT udata.qos = DEFAULT_QOS; udata.pubprefix = NULL; + udata.username = NULL; + udata.password = NULL; + udata.hostname = strdup("localhost"); + udata.port = 1883; + udata.topics = NULL; #endif udata.ignoreretained = TRUE; udata.skipdemo = TRUE; @@ -1149,8 +1147,11 @@ int main(int argc, char **argv) udata.t2t = NULL; /* Topic to TID */ #ifdef WITH_HTTP udata.mgserver = NULL; + udata.http_host = strdup("localhost"); + udata.http_port = 8083; #endif #ifdef WITH_LUA + udata.luascript = NULL; udata.luadata = NULL; udata.luadb = NULL; #endif /* WITH_LUA */ @@ -1162,13 +1163,14 @@ int main(int argc, char **argv) get_defaults(CONFIGFILE, &udata); + #if WITH_MQTT if ((p = getenv("OTR_HOST")) != NULL) { - hostname = strdup(p); + ud->hostname = strdup(p); } if ((p = getenv("OTR_PORT")) != NULL) { - port = atoi(p); + ud->port = atoi(p); } #endif @@ -1177,6 +1179,9 @@ int main(int argc, char **argv) } #if WITH_MQTT + ud->username = getenv("OTR_USER"); + ud->password = getenv("OTR_PASS"); + utstring_new(clientid); utstring_printf(clientid, "ot-recorder"); if (uname(&uts) == 0) { @@ -1228,6 +1233,7 @@ int main(int argc, char **argv) udata.debug = TRUE; break; case 12: + if (udata.geokey) free(udata.geokey); udata.geokey = strdup(optarg); break; case 11: @@ -1245,7 +1251,8 @@ int main(int argc, char **argv) break; #ifdef WITH_LUA case 7: - luascript = strdup(optarg); + if (ud->luascript) free(ud->luascript); + ud->luascript = strdup(optarg); break; #endif #ifdef WITH_MQTT @@ -1267,11 +1274,11 @@ int main(int argc, char **argv) ud->ignoreretained = FALSE; break; case 'H': - free(hostname); - hostname = strdup(optarg); + free(ud->hostname); + ud->hostname = strdup(optarg); break; case 'p': - port = atoi(optarg); + ud->port = atoi(optarg); break; #endif /* WITH_MQTT */ case 5: @@ -1282,14 +1289,14 @@ int main(int argc, char **argv) break; #ifdef WITH_HTTP case 'A': /* API */ - http_port = atoi(optarg); + ud->http_port = atoi(optarg); break; case 2: /* no short char */ doc_root = strdup(optarg); break; case 3: /* no short char */ - free(http_host); - http_host = strdup(optarg); + free(ud->http_host); + ud->http_host = strdup(optarg); break; #endif case 'D': @@ -1369,19 +1376,27 @@ int main(int argc, char **argv) argv += (optind); #ifdef WITH_MQTT - if (argc < 1) { + if (ud->topics == NULL && argc < 1) { /* no topics set via config file */ usage(progname); return (-1); } + + /* + * Push list of topics into the array so that we can (re)subscribe + * in on_connect() + */ + + if (ud->topics == NULL) { + ud->topics = json_mkarray(); + + for (i = 0; i < argc; i++) { + json_append_element(ud->topics, json_mkstring(argv[i])); + } + } #endif -#ifdef WITH_MQTT - username = getenv("OTR_USER"); - password = getenv("OTR_PASS"); -#endif /* WITH_MQTT */ - #ifdef WITH_HTTP - if (http_port) { + if (ud->http_port) { if (!is_directory(doc_root)) { olog(LOG_ERR, "%s is not a directory", doc_root); exit(1); @@ -1423,9 +1438,9 @@ int main(int argc, char **argv) * If option for lua-script has not been given, ignore all hooks. */ - if (luascript) { - if ((udata.luadata = hooks_init(ud, luascript)) == NULL) { - olog(LOG_ERR, "Stopping because Lua load failed"); + if (ud->luascript) { + if ((udata.luadata = hooks_init(ud, ud->luascript)) == NULL) { + olog(LOG_ERR, "Stopping because loading of Lua script %s failed", ud->luascript); exit(1); } } @@ -1451,14 +1466,6 @@ int main(int argc, char **argv) return 1; } - /* - * Pushing list of topics into the array so that we can (re)subscribe on_connect() - */ - - ud->topics = json_mkarray(); - for (i = 0; i < argc; i++) { - json_append_element(ud->topics, json_mkstring(argv[i])); - } mosquitto_reconnect_delay_set(mosq, 2, /* delay */ @@ -1469,8 +1476,8 @@ int main(int argc, char **argv) mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); - if (username && password) { - mosquitto_username_pw_set(mosq, username, password); + if (ud->username && ud->password) { + mosquitto_username_pw_set(mosq, ud->username, ud->password); } cafile = getenv("OTR_CAFILE"); @@ -1499,11 +1506,11 @@ int main(int argc, char **argv) } olog(LOG_INFO, "connecting to MQTT on %s:%d as clientID %s %s TLS", - hostname, port, + ud->hostname, ud->port, UB(clientid), (cafile) ? "with" : "without"); - rc = mosquitto_connect(mosq, hostname, port, 60); + rc = mosquitto_connect(mosq, ud->hostname, ud->port, 60); if (rc) { if (rc == MOSQ_ERR_ERRNO) { strerror_r(errno, err, 1024); @@ -1518,11 +1525,11 @@ int main(int argc, char **argv) #endif /* WITH_MQTT */ #ifdef WITH_HTTP - if (http_port) { + if (ud->http_port) { char address[BUFSIZ]; const char *addressinfo; - sprintf(address, "%s:%d", http_host, http_port); + sprintf(address, "%s:%d", ud->http_host, ud->http_port); mg_set_option(udata.mgserver, "listening_port", address); // mg_set_option(udata.mgserver, "listening_port", "8090,ssl://8091:cert.pem"); @@ -1583,6 +1590,7 @@ int main(int argc, char **argv) #ifdef WITH_HTTP mg_destroy_server(&udata.mgserver); + free(ud->http_host); #endif #if WITH_LUA @@ -1596,9 +1604,8 @@ int main(int argc, char **argv) mosquitto_destroy(mosq); mosquitto_lib_cleanup(); - free(hostname); + free(ud->hostname); #endif - free(http_host); return (0); } diff --git a/udata.h b/udata.h index 059a30c..4033187 100644 --- a/udata.h +++ b/udata.h @@ -15,6 +15,10 @@ struct udata { #if WITH_MQTT char *pubprefix; /* If not NULL (default), republish modified payload to /topic */ int qos; /* Subscribe QoS */ + char *hostname; /* MQTT broker */ + int port; /* MQTT port */ + char *username; /* MQTT user */ + char *password; /* MQTT password */ #endif int skipdemo; /* True if _demo users are to be skipped */ int revgeo; /* True (default) if we should do reverse Geo lookups */ @@ -24,8 +28,11 @@ struct udata { struct gcache *t2t; /* topic to tid */ #ifdef WITH_HTTP struct mg_server *mgserver; /* Mongoose */ + char *http_host; /* address of http bind */ + int http_port; /* port number for above */ #endif #ifdef WITH_LUA + char *luascript; /* Path to Lua script */ struct luadata *luadata; /* Lua stuff */ struct gcache *luadb; /* lmdb named database 'luadb' */ #endif