aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 */