Hi

[2024-05-30 21:54] Philipp <phil...@bureaucracy.de>
> I'm currently working on the C API for the tables implementations. The
> current API has the problem that the callbacks mixes request and
> response. So I have created a new C api with callbacks for requests
> and extra functios for response. The callbacks looks the following:
>
> void update_cb(const char *id, const char *tname)
> void check_cb(const char *id, const char *tname, int service, const char *key)
> void lookup_cb(const char *id, const char *tname, int service, const char 
> *key)
> void fetch_cb(const char *id, const char *tname, int service)
>
> These are called on for each request. The backend register these callbacks
> and calles a response function:
>
> void table_api_update_finish(const char *id)
> void table_api_check_result(const char *id, bool found)
> void table_api_lookup_result(const char *id, const char *buf)
> void table_api_lookup_finish(const char *id)
> void table_api_fetch_result(const char *id, const char *buf)
> table_api_error(const char *id, const char *error)
>
> In most cases the return value (found/not-found/ok) is implicit. For
> lookup there are two functions for response, one for the value and one
> to finish the lookup. This has two reasons: First this makes it simpler
> to handle multible (i.e. for alias) responses. Secound this allows to
> change the protokoll for more flexible lookup tables (usefull for dns
> tables). The error function implicit respones with the correct result
> type.
>
> What do you think about the new API?
>
> I have also implement some fallback handler for the old api. They are
> primary for simpler testing and development. Patcher are tested with
> table-ldap and attached. The patches are currenty seperated by query
> type. For the push I would put them togetter in one commit.
>
> My next step is to change the getline loop to a pool based loop. Then
> add some registration for other fd. This way the backend can be full
> asyncron.

I now also have implemented a simple poll based event loop. So a table
implementation can register fds with the following api:

void table_api_register_fd(int fd, short events, fd_callback cb);
void table_api_replace_fd(int old, int new);
void table_api_remove_fd(int fd);

The callback looks like this:

typedef void (*fd_callback)(int fd, short revent);

So you the registered callback gets called with the fd and the revent
from poll. This make it easy to implement a full async table.

Patches[0] are attached. I don't have full tested them but first tests
look good.

Philipp

[0] I have included the previous patches, because I don't know if I have
send all updates and haven't rebased my local branch.
From c3996e9007e198f3c69f31c580890d8d1f42c938 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Sun, 19 May 2024 11:18:26 +0200
Subject: [PATCH 01/16] move request handling to seperate function

---
 table_stdio.c | 230 ++++++++++++++++++++++++++------------------------
 1 file changed, 121 insertions(+), 109 deletions(-)

diff --git a/table_stdio.c b/table_stdio.c
index 289deab..98b8df4 100644
--- a/table_stdio.c
+++ b/table_stdio.c
@@ -123,15 +123,132 @@ table_api_get_name(void)
 	return tablename;
 }
 
-int
-table_api_dispatch(void)
+static void
+handle_request(char *line, size_t linelen)
 {
 	char		 buf[LINE_MAX];
 	char		*t, *vers, *tname, *type, *service, *id, *key;
+	int		 sid, r;
+
+	t = line;
+	(void) linelen;
+
+	if (strncmp(t, "table|", 6))
+		errx(1, "malformed line");
+	t += 6;
+
+	vers = t;
+	if ((t = strchr(t, '|')) == NULL)
+		errx(1, "malformed line: missing version");
+	*t++ = '\0';
+
+	if (strcmp(vers, "0.1") != 0)
+		errx(1, "unsupported protocol version: %s", vers);
+
+	/* skip timestamp */
+	if ((t = strchr(t, '|')) == NULL)
+		errx(1, "malformed line: missing timestamp");
+	*t++ = '\0';
+
+	tname = t;
+	if ((t = strchr(t, '|')) == NULL)
+		errx(1, "malformed line: missing table name");
+	*t++ = '\0';
+	strlcpy(tablename, tname, sizeof(tablename));
+
+	type = t;
+	if ((t = strchr(t, '|')) == NULL)
+		errx(1, "malformed line: missing type");
+	*t++ = '\0';
+
+	if (!strcmp(type, "update")) {
+		if (handler_update == NULL)
+			errx(1, "no update handler registered");
+
+		id = t;
+		r = handler_update();
+		printf("update-result|%s|%s\n", id,
+		    r == -1 ? "error" : "ok");
+		if (fflush(stdout) == EOF)
+			err(1, "fflush");
+		return;
+	}
+
+	service = t;
+	if ((t = strchr(t, '|')) == NULL)
+		errx(1, "malformed line: missing service");
+	*t++ = '\0';
+	sid = service_id(service);
+
+	id = t;
+
+	r = -1;
+	if (!strcmp(type, "fetch")) {
+		if (handler_fetch == NULL)
+			errx(1, "no fetch handler registered");
+
+		if (registered_services & sid) {
+			r = handler_fetch(sid, &params,
+			    buf, sizeof(buf));
+		}
+		if (r == 1)
+			printf("fetch-result|%s|found|%s\n", id, buf);
+		else if (r == 0)
+			printf("fetch-result|%s|not-found\n", id);
+		else
+			printf("fetch-result|%s|error\n", id);
+		if (fflush(stdout) == EOF)
+			err(1, "fflush");
+		memset(buf, 0, sizeof(buf));
+		return;
+	}
+
+	if ((t = strchr(t, '|')) == NULL)
+		errx(1, "malformed line: missing key");
+	*t++ = '\0';
+	key = t;
+
+	if (!strcmp(type, "check")) {
+		if (handler_check == NULL)
+			errx(1, "no check handler registered");
+		if (registered_services & sid) {
+			r = handler_check(sid, &params, key);
+		}
+		if (r == 1)
+			printf("check-result|%s|found\n", id);
+		else if (r == 0)
+			printf("check-result|%s|not-found\n", id);
+		else
+			printf("check-result|%s|error\n", id);
+	} else if (!strcmp(type, "lookup")) {
+		if (handler_lookup == NULL)
+			errx(1, "no lookup handler registered");
+		if (registered_services & sid) {
+			r = handler_lookup(sid, &params, key,
+			    buf, sizeof(buf));
+		}
+		if (r == 1)
+			printf("lookup-result|%s|found|%s\n", id, buf);
+		else if (r == 0)
+			printf("lookup-result|%s|not-found\n", id);
+		else
+			printf("lookup-result|%s|error\n", id);
+		memset(buf, 0, sizeof(buf));
+	} else
+		errx(1, "unknown action %s", type);
+
+	if (fflush(stdout) == EOF)
+		err(1, "fflush");
+}
+
+int
+table_api_dispatch(void)
+{
+	char		*t;
 	char		*line = NULL;
 	size_t		 linesize = 0;
 	ssize_t		 linelen;
-	int		 sid, r, configured = 0;
+	int		 configured = 0;
 
 	dict_init(&params);
 
@@ -161,112 +278,7 @@ table_api_dispatch(void)
 			continue;
 		}
 
-		if (strncmp(t, "table|", 6))
-			errx(1, "malformed line");
-		t += 6;
-
-		vers = t;
-		if ((t = strchr(t, '|')) == NULL)
-			errx(1, "malformed line: missing version");
-		*t++ = '\0';
-
-		if (strcmp(vers, "0.1") != 0)
-			errx(1, "unsupported protocol version: %s", vers);
-
-		/* skip timestamp */
-		if ((t = strchr(t, '|')) == NULL)
-			errx(1, "malformed line: missing timestamp");
-		*t++ = '\0';
-
-		tname = t;
-		if ((t = strchr(t, '|')) == NULL)
-			errx(1, "malformed line: missing table name");
-		*t++ = '\0';
-		strlcpy(tablename, tname, sizeof(tablename));
-
-		type = t;
-		if ((t = strchr(t, '|')) == NULL)
-			errx(1, "malformed line: missing type");
-		*t++ = '\0';
-
-		if (!strcmp(type, "update")) {
-			if (handler_update == NULL)
-				errx(1, "no update handler registered");
-
-			id = t;
-			r = handler_update();
-			printf("update-result|%s|%s\n", id,
-			    r == -1 ? "error" : "ok");
-			if (fflush(stdout) == EOF)
-				err(1, "fflush");
-			continue;
-		}
-
-		service = t;
-		if ((t = strchr(t, '|')) == NULL)
-			errx(1, "malformed line: missing service");
-		*t++ = '\0';
-		sid = service_id(service);
-
-		id = t;
-
-		r = -1;
-		if (!strcmp(type, "fetch")) {
-			if (handler_fetch == NULL)
-				errx(1, "no fetch handler registered");
-
-			if (registered_services & sid) {
-				r = handler_fetch(sid, &params,
-				    buf, sizeof(buf));
-			}
-			if (r == 1)
-				printf("fetch-result|%s|found|%s\n", id, buf);
-			else if (r == 0)
-				printf("fetch-result|%s|not-found\n", id);
-			else
-				printf("fetch-result|%s|error\n", id);
-			if (fflush(stdout) == EOF)
-				err(1, "fflush");
-			memset(buf, 0, sizeof(buf));
-			continue;
-		}
-
-		if ((t = strchr(t, '|')) == NULL)
-			errx(1, "malformed line: missing key");
-		*t++ = '\0';
-		key = t;
-
-		if (!strcmp(type, "check")) {
-			if (handler_check == NULL)
-				errx(1, "no check handler registered");
-			if (registered_services & sid) {
-				r = handler_check(sid, &params, key);
-			}
-			if (r == 1)
-				printf("check-result|%s|found\n", id);
-			else if (r == 0)
-				printf("check-result|%s|not-found\n", id);
-			else
-				printf("check-result|%s|error\n", id);
-		} else if (!strcmp(type, "lookup")) {
-			if (handler_lookup == NULL)
-				errx(1, "no lookup handler registered");
-			if (registered_services & sid) {
-				r = handler_lookup(sid, &params, key,
-				    buf, sizeof(buf));
-			}
-			if (r == 1)
-				printf("lookup-result|%s|found|%s\n", id, buf);
-			else if (r == 0)
-				printf("lookup-result|%s|not-found\n", id);
-			else
-				printf("lookup-result|%s|error\n", id);
-			memset(buf, 0, sizeof(buf));
-		} else
-			errx(1, "unknown action %s", type);
-
-		if (fflush(stdout) == EOF)
-			err(1, "fflush");
+		handle_request(t, linelen);
 	}
 
 	if (ferror(stdin))
-- 
2.39.2

From 192aaef2de70fe0c1a3967eee545e375af2003ce Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Thu, 23 May 2024 07:09:05 +0200
Subject: [PATCH 02/16] add error result function

generic function to response with error
---
 table_stdio.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 table_stdio.h |  1 +
 2 files changed, 43 insertions(+)

diff --git a/table_stdio.c b/table_stdio.c
index 98b8df4..d244c7b 100644
--- a/table_stdio.c
+++ b/table_stdio.c
@@ -123,6 +123,48 @@ table_api_get_name(void)
 	return tablename;
 }
 
+void
+table_api_error(const char *id, const char *error)
+{
+	struct request  *req;
+
+	req = dict_pop(&requests, id);
+
+	if (!req) {
+		log_warnx("%s: unknow id %s", __func__, id);
+		return;
+	}
+
+	switch(req->o) {
+	case O_UPDATE:
+		printf("update-result|%s|error", id);
+		break;
+	case O_CHECK:
+		printf("check-result|%s|error", id);
+		break;
+	case O_LOOKUP:
+		dict_pop(&lookup_entries, id);
+		printf("lookup-result|%s|error", id);
+		break;
+	case O_FETCH:
+		printf("fetch-result|%s|error", id);
+		break;
+	}
+
+#ifdef errormassage
+	if (err && *err) {
+		puts(err);
+	} else {
+		puts("unknown");
+	}
+#else
+	(void)err;
+	puts("");
+#endif
+	if (fflush(stdout) == EOF)
+		err(1, "fflush");
+}
+
 static void
 handle_request(char *line, size_t linelen)
 {
diff --git a/table_stdio.h b/table_stdio.h
index 3afbbf2..ac6c725 100644
--- a/table_stdio.h
+++ b/table_stdio.h
@@ -34,4 +34,5 @@ void		 table_api_on_check(int(*)(int, struct dict *, const char *));
 void		 table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t));
 void		 table_api_on_fetch(int(*)(int, struct dict *, char *, size_t));
 int		 table_api_dispatch(void);
+void		 table_api_error(const char *, const char *);
 const char	*table_api_get_name(void);
-- 
2.39.2

From b9bbfa09e6644473dd5c814fbdf00ca4ce4cb03c Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Sun, 19 May 2024 15:02:24 +0200
Subject: [PATCH 03/16] implement async update

---
 table_stdio.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++-----
 table_stdio.h |  2 ++
 2 files changed, 80 insertions(+), 7 deletions(-)

diff --git a/table_stdio.c b/table_stdio.c
index d244c7b..9d040f4 100644
--- a/table_stdio.c
+++ b/table_stdio.c
@@ -22,12 +22,29 @@
 
 #include <err.h>
 #include <limits.h>
+#include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "dict.h"
+#include "log.h"
 #include "table_stdio.h"
 
+enum table_operation {
+	O_UPDATE,
+	O_CHECK,
+	O_LOOKUP,
+	O_FETCH,
+};
+
+struct request {
+	enum	 table_operation o;
+	char	*table;
+	enum	 table_service s;
+	char	*key;
+};
+
+static void (*handler_async_update)(const char *, const char *);
 static int (*handler_update)(void);
 static int (*handler_check)(int, struct dict *, const char *);
 static int (*handler_lookup)(int, struct dict *, const char *, char *, size_t);
@@ -44,6 +61,7 @@ static int		 registered_services = K_ANY;
 
 /* Dummy; just kept for backward compatibility */
 static struct dict	 params;
+static struct dict	 requests;
 
 static int
 service_id(const char *service)
@@ -87,6 +105,22 @@ table_api_service_name(enum table_service s)
 	}
 }
 
+static void
+fallback_update_handler(const char *id, const char *tname)
+{
+	int r;
+	strlcpy(tablename, tname, sizeof(tablename));
+
+	if (handler_update == NULL)
+		errx(1, "no update handler registered");
+
+	r = handler_update();
+	if (r == 1)
+		table_api_update_finish(id);
+	else
+		table_api_error(id, NULL);
+}
+
 void
 table_api_register_services(int s)
 {
@@ -99,6 +133,12 @@ table_api_on_update(int(*cb)(void))
 	handler_update = cb;
 }
 
+void
+table_api_on_update_async(void(*cb)(const char *, const char *))
+{
+	handler_async_update = cb;
+}
+
 void
 table_api_on_check(int(*cb)(int, struct dict *, const char *))
 {
@@ -124,6 +164,7 @@ table_api_get_name(void)
 }
 
 void
+
 table_api_error(const char *id, const char *error)
 {
 	struct request  *req;
@@ -165,12 +206,39 @@ table_api_error(const char *id, const char *error)
 		err(1, "fflush");
 }
 
+void
+table_api_update_finish(const char *id)
+{
+	struct request	*req;
+
+	req = dict_get(&requests, id);
+
+	if (!req) {
+		log_warnx("%s: unknow id %s", __func__, id);
+		return;
+	}
+
+	if (req->o != O_UPDATE) {
+		table_api_error(id, NULL);
+		return;
+	}
+
+	dict_pop(&requests, id);
+	free(req->table);
+	free(req);
+
+	printf("update-result|%s|ok\n", id);
+	if (fflush(stdout) == EOF)
+		err(1, "fflush");
+}
+
 static void
 handle_request(char *line, size_t linelen)
 {
 	char		 buf[LINE_MAX];
 	char		*t, *vers, *tname, *type, *service, *id, *key;
 	int		 sid, r;
+	struct request	 *req = calloc(1, sizeof(*req));
 
 	t = line;
 	(void) linelen;
@@ -196,7 +264,7 @@ handle_request(char *line, size_t linelen)
 	if ((t = strchr(t, '|')) == NULL)
 		errx(1, "malformed line: missing table name");
 	*t++ = '\0';
-	strlcpy(tablename, tname, sizeof(tablename));
+	req->table = strdup(tname);
 
 	type = t;
 	if ((t = strchr(t, '|')) == NULL)
@@ -204,15 +272,14 @@ handle_request(char *line, size_t linelen)
 	*t++ = '\0';
 
 	if (!strcmp(type, "update")) {
-		if (handler_update == NULL)
+		if (handler_async_update == NULL)
 			errx(1, "no update handler registered");
 
 		id = t;
-		r = handler_update();
-		printf("update-result|%s|%s\n", id,
-		    r == -1 ? "error" : "ok");
-		if (fflush(stdout) == EOF)
-			err(1, "fflush");
+		req->o = O_UPDATE;
+		dict_set(&requests, id, req);
+
+		handler_async_update(id, tname);
 		return;
 	}
 
@@ -293,6 +360,10 @@ table_api_dispatch(void)
 	int		 configured = 0;
 
 	dict_init(&params);
+	dict_init(&requests);
+
+	if (!handler_async_update)
+		table_api_on_update_async(fallback_update_handler);
 
 	while ((linelen = getline(&line, &linesize, stdin)) != -1) {
 		if (line[linelen - 1] == '\n')
diff --git a/table_stdio.h b/table_stdio.h
index ac6c725..3db5a78 100644
--- a/table_stdio.h
+++ b/table_stdio.h
@@ -30,9 +30,11 @@ enum table_service {
 
 void		 table_api_register_services(int);
 void		 table_api_on_update(int(*)(void));
+void		 table_api_on_update_async(void(*)(const char *, const char *));
 void		 table_api_on_check(int(*)(int, struct dict *, const char *));
 void		 table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t));
 void		 table_api_on_fetch(int(*)(int, struct dict *, char *, size_t));
 int		 table_api_dispatch(void);
 void		 table_api_error(const char *, const char *);
+void		 table_api_update_finish(const char *);
 const char	*table_api_get_name(void);
-- 
2.39.2

From a94fd453691fda9284760be338173d8c7f8acdbe Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Sun, 19 May 2024 16:27:52 +0200
Subject: [PATCH 04/16] implement async check

---
 table_stdio.c | 77 +++++++++++++++++++++++++++++++++++++++++++++------
 table_stdio.h |  2 ++
 2 files changed, 70 insertions(+), 9 deletions(-)

diff --git a/table_stdio.c b/table_stdio.c
index 9d040f4..7b1fc69 100644
--- a/table_stdio.c
+++ b/table_stdio.c
@@ -45,6 +45,7 @@ struct request {
 };
 
 static void (*handler_async_update)(const char *, const char *);
+static void (*handler_async_check)(const char *, const char *, int, const char *);
 static int (*handler_update)(void);
 static int (*handler_check)(int, struct dict *, const char *);
 static int (*handler_lookup)(int, struct dict *, const char *, char *, size_t);
@@ -121,6 +122,22 @@ fallback_update_handler(const char *id, const char *tname)
 		table_api_error(id, NULL);
 }
 
+static void
+fallback_check_handler(const char *id, const char *tname, int service, const char *key)
+{
+	int r;
+	strlcpy(tablename, tname, sizeof(tablename));
+
+	if (handler_check == NULL)
+		errx(1, "no check handler registered");
+
+	r = handler_check(service, &params, key);
+	if (r == 0 || r == 1)
+		table_api_check_result(id, r == 1);
+	else
+		table_api_error(id, NULL);
+}
+
 void
 table_api_register_services(int s)
 {
@@ -145,6 +162,12 @@ table_api_on_check(int(*cb)(int, struct dict *, const char *))
 	handler_check = cb;
 }
 
+void
+table_api_on_check_async(void(*cb)(const char *, const char *, int, const char *))
+{
+	handler_async_check = cb;
+}
+
 void
 table_api_on_lookup(int(*cb)(int, struct dict  *, const char *, char *, size_t))
 {
@@ -232,6 +255,37 @@ table_api_update_finish(const char *id)
 		err(1, "fflush");
 }
 
+void
+table_api_check_result(const char *id, bool found)
+{
+	struct request	*req;
+
+	req = dict_get(&requests, id);
+
+	if (!req) {
+		log_warnx("%s: unknow id %s", __func__, id);
+		return;
+	}
+
+	if (req->o != O_CHECK) {
+		table_api_error(id, NULL);
+		return;
+	}
+
+	dict_pop(&requests, id);
+	free(req->table);
+	free(req->key);
+	free(req);
+
+	if (found)
+		printf("check-result|%s|found\n", id);
+	else
+		printf("check-result|%s|not-found\n", id);
+
+	if (fflush(stdout) == EOF)
+		err(1, "fflush");
+}
+
 static void
 handle_request(char *line, size_t linelen)
 {
@@ -318,17 +372,20 @@ handle_request(char *line, size_t linelen)
 	key = t;
 
 	if (!strcmp(type, "check")) {
-		if (handler_check == NULL)
+		if (handler_async_check == NULL)
 			errx(1, "no check handler registered");
-		if (registered_services & sid) {
-			r = handler_check(sid, &params, key);
-		}
-		if (r == 1)
-			printf("check-result|%s|found\n", id);
-		else if (r == 0)
-			printf("check-result|%s|not-found\n", id);
-		else
+		if (!(registered_services & sid)) {
 			printf("check-result|%s|error\n", id);
+			if (fflush(stdout) == EOF)
+				err(1, "fflush");
+			return;
+		}
+		req->o = O_CHECK;
+		req->s = sid;
+		req->key = strdup(key);
+		dict_set(&requests, id, req);
+		handler_async_check(id, tname, sid, key);
+		return;
 	} else if (!strcmp(type, "lookup")) {
 		if (handler_lookup == NULL)
 			errx(1, "no lookup handler registered");
@@ -364,6 +421,8 @@ table_api_dispatch(void)
 
 	if (!handler_async_update)
 		table_api_on_update_async(fallback_update_handler);
+	if (!handler_async_check)
+		table_api_on_check_async(fallback_check_handler);
 
 	while ((linelen = getline(&line, &linesize, stdin)) != -1) {
 		if (line[linelen - 1] == '\n')
diff --git a/table_stdio.h b/table_stdio.h
index 3db5a78..022356f 100644
--- a/table_stdio.h
+++ b/table_stdio.h
@@ -32,9 +32,11 @@ void		 table_api_register_services(int);
 void		 table_api_on_update(int(*)(void));
 void		 table_api_on_update_async(void(*)(const char *, const char *));
 void		 table_api_on_check(int(*)(int, struct dict *, const char *));
+void		 table_api_on_check_async(void(*)(const char *, const char *, int, const char *));
 void		 table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t));
 void		 table_api_on_fetch(int(*)(int, struct dict *, char *, size_t));
 int		 table_api_dispatch(void);
 void		 table_api_error(const char *, const char *);
 void		 table_api_update_finish(const char *);
+void		 table_api_check_result(const char *, bool);
 const char	*table_api_get_name(void);
-- 
2.39.2

From 5196109b74bc84f8e45a3b5aeeacae0fee9ddf6f Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Mon, 20 May 2024 14:46:26 +0200
Subject: [PATCH 05/16] implement async lookup

---
 table_ldap.c  |   1 +
 table_stdio.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++----
 table_stdio.h |   3 +
 3 files changed, 143 insertions(+), 11 deletions(-)

diff --git a/table_ldap.c b/table_ldap.c
index bc8e1c3..bb460ae 100644
--- a/table_ldap.c
+++ b/table_ldap.c
@@ -25,6 +25,7 @@
 #include <netdb.h>
 
 #include <ctype.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
diff --git a/table_stdio.c b/table_stdio.c
index 7b1fc69..a62a44f 100644
--- a/table_stdio.c
+++ b/table_stdio.c
@@ -22,10 +22,13 @@
 
 #include <err.h>
 #include <limits.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 
+#include <event.h>
+
 #include "dict.h"
 #include "log.h"
 #include "table_stdio.h"
@@ -46,6 +49,7 @@ struct request {
 
 static void (*handler_async_update)(const char *, const char *);
 static void (*handler_async_check)(const char *, const char *, int, const char *);
+static void (*handler_async_lookup)(const char *, const char *, int, const char *);
 static int (*handler_update)(void);
 static int (*handler_check)(int, struct dict *, const char *);
 static int (*handler_lookup)(int, struct dict *, const char *, char *, size_t);
@@ -63,6 +67,7 @@ static int		 registered_services = K_ANY;
 /* Dummy; just kept for backward compatibility */
 static struct dict	 params;
 static struct dict	 requests;
+static struct dict	 lookup_entries;
 
 static int
 service_id(const char *service)
@@ -138,6 +143,26 @@ fallback_check_handler(const char *id, const char *tname, int service, const cha
 		table_api_error(id, NULL);
 }
 
+static void
+fallback_lookup_handler(const char *id, const char *tname, int service, const char *key)
+{
+	char buf[LINE_MAX];
+	int r;
+	strlcpy(tablename, tname, sizeof(tablename));
+
+	if (handler_lookup == NULL)
+		errx(1, "no lookup handler registered");
+
+	r = handler_lookup(service, &params, key, buf, sizeof(buf));
+	if (r == 1) {
+		table_api_lookup_result(id, buf);
+	}
+	if (r == 1 || r == 0)
+		table_api_lookup_finish(id);
+	else
+		table_api_error(id, NULL);
+}
+
 void
 table_api_register_services(int s)
 {
@@ -174,6 +199,12 @@ table_api_on_lookup(int(*cb)(int, struct dict  *, const char *, char *, size_t))
 	handler_lookup = cb;
 }
 
+void
+table_api_on_lookup_async(void(*cb)(const char *, const char *, int, const char *))
+{
+	handler_async_lookup = cb;
+}
+
 void
 table_api_on_fetch(int(*cb)(int, struct dict *, char *, size_t))
 {
@@ -286,6 +317,99 @@ table_api_check_result(const char *id, bool found)
 		err(1, "fflush");
 }
 
+void
+table_api_lookup_result(const char *id, const char *buf)
+{
+	const char alias_sep[] = ", ";
+	struct request	*req;
+	struct evbuffer *res;
+
+	req = dict_get(&requests, id);
+
+	if (!req) {
+		log_warnx("%s: unknow id %s", __func__, id);
+		return;
+	}
+
+	if (req->o != O_LOOKUP) {
+		table_api_error(id, NULL);
+		return;
+	}
+
+	res = dict_get(&lookup_entries, id);
+
+	if (!res) {
+		res = evbuffer_new();
+		if (!res) {
+			table_api_error(id, "can not alloc result");
+			return;
+		}
+		if (evbuffer_add(res, buf, strlen(buf)) == -1) {
+			table_api_error(id, "can not alloc result");
+			return;
+		}
+		dict_set(&lookup_entries, id, res);
+		return;
+	}
+	switch(req->s) {
+	case K_ALIAS:
+		if (evbuffer_add(res, alias_sep, sizeof(alias_sep)-1) == -1) {
+			table_api_error(id, "can not extend result");
+			return;
+		}
+		if (evbuffer_add(res, buf, 0) == -1) {
+			table_api_error(id, "can not extend result");
+			return;
+		}
+		break;
+	default:
+		log_warnx("id: %s lookup result override", id);
+		evbuffer_drain(res, evbuffer_get_length(res));
+		if (evbuffer_add(res, buf, sizeof(buf)) == -1) {
+			table_api_error(id, "can not alloc result");
+			return;
+		}
+	}
+}
+
+void
+table_api_lookup_finish(const char *id)
+{
+	struct request	*req;
+	struct evbuffer	*res;
+
+	req = dict_get(&requests, id);
+	if (!req) {
+		log_warnx("%s: unknow id %s", __func__, id);
+		return;
+	}
+	if (req->o != O_LOOKUP) {
+		table_api_error(id, NULL);
+		return;
+	}
+
+	res = dict_get(&lookup_entries, id);
+	if (res && evbuffer_get_length(res)) {
+		if (evbuffer_add(res, "\0", 1) == -1) {
+			table_api_error(id, "can not extend result");
+			return;
+		}
+		printf("lookup-result|%s|found|%s\n", id, evbuffer_pullup(res, -1));
+	} else {
+		printf("lookup-result|%s|not-found\n", id);
+	}
+
+	if (fflush(stdout) == EOF)
+		err(1, "fflush");
+	dict_pop(&requests, id);
+	free(req->table);
+	free(req->key);
+	free(req);
+	res = dict_pop(&lookup_entries, id);
+	if (res)
+		evbuffer_free(res);
+}
+
 static void
 handle_request(char *line, size_t linelen)
 {
@@ -387,19 +511,20 @@ handle_request(char *line, size_t linelen)
 		handler_async_check(id, tname, sid, key);
 		return;
 	} else if (!strcmp(type, "lookup")) {
-		if (handler_lookup == NULL)
+		if (handler_async_lookup == NULL)
 			errx(1, "no lookup handler registered");
-		if (registered_services & sid) {
-			r = handler_lookup(sid, &params, key,
-			    buf, sizeof(buf));
-		}
-		if (r == 1)
-			printf("lookup-result|%s|found|%s\n", id, buf);
-		else if (r == 0)
-			printf("lookup-result|%s|not-found\n", id);
-		else
+		if (!(registered_services & sid)) {
 			printf("lookup-result|%s|error\n", id);
-		memset(buf, 0, sizeof(buf));
+			if (fflush(stdout) == EOF)
+				err(1, "fflush");
+			return;
+		}
+		req->o = O_LOOKUP;
+		req->s = sid;
+		req->key = strdup(key);
+		dict_set(&requests, id, req);
+		handler_async_lookup(id, tname, sid, key);
+		return;
 	} else
 		errx(1, "unknown action %s", type);
 
@@ -418,11 +543,14 @@ table_api_dispatch(void)
 
 	dict_init(&params);
 	dict_init(&requests);
+	dict_init(&lookup_entries);
 
 	if (!handler_async_update)
 		table_api_on_update_async(fallback_update_handler);
 	if (!handler_async_check)
 		table_api_on_check_async(fallback_check_handler);
+	if (!handler_async_lookup)
+		table_api_on_lookup_async(fallback_lookup_handler);
 
 	while ((linelen = getline(&line, &linesize, stdin)) != -1) {
 		if (line[linelen - 1] == '\n')
diff --git a/table_stdio.h b/table_stdio.h
index 022356f..1313c6a 100644
--- a/table_stdio.h
+++ b/table_stdio.h
@@ -34,9 +34,12 @@ void		 table_api_on_update_async(void(*)(const char *, const char *));
 void		 table_api_on_check(int(*)(int, struct dict *, const char *));
 void		 table_api_on_check_async(void(*)(const char *, const char *, int, const char *));
 void		 table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t));
+void		 table_api_on_lookup_async(void(*)(const char *, const char *, int, const char *));
 void		 table_api_on_fetch(int(*)(int, struct dict *, char *, size_t));
 int		 table_api_dispatch(void);
 void		 table_api_error(const char *, const char *);
 void		 table_api_update_finish(const char *);
 void		 table_api_check_result(const char *, bool);
+void		 table_api_lookup_result(const char *, const char *);
+void		 table_api_lookup_finish(const char *);
 const char	*table_api_get_name(void);
-- 
2.39.2

From 5c6d587d7d746cbd54484988bb4a5cb6064a9938 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Mon, 20 May 2024 16:13:16 +0200
Subject: [PATCH 06/16] implement async fetch

---
 table_stdio.c | 85 +++++++++++++++++++++++++++++++++++++++++++--------
 table_stdio.h |  2 ++
 2 files changed, 74 insertions(+), 13 deletions(-)

diff --git a/table_stdio.c b/table_stdio.c
index a62a44f..7b39b74 100644
--- a/table_stdio.c
+++ b/table_stdio.c
@@ -50,6 +50,7 @@ struct request {
 static void (*handler_async_update)(const char *, const char *);
 static void (*handler_async_check)(const char *, const char *, int, const char *);
 static void (*handler_async_lookup)(const char *, const char *, int, const char *);
+static void (*handler_async_fetch)(const char *, const char *, int);
 static int (*handler_update)(void);
 static int (*handler_check)(int, struct dict *, const char *);
 static int (*handler_lookup)(int, struct dict *, const char *, char *, size_t);
@@ -163,6 +164,29 @@ fallback_lookup_handler(const char *id, const char *tname, int service, const ch
 		table_api_error(id, NULL);
 }
 
+static void
+fallback_fetch_handler(const char *id, const char *tname, int service)
+{
+	char buf[LINE_MAX];
+	int r;
+	strlcpy(tablename, tname, sizeof(tablename));
+
+	if (handler_fetch == NULL)
+		errx(1, "no fetch handler registered");
+
+	r = handler_fetch(service, &params, buf, sizeof(buf));
+	switch(r) {
+	case 1:
+		table_api_fetch_result(id, buf);
+		break;
+	case 0:
+		table_api_fetch_result(id, NULL);
+		break;
+	default:
+		table_api_error(id, NULL);
+	}
+}
+
 void
 table_api_register_services(int s)
 {
@@ -211,6 +235,12 @@ table_api_on_fetch(int(*cb)(int, struct dict *, char *, size_t))
 	handler_fetch = cb;
 }
 
+void
+table_api_on_fetch_async(void(*cb)(const char *, const char *, int))
+{
+	handler_async_fetch = cb;
+}
+
 const char *
 table_api_get_name(void)
 {
@@ -410,6 +440,35 @@ table_api_lookup_finish(const char *id)
 		evbuffer_free(res);
 }
 
+void
+table_api_fetch_result(const char *id, const char *buf)
+{
+	struct request	*req;
+
+	req = dict_get(&requests, id);
+
+	if (!req) {
+		log_warnx("%s: unknow id %s", __func__, id);
+		return;
+	}
+
+	if (req->o != O_FETCH) {
+		table_api_error(id, NULL);
+		return;
+	}
+
+	if (buf && *buf)
+		printf("fetch-result|%s|found|%s\n", id, buf);
+	else
+		printf("fetch-result|%s|not-found\n", id);
+
+	if (fflush(stdout) == EOF)
+		err(1, "fflush");
+	dict_pop(&requests, id);
+	free(req->table);
+	free(req);
+}
+
 static void
 handle_request(char *line, size_t linelen)
 {
@@ -471,22 +530,20 @@ handle_request(char *line, size_t linelen)
 
 	r = -1;
 	if (!strcmp(type, "fetch")) {
-		if (handler_fetch == NULL)
+		if (handler_async_fetch == NULL)
 			errx(1, "no fetch handler registered");
 
-		if (registered_services & sid) {
-			r = handler_fetch(sid, &params,
-			    buf, sizeof(buf));
+		if (!(registered_services & sid)) {
+			printf("check-result|%s|error\n", id);
+			if (fflush(stdout) == EOF)
+				err(1, "fflush");
+			return;
 		}
-		if (r == 1)
-			printf("fetch-result|%s|found|%s\n", id, buf);
-		else if (r == 0)
-			printf("fetch-result|%s|not-found\n", id);
-		else
-			printf("fetch-result|%s|error\n", id);
-		if (fflush(stdout) == EOF)
-			err(1, "fflush");
-		memset(buf, 0, sizeof(buf));
+		req->o = O_FETCH;
+		req->s = sid;
+		req->key = strdup(key);
+		dict_set(&requests, id, req);
+		handler_async_check(id, tname, sid, key);
 		return;
 	}
 
@@ -551,6 +608,8 @@ table_api_dispatch(void)
 		table_api_on_check_async(fallback_check_handler);
 	if (!handler_async_lookup)
 		table_api_on_lookup_async(fallback_lookup_handler);
+	if (!handler_async_fetch)
+		table_api_on_fetch_async(fallback_fetch_handler);
 
 	while ((linelen = getline(&line, &linesize, stdin)) != -1) {
 		if (line[linelen - 1] == '\n')
diff --git a/table_stdio.h b/table_stdio.h
index 1313c6a..fc720ef 100644
--- a/table_stdio.h
+++ b/table_stdio.h
@@ -36,10 +36,12 @@ void		 table_api_on_check_async(void(*)(const char *, const char *, int, const c
 void		 table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t));
 void		 table_api_on_lookup_async(void(*)(const char *, const char *, int, const char *));
 void		 table_api_on_fetch(int(*)(int, struct dict *, char *, size_t));
+void		 table_api_on_fetch_async(void(*)(const char *, const char *, int));
 int		 table_api_dispatch(void);
 void		 table_api_error(const char *, const char *);
 void		 table_api_update_finish(const char *);
 void		 table_api_check_result(const char *, bool);
 void		 table_api_lookup_result(const char *, const char *);
 void		 table_api_lookup_finish(const char *);
+void		 table_api_fetch_result(const char *, const char *);
 const char	*table_api_get_name(void);
-- 
2.39.2

From 42cac5fa452a4666450ca1a52c4a7f7bda4a88c4 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Mon, 20 May 2024 16:27:18 +0200
Subject: [PATCH 07/16] cleanup request handler

---
 table_stdio.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/table_stdio.c b/table_stdio.c
index 7b39b74..c1c096e 100644
--- a/table_stdio.c
+++ b/table_stdio.c
@@ -472,9 +472,8 @@ table_api_fetch_result(const char *id, const char *buf)
 static void
 handle_request(char *line, size_t linelen)
 {
-	char		 buf[LINE_MAX];
 	char		*t, *vers, *tname, *type, *service, *id, *key;
-	int		 sid, r;
+	int		 sid;
 	struct request	 *req = calloc(1, sizeof(*req));
 
 	t = line;
@@ -528,7 +527,6 @@ handle_request(char *line, size_t linelen)
 
 	id = t;
 
-	r = -1;
 	if (!strcmp(type, "fetch")) {
 		if (handler_async_fetch == NULL)
 			errx(1, "no fetch handler registered");
@@ -584,9 +582,6 @@ handle_request(char *line, size_t linelen)
 		return;
 	} else
 		errx(1, "unknown action %s", type);
-
-	if (fflush(stdout) == EOF)
-		err(1, "fflush");
 }
 
 int
-- 
2.39.2

From b903242dde498abb6718305aeb530dcbe6900553 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Mon, 20 May 2024 22:21:14 +0200
Subject: [PATCH 08/16] add deprication warnings

---
 table_stdio.h | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/table_stdio.h b/table_stdio.h
index fc720ef..0fa97c9 100644
--- a/table_stdio.h
+++ b/table_stdio.h
@@ -15,6 +15,12 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#if defined(__clang__) || defined(__GNUC__)
+#define DEPRECATED __attribute__((deprecated))
+#else
+#define DEPRECATED
+#endif
+
 enum table_service {
 	K_ALIAS =	0x001,	/* returns struct expand	*/
 	K_DOMAIN =	0x002,	/* returns struct destination	*/
@@ -29,11 +35,11 @@ enum table_service {
 };
 
 void		 table_api_register_services(int);
-void		 table_api_on_update(int(*)(void));
+void		 table_api_on_update(int(*)(void)) DEPRECATED;
 void		 table_api_on_update_async(void(*)(const char *, const char *));
-void		 table_api_on_check(int(*)(int, struct dict *, const char *));
+void		 table_api_on_check(int(*)(int, struct dict *, const char *)) DEPRECATED;
 void		 table_api_on_check_async(void(*)(const char *, const char *, int, const char *));
-void		 table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t));
+void		 table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t)) DEPRECATED;
 void		 table_api_on_lookup_async(void(*)(const char *, const char *, int, const char *));
 void		 table_api_on_fetch(int(*)(int, struct dict *, char *, size_t));
 void		 table_api_on_fetch_async(void(*)(const char *, const char *, int));
-- 
2.39.2

From 459dab0d8b0a496ce15c624f7bfd538b9c097e0d Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Mon, 20 May 2024 22:32:07 +0200
Subject: [PATCH 09/16] rename table_stdio to table_api

---
 Makefile.am                  | 4 ++--
 table_stdio.c => table_api.c | 2 +-
 table_stdio.h => table_api.h | 0
 table_ldap.c                 | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)
 rename table_stdio.c => table_api.c (99%)
 rename table_stdio.h => table_api.h (100%)

diff --git a/Makefile.am b/Makefile.am
index 5572b66..9132a4f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,11 +1,11 @@
 noinst_PROGRAMS =	table-ldap
 
-table_ldap_SOURCES =	table_ldap.c aldap.c ber.c dict.c log.c table_stdio.c util.c
+table_ldap_SOURCES =	table_ldap.c aldap.c ber.c dict.c log.c table_api.c util.c
 
 LDADD =			$(LIBOBJS)
 
 EXTRA_DIST =		aldap.h ber.h compat.h config.h.in \
-			dict.h log.h table_stdio.h util.h
+			dict.h log.h table_api.h util.h
 
 smtpdir =		${prefix}/libexec/smtpd
 
diff --git a/table_stdio.c b/table_api.c
similarity index 99%
rename from table_stdio.c
rename to table_api.c
index c1c096e..694be68 100644
--- a/table_stdio.c
+++ b/table_api.c
@@ -31,7 +31,7 @@
 
 #include "dict.h"
 #include "log.h"
-#include "table_stdio.h"
+#include "table_api.h"
 
 enum table_operation {
 	O_UPDATE,
diff --git a/table_stdio.h b/table_api.h
similarity index 100%
rename from table_stdio.h
rename to table_api.h
diff --git a/table_ldap.c b/table_ldap.c
index bb460ae..35ae7fb 100644
--- a/table_ldap.c
+++ b/table_ldap.c
@@ -34,7 +34,7 @@
 #include "aldap.h"
 #include "dict.h"
 #include "log.h"
-#include "table_stdio.h"
+#include "table_api.h"
 #include "util.h"
 
 #ifndef MAXIMUM
-- 
2.39.2

From f1492ea87f501c0949ff410ede064b9431842f82 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Fri, 31 May 2024 15:10:12 +0200
Subject: [PATCH 10/16] fixup! add error result function

---
 table_api.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/table_api.c b/table_api.c
index 694be68..9f0fe9c 100644
--- a/table_api.c
+++ b/table_api.c
@@ -288,6 +288,7 @@ table_api_error(const char *id, const char *error)
 #endif
 	if (fflush(stdout) == EOF)
 		err(1, "fflush");
+	free(req);
 }
 
 void
-- 
2.39.2

From ff1bf8dfe89dbf6a01e5a32c6530ca04b7287866 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Fri, 31 May 2024 15:11:09 +0200
Subject: [PATCH 11/16] fixup! implement async lookup

---
 table_api.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/table_api.c b/table_api.c
index 9f0fe9c..b5cce42 100644
--- a/table_api.c
+++ b/table_api.c
@@ -252,6 +252,7 @@ void
 table_api_error(const char *id, const char *error)
 {
 	struct request  *req;
+	struct evbuffer	*res;
 
 	req = dict_pop(&requests, id);
 
@@ -289,6 +290,9 @@ table_api_error(const char *id, const char *error)
 	if (fflush(stdout) == EOF)
 		err(1, "fflush");
 	free(req);
+	res = dict_pop(&lookup_entries, id);
+	if (res)
+		evbuffer_free(res);
 }
 
 void
-- 
2.39.2

From 417271b67020cb68693c749c49751c66894b643e Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Sun, 9 Jun 2024 13:32:06 +0200
Subject: [PATCH 12/16] fixup! implement async fetch

---
 table_api.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/table_api.c b/table_api.c
index b5cce42..b2fd0d6 100644
--- a/table_api.c
+++ b/table_api.c
@@ -544,9 +544,9 @@ handle_request(char *line, size_t linelen)
 		}
 		req->o = O_FETCH;
 		req->s = sid;
-		req->key = strdup(key);
+		req->key = NULL;
 		dict_set(&requests, id, req);
-		handler_async_check(id, tname, sid, key);
+		handler_async_fetch(id, tname, sid);
 		return;
 	}
 
-- 
2.39.2

From 978e58175873eb7db57a7b4b38a294bf5bb9492e Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Sun, 9 Jun 2024 13:27:52 +0200
Subject: [PATCH 13/16] switch to poll based api

untested
---
 table_api.c | 129 +++++++++++++++++++++++++++++++++++++++-------------
 table_api.h |   2 +
 2 files changed, 100 insertions(+), 31 deletions(-)

diff --git a/table_api.c b/table_api.c
index b2fd0d6..397c322 100644
--- a/table_api.c
+++ b/table_api.c
@@ -22,10 +22,12 @@
 
 #include <err.h>
 #include <limits.h>
+#include <poll.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <event.h>
 
@@ -33,6 +35,10 @@
 #include "log.h"
 #include "table_api.h"
 
+#ifndef MAXFDS
+#define MAXFDS 16
+#endif
+
 enum table_operation {
 	O_UPDATE,
 	O_CHECK,
@@ -56,6 +62,14 @@ static int (*handler_check)(int, struct dict *, const char *);
 static int (*handler_lookup)(int, struct dict *, const char *, char *, size_t);
 static int (*handler_fetch)(int, struct dict *, char *, size_t);
 
+static nfds_t		 nfds;
+static struct pollfd	 fds[MAXFDS];
+static fd_callback	 cbs[MAXFDS];
+
+static struct evbuffer	*inbuffer;
+
+static bool		 configured;
+
 static char		 tablename[128];
 
 /*
@@ -589,14 +603,72 @@ handle_request(char *line, size_t linelen)
 		errx(1, "unknown action %s", type);
 }
 
+static void
+api_callback(int fd, short revents)
+{
+	bool	 retry = true;
+	char	 buf[BUFSIZ];
+	size_t	 linelen;
+	int	 serrno, ret;
+	char	*line, *t;
+	
+
+	if (revents & (POLLERR|POLLNVAL)) {
+		exit(1);
+	}
+	if (revents & POLLHUP) {
+		exit(0);
+	}
+
+	do {
+		ret = read(fd, buf, sizeof(buf));
+		if (ret == 0) {
+			/* EOF */
+			exit(0);
+		}
+		if (ret < 0) {
+			serrno = errno;
+			if (serrno == EAGAIN || serrno == EWOULDBLOCK) {
+				retry = false;
+				continue;
+			}
+		}
+		evbuffer_add(inbuffer, buf, ret);
+	} while (retry);
+
+	while ((line = evbuffer_readline(inbuffer))) {
+		t = line;
+		linelen = strlen(line);
+		if (configured) {
+			handle_request(t, linelen);
+			free(line);
+			continue;
+		}
+
+		if (strncmp(t, "config|", 7) != 0)
+			errx(1, "unexpected config line: %s", line);
+		t += 7;
+
+		if (!strcmp(t, "ready")) {
+			configured = 1;
+
+			for (int s = K_ALIAS; s <= K_MAILADDRMAP; s <<= 1) {
+				printf("register|%s\n", table_api_service_name(s));
+			}
+
+			puts("register|ready");
+			if (fflush(stdout) == EOF)
+				err(1, "fflush");
+		}
+
+		free(line);
+	}
+}
+
 int
 table_api_dispatch(void)
 {
-	char		*t;
-	char		*line = NULL;
-	size_t		 linesize = 0;
-	ssize_t		 linelen;
-	int		 configured = 0;
+	int	 serrno, r;
 
 	dict_init(&params);
 	dict_init(&requests);
@@ -611,37 +683,32 @@ table_api_dispatch(void)
 	if (!handler_async_fetch)
 		table_api_on_fetch_async(fallback_fetch_handler);
 
-	while ((linelen = getline(&line, &linesize, stdin)) != -1) {
-		if (line[linelen - 1] == '\n')
-			line[--linelen] = '\0';
-		t = line;
-
-		if (!configured) {
-			if (strncmp(t, "config|", 7) != 0)
-				errx(1, "unexpected config line: %s", line);
-			t += 7;
-
-			if (!strcmp(t, "ready")) {
-				configured = 1;
-
-				for (int s = K_ALIAS; s <= K_MAILADDRMAP; s <<= 1) {
-					printf("register|%s\n", table_api_service_name(s));
-				}
-
-				puts("register|ready");
-				if (fflush(stdout) == EOF)
-					err(1, "fflush");
+	fds[0].fd = STDIN_FILENO;
+	fds[0].events = POLLIN;
+	fds[0].revents = 0;
+	cbs[0] = api_callback;
+	if (!nfds)
+		nfds++;
+
+	while (nfds) {
+		r = poll(fds, nfds, 1024);
+		if (r == 0) {
+			/* TODO implement some timeout handling */
+			continue;
+		}
+		if (r < 0) {
+			serrno = errno;
+			if (serrno == ENOMEM || serrno == EAGAIN) {
 				continue;
 			}
-
-			continue;
 		}
 
-		handle_request(t, linelen);
+		for (nfds_t i = 0; i < nfds; i++) {
+			if (fds[i].revents) {
+				cbs[i](fds[i].fd, fds[i].revents);
+			}
+		}
 	}
 
-	if (ferror(stdin))
-		err(1, "getline");
-
 	return (0);
 }
diff --git a/table_api.h b/table_api.h
index 0fa97c9..dd7310b 100644
--- a/table_api.h
+++ b/table_api.h
@@ -21,6 +21,8 @@
 #define DEPRECATED
 #endif
 
+typedef void (*fd_callback)(int, short);
+
 enum table_service {
 	K_ALIAS =	0x001,	/* returns struct expand	*/
 	K_DOMAIN =	0x002,	/* returns struct destination	*/
-- 
2.39.2

From f8cd9a0d8798047a99151ce0fbef0fb731ca3c78 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Sun, 9 Jun 2024 16:31:01 +0200
Subject: [PATCH 14/16] fixup! fixup! add error result function

---
 table_api.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/table_api.c b/table_api.c
index 397c322..ef77258 100644
--- a/table_api.c
+++ b/table_api.c
@@ -292,13 +292,13 @@ table_api_error(const char *id, const char *error)
 	}
 
 #ifdef errormassage
-	if (err && *err) {
-		puts(err);
+	if (error && *error) {
+		puts(error);
 	} else {
 		puts("unknown");
 	}
 #else
-	(void)err;
+	(void)error;
 	puts("");
 #endif
 	if (fflush(stdout) == EOF)
-- 
2.39.2

From eea7727c898ac76372571a985935385929ae5e59 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Sun, 9 Jun 2024 16:37:26 +0200
Subject: [PATCH 15/16] fixup! switch to poll based api

---
 table_api.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
 table_api.h |  3 +++
 2 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/table_api.c b/table_api.c
index ef77258..469a8c8 100644
--- a/table_api.c
+++ b/table_api.c
@@ -488,6 +488,49 @@ table_api_fetch_result(const char *id, const char *buf)
 	free(req);
 }
 
+void
+table_api_register_fd(int fd, short events, fd_callback cb)
+{
+	/* first fd is reservated for stdin */
+	if (!nfds)
+		nfds++;
+
+	if (nfds >= MAXFDS)
+		exit(1);
+
+	fds[nfds].fd = fd;
+	fds[nfds].events = events;
+	cbs[nfds] = cb;
+	nfds++;
+}
+
+void
+table_api_replace_fd(int old, int new)
+{
+	for (nfds_t i = 1; i < nfds; i++) {
+		if (fds[i].fd != old) {
+			continue;
+		}
+		fds[i].fd = new;
+	}
+}
+
+void
+table_api_remove_fd(int fd)
+{
+	for (nfds_t i = 1; i < nfds; i++) {
+		if (fds[i].fd != fd) {
+			continue;
+		}
+		nfds--;
+		if (i+1 > nfds) {
+			break;
+		}
+		memmove(fds+i, fds+i+1, (nfds-i)*sizeof(*fds));
+		memmove(cbs+i, cbs+i+1, (nfds-i)*sizeof(*cbs));
+	}
+}
+
 static void
 handle_request(char *line, size_t linelen)
 {
@@ -611,7 +654,6 @@ api_callback(int fd, short revents)
 	size_t	 linelen;
 	int	 serrno, ret;
 	char	*line, *t;
-	
 
 	if (revents & (POLLERR|POLLNVAL)) {
 		exit(1);
diff --git a/table_api.h b/table_api.h
index dd7310b..c21b5a5 100644
--- a/table_api.h
+++ b/table_api.h
@@ -53,3 +53,6 @@ void		 table_api_lookup_result(const char *, const char *);
 void		 table_api_lookup_finish(const char *);
 void		 table_api_fetch_result(const char *, const char *);
 const char	*table_api_get_name(void);
+void		 table_api_register_fd(int fd, short events, fd_callback cb);
+void		 table_api_replace_fd(int old, int new);
+void		 table_api_remove_fd(int fd);
-- 
2.39.2

From 6199916a1bcbf52f680c3ccd7683828fda93a338 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Tue, 9 Jul 2024 13:24:30 +0200
Subject: [PATCH 16/16] fixup! fixup! switch to poll based api

---
 table_api.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/table_api.c b/table_api.c
index 469a8c8..2c39b53 100644
--- a/table_api.c
+++ b/table_api.c
@@ -21,6 +21,7 @@
 #include <sys/tree.h>
 
 #include <err.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <poll.h>
 #include <stdbool.h>
@@ -711,6 +712,7 @@ int
 table_api_dispatch(void)
 {
 	int	 serrno, r;
+	int flags;
 
 	dict_init(&params);
 	dict_init(&requests);
@@ -725,6 +727,9 @@ table_api_dispatch(void)
 	if (!handler_async_fetch)
 		table_api_on_fetch_async(fallback_fetch_handler);
 
+	flags = fcntl(STDIN_FILENO, F_GETFL, 0);
+	fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
+
 	fds[0].fd = STDIN_FILENO;
 	fds[0].events = POLLIN;
 	fds[0].revents = 0;
-- 
2.39.2

Reply via email to