Initial commit

This commit is contained in:
Stefan Bühler 2008-09-20 11:43:01 +02:00
commit c81c68f300
9 changed files with 585 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*build
Doxyfile
*kdev*
*~

20
CMakeLists.txt Normal file
View File

@ -0,0 +1,20 @@
PROJECT(fcgi-debug C)
CMAKE_MINIMUM_REQUIRED(VERSION 2.4.0 FATAL_ERROR)
SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
SET(CPACK_PACKAGE_VERSION_MAJOR 2)
SET(CPACK_PACKAGE_VERSION_MINOR 0)
SET(CPACK_PACKAGE_VERSION_PATCH 0)
#SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING")
#SET(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README")
SET(CPACK_PACKAGE_VENDOR "lighttpd@stbuehler.de")
SET(CPACK_SOURCE_GENERATOR "TGZ")
SET(CPACK_SOURCE_IGNORE_FILES "/\\\\.;~$;/_;build/;CMakeFiles/;CMakeCache;gz$;Makefile\\\\.;trace;Testing/;foo;autom4te;cmake_install;CPack;\\\\.pem;ltmain.sh;configure;libtool;/config\\\\.;missing;autogen.sh;install-sh;Dart;aclocal;log$;Makefile$")
SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
ADD_SUBDIRECTORY(src build)

View File

@ -0,0 +1,14 @@
MACRO(ADD_TARGET_PROPERTIES _target _name)
SET(_properties)
FOREACH(_prop ${ARGN})
SET(_properties "${_properties} ${_prop}")
ENDFOREACH(_prop)
GET_TARGET_PROPERTY(_old_properties ${_target} ${_name})
MESSAGE("adding property to ${_target} ${_name}:" ${_properties})
IF(NOT _old_properties)
# in case it's NOTFOUND
SET(_old_properties)
ENDIF(NOT _old_properties)
SET_TARGET_PROPERTIES(${_target} PROPERTIES ${_name} "${_old_properties} ${_properties}")
ENDMACRO(ADD_TARGET_PROPERTIES)

65
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,65 @@
INCLUDE(CheckIncludeFiles)
INCLUDE(CheckFunctionExists)
INCLUDE(CheckVariableExists)
INCLUDE(CheckTypeSize)
INCLUDE(CheckLibraryExists)
INCLUDE(CMakeDetermineCCompiler)
INCLUDE(FindThreads)
INCLUDE(CPack)
INCLUDE(FindPkgConfig)
INCLUDE(LighttpdMacros)
cmake_policy(SET CMP0005 OLD)
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES)
# libev
CHECK_INCLUDE_FILES(ev.h HAVE_EV_H)
IF(HAVE_EV_H)
CHECK_LIBRARY_EXISTS(ev ev_loop "" HAVE_LIBEV)
IF(HAVE_LIBEV)
SET(EV_LDFLAGS -lev)
ELSE(HAVE_LIBEV)
MESSAGE(FATAL_ERROR "Couldn't find lib ev")
ENDIF(HAVE_LIBEV)
ELSE(HAVE_EV_H)
MESSAGE(FATAL_ERROR "Couldn't find <ev.h>")
ENDIF(HAVE_EV_H)
# glib/gthread
pkg_check_modules(GTHREAD REQUIRED gthread-2.0)
#INCLUDE_DIRECTORIES(${GTHREAD_INCLUDE_DIRS})
ADD_DEFINITIONS(
-DPACKAGE_NAME="\\"${CMAKE_PROJECT_NAME}\\""
-DPACKAGE_VERSION="\\"${CPACK_PACKAGE_VERSION}\\""
)
## Write out config.h
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
ADD_DEFINITIONS(-DHAVE_CONFIG_H)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
ADD_EXECUTABLE(fcgi-debug
fcgi-debug.c
connection.c
tools.c
)
ADD_TARGET_PROPERTIES(fcgi-debug LINK_FLAGS ${EV_LDFLAGS})
ADD_TARGET_PROPERTIES(fcgi-debug COMPILE_FLAGS ${EV_CFLAGS})
IF(CMAKE_COMPILER_IS_GNUCC)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -g -Wshadow -W -pedantic")
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_WITHDEBINFO} -O2")
ADD_DEFINITIONS(-D_GNU_SOURCE)
ENDIF(CMAKE_COMPILER_IS_GNUCC)
ADD_TARGET_PROPERTIES(fcgi-debug LINK_FLAGS ${GTHREAD_LDFLAGS})
ADD_TARGET_PROPERTIES(fcgi-debug COMPILE_FLAGS ${GTHREAD_CFLAGS})
SET_TARGET_PROPERTIES(fcgi-debug PROPERTIES CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})

0
src/config.h.cmake Normal file
View File

174
src/connection.c Normal file
View File

@ -0,0 +1,174 @@
#include "fcgi-debug.h"
static void connection_close(connection *con) {
ev_io_stop(con->srv->loop, &con->w_server);
ev_io_stop(con->srv->loop, &con->w_client);
if (-1 != con->fd_server) {
shutdown(con->fd_server, SHUT_RDWR);
close(con->fd_server);
}
if (-1 != con->fd_client) {
shutdown(con->fd_client, SHUT_RDWR);
close(con->fd_client);
}
g_string_free(con->send_client_buf, TRUE);
g_string_free(con->send_server_buf, TRUE);
g_slice_free(connection, con);
}
static gboolean connection_connect(connection *con) {
server *srv = con->srv;
if (con->client_connected) return TRUE;
ev_io_start(srv->loop, &srv->w_accept);
if (-1 == connect(con->fd_client, srv->client.saddr, srv->client.addr_len)) {
switch (errno) {
case EALREADY:
case EINPROGRESS:
case EINTR:
ev_io_stop(srv->loop, &srv->w_accept); /* no new connections until we have a new connection to the client */
ev_io_set_events(srv->loop, &con->w_client, EV_WRITE | EV_READ);
break;
default:
g_warning("couldn't connect: %s", g_strerror(errno));
connection_close(con);
}
return FALSE;
} else {
con->client_connected = TRUE;
ev_io_set_events(srv->loop, &con->w_client, EV_READ);
ev_io_set_events(srv->loop, &con->w_server, EV_READ);
return TRUE;
}
}
static char readbuf[4096];
static void fd_server_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
connection *con = (connection*) w->data;
server *srv = con->srv;
UNUSED(loop);
if (revents & EV_READ) {
int l = read(w->fd, readbuf, sizeof(readbuf));
switch (l) {
case -1:
switch (errno) {
case EAGAIN:
case EINTR:
break;
default:
g_warning("couldn't read from server: %s", g_strerror(errno));
connection_close(con);
}
return;
case 0:
/* end of file */
connection_close(con);
return;
default:
break;
}
g_string_append_len(con->send_client_buf, readbuf, l);
if (con->send_client_buf->len > 4*4096) ev_io_rem_events(srv->loop, w, EV_READ);
ev_io_add_events(srv->loop, &con->w_client, EV_WRITE);
}
if (revents & EV_WRITE) {
if (con->send_server_buf->len > 0) {
int l = write(w->fd, con->send_server_buf->str, con->send_server_buf->len);
switch (l) {
case -1:
switch (errno) {
case EAGAIN:
case EINTR:
break;
default:
g_warning("couldn't write to server: %s", g_strerror(errno));
connection_close(con);
}
return;
}
g_string_erase(con->send_server_buf, 0, l);
if (con->send_server_buf->len < 4*4096) ev_io_add_events(srv->loop, &con->w_server, EV_READ);
}
if (con->send_server_buf->len == 0) ev_io_rem_events(srv->loop, w, EV_WRITE);
}
}
static void fd_client_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
connection *con = (connection*) w->data;
server *srv = con->srv;
UNUSED(loop);
if (!connection_connect(con)) return;
if (revents & EV_READ) {
int l = read(w->fd, readbuf, sizeof(readbuf));
switch (l) {
case -1:
switch (errno) {
case EAGAIN:
case EINTR:
break;
default:
g_warning("couldn't read from client: %s", g_strerror(errno));
connection_close(con);
}
return;
case 0:
/* end of file */
connection_close(con);
return;
default:
break;
}
g_string_append_len(con->send_server_buf, readbuf, l);
if (con->send_server_buf->len > 4*4096) ev_io_rem_events(srv->loop, w, EV_READ);
ev_io_add_events(srv->loop, &con->w_server, EV_WRITE);
}
if (revents & EV_WRITE) {
if (con->send_client_buf->len > 0) {
int l = write(w->fd, con->send_client_buf->str, con->send_client_buf->len);
switch (l) {
case -1:
switch (errno) {
case EAGAIN:
case EINTR:
break;
default:
g_warning("couldn't write to client: %s", g_strerror(errno));
connection_close(con);
}
return;
}
g_string_erase(con->send_client_buf, 0, l);
if (con->send_client_buf->len < 4*4096) ev_io_add_events(srv->loop, &con->w_client, EV_READ);
}
if (con->send_client_buf->len == 0) ev_io_rem_events(srv->loop, w, EV_WRITE);
}
}
void connection_new(server *srv, int fd_server) {
connection *con;
int fd_client;
fd_init(fd_server);
con = g_slice_new0(connection);
con->srv = srv;
con->fd_server = fd_server;
con->fd_client = -1;
ev_io_init(&con->w_server, fd_server_cb, fd_server, 0);
con->w_server.data = con;
ev_io_init(&con->w_client, fd_client_cb, -1, 0);
con->w_client.data = con;
con->send_client_buf = g_string_sized_new(0);
con->send_server_buf = g_string_sized_new(0);
if (-1 == (fd_client = socket(AF_UNIX, SOCK_STREAM, 0))) {
g_warning("couldn't create socket: %s", g_strerror(errno));
connection_close(con);
}
fd_init(fd_client);
con->fd_client = fd_client;
ev_io_set(&con->w_client, fd_client, EV_WRITE | EV_READ);
con->client_connected = FALSE;
connection_connect(con);
}

187
src/fcgi-debug.c Normal file
View File

@ -0,0 +1,187 @@
#include "fcgi-debug.h"
static void accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
server *srv = (server*) w->data;
int fd;
UNUSED(loop);
UNUSED(revents);
if (-1 == (fd = accept(w->fd, NULL, NULL))) {
g_error("Couldn't accept: %s", g_strerror(errno));
}
connection_new(srv, fd);
}
#define CATCH_SIGNAL(loop, cb, n) do {\
ev_init(&srv->sig_w_##n, cb); \
ev_signal_set(&srv->sig_w_##n, SIG##n); \
ev_signal_start(loop, &srv->sig_w_##n); \
srv->sig_w_##n.data = srv; \
ev_unref(loop); /* Signal watchers shouldn't keep loop alive */ \
} while (0)
#define UNCATCH_SIGNAL(loop, n) do {\
ev_ref(loop); \
ev_signal_stop(loop, &srv->sig_w_##n); \
} while (0)
void server_stop(server *srv) {
if (srv->tmpfile_name) {
unlink(srv->tmpfile_name);
g_free(srv->tmpfile_name);
srv->tmpfile_name = NULL;
}
if (srv->sockfile_name) {
unlink(srv->sockfile_name);
g_free(srv->sockfile_name);
srv->sockfile_name = NULL;
}
ev_io_stop(srv->loop, &srv->w_accept);
close(0);
if (!srv->exiting) {
if (-1 != srv->child) kill(srv->child, SIGINT);
} else {
if (-1 != srv->child) kill(srv->child, SIGTERM);
if (!srv->stopped_signals) {
srv->stopped_signals = TRUE;
/* reset default behaviour which will kill us next time */
UNCATCH_SIGNAL(srv->loop, INT);
UNCATCH_SIGNAL(srv->loop, TERM);
UNCATCH_SIGNAL(srv->loop, PIPE);
UNCATCH_SIGNAL(srv->loop, HUP);
}
}
}
static void child_cb(struct ev_loop *loop, struct ev_child *w, int revents) {
server *srv = (server*) w->data;
UNUSED(revents);
ev_child_stop(loop, w);
g_message ("process %d exited with status %d", w->rpid, w->rstatus);
srv->child = -1;
server_stop(srv);
}
static void sigint_cb(struct ev_loop *loop, struct ev_signal *w, int revents) {
server *srv = (server*) w->data;
UNUSED(loop);
UNUSED(revents);
if (!srv->exiting) {
g_message("Got signal, shutdown");
} else if (w->signum != SIGINT && w->signum != SIGTERM) {
return; /* ignore */
} else {
g_message("Got second signal, force shutdown");
}
server_stop(srv);
if (w->signum == SIGINT || w->signum == SIGTERM) srv->exiting = TRUE;
}
static void sigpipe_cb(struct ev_loop *loop, struct ev_signal *w, int revents) {
/* ignore */
UNUSED(loop); UNUSED(w); UNUSED(revents);
}
void create_tmp_addr(server *srv) {
gchar *fn = g_strdup("/tmp/fcgi-debug.XXXXXX");
int fd;
struct sockaddr_un *sun;
gsize slen = strlen(fn) + sizeof(".sock") - 1, len = 1 + slen + (gsize) (((struct sockaddr_un *) 0)->sun_path);
sun = (struct sockaddr_un*) g_malloc0(len);
sun->sun_family = AF_UNIX;
if (-1 == (fd = mkstemp(fn))) {
g_error("Couldn't make a tmpfile name");
}
close(fd);
strcpy(sun->sun_path, fn);
strcat(sun->sun_path, ".sock");
srv->sockfile_name = g_strdup(sun->sun_path);
srv->tmpfile_name = fn;
srv->client.saddr = (struct sockaddr*) sun;
srv->client.addr_len = len;
}
int client_bind(server *srv) {
int s;
if (-1 == (s = socket(AF_UNIX, SOCK_STREAM, 0))) {
g_error("Couldn't create socket: %s", g_strerror(errno));
}
if (-1 == bind(s, srv->client.saddr, srv->client.addr_len)) {
g_error("Couldn't bind socket: %s", g_strerror(errno));
}
if (-1 == listen(s, 1024)) {
g_error("Couldn't listen on socket: %s", g_strerror(errno));
}
return s;
}
pid_t spawn(char **argv, int s) {
pid_t child;
switch (child = fork()) {
case -1:
g_error("Fork failed: %s", g_strerror(errno));
break;
case 0: /* child */
setsid();
move2fd(s, 0);
execv(argv[1], argv+1);
g_error("execv failed: %s", g_strerror(errno));
break;
default:
close(s);
break;
}
return child;
}
int main(int argc, char **argv) {
server *srv;
int s;
pid_t ch;
UNUSED(argc);
srv = g_slice_new0(server);
srv->exiting = FALSE;
srv->child = -1;
srv->loop = ev_default_loop (0);
CATCH_SIGNAL(srv->loop, sigint_cb, INT);
CATCH_SIGNAL(srv->loop, sigint_cb, TERM);
CATCH_SIGNAL(srv->loop, sigint_cb, HUP);
CATCH_SIGNAL(srv->loop, sigpipe_cb, PIPE);
create_tmp_addr(srv);
s = client_bind(srv);
ch = spawn(argv, s);
srv->child = ch;
ev_child_init(&srv->w_child, child_cb, ch, 0);
srv->w_child.data = srv;
ev_child_start(srv->loop, &srv->w_child);
fd_init(0);
ev_io_init(&srv->w_accept, accept_cb, 0, EV_READ);
srv->w_accept.data = srv;
ev_io_start(srv->loop, &srv->w_accept);
ev_loop(srv->loop, 0);
g_message("exit fcgi-debug");
server_stop(srv);
return 0;
}

70
src/fcgi-debug.h Normal file
View File

@ -0,0 +1,70 @@
#include <ev.h>
#include <glib.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#define UNUSED(x) ( (void)(x) )
struct addr;
typedef struct addr addr;
struct server;
typedef struct server server;
struct connection;
typedef struct connection connection;
struct addr {
struct sockaddr *saddr;
socklen_t addr_len;
};
struct server {
struct ev_loop *loop;
ev_signal
sig_w_INT,
sig_w_TERM,
sig_w_PIPE,
sig_w_HUP
;
ev_child w_child;
ev_io w_accept;
int child;
gchar *tmpfile_name, *sockfile_name;
addr client;
gboolean exiting, stopped_signals;
};
struct connection {
server *srv;
int fd_server, fd_client;
ev_io w_server, w_client;
gboolean client_connected;
GString *send_client_buf, *send_server_buf;
};
/* tools.c */
void move2fd(int srcfd, int dstfd);
void move2devnull(int fd);
void fd_init(int fd);
void ev_io_add_events(struct ev_loop *loop, ev_io *watcher, int events);
void ev_io_rem_events(struct ev_loop *loop, ev_io *watcher, int events);
void ev_io_set_events(struct ev_loop *loop, ev_io *watcher, int events);
/* connection.c */
void connection_new(server *srv, int fd_server);

51
src/tools.c Normal file
View File

@ -0,0 +1,51 @@
#include "fcgi-debug.h"
/* move a fd to another and close the old one */
void move2fd(int srcfd, int dstfd) {
if (srcfd != dstfd) {
close(dstfd);
dup2(srcfd, dstfd);
close(srcfd);
}
}
/* replace an fd with /dev/null */
void move2devnull(int fd) {
move2fd(open("/dev/null", O_RDWR), fd);
}
void fd_init(int fd) {
#ifdef _WIN32
int i = 1;
#endif
#ifdef FD_CLOEXEC
/* close fd on exec (cgi) */
fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
#ifdef O_NONBLOCK
fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
#elif defined _WIN32
ioctlsocket(fd, FIONBIO, &i);
#endif
}
void ev_io_add_events(struct ev_loop *loop, ev_io *watcher, int events) {
if ((watcher->events & events) == events) return;
ev_io_stop(loop, watcher);
ev_io_set(watcher, watcher->fd, watcher->events | events);
ev_io_start(loop, watcher);
}
void ev_io_rem_events(struct ev_loop *loop, ev_io *watcher, int events) {
if (0 == (watcher->events & events)) return;
ev_io_stop(loop, watcher);
ev_io_set(watcher, watcher->fd, watcher->events & ~events);
ev_io_start(loop, watcher);
}
void ev_io_set_events(struct ev_loop *loop, ev_io *watcher, int events) {
if (events == (watcher->events & (EV_READ | EV_WRITE))) return;
ev_io_stop(loop, watcher);
ev_io_set(watcher, watcher->fd, (watcher->events & ~(EV_READ | EV_WRITE)) | events);
ev_io_start(loop, watcher);
}