aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--NEWS1
-rw-r--r--doc/index.html2
-rw-r--r--doc/upgrade.html1
-rw-r--r--package/deps.mak3
-rw-r--r--package/modes1
-rw-r--r--package/targets.mak3
-rw-r--r--src/misc/deps-exe/tipidee-logaggregate1
-rw-r--r--src/misc/tipidee-logaggregate.c447
9 files changed, 458 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index 300cbda..7b81c46 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,5 @@
/tipideed
/tipidee-config
/tipidee-config-preprocess
+/tipidee-logaggregate
/ls.cgi
diff --git a/NEWS b/NEWS
index d8fd8a2..ba922e4 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ In 0.0.7.0
- Support for shared libraries on MacOS.
- New directive: noredirect, to carve exceptions to generic redirections.
+ - New directives: rproxy, for basic reverse proxying
In 0.0.6.0
diff --git a/doc/index.html b/doc/index.html
index d5719b7..d405c83 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -143,7 +143,7 @@ Don't take my word for it; try it out for yourself. </li>
<ul>
<li> The current released version of tipidee is
-<a href="tipidee-0.0.7.0.tar.gz">0.0.6.0</a>.
+<a href="tipidee-0.0.7.0.tar.gz">0.0.7.0</a>.
You can access its checksum <a href="tipidee-0.0.7.0.tar.gz.sha256">here</a>. </li>
<li> You can checkout a copy of the
<a href="//git.skarnet.org/cgi-bin/cgit.cgi/tipidee/">tipidee
diff --git a/doc/upgrade.html b/doc/upgrade.html
index e8447d5..f1fdc1a 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -25,6 +25,7 @@
dependency bumped to 2.14.5.0 </li>
<li> <a href="//skarnet.org/software/s6-networking/">s6-networking</a>
recommendation bumped to 2.7.2.0 </li>
+ <li> Shared libraries are now supported on MacOS. </li>
</ul>
<h2> in 0.0.6.0 </h2>
diff --git a/package/deps.mak b/package/deps.mak
index 5c91a0c..83dacd8 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -65,6 +65,7 @@ src/libtipidee/tipidee_util_htmlescape.o src/libtipidee/tipidee_util_htmlescape.
src/libtipidee/tipidee_util_httpdate.o src/libtipidee/tipidee_util_httpdate.lo: src/libtipidee/tipidee_util_httpdate.c src/include/tipidee/util.h
src/libtipidee/tipidee_util_parse_range.o src/libtipidee/tipidee_util_parse_range.lo: src/libtipidee/tipidee_util_parse_range.c src/include/tipidee/util.h
src/misc/ls.cgi.o src/misc/ls.cgi.lo: src/misc/ls.cgi.c src/include/tipidee/response.h src/include/tipidee/util.h
+src/misc/tipidee-logaggregate.o src/misc/tipidee-logaggregate.lo: src/misc/tipidee-logaggregate.c
src/tipideed/cgi.o src/tipideed/cgi.lo: src/tipideed/cgi.c src/include/tipidee/tipidee.h src/tipideed/tipideed-internal.h
src/tipideed/errors.o src/tipideed/errors.lo: src/tipideed/errors.c src/include/tipidee/log.h src/include/tipidee/response.h src/include/tipidee/util.h src/tipideed/tipideed-internal.h
src/tipideed/harden.o src/tipideed/harden.lo: src/tipideed/harden.c src/tipideed/tipideed-internal.h
@@ -94,6 +95,8 @@ libtipidee.dylib.xyzzy: EXTRA_LIBS := -lskarnet
libtipidee.dylib.xyzzy:src/libtipidee/tipidee_conf_free.lo src/libtipidee/tipidee_conf_get.lo src/libtipidee/tipidee_conf_get_argv.lo src/libtipidee/tipidee_conf_get_content_type.lo src/libtipidee/tipidee_conf_get_errorfile.lo src/libtipidee/tipidee_conf_get_redirection.lo src/libtipidee/tipidee_conf_get_resattr.lo src/libtipidee/tipidee_conf_get_resattr1.lo src/libtipidee/tipidee_conf_get_responseheaders.lo src/libtipidee/tipidee_conf_get_string.lo src/libtipidee/tipidee_conf_get_uint32.lo src/libtipidee/tipidee_conf_init.lo src/libtipidee/tipidee_headers_get_content_length.lo src/libtipidee/tipidee_headers_init.lo src/libtipidee/tipidee_headers_parse.lo src/libtipidee/tipidee_headers_search.lo src/libtipidee/tipidee_log_answer.lo src/libtipidee/tipidee_log_exit.lo src/libtipidee/tipidee_log_request.lo src/libtipidee/tipidee_log_resource.lo src/libtipidee/tipidee_log_start.lo src/libtipidee/tipidee_method.lo src/libtipidee/tipidee_response_error_nofile.lo src/libtipidee/tipidee_response_error_nofile_G.lo src/libtipidee/tipidee_response_file.lo src/libtipidee/tipidee_response_file_G.lo src/libtipidee/tipidee_response_header_date.lo src/libtipidee/tipidee_response_header_date_G.lo src/libtipidee/tipidee_response_header_date_fmt.lo src/libtipidee/tipidee_response_header_end.lo src/libtipidee/tipidee_response_header_lastmodified.lo src/libtipidee/tipidee_response_header_preparebuiltin.lo src/libtipidee/tipidee_response_header_write.lo src/libtipidee/tipidee_response_header_writeall.lo src/libtipidee/tipidee_response_header_writeall_G.lo src/libtipidee/tipidee_response_header_writemerge.lo src/libtipidee/tipidee_response_header_writemerge_G.lo src/libtipidee/tipidee_response_partial.lo src/libtipidee/tipidee_response_partial_G.lo src/libtipidee/tipidee_response_status.lo src/libtipidee/tipidee_rql_read.lo src/libtipidee/tipidee_uri_parse.lo src/libtipidee/tipidee_util_chunked_read.lo src/libtipidee/tipidee_util_defaulttext.lo src/libtipidee/tipidee_util_htmlescape.lo src/libtipidee/tipidee_util_httpdate.lo src/libtipidee/tipidee_util_parse_range.lo
ls.cgi: EXTRA_LIBS :=
ls.cgi: src/misc/ls.cgi.o libtipidee.a.xyzzy -lskarnet
+tipidee-logaggregate: EXTRA_LIBS :=
+tipidee-logaggregate: src/misc/tipidee-logaggregate.o -lskarnet
tipideed: EXTRA_LIBS :=
tipideed: src/tipideed/tipideed.o src/tipideed/cgi.o src/tipideed/errors.o src/tipideed/harden.o src/tipideed/options.o src/tipideed/regular.o src/tipideed/redirection.o src/tipideed/rproxy.o src/tipideed/send_file.o src/tipideed/stream.o src/tipideed/trace.o src/tipideed/util.o libtipidee.a.xyzzy -lskarnet
INTERNAL_LIBS :=
diff --git a/package/modes b/package/modes
index 131b6bb..2bdeb12 100644
--- a/package/modes
+++ b/package/modes
@@ -2,3 +2,4 @@ tipideed 0755
tipidee-config-preprocess 0755
tipidee-config 0755
ls.cgi 0755
+tipidee-logaggregate 0755
diff --git a/package/targets.mak b/package/targets.mak
index a600e23..02780d7 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -1,6 +1,7 @@
BIN_TARGETS := \
tipideed \
-tipidee-config
+tipidee-config \
+tipidee-logaggregate
LIBEXEC_TARGETS := \
tipidee-config-preprocess \
diff --git a/src/misc/deps-exe/tipidee-logaggregate b/src/misc/deps-exe/tipidee-logaggregate
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/misc/deps-exe/tipidee-logaggregate
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/misc/tipidee-logaggregate.c b/src/misc/tipidee-logaggregate.c
new file mode 100644
index 0000000..83cda5e
--- /dev/null
+++ b/src/misc/tipidee-logaggregate.c
@@ -0,0 +1,447 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <skalibs/uint64.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/types.h>
+#include <skalibs/buffer.h>
+#include <skalibs/prog.h>
+#include <skalibs/gol.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/avltree.h>
+#include <skalibs/lolstdio.h>
+
+#define USAGE "tipidee-logaggregate [ -4 | -6 ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+#define dienomem() strerr_diefu1sys(111, "stralloc_catb")
+#define dieout() strerr_diefu1sys(111, "write to stdout")
+#define dietoobig() strerr_dief1x(100, "too much data to process")
+
+enum golb_e
+{
+ GOLB_IS6,
+ GOLB_N
+} ;
+
+static uint64_t wgolb = 0 ;
+static uint32_t line = 1 ;
+
+
+ /* pidip */
+
+typedef struct pidip_s pidip, pidip_ref ;
+struct pidip_s
+{
+ pid_t pid ;
+ char ip[16] ;
+} ;
+#define PIDIP_ZERO { .pid = 0, .ip = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }
+
+static void *pidip_dtok (uint32_t d, void *data)
+{
+ gensetdyn *g = data ;
+ pidip *p = GENSETDYN_P(pidip, g, d) ;
+ return &p->pid ;
+}
+
+static int pid_cmp (void const *a, void const *b, void *data)
+{
+ (void)data ;
+ pid_t aa = *(pid_t const *)a ;
+ pid_t bb = *(pid_t const *)b ;
+ return aa < bb ? -1 : aa > bb ;
+}
+
+static gensetdyn pidip_list = GENSETDYN_INIT(pidip, 16, 3, 8) ;
+static avltree pidip_map = AVLTREE_ZERO ;
+#define PIDIP(d) GENSETDYN_P(pidip, &pidip_list, (d))
+
+static int pidip_search (pid_t pid, uint32_t *d)
+{
+ if (!avltree_search(&pidip_map, &pid, d))
+ {
+ char fmtline[UINT32_FMT] ;
+ char fmt[PID_FMT] ;
+ fmtline[uint32_fmt(fmtline, line)] = 0 ;
+ fmt[pid_fmt(fmt, pid)] = 0 ;
+ strerr_warnw("line ", fmtline, ": pid ", fmt, " not registered") ;
+ return 0 ;
+ }
+ return 1 ;
+}
+
+static void pidip_delete (pid_t pid)
+{
+ uint32_t d ;
+ if (!pidip_search(pid, &d)) return ;
+ avltree_delete(&pidip_map, &pid) ;
+ gensetdyn_delete(&pidip_list, d) ;
+}
+
+static void pidip_add (pid_t pid, char const *ip)
+{
+ uint32_t d ;
+ if (avltree_search(&pidip_map, &pid, &d))
+ {
+ char fmtline[UINT32_FMT] ;
+ char fmt[PID_FMT] ;
+ fmtline[uint32_fmt(fmtline, line)] = 0 ;
+ fmt[pid_fmt(fmt, pid)] = 0 ;
+ strerr_dief(1, "line ", fmtline, ": pid ", fmt, " started but was already registered") ;
+ }
+ if (!gensetdyn_new(&pidip_list, &d)) dienomem() ;
+ pidip *p = GENSETDYN_P(pidip, &pidip_list, d) ;
+ p->pid = pid ;
+ memcpy(p->ip, ip, wgolb & (1 << GOLB_IS6) ? 16 : 4) ;
+ if (!avltree_insert(&pidip_map, d)) dienomem() ;
+}
+
+
+ /* ipinfo */
+
+typedef struct ipinfo_s ipinfo, *ipinfo_ref ;
+struct ipinfo_s
+{
+ char ip[16] ;
+ genalloc indices ; /* uint32_t */
+} ;
+#define IPINFO_ZERO { .indices = GENALLOC_ZERO }
+
+static void *ipinfo_dtok (uint32_t d, void *data)
+{
+ genalloc *g = data ;
+ ipinfo *p = genalloc_s(ipinfo, g) + d ;
+ return p->ip ;
+}
+
+static int ip_cmp (void const *a, void const *b, void *data)
+{
+ (void)data ;
+ return memcmp(a, b, wgolb & (1 << GOLB_IS6) ? 16 : 4) ;
+}
+
+
+static genalloc ipinfo_list = GENALLOC_ZERO ; /* ipinfo */
+static avltree ipinfo_map = AVLTREE_ZERO ;
+#define IPINFO(d) (genalloc_s(ipinfo, &ipinfo_list) + (d))
+
+
+ /* storage */
+
+static int string_cmp (void const *a, void const *b, void *data)
+{
+ (void)data ;
+ return strcmp((char const *)a, (char const *)b) ;
+}
+
+static void *string_dtok (uint32_t d, void *data)
+{
+ stralloc *sa = data ;
+ return sa->s + d ;
+}
+
+static stralloc storage = STRALLOC_ZERO ;
+static avltree storage_map = AVLTREE_ZERO ;
+
+static void add_request (pid_t pid, char const *host, char const *path, char const *query)
+{
+ ipinfo *info ;
+ pidip *p ;
+ uint32_t d ;
+ size_t pos ;
+ if (!pidip_search(pid, &d)) return ;
+ p = PIDIP(d) ;
+ if (!avltree_search(&ipinfo_map, p->ip, &d))
+ {
+ ipinfo i = IPINFO_ZERO ;
+ memcpy(i.ip, p->ip, wgolb & (1 << GOLB_IS6) ? 16 : 4) ;
+ d = genalloc_len(ipinfo, &ipinfo_list) ;
+ if (!genalloc_append(ipinfo, &ipinfo_list, &i)) dienomem() ;
+ if (!avltree_insert(&ipinfo_map, d)) dienomem() ;
+ }
+ info = IPINFO(d) ;
+ pos = storage.len ;
+ if (pos > UINT32_MAX) dietoobig() ;
+ if (!stralloc_cats(&storage, host)
+ || !stralloc_catb(&storage, ":", 1)
+ || !stralloc_cats(&storage, path)) dienomem() ;
+ if (query)
+ {
+ if (!stralloc_catb(&storage, "?", 1)
+ || !stralloc_cats(&storage, query)) dienomem() ;
+ }
+ if (!stralloc_0(&storage)) dienomem() ;
+ if (!avltree_search(&storage_map, storage.s + pos, &d))
+ {
+ d = pos ;
+ if (!avltree_insert(&storage_map, d)) dienomem() ;
+ }
+ else storage.len = pos ;
+ if (!genalloc_append(uint32_t, &info->indices, &d)) dienomem() ;
+}
+
+ /* parse */
+
+static void parse_answer (pid_t pid, char const *host, char *s)
+{
+ (void)pid ;
+ (void)host ;
+ (void)s ;
+}
+
+static void parse_exit (pid_t pid, char const *host, char *s)
+{
+ (void)s ;
+ (void)host ;
+ pidip_delete(pid) ;
+}
+
+static char *parse_host (char *s, char const **h)
+{
+ char *p ;
+ if (strncmp(s, "host ", 5)) return 0 ;
+ s += 5 ;
+ p = strchr(s, ' ') ;
+ if (!p) return 0 ;
+ *p = 0 ;
+ *h = s ;
+ return p+1 ;
+}
+
+static void parse_request (pid_t pid, char const *host, char *s)
+{
+ char *p ;
+ char *path = 0 ;
+ char *query = 0 ;
+ if (strncmp(s, "GET ", 4)) return ;
+ s += 4 ;
+ if (!host)
+ {
+ s = parse_host(s, &host) ;
+ if (!host)
+ {
+ char fmtline[UINT32_FMT] ;
+ fmtline[uint32_fmt(fmtline, line)] = 0 ;
+ strerr_warnw("line ", fmtline, ": request line with invalid/no ", "host") ;
+ return ;
+ }
+ }
+ if (strncmp(s, "path \"", 6))
+ {
+ char fmtline[UINT32_FMT] ;
+ fmtline[uint32_fmt(fmtline, line)] = 0 ;
+ strerr_warnw("line ", fmtline, ": request line with invalid/no ", "path") ;
+ return ;
+ }
+ s += 6 ;
+ p = strchr(s, '\"') ;
+ if (!p)
+ {
+ char fmtline[UINT32_FMT] ;
+ fmtline[uint32_fmt(fmtline, line)] = 0 ;
+ strerr_warnw("line ", fmtline, ": request line with invalid/no ", "path") ;
+ return ;
+ }
+ path = s ;
+ *p = 0 ;
+ s = p+1 ;
+ if (!strncmp(s, " query ", 7))
+ {
+ s += 7 ;
+ query = s ;
+ p = strchr(s, ' ') ;
+ if (!p)
+ {
+ char fmtline[UINT32_FMT] ;
+ fmtline[uint32_fmt(fmtline, line)] = 0 ;
+ strerr_warnw("line ", fmtline, ": request line with invalid query") ;
+ return ;
+ }
+ *p = 0 ;
+ s = p+1 ;
+ }
+ add_request(pid, host, path, query) ;
+}
+
+static void parse_resource (pid_t pid, char const *host, char *s)
+{
+ (void)pid ;
+ (void)host ;
+ (void)s ;
+}
+
+static void parse_start (pid_t pid, char const *host, char *s)
+{
+ char ip[16] ;
+ (void)host ;
+ if (strncmp(s, "ip ", 3))
+ {
+ char fmtline[UINT32_FMT] ;
+ fmtline[uint32_fmt(fmtline, line)] = 0 ;
+ strerr_warnw("line ", fmtline, ": start directive without ip") ;
+ return ;
+ }
+ s += 3 ;
+ if (wgolb & GOLB_IS6)
+ {
+ if (!ip6_scan(s, ip))
+ {
+ char fmtline[UINT32_FMT] ;
+ fmtline[uint32_fmt(fmtline, line)] = 0 ;
+ strerr_warnw("line ", fmtline, ": invalid ipv6") ;
+ return ;
+ }
+ }
+ else
+ {
+ if (!ip4_scan(s, ip))
+ {
+ char fmtline[UINT32_FMT] ;
+ fmtline[uint32_fmt(fmtline, line)] = 0 ;
+ strerr_warnw("line ", fmtline, ": invalid ipv6") ;
+ return ;
+ }
+ }
+ pidip_add(pid, ip) ;
+}
+
+struct wordf_s
+{
+ char const *s ;
+ void (*f)(pid_t, char const *, char *) ;
+} ;
+
+static int wordf_cmp (void const *a, void const *b)
+{
+ char const *s = ((struct wordf_s const *)b)->s ;
+ return strncmp((char const *)a, s, strlen(s)) ;
+}
+
+#define WORDF_BSEARCH(key, array) bsearch(key, (array), sizeof(array)/sizeof(struct wordf_s), sizeof(struct wordf_s), &wordf_cmp)
+
+static void parse (pid_t pid, char const *host, char *s)
+{
+ static struct wordf_s const words[5] =
+ {
+ { .s = "answer ", .f = &parse_answer },
+ { .s = "exit ", .f = &parse_exit },
+ { .s = "request ", .f = &parse_request },
+ { .s = "resource ", .f = &parse_resource },
+ { .s = "start ", .f = &parse_start }
+ } ;
+ size_t wlen ;
+ struct wordf_s const *p = WORDF_BSEARCH(s, words) ;
+ if (!p) return ;
+ wlen = strlen(p->s) ;
+ (*p->f)(pid, host, s + wlen) ;
+}
+
+ /* output */
+
+static int print_iter (uint32_t d, unsigned int h, void *data)
+{
+ ipinfo *info = IPINFO(d) ;
+ size_t n = genalloc_len(uint32_t, &info->indices) ;
+ char fmt[IP6_FMT] ;
+ char fsz[SIZE_FMT] ;
+ fmt[wgolb & GOLB_IS6 ? ip6_fmt(fmt, info->ip) : ip4_fmt(fmt, info->ip) ] = 0 ;
+ fsz[size_fmt(fsz, n)] = 0 ;
+ if (buffer_puts(buffer_1, "ip ") < 0
+ || buffer_puts(buffer_1, fmt) < 0
+ || buffer_puts(buffer_1, " hits ") < 0
+ || buffer_puts(buffer_1, fsz) < 0
+ || buffer_put(buffer_1, "\n", 1) < 0) dieout() ;
+ for (size_t i = 0 ; i < n ; i++)
+ {
+ if (buffer_puts(buffer_1, storage.s + genalloc_s(uint32_t, &info->indices)[i]) < 0
+ || buffer_put(buffer_1, "\n", 1) < 0) dieout() ;
+ }
+ if (!buffer_putflush(buffer_1, "\n", 1)) dieout() ;
+ (void)data ;
+ return 1 ;
+}
+
+
+ /* main */
+
+static gol_bool const rgolb[2] =
+{
+ { .so = '4', .lo = "ipv4", .clear = 1 << GOLB_IS6, .set = 0 },
+ { .so = '6', .lo = "ipv6", .clear = 0, .set = 1 << GOLB_IS6 }
+} ;
+
+int main (int argc, char const *const *argv)
+{
+ stralloc in = STRALLOC_ZERO ;
+ unsigned int golc ;
+ PROG = "tipidee-logaggregate" ;
+ golc = gol_main(argc, argv, rgolb, 2, 0, 0, &wgolb, 0) ;
+ argc -= golc ; argv += golc ;
+
+ avltree_init(&pidip_map, 10, 3, 8, &pidip_dtok, &pid_cmp, &pidip_list) ;
+ avltree_init(&ipinfo_map, 10, 3, 8, &ipinfo_dtok, &ip_cmp, &ipinfo_list) ;
+ avltree_init(&storage_map, 10, 3, 8, &string_dtok, &string_cmp, &storage) ;
+
+ for (;; line++, in.len = 0)
+ {
+ pid_t pid ;
+ size_t pos = 0 ;
+ size_t l ;
+ char const *host = 0 ;
+ int r = skagetln(buffer_0, &in, '\n') ;
+ if (r == -1)
+ if (errno != EPIPE) strerr_diefusys(111, "read from stdin") ;
+ else break ;
+ else if (!r) break ;
+ if (!in.len || in.s[in.len - 1] != '\n') continue ;
+ in.s[--in.len] = 0 ;
+ if (in.s[pos] == '@')
+ {
+ if (in.len < 26) continue ;
+ pos += 26 ;
+ }
+ if (pos + 15 > in.len) continue ;
+ if (memcmp(in.s + pos, "tipideed: pid ", 14)) continue ;
+ pos += 14 ;
+ l = pid_scan(in.s + pos, &pid) ;
+ if (!l) continue ;
+ pos += l ;
+ if (pos + 9 > in.len) continue ;
+ if (memcmp(in.s + pos, ": info: ", 8))
+ {
+ if (!memcmp(in.s + pos, ": fatal: ", 9)) pidip_delete(pid) ;
+ continue ;
+ }
+ pos += 8 ;
+ if (pos + 5 > in.len) continue ;
+ if (!memcmp(in.s + pos, "host ", 5))
+ {
+ pos += 5 ;
+ host = in.s + pos ;
+ l = byte_chr(in.s + pos, in.len - pos, ' ') ;
+ if (pos + l >= in.len) continue ;
+ pos += l ;
+ }
+ parse(pid, host, in.s + pos) ;
+ }
+
+ {
+ char fmt[UINT32_FMT] ;
+ fmt[uint32_fmt(fmt, avltree_len(&ipinfo_map))] = 0 ;
+ buffer_puts(buffer_1, "aggregated ") ;
+ buffer_puts(buffer_1, fmt) ;
+ buffer_putsflush(buffer_1, " ips\n") ;
+ }
+ (void)avltree_iter(&ipinfo_map, &print_iter, 0) ;
+ return 0 ;
+}