refactor main message loop, add --quiet

closes #32
	closes #31
This commit is contained in:
Jan-Piet Mens 2015-09-21 11:34:00 +02:00
parent 82e38dfa02
commit 6a877b4834
4 changed files with 251 additions and 283 deletions

View File

@ -30,6 +30,7 @@
#include <mosquitto.h>
#include <getopt.h>
#include <time.h>
#include <math.h>
#include "json.h"
#include <sys/utsname.h>
#include "utstring.h"
@ -54,7 +55,6 @@
#define SSL_VERIFY_NONE (0)
#define TOPIC_PARTS (4) /* owntracks/user/device/info */
#define TOPIC_SUFFIX "info"
#define DEFAULT_QOS (2)
#define CLEAN_SESSION false
@ -77,67 +77,7 @@ double number(JsonNode *j, char *element)
}
}
return (-7.0L);
}
JsonNode *extract(struct udata *ud, char *payload, char *tid, char *t, double *lat, double *lon, long *tst)
{
JsonNode *json, *j;
*tid = *t = 0;
*lat = *lon = -1.0L;
if ((json = json_decode(payload)) == NULL)
return (NULL);
if (ud->skipdemo && (json_find_member(json, "_demo") != NULL)) {
json_delete(json);
return (NULL);
}
if ((j = json_find_member(json, "_type")) == NULL) {
json_delete(json);
return (NULL);
}
if ((j->tag != JSON_STRING) || (strcmp(j->string_, "location") != 0)) {
json_delete(json);
return (NULL);
}
if ((j = json_find_member(json, "tid")) != NULL) {
if (j->tag == JSON_STRING) {
// printf("I got: [%s]\n", m->string_);
strcpy(tid, j->string_);
}
}
if ((j = json_find_member(json, "t")) != NULL) {
if (j && j->tag == JSON_STRING) {
strcpy(t, j->string_);
}
}
/*
* Normalize tst, lat, lon to numbers, particularly for Greenwich
* which produces strings currently.
*/
*tst = time(NULL);
if ((j = json_find_member(json, "tst")) != NULL) {
if (j && j->tag == JSON_STRING) {
*tst = strtoul(j->string_, NULL, 10);
json_remove_from_parent(j);
json_append_member(json, "tst", json_mknumber(*tst));
} else {
*tst = (unsigned long)j->number_;
}
}
*lat = number(json, "lat");
*lon = number(json, "lon");
return (json);
return (NAN);
}
static const char *ltime(time_t t) {
@ -151,10 +91,10 @@ static const char *ltime(time_t t) {
* Process info/ message containing a CARD. If the payload is a card, return TRUE.
*/
int do_info(void *userdata, UT_string *username, UT_string *device, char *payload)
int do_info(void *userdata, UT_string *username, UT_string *device, JsonNode *json)
{
struct udata *ud = (struct udata *)userdata;
JsonNode *json, *j;
JsonNode *j;
static UT_string *name = NULL, *face = NULL;
FILE *fp;
char *img;
@ -163,26 +103,14 @@ int do_info(void *userdata, UT_string *username, UT_string *device, char *payloa
utstring_renew(name);
utstring_renew(face);
if ((json = json_decode(payload)) == NULL) {
fprintf(stderr, "Can't decode INFO payload for username=%s\n", utstring_body(username));
return (FALSE);
}
if (ud->skipdemo && (json_find_member(json, "_demo") != NULL)) {
goto cleanup;
}
if ((j = json_find_member(json, "_type")) == NULL) {
goto cleanup;
}
if ((j->tag != JSON_STRING) || (strcmp(j->string_, "card") != 0)) {
goto cleanup;
}
/* I know the payload is valid JSON: write card */
if ((fp = pathn("wb", "cards", username, NULL, "json")) != NULL) {
fprintf(fp, "%s\n", payload);
char *js = json_stringify(json, NULL);
if (js) {
fprintf(fp, "%s\n", js);
free(js);
}
fclose(fp);
}
rc = TRUE;
@ -202,15 +130,10 @@ int do_info(void *userdata, UT_string *username, UT_string *device, char *payloa
}
}
fprintf(stderr, "* CARD: %s-%s %s\n", utstring_body(username), utstring_body(device), utstring_body(name));
#ifdef HAVE_REDIS /* TODO: LMDB? */
if (ud->useredis) {
redis_ping(&ud->redis);
r = redisCommand(ud->redis, "HMSET card:%s name %s face %s", utstring_body(username), utstring_body(name), utstring_body(face));
if (ud->verbose) {
printf("* CARD: %s-%s %s\n", utstring_body(username), utstring_body(device), utstring_body(name));
}
#endif
/* We have a base64-encoded "face". Decode it and store binary image */
if ((img = malloc(utstring_len(face))) != NULL) {
@ -221,58 +144,34 @@ int do_info(void *userdata, UT_string *username, UT_string *device, char *payloa
fwrite(img, sizeof(char), imglen, fp);
fclose(fp);
}
#ifdef HAVE_REDIS /* TODO: LMDB ? */
if (ud->useredis) {
/* Add photo (binary) to Redis as photo:username */
redis_ping(&ud->redis);
r = redisCommand(ud->redis, "SET photo:%s %b", utstring_body(username), img, imglen);
}
#endif
}
free(img);
}
cleanup:
json_delete(json);
return (rc);
}
void do_msg(void *userdata, UT_string *username, UT_string *device, char *payload)
void do_msg(void *userdata, UT_string *username, UT_string *device, JsonNode *json)
{
struct udata *ud = (struct udata *)userdata;
JsonNode *json, *j;
FILE *fp;
if ((json = json_decode(payload)) == NULL) {
fprintf(stderr, "Can't decode MSG payload for username=%s, device=%s\n",
utstring_body(username), utstring_body(device));
return;
}
if (ud->skipdemo && (json_find_member(json, "_demo") != NULL)) {
goto cleanup;
}
if ((j = json_find_member(json, "_type")) == NULL) {
goto cleanup;
}
if ((j->tag != JSON_STRING) || (strcmp(j->string_, "msg") != 0)) {
goto cleanup;
}
/* I know the payload is valid JSON: write message */
if ((fp = pathn("ab", "msg", username, NULL, "json")) != NULL) {
fprintf(fp, "%s\n", payload);
char *js = json_stringify(json, NULL);
if (js) {
fprintf(fp, "%s\n", js);
free(js);
}
fclose(fp);
}
fprintf(stderr, "* MSG: %s-%s\n", utstring_body(username), utstring_body(device));
cleanup:
json_delete(json);
if (ud->verbose) {
printf("* MSG: %s-%s\n", utstring_body(username), utstring_body(device));
}
}
void republish(struct mosquitto *mosq, struct udata *userdata, char *username, char *topic, double lat, double lon, char *cc, char *addr, long tst, char *t)
@ -315,38 +214,40 @@ void republish(struct mosquitto *mosq, struct udata *userdata, char *username, c
}
/*
* Decode OwnTracks CSV and return a new JsonNode to a JSON object.
* Decode OwnTracks CSV (Greenwich) and return a new JSON object
* of _type = location.
*/
#define MILL 1000000.0
JsonNode *csv(char *payload, char *tid, char *t, double *lat, double *lon, long *tst)
JsonNode *csv_to_json(char *payload)
{
JsonNode *json = NULL;
double dist = 0;
JsonNode *json;
char tid[64], t[10];
double dist = 0, lat, lon, vel, trip, alt, cog;
long tst;
char tmptst[40];
double vel, trip, alt, cog;
if (sscanf(payload, "%[^,],%[^,],%[^,],%lf,%lf,%lf,%lf,%lf,%lf,%lf", tid, tmptst, t, lat, lon, &cog, &vel, &alt, &dist, &trip) != 10) {
if (sscanf(payload, "%[^,],%[^,],%[^,],%lf,%lf,%lf,%lf,%lf,%lf,%lf", tid, tmptst, t, &lat, &lon, &cog, &vel, &alt, &dist, &trip) != 10) {
// fprintf(stderr, "**** payload not CSV: %s\n", payload);
return (NULL);
}
*lat /= MILL;
*lon /= MILL;
lat /= MILL;
lon /= MILL;
cog *= 10;
alt *= 10;
trip *= 1000;
*tst = strtoul(tmptst, NULL, 16);
tst = strtoul(tmptst, NULL, 16);
json = json_mkobject();
json_append_member(json, "_type", json_mkstring("location"));
json_append_member(json, "t", json_mkstring(t));
json_append_member(json, "tid", json_mkstring(tid));
json_append_member(json, "tst", json_mknumber(*tst));
json_append_member(json, "lat", json_mknumber(*lat));
json_append_member(json, "lon", json_mknumber(*lon));
json_append_member(json, "tst", json_mknumber(tst));
json_append_member(json, "lat", json_mknumber(lat));
json_append_member(json, "lon", json_mknumber(lon));
json_append_member(json, "cog", json_mknumber(cog));
json_append_member(json, "vel", json_mknumber(vel));
json_append_member(json, "alt", json_mknumber(alt));
@ -356,23 +257,38 @@ JsonNode *csv(char *payload, char *tid, char *t, double *lat, double *lon, long
return (json);
}
#define RECFORMAT "%s\t%-18s\t%s\n"
static void putrec(time_t now, UT_string *reltopic, UT_string *username, UT_string *device, char *string)
{
FILE *fp;
if ((fp = pathn("a", "rec", username, device, "rec")) == NULL) {
olog(LOG_ERR, "Cannot write REC for %s/%s: %m",
utstring_body(username), utstring_body(device));
}
fprintf(fp, RECFORMAT, isotime(now),
utstring_body(reltopic), string);
fclose(fp);
}
void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *m)
{
JsonNode *json, *fullo, *geo = NULL;
char tid[BUFSIZ], t[BUFSIZ], *p;
JsonNode *json, *j, *geo = NULL;
char *tid = NULL, *t = NULL, *p;
double lat, lon;
long tst;
struct udata *ud = (struct udata *)userdata;
FILE *fp;
char **topics;
int count = 0, cached;
static UT_string *basetopic = NULL, *username = NULL, *device = NULL, *addr = NULL, *cc = NULL, *ghash = NULL, *ts = NULL;
static UT_string *reltopic = NULL;
char *jsonstring;
char *jsonstring, *_typestr = NULL;
time_t now;
int pingping = FALSE;
payload_type _type;
/*
* mosquitto_message->
@ -407,114 +323,151 @@ void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_m
return;
}
/*
* Determine "relative topic", relative to base, i.e. whatever comes
* behind ownntracks/user/device/. If it's the base topic, use "*".
*/
utstring_renew(reltopic);
if (count != 3) {
int j;
for (j = 3; j < count; j++) {
utstring_printf(reltopic, "%s%c", topics[j], (j < count - 1) ? '/' : ' ');
}
} else {
utstring_printf(reltopic, "*");
}
if (utstring_len(reltopic) == 0)
utstring_printf(reltopic, "-");
/* FIXME: handle null leading topic `/` */
utstring_printf(basetopic, "%s/%s/%s", topics[0], topics[1], topics[2]);
utstring_printf(username, "%s", topics[1]);
utstring_printf(device, "%s", topics[2]);
mosquitto_sub_topic_tokens_free(&topics, count);
#ifdef HAVE_PING
if (!strcmp(utstring_body(username), "ping") && !strcmp(utstring_body(device), "ping")) {
pingping = TRUE;
}
#endif
if ((count == TOPIC_PARTS) && (strcmp(topics[count-1], TOPIC_SUFFIX) == 0)) {
if (do_info(ud, username, device, m->payload) == TRUE) { /* this was a card */
return;
}
}
/* owntracks/user/device/msg */
if ((count == TOPIC_PARTS) && (strcmp(topics[count-1], "msg") == 0)) {
do_msg(ud, username, device, m->payload);
}
/*
* Determine "relative topic", relative to base, i.e. whatever comes behind
* ownntracks/user/device/
* First things first: let's see if this contains some sort of valid JSON
* or an OwnTracks CSV. If it doesn't, just store this payload because
* there's nothing left for us to do with it.
*/
utstring_renew(reltopic);
if (count != 3) {
/*
* Not a normal location publish. Build up a string consisting of the remaining
* topic parts, i.e. whatever is after base topic, and record with whatever
* (hopefully non-binary) payload we got.
*/
int j;
for (j = 3; j < count; j++) {
utstring_printf(reltopic, "%s%c", topics[j], (j < count - 1) ? '/' : ' ');
}
if ((fp = pathn("a", "rec", username, device, "rec")) != NULL) {
fprintf(fp, RECFORMAT, isotime(now), utstring_body(reltopic), bindump(m->payload, m->payloadlen));
fclose(fp);
}
mosquitto_sub_topic_tokens_free(&topics, count);
return;
}
if (utstring_len(reltopic) == 0)
utstring_printf(reltopic, "-");
mosquitto_sub_topic_tokens_free(&topics, count);
/*
* Try to decode JSON payload to find _type: location. If that doesn't work,
* see if it's OwnTracks' Greenwich CSV
*/
json = extract(ud, m->payload, tid, t, &lat, &lon, &tst);
if (json == NULL) {
/* Is it OwnTracks Greenwich CSV? */
if ((json = csv(m->payload, tid, t, &lat, &lon, &tst)) == NULL) {
if ((json = json_decode(m->payload)) == NULL) {
if ((json = csv_to_json(m->payload)) == NULL) {
/* It's not JSON or it's not a location CSV; store it */
/* It may be an lwt */
if ((fp = pathn("a", "rec", username, device, "rec")) != NULL) {
fprintf(fp, RECFORMAT, isotime(now),
utstring_body(reltopic),
bindump(m->payload, m->payloadlen));
fclose(fp);
}
putrec(now, reltopic, username, device, bindump(m->payload, m->payloadlen));
return;
}
// fprintf(stderr, "+++++ %s\n", json_stringify(json, NULL));
}
#if 0
if (*t && (!strcmp(t, "p") || !strcmp(t, "b"))) {
// fprintf(stderr, "Ignore `t:%s' for %s\n", t, m->topic);
if (ud->skipdemo && (json_find_member(json, "_demo") != NULL)) {
json_delete(json);
return;
}
#endif
_type = T_UNKNOWN;
if ((j = json_find_member(json, "_type")) != NULL) {
if (j->tag == JSON_STRING) {
_typestr = strdup(j->string_);
if (!strcmp(j->string_, "location")) _type = T_LOCATION;
else if (!strcmp(j->string_, "beacon")) _type = T_BEACON;
else if (!strcmp(j->string_, "card")) _type = T_CARD;
else if (!strcmp(j->string_, "cmd")) _type = T_CMD;
else if (!strcmp(j->string_, "configuration")) _type = T_CONFIG;
else if (!strcmp(j->string_, "lwt")) _type = T_LWT;
else if (!strcmp(j->string_, "msg")) _type = T_MSG;
else if (!strcmp(j->string_, "steps")) _type = T_STEPS;
else if (!strcmp(j->string_, "transition")) _type = T_TRANSITION;
else if (!strcmp(j->string_, "waypoint")) _type = T_WAYPOINT;
else if (!strcmp(j->string_, "waypoints")) _type = T_WAYPOINTS;
}
}
switch (_type) {
case T_CARD:
do_info(ud, username, device, json);
goto cleanup;
case T_MSG:
do_msg(ud, username, device, json);
goto cleanup;
case T_BEACON:
case T_CMD:
case T_CONFIG:
case T_LWT:
case T_STEPS:
case T_WAYPOINTS:
putrec(now, reltopic, username, device, bindump(m->payload, m->payloadlen));
goto cleanup;
case T_WAYPOINT:
case T_TRANSITION:
case T_LOCATION:
break;
default:
putrec(now, reltopic, username, device, bindump(m->payload, m->payloadlen));
goto cleanup;
}
/*
* We are now processing a _type location.
* We are now handling location-related JSON. Normalize tst, lat, lon
* to numbers, particularly for Greenwich which produces strings
* currently. We're normalizing *in* json which replaces strings by
* numbers.
*/
tst = time(NULL);
if ((j = json_find_member(json, "tst")) != NULL) {
if (j && j->tag == JSON_STRING) {
tst = strtoul(j->string_, NULL, 10);
json_remove_from_parent(j);
json_append_member(json, "tst", json_mknumber(tst));
} else {
tst = (unsigned long)j->number_;
}
}
if (isnan(lat = number(json, "lat")) || isnan(lon = number(json, "lon"))) {
olog(LOG_ERR, "lat or lon are NaN");
goto cleanup;
}
if ((j = json_find_member(json, "tid")) != NULL) {
if (j->tag == JSON_STRING) {
tid = strdup(j->string_);
}
}
if ((j = json_find_member(json, "t")) != NULL) {
if (j && j->tag == JSON_STRING) {
t = strdup(j->string_);
}
}
/*
* Chances are high that what we have now contains lat, lon. Attempt to
* perform or retrieve reverse-geo.
*/
utstring_renew(ghash);
utstring_renew(addr);
utstring_renew(cc);
p = geohash_encode(lat, lon, geohash_prec());
if (p != NULL) {
utstring_printf(ghash, "%s", p);
free(p);
}
utstring_renew(addr);
utstring_renew(cc);
cached = FALSE;
if (ud->revgeo == TRUE) {
JsonNode *j;
if ((geo = gcache_json_get(ud->gc, utstring_body(ghash))) != NULL) {
/* Habemus cached data */
@ -548,30 +501,27 @@ void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_m
utstring_printf(addr, "n.a.");
}
/*
* We have exactly three topic parts (owntracks/user/device), and valid JSON.
*/
/*
* Create a new location object containing all the bits and
* pieces we need and push that into connected Websockets.
* and/or into Lua hooks.
* We have normalized data in the JSON, so we can now write it
* out to the REC file.
*/
fullo = json_mkobject();
json_copy_to_object(fullo, json, TRUE);
if (geo != NULL) {
json_copy_to_object(fullo, geo, FALSE);
json_delete(geo);
if (!pingping) {
if ((jsonstring = json_stringify(json, NULL)) != NULL) {
putrec(now, reltopic, username, device, jsonstring);
free(jsonstring);
}
}
/*
* Append a few bits to the location type to add to LAST and
* for Lua / Websockets.
* I need a unique "key" in the Websocket clients to keep track
* of which device is being updated; use topic.
*/
json_append_member(fullo, "topic", json_mkstring(m->topic));
json_append_member(json, "topic", json_mkstring(m->topic));
/*
* We have to know which user/device this is for in order to
@ -579,49 +529,14 @@ void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_m
* to see this. Add user/device
*/
json_append_member(fullo, "user", json_mkstring(utstring_body(username)));
json_append_member(fullo, "device", json_mkstring(utstring_body(device)));
#ifdef HAVE_HTTP
if (ud->mgserver && !pingping) {
http_ws_push_json(ud->mgserver, fullo);
}
#endif
#ifdef WITH_LUA
if (ud->luadata && !pingping) {
hooks_hook(ud, m->topic, fullo);
}
#endif
json_delete(fullo);
if ((jsonstring = json_stringify(json, NULL)) != NULL) {
char *js;
#ifdef HAVE_REDIS /* TODO: shall we store last positions in LMDB? */
if (ud->useredis) {
/* add last to Redis as "lastpos:username-device" */
last_storeredis(&ud->redis, utstring_body(username), utstring_body(device), jsonstring);
}
#endif
if (!pingping) {
if ((fp = pathn("a", "rec", username, device, "rec")) != NULL) {
fprintf(fp, RECFORMAT, isotime(now), "*", jsonstring);
fclose(fp);
}
}
/* Keep track of original username & device name in LAST. */
json_append_member(json, "username", json_mkstring(utstring_body(username)));
json_append_member(json, "device", json_mkstring(utstring_body(device)));
json_append_member(json, "topic", json_mkstring(m->topic));
json_append_member(json, "ghash", json_mkstring(utstring_body(ghash)));
if ((js = json_stringify(json, NULL)) != NULL) {
if (_type == T_LOCATION) {
if ((jsonstring = json_stringify(json, NULL)) != NULL) {
/* Now safewrite the last location */
utstring_printf(ts, "%s/last/%s/%s",
STORAGEDIR, utstring_body(username), utstring_body(device));
@ -631,33 +546,62 @@ void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_m
utstring_printf(ts, "/%s-%s.json",
utstring_body(username), utstring_body(device));
safewrite(utstring_body(ts), js);
free(js);
}
safewrite(utstring_body(ts), jsonstring);
free(jsonstring);
}
/* publish */
// republish(mosq, ud, utstring_body(username), m->topic, lat, lon, utstring_body(cc), utstring_body(addr), tst, t);
if (*t == 0) {
strcpy(t, " ");
}
fprintf(stderr, "%c %s %-35s t=%-1.1s tid=%-2.2s loc=%.5f,%.5f [%s] %s (%s)\n",
/*
* Now add more bits for Lua and Websocket, in particular the
* Geo data.
*/
if (geo) {
json_copy_to_object(json, geo, FALSE);
}
#ifdef HAVE_HTTP
if (ud->mgserver && !pingping) {
http_ws_push_json(ud->mgserver, json);
}
#endif
#ifdef WITH_LUA
if (ud->luadata && !pingping) {
hooks_hook(ud, m->topic, json);
}
#endif
if (ud->verbose) {
if (_type == T_LOCATION) {
printf("%c %s %-35s t=%-1.1s tid=%-2.2s loc=%.5f,%.5f [%s] %s (%s)\n",
(cached) ? '*' : '-',
ltime(tst),
m->topic,
t,
tid,
(t) ? t : " ",
(tid) ? tid : "",
lat, lon,
utstring_body(cc),
utstring_body(addr),
utstring_body(ghash)
);
} else {
if ((jsonstring = json_stringify(json, NULL)) != NULL) {
printf("%s %s\n", _typestr, jsonstring);
free(jsonstring);
} else {
printf("%s received\n", _typestr);
}
}
}
json_delete(json);
cleanup:
if (geo) json_delete(geo);
if (json) json_delete(json);
if (tid) free(tid);
if (t) free(t);
if (_typestr) free(_typestr);
}
void on_connect(struct mosquitto *mosq, void *userdata, int rc)
@ -709,6 +653,7 @@ void usage(char *prog)
printf(" --host -H MQTT host (localhost)\n");
printf(" --port -p MQTT port (1883)\n");
printf(" --logfacility syslog facility (local0)\n");
printf(" --quiet disable printing of messages to stdout\n");
#ifdef HAVE_HTTP
printf(" --http-host <host> HTTP addr to bind to (localhost)\n");
printf(" --http-port <port> -A HTTP port (8083); 0 to disable HTTP\n");
@ -761,6 +706,7 @@ int main(int argc, char **argv)
udata.pubprefix = NULL;
udata.skipdemo = TRUE;
udata.revgeo = TRUE;
udata.verbose = TRUE;
#ifdef HAVE_LMDB
udata.gc = NULL;
#endif
@ -805,6 +751,7 @@ int main(int argc, char **argv)
{ "logfacility", required_argument, 0, 4},
{ "precision", required_argument, 0, 5},
{ "hosted", no_argument, 0, 6},
{ "quiet", no_argument, 0, 8},
#ifdef WITH_LUA
{ "lua-script", required_argument, 0, 7},
#endif
@ -822,6 +769,9 @@ int main(int argc, char **argv)
break;
switch (ch) {
case 8:
ud->verbose = FALSE;
break;
#ifdef WITH_LUA
case 7:
/* FIXME: check existence of script file */

View File

@ -7,6 +7,7 @@
#define DEFAULT_HISTORY_HOURS 6
/* Output types */
typedef enum {
GEOJSON = 0,
CSV,
@ -18,6 +19,22 @@ typedef enum {
XML,
} output_type;
/* JSON payload types */
typedef enum {
T_UNKNOWN = 0,
T_BEACON,
T_CARD,
T_CMD,
T_CONFIG,
T_LOCATION,
T_LWT,
T_MSG,
T_STEPS,
T_TRANSITION,
T_WAYPOINT,
T_WAYPOINTS,
} payload_type;
JsonNode *lister(char *username, char *device, time_t s_lo, time_t s_hi, int reverse);
void locations(char *filename, JsonNode *obj, JsonNode *arr, time_t s_lo, time_t s_hi, output_type otype, int limit, JsonNode *fields);
int make_times(char *time_from, time_t *s_lo, char *time_to, time_t *s_to);

View File

@ -18,6 +18,7 @@ struct udata {
int skipdemo; /* True if _demo users are to be skipped */
int revgeo; /* True (default) if we should do reverse Geo lookups */
int qos; /* Subscribe QoS */
int verbose; /* TRUE if print verbose messages to stdout */
#ifdef HAVE_LMDB
struct gcache *gc;
#endif

2
util.c
View File

@ -455,7 +455,7 @@ FILE *pathn(char *mode, char *prefix, UT_string *user, UT_string *device, char *
ut_clean(path);
if (mkpath(utstring_body(path)) < 0) {
perror(utstring_body(path));
olog(LOG_ERR, "Cannot create directory at %s: %m", utstring_body(path));
return (NULL);
}