diff --git a/configure.in b/configure.in index 3c363cd..3c04fe3 100644 --- a/configure.in +++ b/configure.in @@ -10,6 +10,23 @@ if test "x$ac_cv_c_bigendian" = "xyes" ; then AC_DEFINE([MODMONO_BIGENDIAN],,[Compiling on a big-endian machine.]) fi +AC_PATH_PROG(PKG_CONFIG, pkg-config, no) +if test "x$PKG_CONFIG" = "xno"; then + AC_MSG_ERROR([You need to install pkg-config]) +fi + +PKG_PATH="" +AC_ARG_WITH(crosspkgdir, [ --with-crosspkgdir=/path/to/pkg-config/dir], + if test x$with_crosspkgdir = "x"; then + if test -s $PKG_CONFIG_PATH; then + PKG_PATH=$PKG_CONFIG_PATH + fi + else + PKG_PATH=$with_crosspkgdir + PKG_CONFIG_PATH=$PKG_PATH + export PKG_CONFIG_PATH + fi +) # Checks for header files. AC_HEADER_SYS_WAIT @@ -27,6 +44,10 @@ AC_HEADER_STDC AC_FUNC_SELECT_ARGTYPES AC_CHECK_FUNCS([memset mkdir unsetenv putenv setenv setrlimit select strcasecmp strerror strrchr dup2]) +GLIB_REQUIRED_VERSION=1.3.11 + +PKG_CHECK_MODULES(GLIB_DEPENDENCIES, glib-2.0 >= $GLIB_REQUIRED_VERSION) + # # --enable-debug # @@ -358,24 +379,6 @@ if test "x$enable_gcov" = "xyes" ; then CFLAGS="$CFLAGS -DGCOV -O0 -fprofile-arcs -ftest-coverage" fi -AC_PATH_PROG(PKG_CONFIG, pkg-config, no) -if test "x$PKG_CONFIG" = "xno"; then - AC_MSG_ERROR([You need to install pkg-config]) -fi - -PKG_PATH= -AC_ARG_WITH(crosspkgdir, [ --with-crosspkgdir=/path/to/pkg-config/dir], - if test x$with_crosspkgdir = "x"; then - if test -s $PKG_CONFIG_PATH; then - PKG_PATH=$PKG_CONFIG_PATH - fi - else - PKG_PATH=$with_crosspkgdir - PKG_CONFIG_PATH=$PKG_PATH - export PKG_CONFIG_PATH - fi -) - AM_CONDITIONAL(APACHE2, true) dnl Sources are recompiled if we change the target version diff --git a/src/Makefile.am b/src/Makefile.am index c61fcb2..f1ecef1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,10 +1,10 @@ CLEANFILES = .libs/libmod_mono.so .libs/libmod_mono_old.so *~ lib_LTLIBRARIES = mod_mono.la -mod_mono_la_SOURCES = mod_mono.c mod_mono.h -mod_mono_la_LDFLAGS = -module +mod_mono_la_SOURCES = mod_mono.c mod_mono.h mono-io-portability.c mono-io-portability.h +mod_mono_la_LDFLAGS = -module $(GLIB_DEPENDENCIES_LIBS) #/usr/sbin/apxs -c -I../include -I. -D HAVE_CONFIG_H mod_mono.c -mod_mono_la_CFLAGS = -Wall -DDFLT_MONO_CONFIG_DIR=\"$(DFLT_MONO_CONFIG_DIR)\" +mod_mono_la_CFLAGS = -Wall -DDFLT_MONO_CONFIG_DIR=\"$(DFLT_MONO_CONFIG_DIR)\" $(GLIB_DEPENDENCIES_CFLAGS) install: $(lib_LTLIBRARIES) $(mkinstalldirs) "$(DESTDIR)$(APXS_LIBEXECDIR)" diff --git a/src/mod_mono.c b/src/mod_mono.c index f7c3fbd..12ccf8c 100644 --- a/src/mod_mono.c +++ b/src/mod_mono.c @@ -40,6 +40,7 @@ #include #include "mod_mono.h" +#include "mono-io-portability.h" DEFINE_MODULE (mono_module); @@ -110,6 +111,7 @@ typedef struct xsp_data { char *max_memory; char *debug; char *env_vars; + char *iomap; char status; /* One of the FORK_* in the enum above. * Don't care if run_xsp is "false" */ char is_virtual; /* is the server virtual? */ @@ -125,6 +127,7 @@ typedef struct xsp_data { /* other settings */ unsigned char no_flush; + int portability_level; /* The dashboard */ apr_shm_t *dashboard_shm; @@ -563,6 +566,8 @@ add_xsp_server (apr_pool_t *pool, const char *alias, module_cfg *config, int is_ server->max_memory = NULL; server->debug = NULL; server->env_vars = NULL; + server->iomap = NULL; + server->portability_level = PORTABILITY_UNKNOWN; server->status = FORK_NONE; server->is_virtual = is_virtual; server->start_attempts = NULL; @@ -910,7 +915,7 @@ read_data_string (apr_pool_t *pool, apr_socket_t *sock, char **ptr, int32_t *siz } static int -send_entire_file (request_rec *r, const char *filename, int *result) +send_entire_file (request_rec *r, const char *filename, int *result, xsp_data *xsp) { #ifdef APR_LARGEFILE # define MODMONO_LARGE APR_LARGEFILE @@ -922,19 +927,23 @@ send_entire_file (request_rec *r, const char *filename, int *result) apr_finfo_t info; apr_size_t nbytes; const apr_int32_t flags = APR_READ | APR_SENDFILE_ENABLED | MODMONO_LARGE; + int retval = 0; + gchar *file_path = mono_portability_find_file (xsp->portability_level, filename, TRUE); - st = apr_file_open (&file, filename, flags, APR_OS_DEFAULT, r->pool); + st = apr_file_open (&file, file_path ? file_path : filename, flags, APR_OS_DEFAULT, r->pool); if (st != APR_SUCCESS) { - DEBUG_PRINT (2, "file_open FAILED"); + DEBUG_PRINT (2, "file_open FAILED (path: %s)", file_path ? file_path : filename); *result = HTTP_FORBIDDEN; - return -1; + retval = -1; + goto finish; } st = apr_file_info_get (&info, APR_FINFO_SIZE, file); if (st != APR_SUCCESS) { DEBUG_PRINT (2, "info_get FAILED"); *result = HTTP_FORBIDDEN; - return -1; + retval = -1; + goto finish; } st = ap_send_fd (file, r, 0, info.size, &nbytes); @@ -942,10 +951,15 @@ send_entire_file (request_rec *r, const char *filename, int *result) if (nbytes < 0) { DEBUG_PRINT (2, "SEND FAILED"); *result = HTTP_INTERNAL_SERVER_ERROR; - return -1; + retval = -1; + goto finish; } - return 0; + finish: + if (file_path) + g_free (file_path); + + return retval; } static int @@ -1169,7 +1183,7 @@ do_command (int command, apr_socket_t *sock, request_rec *r, int *result, xsp_da status = -1; break; } - status = send_entire_file (r, str, result); + status = send_entire_file (r, str, result, xsp); if (status == -1) error_message = "failed to send file (file data)"; break; @@ -1512,6 +1526,9 @@ fork_mod_mono_server (apr_pool_t *pool, xsp_data *config) set_environment_variables (pool, config->env_vars); + if (config->iomap && *config->iomap) + SETENV (pool, "MONO_IOMAP", config->iomap); + pid = fork (); if (pid > 0) { wait (&status); @@ -2129,6 +2146,11 @@ mono_execute_request (request_rec *r, char auto_app) } } + if (conf->portability_level > PORTABILITY_MAX) + conf->portability_level = PORTABILITY_UNKNOWN; + + mono_portability_helpers_init (&conf->portability_level, conf->iomap); + rv = -1; /* avoid a warning about uninitialized value */ retrying = connect_attempts; was_starting = 0; @@ -2847,6 +2869,10 @@ static const command_rec mono_cmds [] = { " Default value: Default: \"\"" ), + MAKE_CMD12 (MonoIOMAP, iomap, + "A string with format the same as the MONO_IOMAP variable (see mod_mono (1))." + " Default value: none"), + MAKE_CMD_ITERATE2 (AddMonoApplications, applications, "Appends an application." ), diff --git a/src/mono-io-portability.c b/src/mono-io-portability.c new file mode 100644 index 0000000..9abcb8a --- /dev/null +++ b/src/mono-io-portability.c @@ -0,0 +1,259 @@ +#ifdef HAVE_CONFIG_H +#include "mod_mono_config.h" +#endif + +#include +#include +#include +#include +#include "mono-io-portability.h" + +#include + +#define IS_PORTABILITY_NONE (portability_level & PORTABILITY_NONE) +#define IS_PORTABILITY_UNKNOWN (portability_level & PORTABILITY_UNKNOWN) +#define IS_PORTABILITY_DRIVE (portability_level & PORTABILITY_DRIVE) +#define IS_PORTABILITY_CASE (portability_level & PORTABILITY_CASE) +#define IS_PORTABILITY_SET (portability_level > 0) + +void mono_portability_helpers_init (int *portability_level, char *env) +{ + if (!portability_level || *portability_level != PORTABILITY_UNKNOWN || !env || !*env) + return; + + *portability_level = PORTABILITY_NONE; + + if (env != NULL) { + /* parse the environment setting and set up some vars + * here + */ + gchar **options = g_strsplit (env, ":", 0); + int i; + + if (options == NULL) { + /* This shouldn't happen */ + return; + } + + for (i = 0; options[i] != NULL; i++) { + if (!strncasecmp (options[i], "drive", 5)) { + *portability_level |= PORTABILITY_DRIVE; + } else if (!strncasecmp (options[i], "case", 4)) { + *portability_level |= PORTABILITY_CASE; + } else if (!strncasecmp (options[i], "all", 3)) { + *portability_level |= (PORTABILITY_DRIVE | + PORTABILITY_CASE); + } + } + } +} + +/* Returns newly allocated string, or NULL on failure */ +static gchar *find_in_dir (DIR *current, const gchar *name) +{ + struct dirent *entry; + + while((entry = readdir (current)) != NULL) { + if (!g_ascii_strcasecmp (name, entry->d_name)) { + char *ret; + + ret = g_strdup (entry->d_name); + closedir (current); + return ret; + } + } + + closedir (current); + + return(NULL); +} + +/* Returns newly-allocated string or NULL on failure */ +gchar *mono_portability_find_file (int portability_level, const gchar *pathname, gboolean last_exists) +{ + gchar *new_pathname, **components, **new_components; + int num_components = 0, component = 0; + DIR *scanning = NULL; + size_t len; + + if (IS_PORTABILITY_NONE) { + return(NULL); + } + + new_pathname = g_strdup (pathname); + + if (last_exists && + access (new_pathname, F_OK) == 0) { + return(new_pathname); + } + + /* First turn '\' into '/' and strip any drive letters */ + g_strdelimit (new_pathname, "\\", '/'); + + if (IS_PORTABILITY_DRIVE && + g_ascii_isalpha (new_pathname[0]) && + (new_pathname[1] == ':')) { + int len = strlen (new_pathname); + + g_memmove (new_pathname, new_pathname+2, len - 2); + new_pathname[len - 2] = '\0'; + + } + + len = strlen (new_pathname); + if (len > 1 && new_pathname [len - 1] == '/') { + new_pathname [len - 1] = 0; + } + + if (last_exists && + access (new_pathname, F_OK) == 0) { + return(new_pathname); + } + + /* OK, have to work harder. Take each path component in turn + * and do a case-insensitive directory scan for it + */ + + if (!(IS_PORTABILITY_CASE)) { + g_free (new_pathname); + return(NULL); + } + + components = g_strsplit (new_pathname, "/", 0); + if (components == NULL) { + /* This shouldn't happen */ + g_free (new_pathname); + return(NULL); + } + + while(components[num_components] != NULL) { + num_components++; + } + g_free (new_pathname); + + if (num_components == 0){ + return NULL; + } + + + new_components = (gchar **)g_new0 (gchar **, num_components + 1); + + if (num_components > 1) { + if (strcmp (components[0], "") == 0) { + /* first component blank, so start at / */ + scanning = opendir ("/"); + if (scanning == NULL) { + g_strfreev (new_components); + g_strfreev (components); + return(NULL); + } + + new_components[component++] = g_strdup (""); + } else { + DIR *current; + gchar *entry; + + current = opendir ("."); + if (current == NULL) { + g_strfreev (new_components); + g_strfreev (components); + return(NULL); + } + + entry = find_in_dir (current, components[0]); + if (entry == NULL) { + g_strfreev (new_components); + g_strfreev (components); + return(NULL); + } + + scanning = opendir (entry); + if (scanning == NULL) { + g_free (entry); + g_strfreev (new_components); + g_strfreev (components); + return(NULL); + } + + new_components[component++] = entry; + } + } else { + if (last_exists) { + if (strcmp (components[0], "") == 0) { + /* First and only component blank */ + new_components[component++] = g_strdup (""); + } else { + DIR *current; + gchar *entry; + + current = opendir ("."); + if (current == NULL) { + g_strfreev (new_components); + g_strfreev (components); + return(NULL); + } + + entry = find_in_dir (current, components[0]); + if (entry == NULL) { + g_strfreev (new_components); + g_strfreev (components); + return(NULL); + } + + new_components[component++] = entry; + } + } else { + new_components[component++] = g_strdup (components[0]); + } + } + + g_assert (component == 1); + + for(; component < num_components; component++) { + gchar *entry; + gchar *path_so_far; + + if (!last_exists && + component == num_components -1) { + entry = g_strdup (components[component]); + closedir (scanning); + } else { + entry = find_in_dir (scanning, components[component]); + if (entry == NULL) { + g_strfreev (new_components); + g_strfreev (components); + return(NULL); + } + } + + new_components[component] = entry; + + if (component < num_components -1) { + path_so_far = g_strjoinv ("/", new_components); + + scanning = opendir (path_so_far); + g_free (path_so_far); + if (scanning == NULL) { + g_strfreev (new_components); + g_strfreev (components); + return(NULL); + } + } + } + + g_strfreev (components); + + new_pathname = g_strjoinv ("/", new_components); + + g_strfreev (new_components); + + if ((last_exists && + access (new_pathname, F_OK) == 0) || + (!last_exists)) { + return(new_pathname); + } + + g_free (new_pathname); + return(NULL); +} + diff --git a/src/mono-io-portability.h b/src/mono-io-portability.h new file mode 100644 index 0000000..7174991 --- /dev/null +++ b/src/mono-io-portability.h @@ -0,0 +1,18 @@ +#ifndef __MONO_IO_PORTABILITY_H +#define __MONO_IO_PORTABILITY_H + +#include +#include "mod_mono.h" + +enum { + PORTABILITY_NONE = 0x00, + PORTABILITY_UNKNOWN = 0x01, + PORTABILITY_DRIVE = 0x02, + PORTABILITY_CASE = 0x04, + PORTABILITY_MAX = 0x07 +}; + +void mono_portability_helpers_init (int *portability_level, char *env); +gchar *mono_portability_find_file (int portability_level, const gchar *pathname, gboolean last_exists); + +#endif