aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSeebs <seebs@seebs.net>2018-01-16 19:29:26 -0600
committerSeebs <seebs@seebs.net>2018-01-16 19:40:22 -0600
commit0a3e435085046f535074f498a3de75a7704fb14c (patch)
tree24199cdbe1d5cba07ca9f70c0bcb83af1bac4a2a
parent4a8a71f1e78860262e189ba36a19afca956b81c2 (diff)
downloadpseudo-0a3e435085046f535074f498a3de75a7704fb14c.tar.gz
pseudo-0a3e435085046f535074f498a3de75a7704fb14c.tar.bz2
pseudo-0a3e435085046f535074f498a3de75a7704fb14c.zip
Merge epoll support
Original commit message: The idea came up here: https://bugzilla.yoctoproject.org/show_bug.cgi?id=11309 and here: http://lists.openembedded.org/pipermail/openembedded-core/2017-August/141491.html Signed-off-by: Alexander Kanavin <alexander.kanavin@linux.intel.com> I've adapted this to make epoll a configure-time option; you must use --enable-epoll to get the new behavior. I've also confirmed that it builds both ways and appears to run, and restored the SIGUSR2 functionality (except for the state check) for the epoll case. Signed-off-by: Seebs <seebs@seebs.net>
-rw-r--r--ChangeLog.txt1
-rw-r--r--Makefile.in3
-rwxr-xr-xconfigure19
-rw-r--r--enums/exit_status.in3
-rw-r--r--pseudo_server.c241
5 files changed, 240 insertions, 27 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt
index 165faa8..1d98415 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -5,6 +5,7 @@
in getgrnam and similar functions.
* (seebs) drop the diagnostic for a missing "real" function
as it turns out to be counterproductive at best.
+ * (seebs) merge epoll support from <alexander.kanavin@linux.intel.com>
2017-12-22:
* (seebs) handle the pathological case of LINKAT with
diff --git a/Makefile.in b/Makefile.in
index 76d0518..6e42021 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -25,6 +25,7 @@ SQLITE=@SQLITE@
SQLITE_LIB=@SQLITE_LIB@
SQLITE_MEMORY=@SQLITE_MEMORY@
FORCE_ASYNC=@FORCE_ASYNC@
+EPOLL=@EPOLL@
XATTR=@XATTR@
XATTRDB=@XATTRDB@
PROFILING=@PROFILING@
@@ -54,7 +55,7 @@ LOCALSTATEDIR=$(PREFIX)/$(LOCALSTATE)
CFLAGS_BASE=-pipe -std=gnu99 -Wall -W -Wextra
CFLAGS_CODE=-fPIC -D_LARGEFILE64_SOURCE -D_ATFILE_SOURCE $(ARCH_FLAGS)
-CFLAGS_DEFS=-DPSEUDO_PREFIX='"$(PREFIX)"' -DPSEUDO_SUFFIX='"$(SUFFIX)"' -DPSEUDO_BINDIR='"$(BIN)"' -DPSEUDO_LIBDIR='"$(LIB)"' -DPSEUDO_LOCALSTATEDIR='"$(LOCALSTATE)"' -DPSEUDO_VERSION='"$(VERSION)"' $(SQLITE_MEMORY) $(FORCE_ASYNC) -DPSEUDO_PASSWD_FALLBACK='$(PASSWD_FALLBACK)' $(OPTDEFS)
+CFLAGS_DEFS=-DPSEUDO_PREFIX='"$(PREFIX)"' -DPSEUDO_SUFFIX='"$(SUFFIX)"' -DPSEUDO_BINDIR='"$(BIN)"' -DPSEUDO_LIBDIR='"$(LIB)"' -DPSEUDO_LOCALSTATEDIR='"$(LOCALSTATE)"' -DPSEUDO_VERSION='"$(VERSION)"' $(SQLITE_MEMORY) $(FORCE_ASYNC) -DPSEUDO_PASSWD_FALLBACK='$(PASSWD_FALLBACK)' $(OPTDEFS) $(EPOLL)
CFLAGS_DEBUG=-O2 -g
@DEFAULT_SQLITE@CFLAGS_SQL=-L$(SQLITE)/$(SQLITE_LIB) -I$(SQLITE)/include $(RPATH)
CFLAGS_PSEUDO=$(CFLAGS_BASE) $(CFLAGS_CODE) $(CFLAGS_DEFS) \
diff --git a/configure b/configure
index 22a0692..d7980d7 100755
--- a/configure
+++ b/configure
@@ -25,6 +25,7 @@ opt_arch=x86
opt_bits=
opt_sqlite=/usr
opt_rpath=
+opt_epoll=
opt_memory=
opt_async=
opt_xattr=
@@ -43,6 +44,7 @@ usage()
echo >&2 " [--libdir=...]"
echo >&2 " [--suffix=...]"
echo >&2 " [--enable-memory-db]"
+ echo >&2 " [--enable-epoll]"
echo >&2 " [--enable-xattr]"
echo >&2 " [--enable-xattrdb]"
echo >&2 " [--enable-profiling]"
@@ -116,6 +118,12 @@ do
--enable-profiling=yes | --enable-profiling)
opt_profiling=true
;;
+ --enable-epoll=no)
+ opt_epoll=false
+ ;;
+ --enable-epoll=yes | --enable-epoll)
+ opt_epoll=true
+ ;;
--enable-xattr=no)
opt_xattr=false
;;
@@ -255,6 +263,10 @@ if [ -z "$opt_async" ]; then
opt_async=false
fi
+if [ -z "$opt_epoll" ]; then
+ opt_epoll=false
+fi
+
if $opt_async; then
FORCE_ASYNC="-DPSEUDO_FORCE_ASYNC"
else
@@ -283,6 +295,12 @@ if [ -z "$opt_xattr" ]; then
fi
fi
+if $opt_epoll; then
+ EPOLL="-DPSEUDO_EPOLL"
+else
+ EPOLL=""
+fi
+
if $opt_xattr || $opt_xattrdb; then
if ! $xattr_runs; then
echo >&2 "WARNING: getfattr doesn't work, but xattr-related features requestd."
@@ -322,6 +340,7 @@ touch func_deps.mk
sed -e '
s,@PREFIX@,'"$opt_prefix"',g
+ s,@EPOLL@,'"$EPOLL"',g
s,@XATTR@,'"$opt_xattr"',g
s,@XATTRDB@,'"$opt_xattrdb"',g
s,@PROFILING@,'"$opt_profiling"',g
diff --git a/enums/exit_status.in b/enums/exit_status.in
index 6be44d3..88f94cd 100644
--- a/enums/exit_status.in
+++ b/enums/exit_status.in
@@ -18,3 +18,6 @@ listen_fd, "server loop had no valid listen fd"
pseudo_loaded, "server couldn't get out of pseudo environment"
pseudo_prefix, "couldn't get valid pseudo prefix"
pseudo_invocation, "invalid server command arguments"
+epoll_create, "epoll_create() failed"
+epoll_ctl, "epoll_ctl() failed"
+
diff --git a/pseudo_server.c b/pseudo_server.c
index 68433b3..8c6805b 100644
--- a/pseudo_server.c
+++ b/pseudo_server.c
@@ -32,6 +32,9 @@
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/wait.h>
+#ifdef PSEUDO_EPOLL
+#include <sys/epoll.h>
+#endif
#include <signal.h>
#include "pseudo.h"
@@ -59,6 +62,7 @@ static int active_clients = 0, highest_client = 0, max_clients = 0;
#define LOOP_DELAY 2
#define DEFAULT_PSEUDO_SERVER_TIMEOUT 30
+#define EPOLL_MAX_EVENTS 10
int pseudo_server_timeout = DEFAULT_PSEUDO_SERVER_TIMEOUT;
static int die_peacefully = 0;
static int die_forcefully = 0;
@@ -86,7 +90,11 @@ set_do_list_clients(int sig) {
static int messages = 0, responses = 0;
static struct timeval message_time = { .tv_sec = 0 };
+#ifdef PSEUDO_EPOLL
+static void pseudo_server_loop_epoll(void);
+#else
static void pseudo_server_loop(void);
+#endif
/* helper function to make a directory, just like mkdir -p.
* Can't use system() because the child shell would end up trying
@@ -375,12 +383,16 @@ pseudo_server_start(int daemonize) {
kill(ppid, SIGUSR1);
}
}
+#ifdef PSEUDO_EPOLL
+ pseudo_server_loop_epoll();
+#else
pseudo_server_loop();
+#endif
return 0;
}
/* mess with internal tables as needed */
-static void
+static unsigned int
open_client(int fd) {
pseudo_client_t *new_clients;
int i;
@@ -396,7 +408,7 @@ open_client(int fd) {
++active_clients;
if (i > highest_client)
highest_client = i;
- return;
+ return i;
}
}
@@ -420,9 +432,11 @@ open_client(int fd) {
max_clients += 16;
++active_clients;
+ return max_clients - 16;
} else {
pseudo_diag("error allocating new client, fd %d\n", fd);
close(fd);
+ return 0;
}
}
@@ -468,7 +482,7 @@ serve_client(int i) {
if (in) {
char *response_path = 0;
size_t response_pathlen;
- int send_response = 1;
+ int send_response = 1;
pseudo_debug(PDBGF_SERVER | PDBGF_VERBOSE, "got a message (%d): %s\n", in->type, (in->pathlen ? in->path : "<no path>"));
/* handle incoming ping */
if (in->type == PSEUDO_MSG_PING && !clients[i].pid) {
@@ -504,8 +518,8 @@ serve_client(int i) {
* pseudo_server_response.
*/
if (in->type != PSEUDO_MSG_SHUTDOWN) {
- if (in->type == PSEUDO_MSG_FASTOP)
- send_response = 0;
+ if (in->type == PSEUDO_MSG_FASTOP)
+ send_response = 0;
/* most messages don't need these, but xattr may */
response_path = 0;
response_pathlen = -1;
@@ -552,14 +566,14 @@ serve_client(int i) {
die_peacefully = 1;
}
}
- if (send_response) {
- if ((rc = pseudo_msg_send(clients[i].fd, in, in->pathlen, response_path)) != 0) {
- pseudo_debug(PDBGF_SERVER, "failed to send response to client %d [%d]: %d (%s)\n",
- i, (int) clients[i].pid, rc, strerror(errno));
- }
- } else {
- rc = 1;
- }
+ if (send_response) {
+ if ((rc = pseudo_msg_send(clients[i].fd, in, in->pathlen, response_path)) != 0) {
+ pseudo_debug(PDBGF_SERVER, "failed to send response to client %d [%d]: %d (%s)\n",
+ i, (int) clients[i].pid, rc, strerror(errno));
+ }
+ } else {
+ rc = 1;
+ }
free(response_path);
return rc;
} else {
@@ -572,6 +586,180 @@ serve_client(int i) {
}
}
+#ifdef PSEUDO_EPOLL
+static void pseudo_server_loop_epoll(void)
+{
+ struct sockaddr_un client;
+ socklen_t len;
+ int i;
+ int rc;
+ int fd;
+ int timeout;
+ struct epoll_event ev, events[EPOLL_MAX_EVENTS];
+ int loop_timeout = pseudo_server_timeout;
+ struct sigaction eat_usr2 = {
+ .sa_handler = set_do_list_clients
+ };
+
+ clients = malloc(16 * sizeof(*clients));
+
+ sigaction(SIGUSR2, &eat_usr2, NULL);
+
+ clients[0].fd = listen_fd;
+ clients[0].pid = getpid();
+
+ for (i = 1; i < 16; ++i) {
+ clients[i].fd = -1;
+ clients[i].pid = 0;
+ clients[i].tag = NULL;
+ clients[i].program = NULL;
+ }
+
+ active_clients = 1;
+ max_clients = 16;
+ highest_client = 0;
+
+ pseudo_debug(PDBGF_SERVER, "server loop started.\n");
+ if (listen_fd < 0) {
+ pseudo_diag("got into loop with no valid listen fd.\n");
+ exit(PSEUDO_EXIT_LISTEN_FD);
+ }
+
+ timeout = LOOP_DELAY * 1000;
+
+ int epollfd = epoll_create1(0);
+ if (epollfd == -1) {
+ pseudo_diag("epoll_create1() failed.\n");
+ exit(PSEUDO_EXIT_EPOLL_CREATE);
+ }
+ ev.events = EPOLLIN;
+ ev.data.u64 = 0;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, clients[0].fd, &ev) == -1) {
+ pseudo_diag("epoll_ctl() failed with listening socket.\n");
+ exit(PSEUDO_EXIT_EPOLL_CTL);
+ }
+
+ pdb_log_msg(SEVERITY_INFO, NULL, NULL, NULL, "server started (pid %d)", getpid());
+
+ for (;;) {
+ rc = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, timeout);
+ if (rc == 0 || (rc == -1 && errno == EINTR)) {
+ /* If there's no clients, start timing out. If there
+ * are active clients, never time out.
+ */
+ if (active_clients == 1) {
+ loop_timeout -= LOOP_DELAY;
+ /* maybe flush database to disk */
+ pdb_maybe_backup();
+ if (loop_timeout <= 0) {
+ pseudo_debug(PDBGF_SERVER, "no more clients, got bored.\n");
+ die_peacefully = 1;
+ } else {
+ /* display this if not exiting */
+ pseudo_debug(PDBGF_SERVER | PDBGF_BENCHMARK, "%d messages handled in %.4f seconds, %d responses\n",
+ messages,
+ (double) message_time.tv_sec +
+ (double) message_time.tv_usec / 1000000.0,
+ responses);
+ }
+ }
+ } else if (rc > 0) {
+ loop_timeout = pseudo_server_timeout;
+ for (i = 0; i < rc; ++i) {
+ if (clients[events[i].data.u64].fd == listen_fd) {
+ if (!die_forcefully) {
+ len = sizeof(client);
+ if ((fd = accept(listen_fd, (struct sockaddr *) &client, &len)) != -1) {
+ /* Don't allow clients to end up on fd 2, because glibc's
+ * malloc debug uses that fd unconditionally.
+ */
+ if (fd == 2) {
+ int newfd = fcntl(fd, F_DUPFD, 3);
+ close(fd);
+ fd = newfd;
+ }
+ pseudo_debug(PDBGF_SERVER, "new client fd %d\n", fd);
+ /* A new client implicitly cancels any
+ * previous shutdown request, or a
+ * shutdown for lack of clients.
+ */
+ pseudo_server_timeout = DEFAULT_PSEUDO_SERVER_TIMEOUT;
+ die_peacefully = 0;
+
+ ev.events = EPOLLIN;
+ ev.data.u64 = open_client(fd);
+ if (ev.data.u64 != 0 && epoll_ctl(epollfd, EPOLL_CTL_ADD, clients[ev.data.u64].fd, &ev) == -1) {
+ pseudo_diag("epoll_ctl() failed with accepted socket.\n");
+ exit(PSEUDO_EXIT_EPOLL_CTL);
+ }
+ } else if (errno == EMFILE) {
+ // select() loop would drop a client here, we do nothing (for now)
+ pseudo_debug(PDBGF_SERVER, "Hit max open files.\n");
+ }
+ }
+ } else {
+ struct timeval tv1, tv2;
+ int rc;
+ gettimeofday(&tv1, NULL);
+ rc = serve_client(events[i].data.u64);
+ gettimeofday(&tv2, NULL);
+ ++messages;
+ if (rc == 0)
+ ++responses;
+ message_time.tv_sec += (tv2.tv_sec - tv1.tv_sec);
+ message_time.tv_usec += (tv2.tv_usec - tv1.tv_usec);
+ if (message_time.tv_usec < 0) {
+ message_time.tv_usec += 1000000;
+ --message_time.tv_sec;
+ } else while (message_time.tv_usec > 1000000) {
+ message_time.tv_usec -= 1000000;
+ ++message_time.tv_sec;
+ }
+ }
+ if (die_forcefully)
+ break;
+ }
+ pseudo_debug(PDBGF_SERVER, "server loop complete [%d clients left]\n", active_clients);
+ } else {
+ pseudo_diag("epoll_wait failed: %s\n", strerror(errno));
+ break;
+ }
+ if (do_list_clients) {
+ do_list_clients = 0;
+ pseudo_diag("listing clients [1 through %d]:\n", highest_client);
+ for (i = 1; i <= highest_client; ++i) {
+ if (clients[i].fd == -1) {
+ pseudo_diag("client %4d: inactive.\n", i);
+ continue;
+ }
+ pseudo_diag("client %4d: fd %4d, pid %5d, program %s\n",
+ i, clients[i].fd, clients[i].pid,
+ clients[i].program ? clients[i].program : "<unspecified>");
+ }
+ pseudo_diag("done.\n");
+ }
+ if (die_peacefully || die_forcefully) {
+ pseudo_debug(PDBGF_SERVER, "quitting.\n");
+ pseudo_debug(PDBGF_SERVER | PDBGF_BENCHMARK, "server %d exiting: handled %d messages in %.4f seconds\n",
+ getpid(), messages,
+ (double) message_time.tv_sec +
+ (double) message_time.tv_usec / 1000000.0);
+ pdb_log_msg(SEVERITY_INFO, NULL, NULL, NULL, "server %d exiting: handled %d messages in %.4f seconds",
+ getpid(), messages,
+ (double) message_time.tv_sec +
+ (double) message_time.tv_usec / 1000000.0);
+ /* and at this point, we'll start refusing connections */
+ close(clients[0].fd);
+ /* This is a good place to insert a delay for
+ * debugging race conditions during startup. */
+ /* usleep(300000); */
+ exit(0);
+ }
+ }
+
+}
+#else
+
/* get clients, handle messages, shut down.
* This doesn't actually do any work, it just calls a ton of things which
* do work.
@@ -639,8 +827,8 @@ pseudo_server_loop(void) {
*/
if (active_clients == 1) {
loop_timeout -= LOOP_DELAY;
- /* maybe flush database to disk */
- pdb_maybe_backup();
+ /* maybe flush database to disk */
+ pdb_maybe_backup();
if (loop_timeout <= 0) {
pseudo_debug(PDBGF_SERVER, "no more clients, got bored.\n");
die_peacefully = 1;
@@ -650,7 +838,7 @@ pseudo_server_loop(void) {
messages,
(double) message_time.tv_sec +
(double) message_time.tv_usec / 1000000.0,
- responses);
+ responses);
}
}
} else if (rc > 0) {
@@ -663,13 +851,13 @@ pseudo_server_loop(void) {
close_client(i);
} else if (FD_ISSET(clients[i].fd, &reads)) {
struct timeval tv1, tv2;
- int rc;
+ int rc;
gettimeofday(&tv1, NULL);
rc = serve_client(i);
gettimeofday(&tv2, NULL);
++messages;
- if (rc == 0)
- ++responses;
+ if (rc == 0)
+ ++responses;
message_time.tv_sec += (tv2.tv_sec - tv1.tv_sec);
message_time.tv_usec += (tv2.tv_usec - tv1.tv_usec);
if (message_time.tv_usec < 0) {
@@ -698,12 +886,12 @@ pseudo_server_loop(void) {
}
pseudo_debug(PDBGF_SERVER, "new client fd %d\n", fd);
open_client(fd);
- /* A new client implicitly cancels any
- * previous shutdown request, or a
- * shutdown for lack of clients.
- */
- pseudo_server_timeout = DEFAULT_PSEUDO_SERVER_TIMEOUT;
- die_peacefully = 0;
+ /* A new client implicitly cancels any
+ * previous shutdown request, or a
+ * shutdown for lack of clients.
+ */
+ pseudo_server_timeout = DEFAULT_PSEUDO_SERVER_TIMEOUT;
+ die_peacefully = 0;
}
}
pseudo_debug(PDBGF_SERVER, "server loop complete [%d clients left]\n", active_clients);
@@ -718,7 +906,7 @@ pseudo_server_loop(void) {
}
pseudo_diag("client %4d: fd %4d, pid %5d, state %s, program %s\n",
i, clients[i].fd, clients[i].pid,
- FD_ISSET(clients[i].fd, &reads) ? "R" : "-",
+ FD_ISSET(clients[i].fd, &reads) ? "R" : "-",
clients[i].program ? clients[i].program : "<unspecified>");
}
pseudo_diag("done.\n");
@@ -770,3 +958,4 @@ pseudo_server_loop(void) {
}
pseudo_diag("select failed: %s\n", strerror(errno));
}
+#endif /* this is the else of #ifdef PSEUDO_EPOLL */