diff --git a/Changelog b/Changelog index 978fb31..6cd0417 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,6 @@ +- NEW: kill has been surfaced into the API (disabled by compile flag by default) +- FIX: kill now also deletes LAST file and its parent directories if empty - NEW: ocat --load-ghash can load revgeo db - NEW: ocat --dump-ghash dumps content of revgeo db - FIX: main index now shows users/devices already in storage diff --git a/Makefile b/Makefile index b3fc23d..8bd8555 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,9 @@ CFLAGS += -DGHASHPREC=$(GHASHPREC) ifeq ($(HAVE_PING),yes) CFLAGS += -DHAVE_PING=1 endif +ifeq ($(HAVE_KILL),yes) + CFLAGS += -DHAVE_KILL=1 +endif ifeq ($(HAVE_LMDB),yes) CFLAGS += -DHAVE_LMDB=1 -Imdb/ diff --git a/README.md b/README.md index 7dc450d..7ea855a 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,33 @@ The _recorder_ has a built-in HTTP server with which it servers static files fro The API basically serves the same data as _ocat_ is able to produce. +## API + +The _recorder_'s API provides most of the function that are surfaced by _ocat_. +Both GET and POST requests are supported, and if a username and device are +needed, these can be passed in via `X-Limit-User` and `X-Limit-Device` headers. +From and To dates may also be specified as `X-Limit-From` and `X-Limit-To` +respectively. + + +#### `kill` + +If support for this is compiled in, this endpoint allows a client to remove data from _storage_. (Note: *any* client can do this, as there is no authentication/authorization in the _recorder_!) + +``` +curl 'http://127.0.0.1:8085/api/0/kill?user=ngin&device=ojo' + +{ + "path": "s0/rec/ngin/ojo", + "status": "OK", + "last": "s0/last/ngin/ojo/ngin-ojo.json", + "killed": [ + "2015-09.rec", + ] +} +``` +The response contains a list of removed `.rec` files, and file system operations are logged to syslog. + #### Environment The following environment variables control _ocat_'s behaviour: diff --git a/config.mk.in b/config.mk.in index 60f465c..429ceae 100644 --- a/config.mk.in +++ b/config.mk.in @@ -11,6 +11,9 @@ HAVE_LMDB ?= yes # Do you want support for the `pingping' monitoring feature? HAVE_PING ?= yes +# Do you want support for removing data via the API? (Dangerous) +HAVE_KILL ?= no + # Where should the recorder store its data? This directory must # exist and be writeable by recorder (and readable by ocat) STORAGEDEFAULT = "/var/spool/owntracks/recorder/store" diff --git a/http.c b/http.c index 6a32888..0d4929a 100644 --- a/http.c +++ b/http.c @@ -278,6 +278,28 @@ static int dispatch(struct mg_connection *conn, const char *uri) time_from = field(conn, "from"); time_to = field(conn, "to"); +#if HAVE_KILL + if (nparts == 1 && !strcmp(uparts[0], "kill")) { + JsonNode *deleted; + + if (!u || !*u || !d || !*d) { + CLEANUP; + mg_send_status(conn, 416); + mg_printf_data(conn, "user and device are required\n"); + return (MG_TRUE); + } + + if ((deleted = kill_datastore(u, d)) == NULL) { + mg_send_status(conn, 416); + mg_printf_data(conn, "cannot kill data for %s/%s\n", u, d); + CLEANUP; + return (MG_TRUE); + } + CLEANUP; + return (json_response(conn, deleted)); + } +#endif /* HAVE_KILL */ + if (nparts == 1 && !strcmp(uparts[0], "last")) { JsonNode *user_array; diff --git a/ocat.c b/ocat.c index 890f0bb..d9f6fe6 100644 --- a/ocat.c +++ b/ocat.c @@ -140,7 +140,9 @@ void usage(char *prog) printf(" payload Like RAW but JSON payload only\n"); printf(" --fields tst,lat,lon,... Choose fields for CSV. (dflt: ALL)\n"); printf(" --last -L JSON object with last users\n"); +#if HAVE_KILL printf(" --killdata requires -u and -d\n"); +#endif printf(" --storage -S storage dir (%s)\n", STORAGEDEFAULT); printf(" --norevgeo -G disable ghash to reverge-geo lookups\n"); printf(" --precision ghash precision (dflt: %d)\n", GHASHPREC); @@ -182,7 +184,10 @@ int main(int argc, char **argv) { char *progname = *argv, *p; int c; - int list = 0, killdata = 0, last = 0, limit = 0, dumpghash = FALSE, loadghash = FALSE; + int list = 0, last = 0, limit = 0, dumpghash = FALSE, loadghash = FALSE; +#if HAVE_KILL + int killdata = FALSE; +#endif int revgeo = TRUE; char *username = NULL, *device = NULL, *time_from = NULL, *time_to = NULL; JsonNode *json, *obj, *locs; @@ -231,7 +236,9 @@ int main(int argc, char **argv) { "precision", required_argument, 0, 2}, { "dump-ghash", no_argument, 0, 3}, { "load-ghash", no_argument, 0, 4}, +#if HAVE_KILL { "killdata", no_argument, 0, 'K'}, +#endif { "norevgeo", no_argument, 0, 'G'}, {0, 0, 0, 0} }; @@ -302,9 +309,11 @@ int main(int argc, char **argv) exit(2); } break; +#if HAVE_KILL case 'K': killdata = 1; break; +#endif case 'L': last = 1; break; @@ -334,6 +343,7 @@ int main(int argc, char **argv) exit(0); } +#if HAVE_KILL if (killdata) { JsonNode *obj; //, *killed, *f; @@ -358,6 +368,7 @@ int main(int argc, char **argv) } return (0); } +#endif if (last) { JsonNode *user_array; diff --git a/storage.c b/storage.c index 332e40c..93ab42c 100644 --- a/storage.c +++ b/storage.c @@ -842,6 +842,8 @@ char *gpx_string(JsonNode *location_array) return (utstring_body(xml)); } +#if HAVE_KILL + /* * Remove all data for a user's device. Return a JSON object with a status * and an array of deleted files. @@ -893,6 +895,7 @@ JsonNode *kill_datastore(char *user, char *device) p = utstring_body(fname); if (remove(p) == 0) { + olog(LOG_NOTICE, "removed %s", p); json_append_element(killed, json_mkstring(namelist[i]->d_name)); } else { perror(p); @@ -906,8 +909,33 @@ JsonNode *kill_datastore(char *user, char *device) if (rmdir(utstring_body(path)) != 0) { json_append_member(obj, "status", json_mkstring("ERROR")); json_append_member(obj, "error", json_mkstring( strerror(errno))); + } else { + olog(LOG_NOTICE, "removed %s", utstring_body(path)); } + utstring_renew(path); + utstring_printf(path, "%s/last/%s/%s/%s-%s.json", STORAGEDIR, user, device, user, device); + if (remove(utstring_body(path)) == 0) { + olog(LOG_NOTICE, "removed %s", utstring_body(path)); + json_append_member(obj, "last", json_mkstring(utstring_body(path))); + } + + /* Attempt to remove containing directory */ + utstring_renew(path); + utstring_printf(path, "%s/last/%s/%s", STORAGEDIR, user, device); + if (rmdir(utstring_body(path)) == 0) { + olog(LOG_NOTICE, "removed %s", utstring_body(path)); + } + + /* Attempt to remove it's parent directory */ + utstring_renew(path); + utstring_printf(path, "%s/last/%s", STORAGEDIR, user); + if (rmdir(utstring_body(path)) == 0) { + olog(LOG_NOTICE, "removed %s", utstring_body(path)); + } + + json_append_member(obj, "killed", killed); return (obj); } +#endif /* HAVE_KILL */